From: support Date: Fri, 14 Oct 2022 08:32:19 +0000 (+0000) Subject: Built motion from commit 7a6bebd6.|2.6.19 X-Git-Url: http://repos.xcallymotion.com/base/%22javascript:void%28%27%7BtitleJs%7D%27%29//%22%7B%7Blicense.chat?a=commitdiff_plain;h=bd8930a8091e10c74ab8d56b5c338102de0a9394;p=motion2.git Built motion from commit 7a6bebd6.|2.6.19 --- diff --git a/apidoc/api_project.js b/apidoc/api_project.js index 4a9a8a6..be2e6b2 100644 --- a/apidoc/api_project.js +++ b/apidoc/api_project.js @@ -17,7 +17,7 @@ define({ "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2022-10-14T06:31:54.962Z", + "time": "2022-10-14T08:24:07.795Z", "url": "http://apidocjs.com", "version": "0.24.0" } diff --git a/apidoc/api_project.json b/apidoc/api_project.json index 7df2175..daa2e28 100644 --- a/apidoc/api_project.json +++ b/apidoc/api_project.json @@ -17,7 +17,7 @@ "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2022-10-14T06:31:54.962Z", + "time": "2022-10-14T08:24:07.795Z", "url": "http://apidocjs.com", "version": "0.24.0" } diff --git a/public/index.html b/public/index.html index 63012d9..6e1f055 100644 --- a/public/index.html +++ b/public/index.html @@ -66,7 +66,7 @@ - + diff --git a/public/scripts/app.f0c3e013.js b/public/scripts/app.6b84cfef.js similarity index 52% rename from public/scripts/app.f0c3e013.js rename to public/scripts/app.6b84cfef.js index dcd02f4..2161695 100644 --- a/public/scripts/app.f0c3e013.js +++ b/public/scripts/app.6b84cfef.js @@ -1 +1 @@ -!function(){"use strict";angular.module("app.auth",[])}(),function(){"use strict";angular.module("app.client-side-logging",[])}(),function(){"use strict";angular.module("app.core",["ngAnimate","ngAria","ngCookies","ngMessages","ngResource","ngSanitize","ngMaterial","pascalprecht.translate","ui.router","ui.router.state.events","gridster","ds.clock","angucomplete-alt"])}(),function(){"use strict";function e(e,n){e.state("app.errors_error-404",{url:"/errors/error-404",views:{"main@":{templateUrl:"app/core/layouts/content-only.html",controller:"MainController as vm"},"content@app.errors_error-404":{templateUrl:"app/errors/404/error-404.html",controller:"Error404Controller as vm"}},params:{status:404,statusText:"",data:{},config:{}},bodyClass:"error-404"}),n.addPart("app/errors/404")}e.$inject=["$stateProvider","$translatePartialLoaderProvider"],angular.module("app.errors.error-404",[]).config(e)}(),function(){"use strict";function e(e,n){e.state("app.errors_error-500",{url:"/errors/error-500",views:{"main@":{templateUrl:"app/core/layouts/content-only.html",controller:"MainController as vm"},"content@app.errors_error-500":{templateUrl:"app/errors/500/error-500.html",controller:"Error500Controller as vm"}},params:{status:500,statusText:"",data:{},config:{}},bodyClass:"error-500"}),n.addPart("app/errors/500")}e.$inject=["$stateProvider","$translatePartialLoaderProvider"],angular.module("app.errors.error-500",[]).config(e)}(),function(){"use strict";angular.module("app.errors",["app.errors.error-404","app.errors.error-500"])}(),function(){"use strict";function e(e){e.addPart("app/footer")}e.$inject=["$translatePartialLoaderProvider"],angular.module("app.footer",[]).config(e)}(),function(){"use strict";function e(e,n){e.state("app.forgot",{url:"/forgot",views:{"main@":{templateUrl:"app/core/layouts/content-only.html",controller:"MainController as vm"},"content@app.forgot":{templateUrl:"app/forgot/forgot.html",controller:"ForgotPasswordController as vm"}},bodyClass:"forgot"}),n.addPart("app/forgot")}e.$inject=["$stateProvider","$translatePartialLoaderProvider"],angular.module("app.forgot",[]).config(e)}(),function(){"use strict";function e(e){e.addPart("app/header")}e.$inject=["$translatePartialLoaderProvider"],angular.module("app.header",[]).config(e)}(),function(){"use strict";angular.module("motion",["angular-toasty","app.core","app.auth","app.navigation","app.toolbar","app.quick-panel","app.header","app.footer","app.realtime","app.dashboards","app.staff","app.contactmanager","app.voice","app.chat","app.mail","app.sms","app.openchannel","app.fax","app.tools","app.callysquare","app.analytics","app.integrations","app.settings","app.motiondialer","app.help","app.jscripty","app.marketplace","app.plugins","app.video","app.whatsapp","app.login","app.forgot","app.reset","app.errors","app.user-setting","app.client-side-logging"])}(),function(){"use strict";function e(e,n){e.state("app.login",{url:"/login?token&userId",views:{"main@":{templateUrl:"app/core/layouts/content-only.html",controller:"MainController as vm"},"content@app.login":{templateUrl:"app/login/login.html",controller:"LoginController as vm"}},bodyClass:"login"}),n.addPart("app/login")}e.$inject=["$stateProvider","$translatePartialLoaderProvider"],angular.module("app.login",[]).config(e)}(),function(){"use strict";function e(e,n,t,a){e.state("app.analytics",{abstract:!0,url:"/analytics"}).state("app.analytics.metrics",{url:"/metrics",views:{"content@app":{templateUrl:"app/main/apps/analytics/views/metrics/metrics.html",controller:"MetricsController as vm"}},resolve:{metrics:["apiResolver","Auth",function(e,n){return n.hasRole("admin")?e.resolve("analyticMetric@get",{fields:"createdAt,updatedAt,id,name,table,metric,description",sort:"-updatedAt",limit:10,offset:0}):e.resolve("userProfile@getResources",{id:n.getCurrentUser().userProfileId,section:"Metrics",fields:"createdAt,updatedAt,id,name,table,metric,description",sort:"-updatedAt",limit:10,offset:0})}],userProfile:["apiResolver","Auth",function(e,n){return n.hasRole("admin")?null:e.resolve("userProfile@get",{fields:"id,name,crudPermissions",id:n.getCurrentUser().userProfileId})}],userProfileSection:["apiResolver","Auth",function(e,n){return n.hasRole("admin")?null:e.resolve("userProfileSection@get",{fields:"id,name,enabled,includeAll,autoAssociation,crudPermissions",userProfileId:n.getCurrentUser().userProfileId,sectionId:1201})}]},authenticate:!0,permissionId:1201,bodyClass:"analytics"}).state("app.analytics.extractedReports",{url:"/extractedReports",views:{"content@app":{templateUrl:"app/main/apps/analytics/views/extractedReports/extractedReports.html",controller:"ExtractedReportsController as vm"}},resolve:{extractedReports:["apiResolver","Auth",function(e,n){return n.hasRole("admin")?e.resolve("analyticExtractedReport@get",{fields:"createdAt,updatedAt,id,name,basename,savename,type,startDate,endDate,status,output,reportId,reportType",sort:"-updatedAt",limit:10,offset:0}):e.resolve("userProfile@getResources",{id:n.getCurrentUser().userProfileId,section:"ExtractedReports",fields:"createdAt,updatedAt,id,name,basename,savename,type,startDate,endDate,status,output,reportId,reportType",sort:"-updatedAt",limit:10,offset:0})}],userProfile:["apiResolver","Auth",function(e,n){return n.hasRole("admin")?null:e.resolve("userProfile@get",{fields:"id,name,crudPermissions",id:n.getCurrentUser().userProfileId})}],userProfileSection:["apiResolver","Auth",function(e,n){return n.hasRole("admin")?null:e.resolve("userProfileSection@get",{fields:"id,name,enabled,includeAll,autoAssociation,crudPermissions",userProfileId:n.getCurrentUser().userProfileId,sectionId:1202})}]},authenticate:!0,permissionId:1202,bodyClass:"analytics"}),n.addPart("app/main/apps/analytics")}e.$inject=["$stateProvider","$translatePartialLoaderProvider","msApiProvider","msNavigationServiceProvider"],angular.module("app.analytics",["ngCsv","ngPassword","md.data.table","flow","mdColorPicker","ckeditor","ng-sortable","ngAria","ngAnimate","mdPickers","mwFormBuilder","mwFormViewer","mwFormUtils","ngclipboard","angular-cron-jobs","ngEmbed","angularMaterialFormBuilder","material.components.expansionPanels","chart.js","angular.filter","app.analytics.reports"]).config(e)}(),function(){"use strict";function e(e){e.state("app.analytics.reports",{url:"/reports",views:{"content@app":{templateUrl:"app/main/apps/analytics/views/reports/reports.html",controller:"ReportsController as vm"}},resolve:{treeReports:["apiResolver",function(e){return e.resolve("analyticTreeReport@get",{fields:"id,tree",limit:10,offset:0})}],userProfile:["apiResolver","Auth",function(e,n){return e.resolve("userProfile@get",{fields:"id,name,crudPermissions",id:n.getCurrentUser().userProfileId})}],userProfileSection:["apiResolver","Auth",function(e,n){return e.resolve("userProfileSection@get",{fields:"id,name,enabled,includeAll,autoAssociation,crudPermissions",userProfileId:n.getCurrentUser().userProfileId,sectionId:1203})}]},authenticate:!0,permissionId:1203,bodyClass:"analytics"}).state("app.analytics.reports.edit",{url:"/:id?tab",views:{"content@app":{templateUrl:"app/main/apps/analytics/views/reports/edit/view.html",controller:"ReportController as vm"}},resolve:{report:["apiResolver","$stateParams","$q","describeTable",function(e,n,a,i){var s;return e.resolve("analyticCustomReport@get",{fields:"createdAt,updatedAt,id,name,description,table,conditions,joins",id:n.id}).then(function(e){var n=[];if((s=e).joins)try{s.joins=JSON.parse(s.joins);for(var t=0;t=c.indexOf(m)))return;var i;var s=n,o=t;"string"==typeof n&&void 0===t&&(s=e,o=n);u[e].call(u,s,{data:p(o)})}).call(this,r,s,o)}};return{error:e("error"),warn:e("warn"),info:e("info"),debug:e("debug")}},getLoggingColors:function(){return{disabled:{color:"#616161"},debug:{color:"#283593"},info:{color:"#000000"},warning:{color:"#FBC02D"},error:{color:"#FF5252"}}},flush:a},c=["debug","info","warn","error"],m="debug",u=e.initLogger();function p(t){return Array.isArray(t)?t.map(p):null===t||"object"!=typeof t?t:_.isPlainObject(t)?t:t&&"function"==typeof t.toJSON?t.toJSON():t instanceof Error?Object.getOwnPropertyNames(t).reduce(function(e,n){return e[n]=p(t[n]),e},{}):JSON.parse(JSON.stringifyOnce(t))}function a(){return u.flush()}return t.$on("local:user-setting-updated",function(e,n){n.clientSideLoggingEnabled?t.$broadcast("client-side-logging:enabled"):(a(),t.$broadcast("client-side-logging:disabled")),m=n.clientSideLoggingLevel}),t.$on("local:user-logout",function(){m="debug"}),JSON.stringifyOnce=function(i,s,e){var o=[],r=[];return e||(e=2),JSON.stringify(i,function(e,t){if(2e3');a.append(n),o(function(){!function(e,n){var t=n.find("code"),a=e.split("\n"),i=(a=a.filter(function(e){return e.trim().length}))[0].match(/^\s*/)[0],s=new RegExp("^"+i);a=a.map(function(e){return e.replace(s,"").replace(/\s+$/,"")});var o=hljs.highlight(r.language||r.lang,a.join("\n"),!0);o.value=o.value.replace(/=""<\/span>/gi,"").replace("","").replace("",""),t.append(o.value).addClass("highlight")}(e,n)},34,!1)}})}}}}e.$inject=["$timeout","$q","$interpolate"],angular.module("app.core").directive("hljs",e)}(),function(){"use strict";function e(n,a,e,t){var o=this;function i(){var a=o.selectedField.options&&!_.isEmpty(o.selectedField.options.extraOperators),i=o.selectedField.options&&!_.isEmpty(o.selectedField.options.excludedOperators);o.availableOperators=_.filter(o.operators,function(n){var e=!n.applyTo||_.includes(n.applyTo,o.selectedField.type);n.isExtra=a&&_.some(o.selectedField.options.extraOperators,function(e){return e===n.type});var t=_.includes(n.excludeFrom,o.selectedField.type)||i&&_.includes(o.selectedField.options.excludedOperators,n.type);if((e||n.isExtra)&&!t)return n})}function s(n){return e[n.model][n.action](n.params).$promise.then(function(e){return e.rows}).catch(function(e){t.error({title:e.status?"API:"+e.status+" - "+e.statusText:n.model.toUpperCase()+"."+n.action.toUpperCase(),msg:e.data?JSON.stringify(e.data.message):e.toString()})})}function r(){!o.value||_.isArray(o.value)&&_.isEmpty(o.value)?o.isValidCondition=!1:o.selectedOperator?o.selectedOperator&&"$between"===o.selectedOperator.type?o.isValidCondition=!(!o.endRangeValue||!o.isValidRange):o.isValidCondition=!0:o.isValidCondition=!1}function l(){"number"===o.selectedField.type?_.isNumber(o.endRangeValue)?o.isValidRange=Number(o.endRangeValue)>Number(o.value):o.isValidRange=!0:"date"===o.selectedField.type&&(o.maxDateRange=moment(o.endRangeValue).subtract(1,"day"),o.isValidRange=!0),r()}function d(){var e=m();o.options.conditions.push(e),p()}function c(){var e=m(),n=_.findIndex(o.options.conditions,"style");o.options.conditions.splice(n,1,e),p(),o.pendingCondition.style=null,o.pendingCondition=void 0}function m(){return{name:o.selectedField.name,operator:n.instant("DASHBOARDS."+o.selectedOperator.translate),value:o.value,endValue:o.endRangeValue,displayValue:u(o.value),displayEndValue:u(o.endRangeValue),query:{column:o.selectedField.column,operator:o.selectedOperator.type,value:function(){var e,n=o.value,t=o.endRangeValue;switch(o.selectedField.type){case"date":var a=moment(n).utcOffset(0,!0).format("YYYY-MM-DD");if("$between"===o.selectedOperator.type){var i=moment(t).utcOffset(0,!0).format("YYYY-MM-DD");e=[a,i]}else e=a;break;case"number":e="$between"===o.selectedOperator.type?[n,t]:n;break;case"multiselect":var s=o.selectedField.options.field||"name";e=_.map(n,s);break;case"select":e=n.id;break;case"autocomplete":e=o.selectedOperator.isExtra?n:n.id;break;default:e=n}return e}()},disabled:!1}}function u(e){var n;if(e){switch(o.selectedField.type){case"date":n=moment(e).format("YYYY-MM-DD");break;case"multiselect":var t=_.map(e,function(e){return e.name});n="$eq"===o.selectedOperator.type?t.join(" or "):t.join(" and ");break;case"select":n=e.name;break;case"autocomplete":n=o.selectedOperator.isExtra?e:e.displayValue;break;default:n=e}return n}}function p(){o.value=null,o.endRangeValue=null,o.minDateRange=null,o.maxDateRange=null,o.isValidCondition=!1}o.operators=o.availableOperators=[{type:"$eq",symbol:"=",translate:"EQUALS",excludeFrom:["multiselect"]},{type:"$ne",symbol:"≠",translate:"IS_NOT_EQUAL",excludeFrom:["multiselect"]},{type:"$in",symbol:"∈",translate:"IS_AMONG",applyTo:["multiselect"]},{type:"$notIn",symbol:"∉",translate:"IS_NOT_AMONG",applyTo:["multiselect"]},{type:"$substring",symbol:"⊃",translate:"CONTAINS",applyTo:["text"]},{type:"$startsWith",symbol:"a..",translate:"STARTS_WITH",applyTo:["text"]},{type:"$endsWith",symbol:"..a",translate:"ENDS_WITH",applyTo:["text"]},{type:"$lt",symbol:"<",translate:"IS_LESS_THAN",applyTo:["date","number"]},{type:"$gt",symbol:">",translate:"IS_GREATER_THAN",applyTo:["date","number"]},{type:"$lte",symbol:"≤",translate:"IS_LESS_THAN_OR_EQUAL_TO",applyTo:["date","number"]},{type:"$gte",symbol:"≥",translate:"IS_GREATER_THAN_OR_EQUAL_TO",applyTo:["date","number"]},{type:"$between",symbol:"≬",translate:"IS_BETWEEN",applyTo:["date","number"]}],o.addCondition=d,o.editCondition=function(e){o.pendingCondition=e,o.pendingCondition.style="md-amber-200-bg",o.selectedField=_.find(o.options.fields,["name",o.pendingCondition.name]),i(),o.selectedOperator=_.find(o.availableOperators,["type",o.pendingCondition.query.operator]),"date"===o.selectedField.type&&"string"==typeof o.pendingCondition.value?(o.value=new Date(o.pendingCondition.value),o.minDateRange=moment(o.value).add(1,"day"),o.endRangeValue=o.pendingCondition.endValue?new Date(o.pendingCondition.endValue):void 0):(o.value=o.pendingCondition.value,o.endRangeValue=o.pendingCondition.endValue);o.isValidRange=!0,o.isValidCondition=!0},o.updateCondition=c,o.cancelUpdateCondition=function(){o.pendingCondition.style=null,o.pendingCondition=void 0},o.setConditionStatus=function(e){e.style=e.disabled?void 0:"chip-disabled",e.disabled=!e.disabled},o.removeCondition=function(){_.isEmpty(o.options.conditions)&&o.clearFilter()},o.getValues=function(){return o.selectedField.options.routes&&Array.isArray(o.selectedField.options.routes)?(e=angular.copy(o.selectedField.options.routes).map(function(e){return s(e)}),a.all(e).then(function(e){o.selectedField.values=_.flatten(e)})):o.selectedField.options.route?function(){var e=angular.copy(o.selectedField.options.route);if("autocomplete"===o.selectedField.type)for(var n=0;n',compile:function(e){return e.addClass("ms-card"),function(e,n){e.cardTemplateLoaded=function(){e.$emit("msCard::cardTemplateLoaded",n)}}}}})}(),function(){"use strict";angular.module("app.core").directive("msChipColor",function(){return{restrict:"A",link:function(e,i,n){n.$observe("msChipColor",function(e){var n=i.parent().parent();if(n.hasClass("md-background-bg md-hue-3")||n.addClass("md-background-bg md-hue-3"),_.isEmpty(e)){var t=n[0].className.split(" "),a=t.indexOf("md-hue-3");t.length=a+1,n[0].className=t.join(" ")}else n.addClass(e)})}}})}(),function(){"use strict";angular.module("app.core").directive("msClickToCall",function(){return{restrict:"E",scope:{type:"=",disabled:"=",target:"=",prefix:"=",license:"="},controller:["$scope","$rootScope","$http","$translate","$document","$mdDialog","Auth","toasty","api",function(t,n,a,i,s,o,e,r,l){function d(e){switch(t.currentUser.showWebBar){case 0:return m("http://127.0.0.1:"+(t.currentUser.phoneBarRemoteControlPort||"9888")+"/api/originate/"+c(e));case 2:return n.$broadcast("webrtc::call",{target:c(e)})}}function c(e){return _.isNil(e)?"":e.replace(/[^\w.+#*\-]+/g,"")}function m(e){return a.get(e).success(function(){r.success({title:"Successful call",msg:"Call properly handled!"})}).error(function(e){r.error({title:"PhoneBar API Error",msg:i.instant("CONTACTMANAGER.ERRORS.PHONEBAR_API")})})}t.currentUser=e.getCurrentUser(),t.privacy=t.currentUser.privacyEnabled,t.showMenu=function(){return t.target&&t.target.indexOf("@")<0&&t.target!=t.currentUser.internal&&t.target!=t.currentUser.name&&(0==t.currentUser.showWebBar&&t.currentUser.phoneBarRemoteControl||2==t.currentUser.showWebBar&&t.license.webrtc)&&!t.disabled},t.call=function(e){return 2!==t.currentUser.showWebBar?(n=e,l.user.getVoicePrefixes({id:t.currentUser.id}).$promise.then(function(e){if(e.count)return o.show({controller:"PrefixDialogController",controllerAs:"vm",templateUrl:"assets/ms-phonebar/prefix/dialog.html",parent:angular.element(s.body),clickOutsideToClose:!0,locals:{prefixes:e,required:t.currentUser.phoneBarPrefixRequired}})}).then(function(e){t.currentUser.phoneBarPrefixRequired?e&&-1!==e&&d(e+n):e&&-1!==e?d(e+n):e||d(n)})):d(e);var n},t.transfer=function(e){switch(t.currentUser.showWebBar){case 0:return m("http://127.0.0.1:"+(t.currentUser.phoneBarRemoteControlPort||"9888")+"/api/transfer?number="+c(e));case 2:n.$broadcast("webrtc::transfer",{target:c(e)})}}}],templateUrl:"app/core/directives/ms-click-to-call/ms-click-to-call.html"}})}(),function(){"use strict";n.$inject=["ClientSideLoggingService","$rootScope","$translate"];var e={bindings:{user:"<"},controller:n,controllerAs:"vm",templateUrl:"app/core/directives/ms-client-side-logging/ms-client-side-logging-icon.html"};function n(e,n,t){var a=this,i=e.getLoggingColors();function s(){a.tooltipHTML=a.user.userSetting.clientSideLoggingEnabled?[t.instant("STAFF.CLIENT_LOGS_ENABLED"),t.instant("STAFF.CLIENT_LOGS_LOGLEVEL",{logLevel:a.user.userSetting.clientSideLoggingLevel}),t.instant("STAFF.CLIENT_LOGS_ENABLED_SINCE",{enabledSince:moment(a.user.userSetting.clientSideLoggingEnabledSince).format("LLLL")})].join("
"):t.instant("STAFF.CLIENT_LOGS_DISABLED")}a.tooltipHTML=t.instant("STAFF.CLIENT_LOGS_DISABLED"),a.styleColor=i.disabled,a.styleIcon=a.user.userSetting&&a.user.userSetting.clientSideLoggingEnabled?"icon-checkbox-marked-circle":"icon-close",a.$onInit=function(){if(!a.user.userSetting)return;var e=a.user.userSetting.clientSideLoggingEnabled?a.user.userSetting.clientSideLoggingLevel:"disabled";a.styleColor=i[e],"info"===e&&(a.styleIcon="icon-checkbox-marked-circle-outline");return s()},a.$onDestroy=function(){o()};var o=n.$on("$translateChangeSuccess",s)}angular.module("app.core").component("msClientSideLoggingIcon",e)}(),function(){"use strict";n.$inject=["ClientSideLoggingService","UserSettingService","$q","$translate","$rootScope","$interval"];var e={bindings:{user:"<",onUserUpdate:"&"},controller:n,controllerAs:"vm",templateUrl:"app/core/directives/ms-client-side-logging/ms-client-side-logging.html"};function n(e,t,n,a,i,s){var o=this,r=e.getLoggingColors(),l=null;function d(){l&&s.cancel(l)}o.styleProperties=r.disabled,o.clientSideLoggingEnabledSince="",o.$onInit=function(){m().then(function(){d(),l=s(u,6e4)})},o.$onChanges=function(e){e&&n.resolve().then(m)},o.$onDestroy=function(){d(),c()},o.onEnabledSwitchChange=function(){var e=o.user.userSetting.clientSideLoggingEnabled,n=o.user.userSetting.clientSideLoggingLevel;return p(e,n)},o.onLogLevelSelectionChange=function(){var e=o.user.userSetting.clientSideLoggingEnabled,n=o.user.userSetting.clientSideLoggingLevel;p(e,n)},o.computeStyleForLoggingLevel=function(e){return r[e]};var c=i.$on("$translateChangeSuccess",u);function m(){o.availableClientSideLoggingLevels=Object.keys(r).filter(function(e){return"disabled"!==e});var e=o.user.userSetting&&o.user.userSetting.clientSideLoggingEnabled?o.user.userSetting.clientSideLoggingLevel:"disabled";return o.styleProperties=r[e],u()}function u(){if(o.user.userSetting)return o.user.userSetting.clientSideLoggingEnabled?void(o.clientSideLoggingEnabledSince=a.instant("STAFF.CLIENT_LOGS_ENABLED_SINCE",{enabledSince:moment(moment(o.user.userSetting.clientSideLoggingEnabledSince).format()).fromNow()})):o.clientSideLoggingEnabledSince=""}function p(e,n){return t.updateUserSettingById(o.user.userSetting.id,{clientSideLoggingEnabled:e,clientSideLoggingLevel:n}).then(function(){"function"==typeof o.onUserUpdate&&o.onUserUpdate()})}}angular.module("app.core").component("msClientSideLogging",e)}(),function(){"use strict";function e(i){return{require:"ngModel",priority:1,link:function(e,n,t,a){a.$formatters.push(i.formatter),a.$parsers.push(i.parser)}}}e.$inject=["msDatepickerFixConfig"],angular.module("app.core").provider("msDatepickerFixConfig",function(){var n={formatter:function(e){return e?""===e?e:new Date(e):""},parser:function(e){return e?moment(e).add(moment(e).utcOffset(),"m").toDate():""}};this.config=function(e){n=angular.extend({},n,e)},this.$get=function(){return n}}).directive("msDatepickerFix",e)}(),function(){"use strict";function e(s){return{restrict:"E",scope:{id:"=",model:"=",element:"=",title:"=",path:"="},replace:!0,link:function(n,e){var t=!1,a={};a.id=n.id,_.isNil(n.path)||(a.path=n.path);var i=document.createElement(n.element);i.setAttribute("preload","none"),i.setAttribute("controls","true"),"audio"===n.element&&i.setAttribute("style","width: 265px;"),"video"===n.element&&i.setAttribute("style","max-width: 500px;"),i.setAttribute("title",n.title),i.setAttribute("src"," "),i.onplay=function(e){t||(t=!0,e.preventDefault(),s[n.model].download(a).$promise.then(function(e){var n=[e.buffer],t=new Blob(n,{type:e.type});i.setAttribute("type",e.type),i.setAttribute("src",URL.createObjectURL(t)),i.play()}).catch(function(e){console.error(e)}))},e.append(i)}}}e.$inject=["api"],angular.module("app.core").directive("msDialogRecording",e)}(),function(){"use strict";function e(i){return{restrict:"A",link:function(t,a){a.bind("click",function(e){var n=i('
')(t);a.closest("md-dialog").prepend(n)})}}}e.$inject=["$compile"],angular.module("app.core").directive("msDialogSpinner",e)}(),function(){"use strict";function e(){var o=this;function a(e,n,t,a){var i=[];if(0<=t)i.push(e[t]),n.push(e[t]),e.splice(t,1);else{for(var s=0;s',link:function(t,e){var n=e.emojioneArea({pickerPosition:t.pickerPosition,search:t.search,recentEmojis:t.recentEmojis,placeholder:a.instant(t.placeholder||"Type a message"),attributes:{spellcheck:!0},events:{keyup:function(e,n){t.ngModel=this.getText(),t.onReply({event:n,body:this.getText()})},emojibtn_click:function(){t.ngModel=this.getText()}}});t.ngModel&&n[0].emojioneArea.setText(t.ngModel),t.internalControl=t.ctrlMethods||{},t.internalControl.setText=function(e){n[0].emojioneArea.setText(e)},t.internalControl.getText=function(){return n[0].emojioneArea.getText()},t.$watch("ngModel",function(e){e||n[0].emojioneArea.setText("")})}}}e.$inject=["$translate"],angular.module("app.core").directive("msEmojiArea",e)}(),function(){"use strict";angular.module("app.core").directive("msFontFamily",function(){return{restrict:"E",scope:{ngModel:"="},controller:["$scope",function(e){e.fonts=[{option:"Arial",value:"Arial,Helvetica,sans-serif"},{option:"Century Gothic",value:"Century Gothic,Futura,Didact Gothic,san-serif"},{option:"Calibri",value:"Calibri, Verdana, Geneva, sans-serif"},{option:"Comic Sans MS",value:"Comic Sans MS,cursive"},{option:"Courier New",value:"Courier New,Courier,monospace"},{option:"Georgia",value:"Georgia,serif"},{option:"Lucida Sans Unicode",value:"Lucida Sans Unicode,Lucida Grande,sans-serif"},{option:"Tahoma",value:"Tahoma,Geneva,sans-serif"},{option:"Times New Roman",value:"Times New Roman,Times,serif"},{option:"Trebuchet MS",value:"Trebuchet MS,Helvetica,sans-serif"},{option:"Verdana",value:"Verdana,Geneva,sans-serif"}]}],templateUrl:"app/core/directives/ms-font-family/ms-font-family.html"}})}(),function(){"use strict";angular.module("app.core").controller("MsFormWizardController",function(){var n=this;function e(){return n.forms.length}function t(){return 0===n.selectedIndex}function a(){return n.selectedIndex===e()-1}n.forms=[],n.selectedIndex=0,n.registerForm=function(e){n.forms.push(e)},n.previousStep=function(){if(t())return;n.selectedIndex--},n.nextStep=function(){if(a())return;n.selectedIndex++},n.firstStep=function(){n.selectedIndex=0},n.lastStep=function(){n.selectedIndex=e()-1},n.totalSteps=e,n.isFirstStep=t,n.isLastStep=a,n.currentStepInvalid=function(){return angular.isDefined(n.forms[n.selectedIndex])&&n.forms[n.selectedIndex].$invalid},n.previousStepInvalid=function(){return 0d.options.responsive.md?d.options.responsive.md:d.columnCount:l("sm")?d.columnCount=d.columnCount>d.options.responsive.sm?d.options.responsive.sm:d.columnCount:d.columnCount=d.options.responsive.xs;d.columnWidth=d.containerPos.width/d.columnCount}(),r.$broadcast("msMasonry:relayoutStarted"),d.items=d.container.find("ms-masonry-item");for(var e=Array.apply(null,new Array(d.columnCount)).map(function(){return 0}),n=0;n'),r=angular.element('
'),l=n.parent();function d(){s.addClass("ms-nav-folded"),g.$broadcast("msNav::forceCollapse"),n.scrollTop(0),l.append(o),o.on("mouseenter touchstart",function(e){c(e),i=!0})}function c(e){angular.isDefined(e)&&e.preventDefault(),s.addClass("ms-nav-folded-open"),g.$broadcast("msNav::expandMatchingToggles"),l.find(o).remove(),l.parent().append(r),r.on("mouseenter touchstart",function(e){m(e),i=!1})}function m(e){angular.isDefined(e)&&e.preventDefault(),g.$broadcast("msNav::forceCollapse"),n.scrollTop(0),s.removeClass("ms-nav-folded-open"),l.parent().find(r).remove(),l.append(o),o.on("mouseenter touchstart",function(e){c(e),i=!0})}function u(){s.removeClass("ms-nav-folded ms-nav-folded-open"),g.$broadcast("msNav::expandMatchingToggles"),n.off("mouseenter mouseleave")}v.setFoldable(e,n,a),a?d():u(),e.toggleFold=function(){(a=!a)?d():u()},e.openFolded=c,e.closeFolded=m,e.isNavFoldedOpen=function(){return i},e.$on("$destroy",function(){o.off("mouseenter touchstart"),r.off("mouseenter touchstart"),n.off("mouseenter mouseleave")})}}}function n(t,a,i){return{restrict:"E",scope:{},controller:"MsNavController",compile:function(e){return e.addClass("ms-nav"),function(e){t.$broadcast("msNav::expandMatchingToggles");var n=t.$on("$stateChangeSuccess",function(){t.$broadcast("msNav::expandMatchingToggles"),a.when("navigation").then(function(e){e.close(),i.isNavFoldedOpen()&&i.closeFolded()})});e.$on("$destroy",function(){n()})}}}}function t(m,u,p,g){return{restrict:"A",require:"^msNav",scope:!0,compile:function(e,n){return e.addClass("ms-nav-toggle"),angular.isUndefined(n.collapsed)&&(n.collapsed=!0),e.attr("collapsed",n.collapsed),function(a,i,e,t){var s={expanded:"expanded",expandAnimation:"expand-animation",collapseAnimation:"collapse-animation"},n=i.find("a"),o=[],r=/\(.*\)/g;function l(){return"true"===i.attr("collapsed")}function d(){var e=u.defer();if(!l())return e.reject({error:!0}),e.promise;i.attr("collapsed",!1);var n=angular.element(i.find("ms-nav-toggle-items")[0]);n.css({position:"absolute",visibility:"hidden",display:"block",height:"auto"});var t=n[0].offsetHeight;return n.css({position:"",visibility:"",display:"",height:""}),a.$evalAsync(function(){p.animate(n,{display:"block",height:"0px"},{height:t+"px"},s.expandAnimation).then(function(){n.addClass(s.expanded),n.css({height:""}),e.resolve({success:!0})})}),e.promise}function c(){var e=u.defer();if(l())return e.reject({error:!0}),e.promise;i.attr("collapsed",!0);var n=angular.element(i.find("ms-nav-toggle-items")[0]),t=n[0].offsetHeight;return a.$evalAsync(function(){p.animate(n,{height:t+"px"},{height:"0px"},s.collapseAnimation).then(function(){n.removeClass(s.expanded),n.css({display:"",height:""}),e.resolve({success:!0})})}),e.promise}angular.forEach(n,function(e){var n=angular.element(e).attr("ui-sref");angular.isUndefined(n)||(n=n.replace(r,""),o.push(n))}),t.setToggleItem(i,a),i.children(".ms-nav-button").on("click",function(){if(t.isDisabled())return;t.disable(),l()?(t.clearLockedItems(),a.$emit("msNav::pushToLockedList"),m.$broadcast("msNav::collapse"),d().then(function(){t.enable()})):a.$broadcast("msNav::forceCollapse")}),a.$on("$destroy",function(){i.children(".ms-nav-button").off("click")}),a.$on("msNav::collapse",function(){var e=t.getLockedItems(),n=!1;angular.forEach(e,function(e){angular.equals(e.scope,a)&&(n=!0)}),n||c().then(function(){t.enable()})}),a.$on("msNav::forceCollapse",function(){c().then(function(){t.enable()})}),a.$on("msNav::expandMatchingToggles",function(){var n=g.current.name,t=!1;angular.forEach(o,function(e){n===e&&(t=!0)}),t?d():c()}),a.$on("msNav::pushToLockedList",function(){t.setLockedItem(i,a)})}}}}e.$inject=["$document","$rootScope","msNavFoldService"],n.$inject=["$rootScope","$mdComponentRegistry","msNavFoldService"],t.$inject=["$rootScope","$q","$animate","$state"],angular.module("app.core").factory("msNavFoldService",function(){var t={};return{setFoldable:function(e,n){t={scope:e,element:n}},isNavFoldedOpen:function(){return t.scope.isNavFoldedOpen()},toggleFold:function(){t.scope.toggleFold()},openFolded:function(){t.scope.openFolded()},closeFolded:function(){t.scope.closeFolded()}}}).directive("msNavIsFolded",e).controller("MsNavController",function(){var e=this,n=!1,t=[],a=[];e.isDisabled=function(){return n},e.enable=function(){n=!1},e.disable=function(){n=!0},e.setToggleItem=function(e,n){t.push({element:e,scope:n})},e.getLockedItems=function(){return a},e.setLockedItem=function(e,n){a.push({element:e,scope:n})},e.clearLockedItems=function(){a=[]}}).directive("msNav",n).directive("msNavTitle",function(){return{restrict:"A",compile:function(e){return e.addClass("ms-nav-title"),function(){}}}}).directive("msNavButton",function(){return{restrict:"AE",compile:function(e){return e.addClass("ms-nav-button"),function(){}}}}).directive("msNavToggle",t)}(),function(){"use strict";function e(e,n){e.root?this.navigation=n.getNavigation(e.root):this.navigation=n.getNavigation(),this.toggleHorizontalMobileMenu=function(){angular.element("body").toggleClass("ms-navigation-horizontal-mobile-menu-active")},n.sort()}function n(c,m,u,p){return{restrict:"E",scope:{folded:"=",root:"@"},controller:"MsNavigationController as vm",templateUrl:"app/core/directives/ms-navigation/templates/vertical.html",transclude:!0,compile:function(e){return e.addClass("ms-navigation"),function(e,n){var t=angular.element("body"),a=angular.element('
'),i=angular.element('
'),s=u("navigation");function o(e){if(p.setFolded(e),e)c.$broadcast("msNavigation::collapse"),t.addClass("ms-navigation-folded"),r();else{var n=p.getActiveItem();n&&n.scope.$emit("msNavigation::stateMatched"),t.removeClass("ms-navigation-folded ms-navigation-folded-open"),i.remove()}}function r(){n.parent().append(a),m(function(){a.on("mouseenter touchstart",l)})}function l(e){e&&e.preventDefault(),p.setFoldedOpen(!0);var n=p.getActiveItem();n&&n.scope.$emit("msNavigation::stateMatched"),t.addClass("ms-navigation-folded-open"),a.remove(),t.find("#main").append(i),i.on("mouseenter touchstart",d)}function d(e){e&&e.preventDefault(),p.setFoldedOpen(!1),c.$broadcast("msNavigation::collapse"),t.removeClass("ms-navigation-folded-open"),i.remove(),r()}p.setNavigationScope(e),function(){null===p.getFolded()&&p.setFolded(e.folded);p.getFolded()&&(m(function(){c.$broadcast("msNavigation::collapse")}),t.addClass("ms-navigation-folded"),r())}(),e.$watch(function(){return s.isLockedOpen()},function(e,n){if(!angular.isUndefined(e)&&!angular.equals(e,n)&&p.getFolded())if(e)c.$broadcast("msNavigation::collapse");else{var t=p.getActiveItem();t&&t.scope.$emit("msNavigation::stateMatched")}}),e.$watch("folded",function(e,n){angular.isUndefined(e)||angular.equals(e,n)||o(e)}),e.toggleFolded=function(){o(!p.getFolded())},e.$on("$stateChangeStart",function(){s.close()}),e.$on("$destroy",function(){i.off("mouseenter touchstart"),a.off("mouseenter touchstart")})}}}}function t(t,e,a,i,n,s){var o=this;o.element=e,o.node=t.node,o.hasChildren=void 0,o.collapsed=void 0,o.collapsable=void 0,o.group=void 0,o.animateHeightClass="animate-height",o.toggleCollapsed=function(){o.collapsed?o.expand():o.collapse()},o.collapse=function(){var e=o.element.children("ul"),n=e[0].offsetHeight;t.$evalAsync(function(){o.collapsed=!0,o.element.addClass("collapsing"),i.animate(e,{display:"block",height:n+"px"},{height:"0px"},o.animateHeightClass).then(function(){e.css({display:"",height:""}),o.element.removeClass("collapsing")}),t.$broadcast("msNavigation::collapse")})},o.expand=function(){var e=o.element.children("ul");e.css({position:"absolute",visibility:"hidden",display:"block",height:"auto"});var n=e[0].offsetHeight;e.css({position:"",visibility:"",display:"",height:""}),t.$evalAsync(function(){o.collapsed=!1,o.element.addClass("expanding"),i.animate(e,{display:"block",height:"0px"},{height:n+"px"},o.animateHeightClass).then(function(){e.css({height:""}),o.element.removeClass("expanding")}),a.$broadcast("msNavigation::collapse",o.node._path)})},o.getClass=function(){return o.node.class},o.isHidden=function(){if(angular.isDefined(o.node.hidden)&&angular.isFunction(o.node.hidden))return o.node.hidden();return!1},function(){o.hasChildren=0 target, inbound, fullname",e,n,t),E.calls.unshift({target:e,fullname:t||e,inbound:n,time:moment().format("HH:mm")}),50 session",e._request),e.notification&&(e.notification.close(),e.notification=null),E.conf.microphoneId?E.sessionConf.mediaConstraints.audio={deviceId:E.conf.microphoneId}:E.sessionConf.mediaConstraints.audio=!0,e.answer(E.sessionConf),E.isJabraEnabled&&(jabra.offHook(),E.jabraSession=e)}function O(e,n){y.debug("terminate -> session",e._request),e.notification&&(e.notification.close(),e.notification=null);var t={};if(n?t.status_code=n:e.isEstablished()||(t.status_code=603),e.terminate(t),E.isJabraEnabled)if(0===E.sessions.length)e.localHold&&jabra.resume(),jabra.onHook(),E.jabraSession=null;else{var a=_.last(E.sessions);a.localHold?(jabra.onHook(),jabra.hold(),E.jabraSession=a):jabra.offHook()}}function C(t){return y.debug("getVoiceChannels -> session",t._request),h.rpc.getVoiceChannels().$promise.then(function(e){var n=_.find(e.rows,function(e){return e.sipcalllinkedid===t.call_id?e:e.sipcalluniqueid===t.call_id?e:null});n&&(t.monitor=n.monitor,t.uniqueid=n.uniqueid,t.monitors=n.monitors)})}function w(e){y.debug("onAddstream -> data",e),this.remotePlayer.srcObject=e.stream,this.remotePlayer.play()}function R(){E.canGoInConference=!1,E.isInConference=!1,E.currentconferenceSessions=[]}function x(e,n){switch(e){case"ringing":E.soundPlayer.muted=E.conf.ringingMute,E.conf.ringingId&&E.soundPlayer.setSinkId(E.conf.ringingId).then(function(){E.soundPlayer.volume=E.conf.ringingVolume}).then(function(){n&&S()}).catch(function(e){y.info(e.message,E.conf.ringingId)});break;case"speaker":E.remotePlayer.muted=E.conf.speakerMute,E.conf.speakerId&&E.remotePlayer.setSinkId(E.conf.speakerId).then(function(){E.remotePlayer.volume=E.conf.speakerVolume}).then(function(){n&&S()}).catch(function(e){y.info(e.message,E.conf.speakerId)});break;case"microphone":for(var t=0;t session, textContent, position, delay",e._request,n,t,a),function(){i.show(i.simple().textContent(n).position(t).hideDelay(a)),O(e)}}function k(n){return y.debug("findSessionBySessionId -> sessionId, vm_pb.sessions",n,E.sessions),_.find(E.sessions,function(e){return e.call_id==n})}function M(e,n){return y.debug("callCallback -> url, params",e,n),s({url:e,method:"GET",params:n})}E.currentUser=b.getCurrentUser(),E.direction="right",E.selectedMode="md-scale",E.target="",E.showDialpad=!1,E.soundPlayer=document.getElementById(E.conf.soundPlayerId),E.remotePlayer=document.getElementById(E.conf.remotePlayerId),E.soundPlayer.volume=1,E.sessionConf={mediaConstraints:{audio:!0,video:!1},pcConfig:{iceServers:[]}},E.sessions=[],E.calls=[],E.callbackQueue=[],E.canGoInConference=!1,E.isInConference=!1,E.currentConferenceSessions=[],E.ua=new JsSIP.UA({sockets:[new JsSIP.WebSocketInterface("wss://"+E.conf.host+":"+E.conf.wssPort+"/ws")],uri:new JsSIP.URI("sip",E.conf.name,E.conf.host,5060,null,null).toString(),authorization_user:E.conf.name,ha1:E.conf.ha1,realm:E.conf.realm,user_agent:E.conf.ua,session_timers_refresh_method:"invite",register_expires:E.conf.phoneBarExpires||60,register:!0}),E.conf=_.merge(c.webrtc,E.conf),E.initDeviceInProgress=!1,E.isJabraEnabled=!1,E.isJabraInitialized=!1,E.jabraSession=null,E.jabraClicked=!1,E.buttonClicked=!1,E.originateInProgress=!1,t.bind("keyup",function(e){switch(e.keyCode){case 27:g(function(){E.showDialpad=!1})}}),E.$onInit=function(){m(!1),navigator.mediaDevices.ondevicechange=function(){E.initDeviceInProgress||(E.initDeviceInProgress=!0,jabra&&E.isJabraInitialized?jabra.shutdown().then(function(){E.isJabraInitialized=!1,m(!(E.isJabraEnabled=!1))}).catch(function(e){y.error("Unable to shutdown Jabra library",e)}):m(!0))};var e=[h.network.get({type:"turn",nolimit:!0}).$promise,h.network.get({type:"stun",nolimit:!0}).$promise];o.all(e).then(function(e){var n=e[0].rows,t=e[1].rows,a=[],i=[];t.forEach(function(e){i.push("stun:"+e.value)}),a.push({urls:i}),n.forEach(function(e){var n={urls:"turn:"+e.value};e.username&&(n.username=e.username),e.password&&(n.credential=e.password),a.push(n)}),E.sessionConf.pcConfig={iceServers:a}}),this.ua.on("registered",function(){this.registered=!0}.bind(this)),this.ua.on("unregistered",function(){this.registered=!1}.bind(this)),this.ua.on("newRTCSession",function(e){y.debug("onNewRTCSession -> data",e);var n,t,a,i,s,o=e.session,r=!0,l="",d=0;switch(e.originator){case"local":o.outgoing=!0,o.name=e.request.ruri.user,o.user=e.request.ruri.user,T(o.user,!1),t=e.request.extraHeaders,a="X-callback-url",s=_.find(t,function(e){return _.startsWith(e,a)}),n=!!s&&_.trim(s.replace(a,"").replace(i||":","")),E.isJabraEnabled&&(E.jabraSession&&jabra.resume(),E.jabraSession=o,jabra.offHook());break;case"remote":var c=b.getCurrentUser();c.voicePause&&c.phoneBarDnd&&(r=!1);var m=e.request.getHeader("Call-Type")||"";if(c.ignorePauseForPreviewCalls&&"PREVIEW"===m&&(r=!0),E.originateInProgress&&(r=!1),r&&(o.incoming=!0,o.name=e.request.from.display_name,o.user=e.request.from.uri.user,l=e.request.getHeader("X-Answer-Mode")||"",d=e.request.getHeader("X-Answer-After")||0,function(n){y.debug("getNotification -> session",n._request);var e=n.name?n.name+" <"+n.user+">":n.user;E.currentUser.privacyEnabled&&(e=A.mask(e)),f.create("Incoming call from: ",e,null,function(){I(n)},function(){O(n)},E.conf.autoAnswer).then(function(e){n.notification=e}).catch(function(e){y.error("Error creating notification for incoming call",e)})}(e.session),T(e.session.user,!0),E.isJabraEnabled&&jabra.ring(),E.conf.autoAnswer&&g(function(){o.isInProgress()&&(o.autoAnswer=!0,I(o))},E.conf.autoAnswerDelay?1e3*E.conf.autoAnswerDelay:0),y.debug("xAnswerMode",l),y.debug("xAnswerAfterSec",d),"auto"==l.toLowerCase())){var u=0==d?500:1e3*d;y.debug("auto answer enabled after:"+u+" ms"),g(function(){o.isInProgress()&&(o.autoAnswer=!0,I(o))},u)}}r?(o.call_id=e.request.call_id,n&&M(E.callbackQueue[o.call_id]=n,{call_id:o.call_id}).then(function(){y.info('callbackurl:"'+n+'" called successfully')}).catch(function(e){y.error('fail callbackurl:"'+n+'" err:',e)}),o.connection&&(o.connection.onaddstream=w.bind(this)),o.iceCandidateTimeout=null,o.on("progress",function(e,n){y.debug("onProgress -> session, data",e._request,n),y.error("onProgress -> session, data",e._request,n);var t=!1;switch(n.originator){case"local":this.conf.ringingMute||(this.soundPlayer.setAttribute("src","assets/ms-phonebar/sounds/incoming-call.ogg"),t=!0);break;case"remote":this.putOtherCallsOnHold(e),this.soundPlayer.setAttribute("src","assets/ms-phonebar/sounds/outgoing-call.ogg"),t=!0}E.originateInProgress&&(E.originateInProgress=!1),t&&(this.soundPlayer.loop="loop",this.soundPlayer.play().catch(function(e){y.error(e.message)})),C(e).then(function(){var n=E.callbackQueue[e.call_id];n&&M(n,{uniqueid:e.uniqueid,call_id:e.call_id,number_called:e.user}).then(function(){y.info('callbackurl:"'+n+'" called successfully')}).catch(function(e){y.error('fail callbackurl:"'+n+'" err:',e)}).finally(function(){delete E.callbackQueue[e.call_id]})}).catch(function(e){y.error("Unable to retrieve voice channels",e)})}.bind(this,o)),o.on("confirmed",function(e,n){y.debug("onConfirmed -> session, data",e._request,n);var t=document.getElementById("div_session_"+e.id.substr(0,32));t&&$(t).scope().$broadcast("timer-start"),e.confirmed=!0,"remote"===n.originator&&e.connection&&(e.connection.onaddstream=w.bind(this),_.head(e.connection.getRemoteStreams())&&(this.remotePlayer.srcObject=_.head(e.connection.getRemoteStreams()),this.remotePlayer.play())),this.soundPlayer.pause(),this.soundPlayer.loop=null,this.putOtherCallsOnHold(e),e.autoAnswer&&(this.soundPlayer.setAttribute("src","assets/ms-phonebar/sounds/beep.ogg"),this.soundPlayer.play().catch(function(e){y.error("Unable to play autoAnswer notification",e)})),E.canGoInConference=2==E.sessions.length,C(e).catch(function(e){y.error("Unable to retrieve voice channels",e)})}.bind(this,o)),o.on("hold",function(e,n){y.debug("onHold -> session, data",e._request,n),"local"===n.originator&&(e.localHold=!0)}.bind(this,o)),o.on("unhold",function(e,n){y.debug("onUnhold -> session, data",e._request,n),"local"===n.originator&&(e.localHold=!1)}.bind(this,o)),o.on("ended",function(e,n){if(y.debug("onEnded -> session, data",e._request,n),E.isInConference&&_.includes(E.currentConferenceSessions,e.id)&&(R(),E.buttonClicked=!0),_.remove(this.sessions,{id:e.id}),2!==E.sessions.length&&(E.canGoInConference=!1),E.isJabraEnabled)if(0===E.sessions.length)e.localHold&&jabra.resume(),jabra.onHook(),E.jabraSession=null;else{var t=_.last(E.sessions);t.localHold?(jabra.onHook(),jabra.hold(),E.jabraSession=t):jabra.offHook()}0===E.sessions.length&&E.currentUser.voicePause&&h.user.pause({id:E.currentUser.id,type:_.startsWith(E.currentUser.pauseType,"#")?E.currentUser.pauseType.substring(1):E.currentUser.pauseType}).$promise.catch(function(e){v.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:PAUSEUSER",msg:e.status?JSON.stringify(e.data):e.toString()})}),E.conf.microphoneMute=!1,E.initDevice("microphone",!0)}.bind(this,o)),o.on("failed",function(e,n){if(y.debug("onFailed -> session, data",e._request,n),this.soundPlayer.pause(),this.soundPlayer.loop=null,e.notification&&e.notification.close(),E.isInConference&&_.includes(E.currentConferenceSessions,e.id)&&R(),E.originateInProgress&&(E.originateInProgress=!1),_.remove(this.sessions,{id:e.id}),2!==E.sessions.length&&(E.canGoInConference=!1),E.isJabraEnabled)if(0===E.sessions.length)e.localHold&&jabra.resume(),jabra.onHook(),E.jabraSession=null;else{var t=_.last(E.sessions);t.localHold?(jabra.onHook(),jabra.hold(),E.jabraSession=t):jabra.offHook()}}.bind(this,o)),o.on("icecandidate",function(e,n){y.debug("onIcecandidate -> session, data",e._request,n),null!==e.iceCandidateTimeout&&clearTimeout(e.iceCandidateTimeout),e.iceCandidateTimeout=setTimeout(n.ready,1e3)}.bind(this,o)),E.sessions.push(o),g(function(){p.$apply(),y.debug("onNewRTCSession -> vm_pb.sessions",E.sessions)})):O(o,486)}.bind(this)),r.on("webbar:originate",function(e){y.debug("originateHook -> payload",e);var n=e.callNumber,t=e.callerId,a=e.callbackUrl;E.conf.microphoneId?E.sessionConf.mediaConstraints.audio={deviceId:E.conf.microphoneId}:E.sessionConf.mediaConstraints.audio=!0;var i=n.replace(/ /g,"");if(i=(i=i.replace(/\(/g,"")).replace(/\)/g,"")){var s=angular.copy(E.sessionConf);s.extraHeaders=[],t&&s.extraHeaders.push("X-CID: "+t),a&&s.extraHeaders.push("X-callback-url: "+a),E.originateInProgress=!0,E.ua.call(i,s)}}.bind(this)),r.on("webbar:hangup",function(e){y.debug("hangupHook -> payload",e);var n=E.sessions;if(e.sessionId&&((n=[]).push(k(e.sessionId)),0==n.length))y.warn("call with session Id "+e.sessionId+" not found");else for(var t=0;t sessionToHangup",a),E.terminate(a)}}.bind(this)),r.on("webbar:answer",function(e){var n;y.debug("answerHook -> payload",e),n=e.sessionId?k(e.sessionId):_.find(E.sessions,function(e){return e.incoming&&!e.confirmed}),e.sessionId&&!n&&y.warn("call with session Id "+e.sessionId+" not found"),n&&E.answer(n)}.bind(this)),r.on("webbar:hold",function(e){y.debug("holdHook -> payload",e);var n=E.sessions;if(e.sessionId&&((n=[]).push(k(e.sessionId)),0==n.length))y.warn("call with session Id "+e.sessionId+" not found");else for(var t=0;t payload",e);var n=E.sessions;if(e.sessionId&&((n=[]).push(k(e.sessionId)),0==n.length))y.warn("call with session Id "+e.sessionId+" not found");else for(var t=0;t payload",e),e.sessionId)if(e.transferNumber){var n=k(e.sessionId);n?n.refer(e.transferNumber,{eventHandlers:{requestSucceeded:L(n,"requestSucceeded","top right",3e3),requestFailed:L(n,"requestFailed","top right",3e3)}}):y.warn("call with session Id "+e.sessionId+" not found")}else y.error("transferNumber required");else y.error("sessionId required")}.bind(this)),r.on("webbar:stopmonitors",function(e){if(e.agentId===E.currentUser.id){var n=_.find(E.sessions,["uniqueid",e.uniqueid]);if(!n)return;n.monitors.forEach(function(e){e.status="pause"}),n.monitor=!1}}.bind(this))}.bind(this),E.type=function(e,n){var t=e;switch(E.target||(E.target=""),n&&(E.target+=e,p.$broadcast("angucomplete-alt:changeInput","ms-target-wrap",E.target)),e){case"*":t="asterisk";break;case"#":t="pound"}E.conf.enableDtmfTone&&(E.soundPlayer.setAttribute("src","assets/ms-phonebar/sounds/dialpad/"+t+".ogg"),E.soundPlayer.play().catch(function(e){y.error(e.message)}));for(var a=0;a session",e._request),e.hold({useUpdate:!1}),E.isJabraEnabled&&(jabra.hold(),jabra.onHook())},E.refer=function(n){y.debug("refer -> session",n._request);var e=a.prompt().title("Transfer").textContent("Type the target").placeholder("Target").ariaLabel("Target").ok("Transfer").cancel("Cancel");a.show(e).then(function(e){return D(e.replace(/ /g,"").replace(/\(/g,"").replace(/\)/g,""))}).then(function(e){n.refer(e,{eventHandlers:{requestSucceeded:L(n,"requestSucceeded","top right",3e3),requestFailed:L(n,"requestFailed","top right",3e3)}})})},E.record=function(e){y.debug("record -> session",e._request),a.show({controller:"RecordDialogController",controllerAs:"vm",templateUrl:"assets/ms-phonebar/record/dialog.html",parent:angular.element(t.body),clickOutsideToClose:!0,locals:{session:e,sessions:E.sessions}})},E.unhold=function(e){y.debug("unhold -> session",e._request),e.unhold({useUpdate:!1}),E.putOtherCallsOnHold(e),E.isJabraEnabled&&(jabra.offHook(),jabra.resume())},E.answer=I,E.terminate=O,E.selectSession=function(e){y.debug("selectSession -> session",e._request),E.unhold(e)},E.typeWrapper=function(e){switch(e.key.toLowerCase()){case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":case"*":case"#":E.type(e.key);break;case"enter":E.call()}},E.toggleDialpad=function(){E.showDialpad=!E.showDialpad},E.referAttended=function(e){y.debug("referAttended -> session",e._request),a.show({controller:"ReferAttendedDialogController",controllerAs:"vm",templateUrl:"assets/ms-phonebar/referAttended/dialog.html",parent:angular.element(t.body),clickOutsideToClose:!0,locals:{session:e,sessions:_.reject(E.sessions,{id:e.id}),isJabraEnabled:E.isJabraEnabled}})},E.closeDialpad=function(){E.showDialpad=!1},E.initDevice=x,E.putOtherCallsOnHold=function(e){if(y.debug("putOtherCallsOnHold -> session",e._request),1 evt, data",e,n),n.target&&(E.target=n.target,E.call())}),p.$on("webrtc::transfer",function(e,n){if(y.debug("onRemoteTransfer -> evt, data",e,n),n.target)for(var t=0;t session, textContent, position, delay",e,n,t,a),function(){s.show(s.simple().textContent(n).position(t).hideDelay(a)),e.terminate(),jabra&&i&&jabra.onHook(),l()}}function l(e){n.hide(e)}o.title="Refer Attended",o.sessions=a,o.session=i,o.isJabraEnabled=e,o.sessionTarget=null,a.length&&(o.target=a[0].user),o.closeDialog=l,o.transfer=function(){var e,n,t=_.find(a,function(e){if(e.user===o.target)return e});n=t.outgoing?(e=t,o.session):(e=o.session,t);n.refer(e.user,{replaces:e,eventHandlers:{requestSucceeded:r(i,"requestSucceeded","top right",3e3,o.isJabraEnabled),requestFailed:r(i,"requestFailed","top right",3e3,o.isJabraEnabled)}})}}e.$inject=["$mdDialog","$mdToast","sessions","session","isJabraEnabled"],angular.module("app.core").controller("ReferAttendedDialogController",e)}(),function(){"use strict";function e(e,s,n,t,a,i){var o=this;function r(e,n,t){var a;if(s[t]){if(a=_.find(e,function(e){return e.deviceId===s[t]}))return a.deviceId;var i=_.findIndex(e,function(e){return e.kind===n});if(0<=i)return e[i].deviceId}else(a=_.find(e,function(e){return e.kind===n}))&&(s[t]=a.deviceId);return s[t]||null}o.currentUser=a.getCurrentUser(),o.messengerSoundNotification=o.currentUser.messengerSoundNotification,_.remove(n,function(e){return"audioinput"===e.kind&&("default"===e.deviceId||"communications"===e.deviceId)||"audiooutput"===e.kind&&("default"===e.deviceId||"communications"===e.deviceId)}),s.ringingId=r(n,"audiooutput","ringingId"),s.speakerId=r(n,"audiooutput","speakerId"),s.microphoneId=r(n,"audioinput","microphoneId"),s.ringingVolume=s.ringingVolume||.5,s.speakerVolume=s.speakerVolume||.5,s.microphoneVolume=s.microphoneVolume||1,s.ringingMute=s.ringingMute||!1,s.speakerMute=s.speakerMute||!1,s.microphoneMute=s.microphoneMute||!1,o.conf=angular.copy(s),o.devices=n,o.activeSessions=t,o.saveSettings=function(){i.user.messengerSoundNotification({id:o.currentUser.id,enabled:o.messengerSoundNotification}).$promise.then(function(){a.setMessengerSoundNotification(o.messengerSoundNotification),e.hide(o.conf)})},o.closeDialog=function(){e.hide()},o.isCompatibleBrowser=function(){return"chrome"===o.conf.browserName.toLowerCase()||"opera"===o.conf.browserName.toLowerCase()||"safari"===o.conf.browserName.toLowerCase()}}e.$inject=["$mdDialog","conf","devices","activeSessions","Auth","api"],angular.module("app.toolbar").controller("SettingsController",e)}(),function(){"use strict";function e(e,t,a){var i=this;function s(e,n,t){r(e).then(function(){n&&!1!==t&&i.search()})}function o(e){var n=e.key;i.pickerModels[n].dateStart?!0===e.useFromToKeys?i.query[n]={from:moment(i.pickerModels[n].dateStart).utcOffset(0,!0).format(),to:moment(i.pickerModels[n].dateEnd).utcOffset(0,!0).add(23,"hours").add(59,"minutes").add(59,"seconds").format()}:i.query[n]={$gte:moment(i.pickerModels[n].dateStart).utcOffset(0,!0).format(),$lte:moment(i.pickerModels[n].dateEnd).utcOffset(0,!0).add(23,"hours").add(59,"minutes").add(59,"seconds").format()}:i.query[n]=void 0,i.search()}function r(e){var n=e.key,t={param:i.query[n],resources:e.options,placeholder:e.placeholder,ngValue:e.ngValue};return a.setPlaceholder(t).then(function(e){i.placeholders[n]=e})}i.pickerModels={},i.placeholders={},i.localizationMap=t.localizationMap,i.onMultiselectInit=function(e){r(e)},i.onDateRangeInit=function(e){var n=e.key;i.query[n]?i.pickerModels[n]=t.setSelectedDate(i.query[n]):i.pickerModels[n]={dateStart:null}},i.onMultiselectSelection=s,i.onDateRangeSelection=o,i.clearDate=function(e){var n=e.key;i.pickerModels[n]={dateStart:null},delete i.query[n],i.search()},i.clearSelection=function(e){var n=e.key,t=e.ngValue||"id";i.query[n]=!1===e.clearAll?[_.head(e.options)[t]]:[],r(e).then(function(){i.search()})},i.selectAll=function(e){var n=e.key,t=e.ngValue||"id";i.query[n]=_.map(e.options,t),r(e).then(function(){i.search()})},e.$on("ms-quick-filter:update",function(e,t){t.filters.forEach(function(e){var n=_.find(i.filters,["name",e]);if(n)switch(n.type){case"date":o(n);break;case"multiselect":s(n,!0,t.update);break;case"select":i.search()}})})}e.$inject=["$scope","dateRangeManager","quickFilterManager"],angular.module("app.core").directive("msQuickFilter",function(){return{restrict:"E",scope:{query:"=",filters:"=",search:"&"},controller:e,controllerAs:"vm",bindToController:!0,templateUrl:"app/core/directives/ms-quick-filter/ms-quick-filter.html"}})}(),function(){"use strict";angular.module("app.core").directive("msRandomClass",function(){return{restrict:"A",scope:{msRandomClass:"="},link:function(e,n){var t=e.msRandomClass[Math.floor(Math.random()*e.msRandomClass.length)];n.addClass(t)}}})}(),function(){"use strict";function e(i){return{restrict:"E",scope:{id:"=",model:"=",download:"="},replace:!0,link:function(n,e){var t=!1,a=document.createElement("audio");n.download||a.setAttribute("controlsList","nodownload"),a.setAttribute("preload","none"),a.setAttribute("controls",""),a.setAttribute("style","width: 265px;"),a.setAttribute("src"," "),a.onplay=function(e){t||(t=!0,e.preventDefault(),i[n.model||"voiceRecording"].download({id:n.id}).$promise.then(function(e){var n=[e.buffer],t=new Blob(n,{type:e.type});a.setAttribute("type",e.type),a.setAttribute("src",URL.createObjectURL(t)),a.play()}).catch(function(e){console.error(e)}))},e.append(a)}}}e.$inject=["api"],angular.module("app.core").directive("msRecording",e)}(),function(){"use strict";angular.module("app.core").directive("msResponsiveTable",function(){return{restrict:"A",link:function(e,n){var t=angular.element('
');n.after(t),t.append(n)}}})}(),function(){"use strict";function e(n,i,s,e){var o=this;function t(){angular.isArray(o.onSearch)?a(o.onSearch):o.onResultClick?(o.resultsLoading=!0,n.$parent.$eval("vm.search(query)",{query:o.query.filter}).then(function(e){a(e)}).catch(function(){a([])}).finally(function(){o.resultsLoading=!1})):o.onSearch()}function a(e){o.expanded&&(void 0===e||angular.isArray(e)||null===e)&&(o.selectedResultIndex=0,o.results=e)}function r(){o.expanded=!0,o.displayOn=!0,n.expand()}function l(e){!1!==e&&(o.query.filter=void 0,o.onResultClick?a(null):t()),o.expanded=!1,n.collapse()}function d(e){o.onResultClick&&o.onResultClick({item:e}),l()}function c(){var e=i.find(".ms-search-bar-results"),n=angular.element(e.find(".result")[o.selectedResultIndex]);if(e&&n){var t=n.position().top-8,a=n.position().top+n.outerHeight()+8;o.ignoreMouseEvents=!0,s.cancel(o.mouseEventIgnoreTimeout),o.mouseEventIgnoreTimeout=s(function(){o.ignoreMouseEvents=!1},250),e.scrollTop()>t&&e.scrollTop(t),a>e.height()+e.scrollTop()&&e.scrollTop(a-e.height())}}o.queryOptions={debounce:o.debounce||0},o.searchOnEnterKey=e.get().searchOnEnterKey||!1,o.resultsLoading=!1,o.results=null,o.selectedResultIndex=0,o.ignoreMouseEvents=!1,o.expandBar=r,o.collapseBar=l,o.blurCollapse=function(){if(!o.collapseOnBlur)return;l(!0)},o.onKeyDown=function(e){var n=e.keyCode;-1<[27,38,40].indexOf(n)&&e.preventDefault();switch(n){case 13:if(!o.onResultClick)return t();if(!o.results)return;d(o.results[o.selectedResultIndex]);break;case 27:l(!0);break;case 38:0<=o.selectedResultIndex-1&&(o.selectedResultIndex--,c());break;case 40:if(!o.results)return;o.selectedResultIndex+1e.length)return}else r();t()}})}function n(a){return{restrict:"E",scope:{query:"=?",debounce:"=?",direction:"@",iconColor:"@",onSearch:"&",onResultClick:"&?",onExpand:"&?",onCollapse:"&?",collapseOnBlur:"=d.shortcuts.length&&(d.selectedResultIndex=d.shortcuts.length-1)));d.saveShortcuts()},d.handleResultClick=function(e){e.hasShortcut?d.removeShortcut(e):d.addShortcut(e)},d.absorbEvent=function(e){e.preventDefault()},d.handleKeydown=function(e){var n=e.keyCode;-1<[38,40].indexOf(n)&&e.preventDefault();switch(n){case 13:d.handleResultClick(d.results[d.selectedResultIndex]);break;case 38:0<=d.selectedResultIndex-1&&(d.selectedResultIndex--,d.ensureSelectedResultIsVisible());break;case 40:d.selectedResultIndex+1t&&e.scrollTop(t),a>e.height()+e.scrollTop()&&e.scrollTop(a-e.height())}},d.toggleMobileBar=function(){d.mobileBarActive=!d.mobileBarActive},d.loadShortcuts().then(function(e){d.shortcuts=e,0i.steps.length)}i.mainForm=void 0,i.orientation="horizontal",i.steps=[],i.currentStep=void 0,i.currentStepNumber=1,i.setOrientation=function(e){i.orientation=e||"horizontal"},i.registerMainForm=function(e){i.mainForm=e},i.registerStep=function(e,n,t){var a={element:e,scope:n,form:t,stepNumber:n.step||i.steps.length+1,stepTitle:n.stepTitle,stepTitleTranslate:n.stepTitleTranslate};return i.steps.push(a),i.steps.sort(function(e,n){return e.stepNumber-n.stepNumber}),a},i.setupSteps=function(){i.setCurrentStep(i.currentStepNumber)},i.resetForm=function(){e(function(){for(var e=0;ee.scrollWidth&&0==e.scrollLeft&&0==n.scrollLeft?"right":n.scrollWidth>e.scrollWidth&&n.scrollLeft>e.scrollLeft&&e.scrollWidth+n.scrollLeft>=n.scrollWidth?"left":n.scrollWidth>e.scrollWidth&&n.scrollLeft>e.scrollLeft&&e.scrollWidth+n.scrollLeftt.position().top+i&&(m(function(){o=!0}),r.off("scroll",c))}}}}}e.$inject=["$timeout","$q"],angular.module("app.core").controller("MsTimelineController",function(){var n=this;n.scrollEl=void 0,n.setScrollEl=function(e){n.scrollEl=e},n.getScrollEl=function(){return n.scrollEl}}).directive("msTimeline",function(){return{scope:{msTimeline:"=?",loadMore:"&?msTimelineLoadMore"},controller:"MsTimelineController",compile:function(e){return e.addClass("ms-timeline"),function(e,n,t,a){var i=angular.element('
');n.append(i);var s={scrollEl:"#content"};s=angular.extend(s,e.msTimeline,{});var o=angular.element(s.scrollEl);a.setScrollEl(o);var r=144;function l(){o.scrollTop()+o.height()+r>i.position().top&&(i.addClass("show"),c(),e.loadMore&&e.loadMore().then(function(){i.removeClass("show"),d()},function(){i.remove()}))}function d(){o.on("scroll",l)}function c(){o.off("scroll",l)}d(),e.$on("$destroy",function(){c()})}}}}).directive("msTimelineItem",e)}(),function(){"use strict";function e(e,n,t,a,i){var r=this;function s(){!function(){r.millis=l().diff(moment(r.startingTime));var e=moment.duration(r.millis),n=e.seconds(),t=e.minutes(),a=e.hours(),i=e.days(),s=e.months(),o=e.years();r.seconds=n<10?"0"+n:n,r.minutes=t<10?"0"+t:t,r.hours=a<10?"0"+a:a,r.days=i<10?"0"+i:i,r.months=s<10?"0"+s:s,r.years=o<10?"0"+o:o,r.timer=0{{ vm.timer }}
",compile:function(){return{pre:function(e,n,t){e.interval=t.interval||1e3}}},controller:e,controllerAs:"vm",bindToController:!0}})}(),function(){"use strict";angular.module("app.core").directive("msTimezone",function(){return{restrict:"E",scope:{ngModel:"=",ngChange:"&"},controller:["$scope",function(e){e.timezone=[{name:"Europe/Andorra",utcOffset:60,offsetStr:"+01:00",countries:["AD"]},{name:"Asia/Dubai",utcOffset:240,offsetStr:"+04:00",countries:["AE","OM"]},{name:"Asia/Kabul",utcOffset:270,offsetStr:"+04:30",countries:["AF"]},{name:"Europe/Tirane",utcOffset:60,offsetStr:"+01:00",countries:["AL"]},{name:"Asia/Yerevan",utcOffset:240,offsetStr:"+04:00",countries:["AM"]},{name:"Antarctica/Rothera",utcOffset:-180,offsetStr:"-03:00",countries:["AQ"]},{name:"Antarctica/Palmer",utcOffset:-180,offsetStr:"-03:00",countries:["AQ"]},{name:"Antarctica/Mawson",utcOffset:300,offsetStr:"+05:00",countries:["AQ"]},{name:"Antarctica/Davis",utcOffset:420,offsetStr:"+07:00",countries:["AQ"]},{name:"Antarctica/Casey",utcOffset:480,offsetStr:"+08:00",countries:["AQ"]},{name:"Antarctica/Vostok",utcOffset:360,offsetStr:"+06:00",countries:["AQ"]},{name:"Antarctica/DumontDUrville",utcOffset:600,offsetStr:"+10:00",countries:["AQ"]},{name:"Antarctica/Syowa",utcOffset:180,offsetStr:"+03:00",countries:["AQ"]},{name:"Antarctica/Troll",utcOffset:0,offsetStr:"+00:00",countries:["AQ"]},{name:"America/Argentina/Buenos_Aires",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Cordoba",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Salta",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Jujuy",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Tucuman",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Catamarca",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/La_Rioja",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/San_Juan",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Mendoza",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/San_Luis",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Rio_Gallegos",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"America/Argentina/Ushuaia",utcOffset:-180,offsetStr:"-03:00",countries:["AR"]},{name:"Pacific/Pago_Pago",utcOffset:-660,offsetStr:"-11:00",countries:["AS","UM"]},{name:"Europe/Vienna",utcOffset:60,offsetStr:"+01:00",countries:["AT"]},{name:"Australia/Lord_Howe",utcOffset:660,offsetStr:"+11:00",countries:["AU"]},{name:"Antarctica/Macquarie",utcOffset:660,offsetStr:"+11:00",countries:["AU"]},{name:"Australia/Hobart",utcOffset:660,offsetStr:"+11:00",countries:["AU"]},{name:"Australia/Currie",utcOffset:660,offsetStr:"+11:00",countries:["AU"]},{name:"Australia/Melbourne",utcOffset:660,offsetStr:"+11:00",countries:["AU"]},{name:"Australia/Sydney",utcOffset:660,offsetStr:"+11:00",countries:["AU"]},{name:"Australia/Broken_Hill",utcOffset:630,offsetStr:"+10:30",countries:["AU"]},{name:"Australia/Brisbane",utcOffset:600,offsetStr:"+10:00",countries:["AU"]},{name:"Australia/Lindeman",utcOffset:600,offsetStr:"+10:00",countries:["AU"]},{name:"Australia/Adelaide",utcOffset:630,offsetStr:"+10:30",countries:["AU"]},{name:"Australia/Darwin",utcOffset:570,offsetStr:"+09:30",countries:["AU"]},{name:"Australia/Perth",utcOffset:480,offsetStr:"+08:00",countries:["AU"]},{name:"Australia/Eucla",utcOffset:525,offsetStr:"+08:45",countries:["AU"]},{name:"Asia/Baku",utcOffset:240,offsetStr:"+04:00",countries:["AZ"]},{name:"America/Barbados",utcOffset:-240,offsetStr:"-04:00",countries:["BB"]},{name:"Asia/Dhaka",utcOffset:360,offsetStr:"+06:00",countries:["BD"]},{name:"Europe/Brussels",utcOffset:60,offsetStr:"+01:00",countries:["BE"]},{name:"Europe/Sofia",utcOffset:120,offsetStr:"+02:00",countries:["BG"]},{name:"Atlantic/Bermuda",utcOffset:-240,offsetStr:"-04:00",countries:["BM"]},{name:"Asia/Brunei",utcOffset:480,offsetStr:"+08:00",countries:["BN"]},{name:"America/La_Paz",utcOffset:-240,offsetStr:"-04:00",countries:["BO"]},{name:"America/Noronha",utcOffset:-120,offsetStr:"-02:00",countries:["BR"]},{name:"America/Belem",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Fortaleza",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Recife",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Araguaina",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Maceio",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Bahia",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Sao_Paulo",utcOffset:-120,offsetStr:"-02:00",countries:["BR"]},{name:"America/Campo_Grande",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Cuiaba",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Santarem",utcOffset:-180,offsetStr:"-03:00",countries:["BR"]},{name:"America/Porto_Velho",utcOffset:-240,offsetStr:"-04:00",countries:["BR"]},{name:"America/Boa_Vista",utcOffset:-240,offsetStr:"-04:00",countries:["BR"]},{name:"America/Manaus",utcOffset:-240,offsetStr:"-04:00",countries:["BR"]},{name:"America/Eirunepe",utcOffset:-300,offsetStr:"-05:00",countries:["BR"]},{name:"America/Rio_Branco",utcOffset:-300,offsetStr:"-05:00",countries:["BR"]},{name:"America/Nassau",utcOffset:-300,offsetStr:"-05:00",countries:["BS"]},{name:"Asia/Thimphu",utcOffset:360,offsetStr:"+06:00",countries:["BT"]},{name:"Europe/Minsk",utcOffset:180,offsetStr:"+03:00",countries:["BY"]},{name:"America/Belize",utcOffset:-360,offsetStr:"-06:00",countries:["BZ"]},{name:"America/St_Johns",utcOffset:-210,offsetStr:"-03:30",countries:["CA"]},{name:"America/Halifax",utcOffset:-240,offsetStr:"-04:00",countries:["CA"]},{name:"America/Glace_Bay",utcOffset:-240,offsetStr:"-04:00",countries:["CA"]},{name:"America/Moncton",utcOffset:-240,offsetStr:"-04:00",countries:["CA"]},{name:"America/Goose_Bay",utcOffset:-240,offsetStr:"-04:00",countries:["CA"]},{name:"America/Blanc-Sablon",utcOffset:-240,offsetStr:"-04:00",countries:["CA"]},{name:"America/Toronto",utcOffset:-300,offsetStr:"-05:00",countries:["CA"]},{name:"America/Nipigon",utcOffset:-300,offsetStr:"-05:00",countries:["CA"]},{name:"America/Thunder_Bay",utcOffset:-300,offsetStr:"-05:00",countries:["CA"]},{name:"America/Iqaluit",utcOffset:-300,offsetStr:"-05:00",countries:["CA"]},{name:"America/Pangnirtung",utcOffset:-300,offsetStr:"-05:00",countries:["CA"]},{name:"America/Resolute",utcOffset:-360,offsetStr:"-06:00",countries:["CA"]},{name:"America/Atikokan",utcOffset:-300,offsetStr:"-05:00",countries:["CA"]},{name:"America/Rankin_Inlet",utcOffset:-360,offsetStr:"-06:00",countries:["CA"]},{name:"America/Winnipeg",utcOffset:-360,offsetStr:"-06:00",countries:["CA"]},{name:"America/Rainy_River",utcOffset:-360,offsetStr:"-06:00",countries:["CA"]},{name:"America/Regina",utcOffset:-360,offsetStr:"-06:00",countries:["CA"]},{name:"America/Swift_Current",utcOffset:-360,offsetStr:"-06:00",countries:["CA"]},{name:"America/Edmonton",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Cambridge_Bay",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Yellowknife",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Inuvik",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Creston",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Dawson_Creek",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Fort_Nelson",utcOffset:-420,offsetStr:"-07:00",countries:["CA"]},{name:"America/Vancouver",utcOffset:-480,offsetStr:"-08:00",countries:["CA"]},{name:"America/Whitehorse",utcOffset:-480,offsetStr:"-08:00",countries:["CA"]},{name:"America/Dawson",utcOffset:-480,offsetStr:"-08:00",countries:["CA"]},{name:"Indian/Cocos",utcOffset:390,offsetStr:"+06:30",countries:["CC"]},{name:"Europe/Zurich",utcOffset:60,offsetStr:"+01:00",countries:["CH","DE","LI"]},{name:"Africa/Abidjan",utcOffset:0,offsetStr:"+00:00",countries:["CI","BF","GM","GN","ML","MR","SH","SL","SN","ST","TG"]},{name:"Pacific/Rarotonga",utcOffset:-600,offsetStr:"-10:00",countries:["CK"]},{name:"America/Santiago",utcOffset:-180,offsetStr:"-03:00",countries:["CL"]},{name:"Pacific/Easter",utcOffset:-300,offsetStr:"-05:00",countries:["CL"]},{name:"Asia/Shanghai",utcOffset:480,offsetStr:"+08:00",countries:["CN"]},{name:"Asia/Urumqi",utcOffset:360,offsetStr:"+06:00",countries:["CN"]},{name:"America/Bogota",utcOffset:-300,offsetStr:"-05:00",countries:["CO"]},{name:"America/Costa_Rica",utcOffset:-360,offsetStr:"-06:00",countries:["CR"]},{name:"America/Havana",utcOffset:-300,offsetStr:"-05:00",countries:["CU"]},{name:"Atlantic/Cape_Verde",utcOffset:-60,offsetStr:"-01:00",countries:["CV"]},{name:"America/Curacao",utcOffset:-240,offsetStr:"-04:00",countries:["CW","AW","BQ","SX"]},{name:"Indian/Christmas",utcOffset:420,offsetStr:"+07:00",countries:["CX"]},{name:"Asia/Nicosia",utcOffset:120,offsetStr:"+02:00",countries:["CY"]},{name:"Europe/Prague",utcOffset:60,offsetStr:"+01:00",countries:["CZ","SK"]},{name:"Europe/Berlin",utcOffset:60,offsetStr:"+01:00",countries:["DE"]},{name:"Europe/Copenhagen",utcOffset:60,offsetStr:"+01:00",countries:["DK"]},{name:"America/Santo_Domingo",utcOffset:-240,offsetStr:"-04:00",countries:["DO"]},{name:"Africa/Algiers",utcOffset:60,offsetStr:"+01:00",countries:["DZ"]},{name:"America/Guayaquil",utcOffset:-300,offsetStr:"-05:00",countries:["EC"]},{name:"Pacific/Galapagos",utcOffset:-360,offsetStr:"-06:00",countries:["EC"]},{name:"Europe/Tallinn",utcOffset:120,offsetStr:"+02:00",countries:["EE"]},{name:"Africa/Cairo",utcOffset:120,offsetStr:"+02:00",countries:["EG"]},{name:"Africa/El_Aaiun",utcOffset:0,offsetStr:"+00:00",countries:["EH"]},{name:"Europe/Madrid",utcOffset:60,offsetStr:"+01:00",countries:["ES"]},{name:"Africa/Ceuta",utcOffset:60,offsetStr:"+01:00",countries:["ES"]},{name:"Atlantic/Canary",utcOffset:0,offsetStr:"+00:00",countries:["ES"]},{name:"Europe/Helsinki",utcOffset:120,offsetStr:"+02:00",countries:["FI","AX"]},{name:"Pacific/Fiji",utcOffset:720,offsetStr:"+12:00",countries:["FJ"]},{name:"Atlantic/Stanley",utcOffset:-180,offsetStr:"-03:00",countries:["FK"]},{name:"Pacific/Chuuk",utcOffset:600,offsetStr:"+10:00",countries:["FM"]},{name:"Pacific/Pohnpei",utcOffset:660,offsetStr:"+11:00",countries:["FM"]},{name:"Pacific/Kosrae",utcOffset:660,offsetStr:"+11:00",countries:["FM"]},{name:"Atlantic/Faroe",utcOffset:0,offsetStr:"+00:00",countries:["FO"]},{name:"Europe/Paris",utcOffset:60,offsetStr:"+01:00",countries:["FR"]},{name:"Europe/London",utcOffset:0,offsetStr:"+00:00",countries:["GB","GG","IM","JE"]},{name:"Asia/Tbilisi",utcOffset:240,offsetStr:"+04:00",countries:["GE"]},{name:"America/Cayenne",utcOffset:-180,offsetStr:"-03:00",countries:["GF"]},{name:"Africa/Accra",utcOffset:0,offsetStr:"+00:00",countries:["GH"]},{name:"Europe/Gibraltar",utcOffset:60,offsetStr:"+01:00",countries:["GI"]},{name:"America/Godthab",utcOffset:-180,offsetStr:"-03:00",countries:["GL"]},{name:"America/Danmarkshavn",utcOffset:0,offsetStr:"+00:00",countries:["GL"]},{name:"America/Scoresbysund",utcOffset:-60,offsetStr:"-01:00",countries:["GL"]},{name:"America/Thule",utcOffset:-240,offsetStr:"-04:00",countries:["GL"]},{name:"Europe/Athens",utcOffset:120,offsetStr:"+02:00",countries:["GR"]},{name:"Atlantic/South_Georgia",utcOffset:-120,offsetStr:"-02:00",countries:["GS"]},{name:"America/Guatemala",utcOffset:-360,offsetStr:"-06:00",countries:["GT"]},{name:"Pacific/Guam",utcOffset:600,offsetStr:"+10:00",countries:["GU","MP"]},{name:"Africa/Bissau",utcOffset:0,offsetStr:"+00:00",countries:["GW"]},{name:"America/Guyana",utcOffset:-240,offsetStr:"-04:00",countries:["GY"]},{name:"Asia/Hong_Kong",utcOffset:480,offsetStr:"+08:00",countries:["HK"]},{name:"America/Tegucigalpa",utcOffset:-360,offsetStr:"-06:00",countries:["HN"]},{name:"America/Port-au-Prince",utcOffset:-300,offsetStr:"-05:00",countries:["HT"]},{name:"Europe/Budapest",utcOffset:60,offsetStr:"+01:00",countries:["HU"]},{name:"Asia/Jakarta",utcOffset:420,offsetStr:"+07:00",countries:["ID"]},{name:"Asia/Pontianak",utcOffset:420,offsetStr:"+07:00",countries:["ID"]},{name:"Asia/Makassar",utcOffset:480,offsetStr:"+08:00",countries:["ID"]},{name:"Asia/Jayapura",utcOffset:540,offsetStr:"+09:00",countries:["ID"]},{name:"Europe/Dublin",utcOffset:0,offsetStr:"+00:00",countries:["IE"]},{name:"Asia/Jerusalem",utcOffset:120,offsetStr:"+02:00",countries:["IL"]},{name:"Asia/Kolkata",utcOffset:330,offsetStr:"+05:30",countries:["IN"]},{name:"Indian/Chagos",utcOffset:360,offsetStr:"+06:00",countries:["IO"]},{name:"Asia/Baghdad",utcOffset:180,offsetStr:"+03:00",countries:["IQ"]},{name:"Asia/Tehran",utcOffset:210,offsetStr:"+03:30",countries:["IR"]},{name:"Atlantic/Reykjavik",utcOffset:0,offsetStr:"+00:00",countries:["IS"]},{name:"Europe/Rome",utcOffset:60,offsetStr:"+01:00",countries:["IT","SM","VA"]},{name:"America/Jamaica",utcOffset:-300,offsetStr:"-05:00",countries:["JM"]},{name:"Asia/Amman",utcOffset:120,offsetStr:"+02:00",countries:["JO"]},{name:"Asia/Tokyo",utcOffset:540,offsetStr:"+09:00",countries:["JP"]},{name:"Africa/Nairobi",utcOffset:180,offsetStr:"+03:00",countries:["KE","DJ","ER","ET","KM","MG","SO","TZ","UG","YT"]},{name:"Asia/Bishkek",utcOffset:360,offsetStr:"+06:00",countries:["KG"]},{name:"Pacific/Tarawa",utcOffset:720,offsetStr:"+12:00",countries:["KI"]},{name:"Pacific/Enderbury",utcOffset:780,offsetStr:"+13:00",countries:["KI"]},{name:"Pacific/Kiritimati",utcOffset:840,offsetStr:"+14:00",countries:["KI"]},{name:"Asia/Pyongyang",utcOffset:510,offsetStr:"+08:30",countries:["KP"]},{name:"Asia/Seoul",utcOffset:540,offsetStr:"+09:00",countries:["KR"]},{name:"America/Cayman",utcOffset:-300,offsetStr:"-05:00",countries:["KY"]},{name:"Asia/Almaty",utcOffset:360,offsetStr:"+06:00",countries:["KZ"]},{name:"Asia/Qyzylorda",utcOffset:360,offsetStr:"+06:00",countries:["KZ"]},{name:"Asia/Aqtobe",utcOffset:300,offsetStr:"+05:00",countries:["KZ"]},{name:"Asia/Aqtau",utcOffset:300,offsetStr:"+05:00",countries:["KZ"]},{name:"Asia/Oral",utcOffset:300,offsetStr:"+05:00",countries:["KZ"]},{name:"Asia/Beirut",utcOffset:120,offsetStr:"+02:00",countries:["LB"]},{name:"Asia/Colombo",utcOffset:330,offsetStr:"+05:30",countries:["LK"]},{name:"Africa/Monrovia",utcOffset:0,offsetStr:"+00:00",countries:["LR"]},{name:"Europe/Vilnius",utcOffset:120,offsetStr:"+02:00",countries:["LT"]},{name:"Europe/Luxembourg",utcOffset:60,offsetStr:"+01:00",countries:["LU"]},{name:"Europe/Riga",utcOffset:120,offsetStr:"+02:00",countries:["LV"]},{name:"Africa/Tripoli",utcOffset:120,offsetStr:"+02:00",countries:["LY"]},{name:"Africa/Casablanca",utcOffset:0,offsetStr:"+00:00",countries:["MA"]},{name:"Europe/Monaco",utcOffset:60,offsetStr:"+01:00",countries:["MC"]},{name:"Europe/Chisinau",utcOffset:120,offsetStr:"+02:00",countries:["MD"]},{name:"Pacific/Majuro",utcOffset:720,offsetStr:"+12:00",countries:["MH"]},{name:"Pacific/Kwajalein",utcOffset:720,offsetStr:"+12:00",countries:["MH"]},{name:"Asia/Rangoon",utcOffset:390,offsetStr:"+06:30",countries:["MM"]},{name:"Asia/Ulaanbaatar",utcOffset:480,offsetStr:"+08:00",countries:["MN"]},{name:"Asia/Hovd",utcOffset:420,offsetStr:"+07:00",countries:["MN"]},{name:"Asia/Choibalsan",utcOffset:480,offsetStr:"+08:00",countries:["MN"]},{name:"Asia/Macau",utcOffset:480,offsetStr:"+08:00",countries:["MO"]},{name:"America/Martinique",utcOffset:-240,offsetStr:"-04:00",countries:["MQ"]},{name:"Europe/Malta",utcOffset:60,offsetStr:"+01:00",countries:["MT"]},{name:"Indian/Mauritius",utcOffset:240,offsetStr:"+04:00",countries:["MU"]},{name:"Indian/Maldives",utcOffset:300,offsetStr:"+05:00",countries:["MV"]},{name:"America/Mexico_City",utcOffset:-360,offsetStr:"-06:00",countries:["MX"]},{name:"America/Cancun",utcOffset:-300,offsetStr:"-05:00",countries:["MX"]},{name:"America/Merida",utcOffset:-360,offsetStr:"-06:00",countries:["MX"]},{name:"America/Monterrey",utcOffset:-360,offsetStr:"-06:00",countries:["MX"]},{name:"America/Matamoros",utcOffset:-360,offsetStr:"-06:00",countries:["MX"]},{name:"America/Mazatlan",utcOffset:-420,offsetStr:"-07:00",countries:["MX"]},{name:"America/Chihuahua",utcOffset:-420,offsetStr:"-07:00",countries:["MX"]},{name:"America/Ojinaga",utcOffset:-420,offsetStr:"-07:00",countries:["MX"]},{name:"America/Hermosillo",utcOffset:-420,offsetStr:"-07:00",countries:["MX"]},{name:"America/Tijuana",utcOffset:-480,offsetStr:"-08:00",countries:["MX"]},{name:"America/Santa_Isabel",utcOffset:-480,offsetStr:"-08:00",countries:["MX"]},{name:"America/Bahia_Banderas",utcOffset:-360,offsetStr:"-06:00",countries:["MX"]},{name:"Asia/Kuala_Lumpur",utcOffset:480,offsetStr:"+08:00",countries:["MY"]},{name:"Asia/Kuching",utcOffset:480,offsetStr:"+08:00",countries:["MY"]},{name:"Africa/Maputo",utcOffset:120,offsetStr:"+02:00",countries:["MZ","BI","BW","CD","MW","RW","ZM","ZW"]},{name:"Africa/Windhoek",utcOffset:120,offsetStr:"+02:00",countries:["NA"]},{name:"Pacific/Noumea",utcOffset:660,offsetStr:"+11:00",countries:["NC"]},{name:"Pacific/Norfolk",utcOffset:660,offsetStr:"+11:00",countries:["NF"]},{name:"Africa/Lagos",utcOffset:60,offsetStr:"+01:00",countries:["NG","AO","BJ","CD","CF","CG","CM","GA","GQ","NE"]},{name:"America/Managua",utcOffset:-360,offsetStr:"-06:00",countries:["NI"]},{name:"Europe/Amsterdam",utcOffset:60,offsetStr:"+01:00",countries:["NL"]},{name:"Europe/Oslo",utcOffset:60,offsetStr:"+01:00",countries:["NO","SJ"]},{name:"Asia/Kathmandu",utcOffset:345,offsetStr:"+05:45",countries:["NP"]},{name:"Pacific/Nauru",utcOffset:720,offsetStr:"+12:00",countries:["NR"]},{name:"Pacific/Niue",utcOffset:-660,offsetStr:"-11:00",countries:["NU"]},{name:"Pacific/Auckland",utcOffset:780,offsetStr:"+13:00",countries:["NZ","AQ"]},{name:"Pacific/Chatham",utcOffset:825,offsetStr:"+13:45",countries:["NZ"]},{name:"America/Panama",utcOffset:-300,offsetStr:"-05:00",countries:["PA"]},{name:"America/Lima",utcOffset:-300,offsetStr:"-05:00",countries:["PE"]},{name:"Pacific/Tahiti",utcOffset:-600,offsetStr:"-10:00",countries:["PF"]},{name:"Pacific/Marquesas",utcOffset:-570,offsetStr:"-09:30",countries:["PF"]},{name:"Pacific/Gambier",utcOffset:-540,offsetStr:"-09:00",countries:["PF"]},{name:"Pacific/Port_Moresby",utcOffset:600,offsetStr:"+10:00",countries:["PG"]},{name:"Pacific/Bougainville",utcOffset:660,offsetStr:"+11:00",countries:["PG"]},{name:"Asia/Manila",utcOffset:480,offsetStr:"+08:00",countries:["PH"]},{name:"Asia/Karachi",utcOffset:300,offsetStr:"+05:00",countries:["PK"]},{name:"Europe/Warsaw",utcOffset:60,offsetStr:"+01:00",countries:["PL"]},{name:"America/Miquelon",utcOffset:-180,offsetStr:"-03:00",countries:["PM"]},{name:"Pacific/Pitcairn",utcOffset:-480,offsetStr:"-08:00",countries:["PN"]},{name:"America/Puerto_Rico",utcOffset:-240,offsetStr:"-04:00",countries:["PR"]},{name:"Asia/Gaza",utcOffset:120,offsetStr:"+02:00",countries:["PS"]},{name:"Asia/Hebron",utcOffset:120,offsetStr:"+02:00",countries:["PS"]},{name:"Europe/Lisbon",utcOffset:0,offsetStr:"+00:00",countries:["PT"]},{name:"Atlantic/Madeira",utcOffset:0,offsetStr:"+00:00",countries:["PT"]},{name:"Atlantic/Azores",utcOffset:-60,offsetStr:"-01:00",countries:["PT"]},{name:"Pacific/Palau",utcOffset:540,offsetStr:"+09:00",countries:["PW"]},{name:"America/Asuncion",utcOffset:-180,offsetStr:"-03:00",countries:["PY"]},{name:"Asia/Qatar",utcOffset:180,offsetStr:"+03:00",countries:["QA","BH"]},{name:"Indian/Reunion",utcOffset:240,offsetStr:"+04:00",countries:["RE","TF"]},{name:"Europe/Bucharest",utcOffset:120,offsetStr:"+02:00",countries:["RO"]},{name:"Europe/Belgrade",utcOffset:60,offsetStr:"+01:00",countries:["RS","BA","HR","ME","MK","SI"]},{name:"Europe/Kaliningrad",utcOffset:120,offsetStr:"+02:00",countries:["RU"]},{name:"Europe/Moscow",utcOffset:180,offsetStr:"+03:00",countries:["RU"]},{name:"Europe/Simferopol",utcOffset:180,offsetStr:"+03:00",countries:["RU"]},{name:"Europe/Volgograd",utcOffset:180,offsetStr:"+03:00",countries:["RU"]},{name:"Europe/Samara",utcOffset:240,offsetStr:"+04:00",countries:["RU"]},{name:"Asia/Yekaterinburg",utcOffset:300,offsetStr:"+05:00",countries:["RU"]},{name:"Asia/Omsk",utcOffset:360,offsetStr:"+06:00",countries:["RU"]},{name:"Asia/Novosibirsk",utcOffset:360,offsetStr:"+06:00",countries:["RU"]},{name:"Asia/Novokuznetsk",utcOffset:420,offsetStr:"+07:00",countries:["RU"]},{name:"Asia/Krasnoyarsk",utcOffset:420,offsetStr:"+07:00",countries:["RU"]},{name:"Asia/Irkutsk",utcOffset:480,offsetStr:"+08:00",countries:["RU"]},{name:"Asia/Chita",utcOffset:480,offsetStr:"+08:00",countries:["RU"]},{name:"Asia/Yakutsk",utcOffset:540,offsetStr:"+09:00",countries:["RU"]},{name:"Asia/Khandyga",utcOffset:540,offsetStr:"+09:00",countries:["RU"]},{name:"Asia/Vladivostok",utcOffset:600,offsetStr:"+10:00",countries:["RU"]},{name:"Asia/Sakhalin",utcOffset:600,offsetStr:"+10:00",countries:["RU"]},{name:"Asia/Ust-Nera",utcOffset:600,offsetStr:"+10:00",countries:["RU"]},{name:"Asia/Magadan",utcOffset:600,offsetStr:"+10:00",countries:["RU"]},{name:"Asia/Srednekolymsk",utcOffset:660,offsetStr:"+11:00",countries:["RU"]},{name:"Asia/Kamchatka",utcOffset:720,offsetStr:"+12:00",countries:["RU"]},{name:"Asia/Anadyr",utcOffset:720,offsetStr:"+12:00",countries:["RU"]},{name:"Asia/Riyadh",utcOffset:180,offsetStr:"+03:00",countries:["SA","KW","YE"]},{name:"Pacific/Guadalcanal",utcOffset:660,offsetStr:"+11:00",countries:["SB"]},{name:"Indian/Mahe",utcOffset:240,offsetStr:"+04:00",countries:["SC"]},{name:"Africa/Khartoum",utcOffset:180,offsetStr:"+03:00",countries:["SD","SS"]},{name:"Europe/Stockholm",utcOffset:60,offsetStr:"+01:00",countries:["SE"]},{name:"Asia/Singapore",utcOffset:480,offsetStr:"+08:00",countries:["SG"]},{name:"America/Paramaribo",utcOffset:-180,offsetStr:"-03:00",countries:["SR"]},{name:"America/El_Salvador",utcOffset:-360,offsetStr:"-06:00",countries:["SV"]},{name:"Asia/Damascus",utcOffset:120,offsetStr:"+02:00",countries:["SY"]},{name:"America/Grand_Turk",utcOffset:-240,offsetStr:"-04:00",countries:["TC"]},{name:"Africa/Ndjamena",utcOffset:60,offsetStr:"+01:00",countries:["TD"]},{name:"Indian/Kerguelen",utcOffset:300,offsetStr:"+05:00",countries:["TF"]},{name:"Asia/Bangkok",utcOffset:420,offsetStr:"+07:00",countries:["TH","KH","LA","VN"]},{name:"Asia/Dushanbe",utcOffset:300,offsetStr:"+05:00",countries:["TJ"]},{name:"Pacific/Fakaofo",utcOffset:780,offsetStr:"+13:00",countries:["TK"]},{name:"Asia/Dili",utcOffset:540,offsetStr:"+09:00",countries:["TL"]},{name:"Asia/Ashgabat",utcOffset:300,offsetStr:"+05:00",countries:["TM"]},{name:"Africa/Tunis",utcOffset:60,offsetStr:"+01:00",countries:["TN"]},{name:"Pacific/Tongatapu",utcOffset:780,offsetStr:"+13:00",countries:["TO"]},{name:"Europe/Istanbul",utcOffset:120,offsetStr:"+02:00",countries:["TR"]},{name:"America/Port_of_Spain",utcOffset:-240,offsetStr:"-04:00",countries:["TT","AG","AI","BL","DM","GD","GP","KN","LC","MF","MS","VC","VG","VI"]},{name:"Pacific/Funafuti",utcOffset:720,offsetStr:"+12:00",countries:["TV"]},{name:"Asia/Taipei",utcOffset:480,offsetStr:"+08:00",countries:["TW"]},{name:"Europe/Kiev",utcOffset:120,offsetStr:"+02:00",countries:["UA"]},{name:"Europe/Uzhgorod",utcOffset:120,offsetStr:"+02:00",countries:["UA"]},{name:"Europe/Zaporozhye",utcOffset:120,offsetStr:"+02:00",countries:["UA"]},{name:"Pacific/Wake",utcOffset:720,offsetStr:"+12:00",countries:["UM"]},{name:"America/New_York",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Detroit",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Kentucky/Louisville",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Kentucky/Monticello",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Indiana/Indianapolis",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Indiana/Vincennes",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Indiana/Winamac",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Indiana/Marengo",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Indiana/Petersburg",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Indiana/Vevay",utcOffset:-300,offsetStr:"-05:00",countries:["US"]},{name:"America/Chicago",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/Indiana/Tell_City",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/Indiana/Knox",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/Menominee",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/North_Dakota/Center",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/North_Dakota/New_Salem",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/North_Dakota/Beulah",utcOffset:-360,offsetStr:"-06:00",countries:["US"]},{name:"America/Denver",utcOffset:-420,offsetStr:"-07:00",countries:["US"]},{name:"America/Boise",utcOffset:-420,offsetStr:"-07:00",countries:["US"]},{name:"America/Phoenix",utcOffset:-420,offsetStr:"-07:00",countries:["US"]},{name:"America/Los_Angeles",utcOffset:-480,offsetStr:"-08:00",countries:["US"]},{name:"America/Metlakatla",utcOffset:-480,offsetStr:"-08:00",countries:["US"]},{name:"America/Anchorage",utcOffset:-540,offsetStr:"-09:00",countries:["US"]},{name:"America/Juneau",utcOffset:-540,offsetStr:"-09:00",countries:["US"]},{name:"America/Sitka",utcOffset:-540,offsetStr:"-09:00",countries:["US"]},{name:"America/Yakutat",utcOffset:-540,offsetStr:"-09:00",countries:["US"]},{name:"America/Nome",utcOffset:-540,offsetStr:"-09:00",countries:["US"]},{name:"America/Adak",utcOffset:-600,offsetStr:"-10:00",countries:["US"]},{name:"Pacific/Honolulu",utcOffset:-600,offsetStr:"-10:00",countries:["US","UM"]},{name:"America/Montevideo",utcOffset:-180,offsetStr:"-03:00",countries:["UY"]},{name:"Asia/Samarkand",utcOffset:300,offsetStr:"+05:00",countries:["UZ"]},{name:"Asia/Tashkent",utcOffset:300,offsetStr:"+05:00",countries:["UZ"]},{name:"America/Caracas",utcOffset:-270,offsetStr:"-04:30",countries:["VE"]},{name:"Asia/Ho_Chi_Minh",utcOffset:420,offsetStr:"+07:00",countries:["VN"]},{name:"Pacific/Efate",utcOffset:660,offsetStr:"+11:00",countries:["VU"]},{name:"Pacific/Wallis",utcOffset:720,offsetStr:"+12:00",countries:["WF"]},{name:"Pacific/Apia",utcOffset:840,offsetStr:"+14:00",countries:["WS"]},{name:"Africa/Johannesburg",utcOffset:120,offsetStr:"+02:00",countries:["ZA","LS","SZ"]}]}],link:function(e,n,t,a){e.updateModel=function(e){a.$setViewValue(e)}},templateUrl:"app/core/directives/ms-timezone/ms-timezone.html"}})}(),function(){"use strict";angular.module("app.core").controller("MsWidgetEngineCounterController",function(){var i=this;i.filter=[],i.filtered=!1,i.fontSize=20,i.label="",i.getCount=function(e){var n=0;if(e){i.fontSize=e.attrs[r]&&e.attrs[r].value?parseInt(e.attrs[r].value)-parseInt(e.attrs[r].value)%2:20;var t=e.attrs.find(function(e){return"attrChannel"===e.name});if(t||e.attrs.push({name:"attrChannel",value:"voice"}),"voice"!==e.attrs[l].value?"talking"===e.attrs[s].value?i.label="Opened":"answered"===e.attrs[s].value?i.label="Managed":i.label=e.attrs[s].value:i.label=e.attrs[s].value,e.attrs[l].value.toLowerCase().includes("voice")){e.attrs[o].value&&e.attrs[o].value.length?(i.filtered=!0,i.filter=_.intersection(e.voiceQueuesSelected,e.attrs[o].value)):(i.filtered=!1,i.filter=e.voiceQueuesSelected);for(var a=0;al.properties.length){var n=l.multiBarChart.series.length-l.properties.length;l.multiBarChart.series.splice(0,n),l.multiBarChart.data.splice(0,n)}for(var t=0;tl.filter.length){var i=l.multiBarChart.labels.length-l.filter.length;l.multiBarChart.labels.splice(0,i),l.multiBarChart.data[t].splice(0,i)}switch(e.attrs[4].value){case"voice":for(var s=0,o=0;so.properties.length){var n=o.pieChart.labels.length-o.properties.length;o.pieChart.labels.splice(0,n),o.pieChart.data.splice(0,n)}for(var t=0,a=0;te.length?e.replace(/./g,"*"):e.substring(0,e.length-a).padEnd(e.length,"*")}function o(e){return _.startsWith(e,"<")&&_.endsWith(e,">")&&(e=e.substring(1,e.length-1)),/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}function r(e){if(_.startsWith(e,"<")&&_.endsWith(e,">")){var n=!0;e=e.substring(1,e.length-1)}var t=e.split("@")[0],a=e.split("@")[1],i="";return n&&(i+="<"),i+=s(t,"email"),i+="@",i+=s(a,"email"),n&&(i+=">"),i}return{mask:function n(e){t=e,e=t.replace(/\s\s+/g," ").trim();var t;{if(o(e))return r(e);if(/^[+]?[0-9|.|\-|\s]*$/.test(e))return s(e,"number");var a=[],i=e.split(" ");return 1===i.length?s(e):(i.forEach(function(e){a.push(n(e))}),a.join(" "))}}}}e.$inject=["settingsManager"],angular.module("app.core").factory("privacyManager",e)}(),function(){"use strict";function e(n,i){return{setPlaceholder:function(a){var e;return n(function(n){try{if(_.isEmpty(a.resources))e=i.instant("DASHBOARDS.NONE");else if(_.isEmpty(a.param))e=a.placeholder?i.instant(a.placeholder):i.instant("DASHBOARDS.CHOOSE");else if(a.param.length===a.resources.length)e=i.instant("DASHBOARDS.ALL");else{var t=a.ngValue||"id";e=_(a.resources).filter(function(e){return _.includes(a.param,e[t])}).map("name").value().join(", ")}n(e)}catch(e){n(null)}})}}}e.$inject=["$q","$translate"],angular.module("app.core").factory("quickFilterManager",e)}(),function(){"use strict";function e(t,e){var s=["available","loggedIn","paused","waiting"],o=["busy","inUse","invalid","loggedInDb","notInUse","onHold","ringing","ringInUse","talking","unavailable","unknown"],r=["pTalking","originated","message","statusMessage","dialActive","dialMethod","Trunk","startPredictive","startProgressive","erlangCalls","erlangCallToSecond","erlangAbandonmentRate","erlangBusyFactor","dialPredictiveInterval","dialPredictiveIntervalMaxThreshold","dialPredictiveIntervalMinThreshold","predictiveIntervalAvailable","predictiveIntervalTotalCalls","predictiveIntervalAnsweredCalls","predictiveIntervalDroppedCallsTimeout","predictiveIntervalDroppedCallsCallersExit","predictiveIntervalAvgHoldtime","predictiveIntervalAvgTalktime"],n=["idle","unknown","unavailable","ringing"];function a(e,n){var t=[e.chatPause,e.faxPause,e.mailPause,e.openchannelPause,e.smsPause,e.voicePause,e.whatsappPause];if(!n)return _.every(t);var a=_.filter(t);return 0n.fullname.toLowerCase()?1:-1:(e.pauseType||"")<(n.pauseType||"")||e.fullname.toLowerCase()>n.fullname.toLowerCase()?-1:1:"DESC"===t?e.fullname.toLowerCase()]+>/gm,"")}}).filter("nospace",function(){return function(e){return e?e.replace(/ /g,""):""}}).filter("humanizeDoc",function(){return function(e){if(e)return"directive"===e.type?e.name.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()}):e.label||e.name}})}(),function(){"use strict";angular.module("app.core").filter("capitalize",function(){return function(e){return e?_.capitalize(e):""}})}(),function(){"use strict";angular.module("app.core").filter("filterByIds",function(){return function(e,n){if(0===e.length||!n)return e;if(0===n.length)return[];for(var t=[],a=0;an.openedAt?1:-1:1}),n}})}(),function(){"use strict";angular.module("app.core").filter("secToTime",function(){return function(e){e&&(e=e.toString().replace(",",""));var n=Math.floor(e/86400),t=e%86400,a=new Date(1e3*t).toISOString().substring(11,19);return a.replace(/^(\d+)/,function(e){return(""+(Number(e)+24*n)).padStart(2,"0")})}})}(),function(){"use strict";angular.module("app.core").filter("snakecase",function(){return function(e){return e?_.snakeCase(e):""}})}(),function(){"use strict";angular.module("app.core").filter("startcase",function(){return function(e){return e?_.startCase(e):""}})}(),function(){"use strict";angular.module("app.core").filter("filterByTags",function(){return function(e,t){if(0===e.length||0===t.length)return e;var a=[];return e.forEach(function(e){var n=t.every(function(n){var t=!1;return e.tags.forEach(function(e){e.name!==n.name||(t=!0)}),t});n&&a.push(e)}),a}}).filter("filterSingleByTags",function(){return function(e,n){if(0!==e.length&&0!==n.length){if(e.length')).html(i),s.append(o)})},rgba:l};function l(e,n){var t=n||!1;return 4===e.length&&255===e[0]&&255===e[1]&&255===e[2]&&e.splice(3,4),t&&(e=function(e,n){var t={white:{1:"1",2:"0.7",3:"0.3",4:"0.12"},black:{1:"0.87",2:"0.54",3:"0.26",4:"0.12"}};255===e[0]&&255===e[1]&&255===e[2]?e[3]=t.white[n]:0===e[0]&&0===e[1]&&0===e[2]&&(e[3]=t.black[n]);return e}(e,t)),3===e.length?"rgb("+e.join(",")+")":4===e.length?"rgba("+e.join(",")+")":void a.error("Invalid number of arguments supplied in the color array: "+e.length+"\nThe array must have 3 or 4 colors.")}function d(e){return e.charAt(0).toUpperCase()+e.slice(1)}}e.$inject=["$cookies","$log","motionTheming"],angular.module("app.core").factory("motionGenerator",e)}(),function(){"use strict";angular.module("app.core").constant("motionPalettes",[{name:"motion-blue",options:{50:"#ebf1fa",100:"#c2d4ef",200:"#9ab8e5",300:"#78a0dc",400:"#5688d3",500:"#3470ca",600:"#2e62b1",700:"#275498",800:"#21467e",900:"#1a3865",A100:"#c2d4ef",A200:"#9ab8e5",A400:"#5688d3",A700:"#275498",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400"}},{name:"motion-paleblue",options:{50:"#ececee",100:"#c5c6cb",200:"#9ea1a9",300:"#7d818c",400:"#5c616f",500:"#3c4252",600:"#353a48",700:"#2d323e",800:"#262933",900:"#1e2129",A100:"#c5c6cb",A200:"#9ea1a9",A400:"#5c616f",A700:"#2d323e",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400"}}])}(),function(){"use strict";angular.module("app.core").constant("motionThemes",{default:{primary:{name:"motion-paleblue",hues:{default:"700","hue-1":"500","hue-2":"600","hue-3":"400"}},accent:{name:"light-blue",hues:{default:"600","hue-1":"400","hue-2":"700","hue-3":"A100"}},warn:{name:"red"},background:{name:"grey",hues:{default:"A100","hue-1":"A100","hue-2":"100","hue-3":"300"}}},pinkTheme:{primary:{name:"blue-grey",hues:{default:"800","hue-1":"600","hue-2":"400","hue-3":"A100"}},accent:{name:"pink",hues:{default:"400","hue-1":"300","hue-2":"600","hue-3":"A100"}},warn:{name:"blue"},background:{name:"grey",hues:{default:"A100","hue-1":"A100","hue-2":"100","hue-3":"300"}}},tealTheme:{primary:{name:"motion-blue",hues:{default:"900","hue-1":"600","hue-2":"500","hue-3":"A100"}},accent:{name:"teal",hues:{default:"500","hue-1":"400","hue-2":"600","hue-3":"A100"}},warn:{name:"deep-orange"},background:{name:"grey",hues:{default:"A100","hue-1":"A100","hue-2":"100","hue-3":"300"}}}})}(),function(){"use strict";function e(t,e,n){var a;angular.injector(["ngCookies"]).invoke(["$cookies",function(e){a=e}]);var i=a.getObject("motion.customTheme");i&&(n.custom=i),t.alwaysWatchTheme(!0),angular.forEach(e,function(e){t.definePalette(e.name,e.options)}),angular.forEach(n,function(e,n){t.theme(n).primaryPalette(e.primary.name,e.primary.hues).accentPalette(e.accent.name,e.accent.hues).warnPalette(e.warn.name,e.warn.hues).backgroundPalette(e.background.name,e.background.hues)})}e.$inject=["$mdThemingProvider","motionPalettes","motionThemes"],angular.module("app.core").config(e)}(),function(){"use strict";function e(n,t,e){var a={getRegisteredPalettes:function(){return e.PALETTES},getRegisteredThemes:function(){return e.THEMES},setActiveTheme:function(e){if(angular.isUndefined(a.themes.list[e]))return angular.isUndefined(a.themes.list.default)?void t.error('You must have at least one theme named "default"'):(t.warn('The theme "'+e+'" does not exist! Falling back to the "default" theme.'),a.themes.active.name="default",a.themes.active.theme=a.themes.list.default,void n.put("motion.selectedTheme",a.themes.active.name));a.themes.active.name=e,a.themes.active.theme=a.themes.list[e],n.put("motion.selectedTheme",e)},setThemesList:function(e){a.themes.list=e},themes:{list:{},active:{name:"",theme:{}}}};return a}e.$inject=["$cookies","$log","$mdTheming"],angular.module("app.core").service("motionTheming",e)}(),function(){"use strict";function e(n,s,o,r){this.search=function(n){for(var e=[],t=r.getFlatNavigation(),a=o.defer(),i=0;i"+(e.name||"extractedReport")+" will be deleted.").ariaLabel("delete extractedReport").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){y(e)},function(){console.log("CANCEL")})},f.success=E,f.getExtractedReports=function(){f.query.offset=(f.query.page-1)*f.query.limit,g.hasRole("admin")?f.promise=m.analyticExtractedReport.get(f.query,E).$promise:(f.query.id=f.userProfile.id,f.query.section="ExtractedReports",f.promise=m.userProfile.getResources(f.query,E).$promise)},f.createOrEditExtractedReport=function(e,n){i.show({controller:"CreateOrEditExtractedReportDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/extractedReports/create/dialog.html",parent:angular.element(s.body),targetEvent:e,clickOutsideToClose:!0,locals:{extractedReport:n,extractedReports:f.extractedReports.rows,license:f.license,setting:f.setting,crudPermissions:f.crudPermissions}})},f.deleteExtractedReport=y,f.exportSelectedExtractedReports=function(){var e=angular.copy(f.selectedExtractedReports);return f.selectedExtractedReports=[],e},f.deleteSelectedExtractedReports=function(e){var n=i.confirm().title("Are you sure want to delete the selected extractedReports?").htmlContent(""+f.selectedExtractedReports.length+" selected will be deleted.").ariaLabel("delete ExtractedReports").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){f.selectedExtractedReports.forEach(function(e){y(e)}),f.selectedExtractedReports=[]})},f.deselectExtractedReports=function(){f.selectedExtractedReports=[]},f.selectAllExtractedReports=function(){f.selectedExtractedReports=f.extractedReports.rows};var b=!0,A=1;function E(e){f.extractedReports=e||{count:0,rows:[]}}function y(e){m.analyticExtractedReport.delete({id:e.id}).$promise.then(function(){_.remove(f.extractedReports.rows,{id:e.id}),f.extractedReports.count-=1,f.extractedReports.rows.length||f.getExtractedReports(),p.success({title:_.startCase("ExtractedReport")+" deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){f.errors=e.data.errors||[{message:e.toString(),type:"SYSTEM:DELETEanalyticExtractedReport"}];for(var n=0;n"+(e.name||"metric")+" will be deleted.").ariaLabel("delete metric").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){y(e)},function(){console.log("CANCEL")})},f.success=E,f.getMetrics=function(){f.query.offset=(f.query.page-1)*f.query.limit,g.hasRole("admin")?f.promise=m.analyticMetric.get(f.query,E).$promise:(f.query.id=f.userProfile.id,f.query.section="Metrics",f.promise=m.userProfile.getResources(f.query,E).$promise)},f.createOrEditMetric=function(e,n){i.show({controller:"CreateOrEditMetricDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/metrics/create/dialog.html",parent:angular.element(s.body),targetEvent:e,clickOutsideToClose:!0,locals:{metric:n,metrics:f.metrics.rows,license:f.license,setting:f.setting,crudPermissions:f.crudPermissions}})},f.deleteMetric=y,f.exportSelectedMetrics=function(){var e=angular.copy(f.selectedMetrics);return f.selectedMetrics=[],e},f.deleteSelectedMetrics=function(e){var n=i.confirm().title("Are you sure want to delete the selected metrics?").htmlContent(""+f.selectedMetrics.length+" selected will be deleted.").ariaLabel("delete Metrics").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){f.selectedMetrics.forEach(function(e){y(e)}),f.selectedMetrics=[]})},f.deselectMetrics=function(){f.selectedMetrics=[]},f.selectAllMetrics=function(){f.selectedMetrics=f.metrics.rows};var b=!0,A=1;function E(e){f.metrics=e||{count:0,rows:[]}}function y(e){m.analyticMetric.delete({id:e.id}).$promise.then(function(){_.remove(f.metrics.rows,{id:e.id}),f.metrics.count-=1,f.metrics.rows.length||f.getMetrics(),p.success({title:_.startCase("Metric")+" deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){f.errors=e.data.errors||[{message:e.toString(),type:"SYSTEM:DELETEanalyticMetric"}];for(var n=0;n"+e.field+" will be deleted.").ariaLabel("delete field").targetEvent(n).ok("OK").cancel("CANCEL");a.show(t).then(function(){p(e)},function(){console.log("CANCEL")})},c.success=u,c.getReportFields=function(){c.promise=o.analyticFieldReport.get(c.query,u).$promise},c.createOrEditReportField=function(e,n){a.show({controller:"CreateOrEditReportFieldDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/reports/edit/field/dialog.html",parent:angular.element(i.body),targetEvent:e,clickOutsideToClose:!0,locals:{report:c.report,reportField:n,reportFields:c.reportFields.rows,metrics:c.metrics,columns:c.columns,setting:null,crudPermissions:c.crudPermissions}})},c.deleteReportField=p,c.deleteSelectedReportFields=function(e){var n=a.confirm().title("Are you sure want to delete the selected fields?").htmlContent(""+c.selectedReportFields.length+" selected will be deleted.").ariaLabel("delete fields").targetEvent(e).ok("OK").cancel("CANCEL");a.show(n).then(function(){c.selectedReportFields.forEach(function(e){p(e)}),c.selectedReportFields=[]})},c.getMetricName=function(e){var n=_.find(c.metrics,{id:e});return n?n.name:d.instant("ANALYTICS.NO_METRIC_FOUND")},c.getMetricValue=function(e){var n=_.find(c.metrics,{id:e});return n?n.metric:d.instant("ANALYTICS.NO_METRIC_FOUND")};var m=!0;function u(e){c.reportFields=e||{count:0,rows:[]}}function p(e){o.analyticFieldReport.delete({id:e.id}).$promise.then(function(){c.selectedReportFields=[],_.remove(c.reportFields.rows,{id:e.id}),c.reportFields.count-=1,c.reportFields.rows.length||c.getReportFields(),s.success({title:"Field deleted!",msg:e.field?e.field+" has been deleted!":""})}).catch(function(e){s.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:DELETEFIELD",msg:e.data?JSON.stringify(e.data):e.toString()})})}n.$watch("vm_rf.query.filter",function(e,n){m?t(function(){m=!1}):c.getReportFields()})}e.$inject=["$cookies","$scope","$timeout","$mdDialog","$document","toasty","api","describeTable","sqlUtil","$translate"],angular.module("app.analytics").controller("ReportFieldsController",e)}(),function(){"use strict";angular.module("app.analytics").factory("sqlUtil",function(){return{getFunctions:[{value:"SUM",option:function(e){return"SUM ("+e+")"}},{value:"COUNT",option:function(e){return"COUNT ("+e+")"}},{value:"COUNT DISTINCT",option:function(e){return"COUNT (DISTINCT "+e+")"}},{value:"MAX",option:function(e){return"MAX ("+e+")"}},{value:"MIN",option:function(e){return"MIN ("+e+")"}},{value:"AVG",option:function(e){return"AVG ("+e+")"}},{value:"GROUP_CONCAT",option:function(e){return"GROUP_CONCAT ("+e+")"}},{value:"GROUP_CONCAT ASC",option:function(e){return"GROUP_CONCAT ("+e+" ORDER BY "+e+" ASC)"}},{value:"GROUP_CONCAT DESC",option:function(e){return"GROUP_CONCAT ("+e+" ORDER BY "+e+" DESC)"}}],getFormats:[{value:"SEC_TO_TIME",option:function(e){return"SEC_TO_TIME ("+e+")"}},{value:"DATE",option:function(e){return"DATE ("+e+")"}},{value:"HOUR",option:function(e){return"HOUR ("+e+")"}},{value:"ROUND",option:function(e){return"ROUND ("+e+")"}},{value:"UNIX_TIMESTAMP",option:function(e){return"UNIX_TIMESTAMP ("+e+")"}}],getGroupBy:[{value:!1,option:function(){return"No"}},{value:!0,option:function(e){return"GROUP BY "+e}}],getOrderBy:[{value:"ASC",option:function(e){return"ORDER BY "+e+" ASC"}},{value:"DESC",option:function(e){return"ORDER BY "+e+" DESC"}}],getConditions:["=","!=","<","<=",">",">=","LIKE","NOT LIKE","IS NULL","IS NOT NULL","IS EMPTY","IS NOT EMPTY"]}})}(),function(){"use strict";function e(e,n,t,a,i,s){var o=this;function r(e){return _.isArray(e)}o.report=a||{},o.userProfileSection=s&&1==s.count?s.rows[0]:null,o.crudPermissions=i.parseCrudPermissions(o.userProfileSection?o.userProfileSection.crudPermissions:null),o.selectedTab=e.params.tab||0,o.gotoReports=function(){e.go("app.analytics.reports",{},{reload:"app.analytics.reports"})},o.saveReport=function(){o.report.conditions=angular.toJson(o.report.condition),t.analyticCustomReport.update({id:o.report.id},_.omit(o.report,"joins")).$promise.then(function(){n.success({title:"Report updated!",msg:o.report.name?o.report.name+" has been updated!":""})}).catch(function(e){n.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GETreport",msg:e.data?JSON.stringify(e.data):e.toString()})})},o.previewReport=function(){o.columns=[],o.rows=[],o.error=!1,t.analyticFieldReport.get({fields:"field,alias",nolimit:!0,CustomReportId:o.report.id}).$promise.then(function(e){return o.columns=e?e.rows:[],t.analyticCustomReport.preview({id:o.report.id}).$promise}).then(function(e){o.rows=e}).catch(function(e){console.log(e),o.error={title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:analyticFieldReport",msg:e.data?e.data.message:e.toString(),sql:e.data&&e.data.parent?e.data.parent.sql:"NO QUERY"},n.error(o.error)})},o.queryReport=function(){return o.queryResult="Loading...",t.analyticCustomReport.query({id:o.report.id}).$promise.then(function(e){o.queryResult=e.sql}).catch(function(e){o.queryResult="",console.log(e),o.error={title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:analyticFieldReport",msg:e.data?e.data.message:e.toString(),sql:e.data&&e.data.parent?e.data.parent.sql:"NO QUERY"},n.error(o.error)})},o.mapArray=function(e,n){if(r(e))return _.map(e,n).join(",");return""},o.isArray=r,o.valueReplacer=function(e,n){moment(n,"YYYY-MM-DDTHH:mm:ssZ",!0).isValid()&&(n=moment(n,"").format("YYYY-MM-DD HH:mm:ss"));return n}}e.$inject=["$state","toasty","api","report","Auth","userProfileSection"],angular.module("app.analytics").controller("ReportController",e)}(),function(){"use strict";function e(e,n,t,a,i,s){var o=this;o.errors=[],o.report=angular.copy(a),o.valueReplacer=function(e,n){moment(n,"YYYY-MM-DDTHH:mm:ssZ",!0).isValid()&&(n=moment(n,"").format("YYYY-MM-DD HH:mm:ss"));return n},o.closeDialog=function(){n.hide()},function(){o.columns=[],o.rows=[],o.error=!1;var e={fields:"field,alias",nolimit:!0};e["analyticCustomReport"===s?"CustomReportId":"DefaultReportId"]=o.report.id,o.promise=i.analyticFieldReport.get(e).$promise.then(function(e){return o.columns=e?e.rows:[],i[s].preview({id:o.report.id}).$promise}).then(function(e){o.rows=e}).catch(function(e){console.log(e),o.error={title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:analyticFieldReport",msg:e.data?e.data.message:e.toString(),sql:e.data&&e.data.parent?e.data.parent.sql:"NO QUERY"},t.error(o.error)})}()}e.$inject=["$location","$mdDialog","toasty","report","api","apiName"],angular.module("app.analytics").controller("PreviewReportDialogController",e)}(),function(){"use strict";function e(e,t,a,i,s,o,n,r,l,d,c,m,u){var p=this;p.currentUser=u.getCurrentUser(),p.reports={count:0,rows:[]},p.userProfile=r,p.userProfileSection=l&&1==l.count?l.rows[0]:null,p.crudPermissions=u.parseCrudPermissions(p.userProfileSection?p.userProfileSection.crudPermissions:null),p.selectedReports=[],p.query={fields:"createdAt,updatedAt,id,name,description,table,parent,conditions,joins",limit:10,page:1,sort:"-updatedAt"},p.apiName=null,p.currentPath="",p.customTree=!0,p.editstate=function(e,n){s.go("app.analytics.reports.edit",{id:e.id,crudPermissions:p.crudPermissions})},p.copydialog=function(e,n){i.show({controller:"CopyReportDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/reports/copy/dialog.html",parent:angular.element(a.body),targetEvent:n,clickOutsideToClose:!0,locals:{report:e,apiName:p.apiName,treeCustomData:p.treeCustomInstance.jstree(!0).get_json("#")}}).finally(function(){})},p.previewdialog=function(e,n){i.show({controller:"PreviewReportDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/reports/preview/dialog.html",parent:angular.element(a.body),targetEvent:n,clickOutsideToClose:!0,locals:{report:e,apiName:p.apiName}})},p.rundialog=function(e,n){i.show({controller:"RunReportDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/reports/run/dialog.html",parent:angular.element(a.body),targetEvent:n,clickOutsideToClose:!0,locals:{report:e,apiName:p.apiName,currentPath:p.currentPath}})},p.downloadfile=function(a,e){var i;d.analyticMetric.get({fields:"id,name,metric,table",nolimit:!0}).$promise.then(function(e){i=_.keyBy(e.rows,"id");var n={fields:"field,alias,function,format,groupBy,orderBy,custom,MetricId",nolimit:!0};return n["analyticCustomReport"===p.apiName?"CustomReportId":"DefaultReportId"]=a.id,d.analyticFieldReport.get(n).$promise}).then(function(e){for(var n=0;n"+e.name+" will be deleted.").ariaLabel("delete report").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){b(e)},function(){console.log("CANCEL")})},p.success=h,p.getReports=f,p.createOrEditReport=function(e,n){i.show({controller:"CreateOrEditReportDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/reports/create/dialog.html",parent:angular.element(a.body),targetEvent:e,clickOutsideToClose:!0,locals:{report:n,reports:p.reports.rows,apiName:p.apiName,currentNode:p.currentNode,setting:null,crudPermissions:p.crudPermissions}})},p.importReport=function(e,n,t){if("application/json"===e.file.type){var a=new FileReader;a.onload=function(e){console.log(e.target.result);try{var t=atob(e.target.result.split(",")[1]);t=angular.fromJson(t),d.analyticCustomReport.save({name:t.name,description:t.description,table:t.table,conditions:t.conditions,joins:t.joins,parent:p.currentNode.id}).$promise.then(function(n){p.reports.rows.unshift(n),m.success({title:"Report saved!",msg:n.name?n.name+" has been saved!":""});var e=_.map(t.fields,function(e){return _.extend({},e,{CustomReportId:n.id})});return d.analyticFieldReport.bulkCreate(e).$promise}).then(function(e){m.success({title:"Fields saved!",msg:"Fields has been saved!"})}).catch(function(e){m.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GETanalyticReport",msg:e.data?JSON.stringify(e.data):e.toString()})})}catch(e){console.error(e),m.error({title:"Decode File Error",msg:e.toString()})}},a.readAsDataURL(e.file)}else m.error({title:"Format Error",msg:"Please use only json files"})},p.deleteReport=b,p.exportSelectedReports=function(){var e=angular.copy(p.selectedReports);return p.selectedReports=[],e},p.deleteSelectedReports=function(e){var n=i.confirm().title("Are you sure want to delete the selected reports?").htmlContent(""+p.selectedReports.length+" selected will be deleted.").ariaLabel("delete Reports").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){p.selectedReports.forEach(function(e){b(e)}),p.selectedReports=[]})},p.deselectReports=function(){p.selectedReports=[]},p.selectAllReports=function(){p.selectedReports=p.reports.rows},p.treeDefaultData=c.rows[0]?angular.fromJson(c.rows[0].tree):[],p.treeCustomData=c.rows[1]?angular.fromJson(c.rows[1].tree):[],p.treeDefaultConfig=S(!1),p.treeCustomConfig=S(!0),p.treeDefaultEvents=T(!1),p.treeCustomEvents=T(!0);var g=!0,v=1;function h(e){p.reports=e||{count:0,rows:[]}}function f(){if(p.query.offset=(p.query.page-1)*p.query.limit,p.apiName)if("admin"===p.currentUser.role||p.userProfileSection.autoAssociation)p.promise=d[p.apiName].get(p.query,h).$promise;else{var a=[];p.promise=d[p.apiName].get(p.query).$promise.then(function(e){return 0<(a=e&&e.rows?e.rows:[]).length?d.userProfileResource.get({sectionId:p.userProfileSection.id,type:"analyticDefaultReport"===p.apiName?"DefaultReports":"CustomReports"}).$promise.then(function(e){var n=e&&e.rows?e.rows:[];if(0"+(t.text?t.text:"Node")+" and its subnode will be deleted.").ariaLabel("delete node").ok("OK").cancel("CANCEL");i.show(e).then(function(){var e,n=p.treeCustomInstance.jstree(!0).get_parent(t);n=p.treeCustomInstance.jstree(!0).get_node(n),p.treeCustomInstance.jstree(!0).delete_node(t),e=[t.id].concat(t.children_d||[]),d.analyticCustomReport.get({parent:e.join(","),fields:"id,name"}).$promise.then(function(e){e&&e.rows&&e.rows.forEach(function(e){b(e)})}).catch(function(e){m.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:DELETEreportsByParents",msg:e.data?JSON.stringify(e.data):e.toString()})}),p.treeCustomInstance.jstree(!0).select_node(n)})}}}),e}}}}function T(e){return e?{create_node:y,rename_node:y,move_node:y,delete_node:y,select_node:A}:{select_node:E}}e.$watch("vm.query.filter",function(e,n){g?t(function(){g=!1}):(n||(v=p.query.page),e!==n&&(p.query.page=1),e||(p.query.page=v),p.getReports())}),e.$watch("vm.search",function(e,n){p.treeDefaultInstance&&p.treeDefaultInstance.jstree(!0).search(e),p.treeCustomInstance&&p.treeCustomInstance.jstree(!0).search(e)})}e.$inject=["$scope","$timeout","$document","$mdDialog","$state","$window","$translate","userProfile","userProfileSection","api","treeReports","toasty","Auth"],angular.module("app.analytics").controller("ReportsController",e)}(),function(){"use strict";function e(e,t,a,n,i,s,o,r){var l=this;function d(){l.export={id:l.report.id,name:l.report.name,startTime:new Date(moment().startOf("day")),startDate:new Date(moment().startOf("day")),endTime:new Date(moment().endOf("day")),endDate:new Date(moment().endOf("day")),output:"xlsx",fullPath:r?r+"/"+l.report.name:l.report.name}}function c(){t.hide()}l.errors=[],l.report=angular.copy(n),l.export={},l.runReport=function(n){l.export.name=l.export.name.replace(/\//g,"_"),l.errors=[],l.exportDate=_.assign({},l.export,{startDate:moment(l.export.startDate).set("hour",l.export.startTime.getHours()).set("minute",l.export.startTime.getMinutes()).set("second",l.export.startTime.getSeconds()).format("YYYY-MM-DD HH:mm:ss"),endDate:moment(l.export.endDate).set("hour",l.export.endTime.getHours()).set("minute",l.export.endTime.getMinutes()).set("second",l.export.endTime.getSeconds()).format("YYYY-MM-DD HH:mm:ss")}),i[s].run(l.exportDate).$promise.then(function(e){"web"===l.export.output?t.show({controller:"WebReportDialogController",controllerAs:"vm",templateUrl:"app/main/apps/analytics/views/reports/run/web/dialog.html",parent:angular.element(o.body),targetEvent:n,skipHide:!0,locals:{apiName:s,exportDate:l.exportDate,results:e},resolve:{columns:["apiResolver",function(e){var n={fields:"field,alias",nolimit:!0};return n["analyticCustomReport"===s?"CustomReportId":"DefaultReportId"]=l.report.id,e.resolve("analyticFieldReport@get",n)}]}}):(a.success({title:"Report properly run!",msg:l.report.name?l.report.name+" has been run!":""}),c())}).catch(function(e){a.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:DESCRIBE",msg:e.data?JSON.stringify(e.data.message):e.toString()})})},l.closeDialog=c,(l.refreshDate=d)()}e.$inject=["$location","$mdDialog","toasty","report","api","apiName","$document","currentPath"],angular.module("app.analytics").controller("RunReportDialogController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o){var r=this;function l(e){r.results=e||{count:0,rows:[]}}r.errors=[],r.columns=a?a.rows:[],r.results=i||{rows:[],count:0},r.query={limit:10,page:1},r.closeDialog=function(){e.hide()},r.getResults=function(){o.offset=(r.query.page-1)*r.query.limit,o.limit=r.query.limit,r.promise=t[s].run(o,l).$promise},r.valueReplacer=function(e,n){moment(n,"YYYY-MM-DDTHH:mm:ssZ",!0).isValid()&&(n=moment(n,"").format("YYYY-MM-DD HH:mm:ss"));return n}}e.$inject=["$mdDialog","toasty","api","columns","results","apiName","exportDate"],angular.module("app.analytics").controller("WebReportDialogController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o,r,l,d,c,m,u,p){var g=this;function v(e){a.hide(e)}g.currentUser=c.getCurrentUser(),g.errors=[],g.setting=u,g.license=m,g.crudPermissions=p,g.hasModulePermissions={},g.passwordPattern=g.setting&&g.setting.securePassword?/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[?!@#\$%\^&\*~\-_=+[{\]\}])(?=.{8,})/:"",g.title="CALLYSQUARE.EDIT_ODBC",g.odbc=angular.copy(l),g.odbcs=r,g.newOdbc=!1,g.odbc||(g.odbc={},g.title="CALLYSQUARE.NEW_ODBC",g.newOdbc=!0),g.addNewOdbc=function(){g.errors=[],d.squareOdbc.save(g.odbc).$promise.then(function(e){g.odbcs.unshift(e.toJSON()),o.success({title:"Odbc properly created",msg:g.odbc.name?g.odbc.name+" has been created!":""}),v(e)}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){g.errors=e.data.errors||[{message:e.toString(),type:"api.squareOdbc.save"}];for(var n=0;n"+(e.name||"odbc")+" will be deleted.").ariaLabel("delete odbc").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){y(e)},function(){console.log("CANCEL")})},f.success=E,f.getODBC=function(){f.query.offset=(f.query.page-1)*f.query.limit,g.hasRole("admin")?f.promise=m.squareOdbc.get(f.query,E).$promise:(f.query.id=f.userProfile.id,f.query.section="ODBC",f.promise=m.userProfile.getResources(f.query,E).$promise)},f.createOrEditOdbc=function(e,n){i.show({controller:"CreateOrEditOdbcDialogController",controllerAs:"vm",templateUrl:"app/main/apps/callysquare/views/odbcs/create/dialog.html",parent:angular.element(s.body),targetEvent:e,clickOutsideToClose:!0,locals:{odbc:n,odbcs:f.odbcs.rows,license:f.license,setting:f.setting,crudPermissions:f.crudPermissions}})},f.deleteOdbc=y,f.exportSelectedODBC=function(){var e=angular.copy(f.selectedODBC);return f.selectedODBC=[],e},f.deleteSelectedODBC=function(e){var n=i.confirm().title("Are you sure want to delete the selected odbcs?").htmlContent(""+f.selectedODBC.length+" selected will be deleted.").ariaLabel("delete Odbcs").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){f.selectedODBC.forEach(function(e){y(e)}),f.selectedODBC=[]})},f.deselectODBC=function(){f.selectedODBC=[]},f.selectAllODBC=function(){f.selectedODBC=f.odbcs.rows};var b=!0,A=1;function E(e){f.odbcs=e||{count:0,rows:[]}}function y(e){m.squareOdbc.delete({id:e.id}).$promise.then(function(){_.remove(f.odbcs.rows,{id:e.id}),f.odbcs.count-=1,f.odbcs.rows.length||f.getODBC(),p.success({title:_.startCase("Odbc")+" deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){f.errors=e.data.errors||[{message:e.toString(),type:"SYSTEM:DELETEsquareOdbc"}];for(var n=0;n"+e.name+" will be deleted.").ariaLabel("delete project").targetEvent(n).ok("OK").cancel("CANCEL");l.show(t).then(function(){f(e)},function(){console.log("CANCEL")})},p.success=h,p.getProjects=function(){p.query.offset=(p.query.page-1)*p.query.limit,u.hasRole("admin")?p.promise=o.squareProject.get(p.query,h).$promise:(p.query.id=p.userProfile.id,p.query.section="SquareProjects",p.promise=o.userProfile.getResources(p.query,h).$promise)},p.createOrEditProject=function(e,n){l.show({controller:"CreateOrEditSquareProjectDialogController",controllerAs:"vm",templateUrl:"app/main/apps/callysquare/views/projects/create/dialog.html",parent:angular.element(d.body),targetEvent:e,clickOutsideToClose:!0,locals:{project:n,projects:p.projects.rows,openFromEditor:null,setting:null,crudPermissions:p.crudPermissions}})},p.deleteProject=f,p.exportSelectedProjects=function(){var e=angular.copy(p.selectedProjects);return p.selectedProjects=[],e},p.deleteSelectedProjects=function(e){var n=l.confirm().title("Are you sure want to delete the selected projects?").htmlContent(""+p.selectedProjects.length+" selected will be deleted.").ariaLabel("delete Projects").targetEvent(e).ok("OK").cancel("CANCEL");l.show(n).then(function(){p.selectedProjects.forEach(function(e){f(e)}),p.selectedProjects=[]})},p.deselectProjects=function(){p.selectedProjects=[]},p.selectAllProjects=function(){p.selectedProjects=p.projects.rows};var g=!0,v=1;function h(e){p.projects=e||{count:0,rows:[]}}function f(e){o.squareProject.delete({id:e.id}).$promise.then(function(){_.remove(p.projects.rows,{id:e.id}),p.projects.count-=1,p.projects.rows.length||p.getProjects(),m.success({title:"Project deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){p.errors=e.data.errors||[{message:e.toString(),type:"api.project.delete"}];for(var n=0;n"+(e.name||"squareRecording")+" will be deleted.").ariaLabel("delete squareRecording").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){y(e)},function(){console.log("CANCEL")})},f.success=E,f.getSquareRecordings=function(){f.query.offset=(f.query.page-1)*f.query.limit,g.hasRole("admin")?f.promise=m.squareRecording.get(f.query,E).$promise:(f.query.id=f.userProfile.id,f.query.section="SquareRecordings",f.promise=m.userProfile.getResources(f.query,E).$promise)},f.createOrEditSquareRecording=function(e,n){i.show({controller:"CreateOrEditSquareRecordingDialogController",controllerAs:"vm",templateUrl:"app/main/apps/callysquare/views/squareRecordings/create/dialog.html",parent:angular.element(s.body),targetEvent:e,clickOutsideToClose:!0,locals:{squareRecording:n,squareRecordings:f.squareRecordings.rows,license:f.license,setting:f.setting,crudPermissions:f.crudPermissions}})},f.deleteSquareRecording=y,f.exportSelectedSquareRecordings=function(){var e=angular.copy(f.selectedSquareRecordings);return f.selectedSquareRecordings=[],e},f.deleteSelectedSquareRecordings=function(e){var n=i.confirm().title("Are you sure want to delete the selected squareRecordings?").htmlContent(""+f.selectedSquareRecordings.length+" selected will be deleted.").ariaLabel("delete SquareRecordings").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){f.selectedSquareRecordings.forEach(function(e){y(e)}),f.selectedSquareRecordings=[]})},f.deselectSquareRecordings=function(){f.selectedSquareRecordings=[]},f.selectAllSquareRecordings=function(){f.selectedSquareRecordings=f.squareRecordings.rows};var b=!0,A=1;function E(e){f.squareRecordings=e||{count:0,rows:[]}}function y(e){m.squareRecording.delete({id:e.id}).$promise.then(function(){_.remove(f.squareRecordings.rows,{id:e.id}),f.squareRecordings.count-=1,f.squareRecordings.rows.length||f.getSquareRecordings(),p.success({title:_.startCase("SquareRecording")+" deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){f.errors=e.data.errors||[{message:e.toString(),type:"SYSTEM:DELETEsquareRecording"}];for(var n=0;n"+(e.name||"chatQueue")+" will be deleted.").ariaLabel("delete chatQueue").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){y(e)},function(){console.log("CANCEL")})},f.gotorealtimegoto=function(e,n){{if(!g.hasRole("admin"))return m.userProfileSection.get({userProfileId:g.getCurrentUser().userProfileId,sectionId:510}).$promise.then(function(e){var n=e&&e.rows?e.rows[0]:null;n&&n.enabled?t.go("app.chat.realtime.queues",{}):p.info({title:r.instant("STAFF.PERMISSIONS_UNAUTHORIZED_REDIRECT_TITLE"),msg:r.instant("STAFF.PERMISSIONS_UNAUTHORIZED_REDIRECT_MESSAGE")})}).catch(function(e){p.error({title:e.status?"API:"+e.status+" - "+e.statusText:"USERPROFILE:GET_SECTION",msg:e.status?JSON.stringify(e.data):e.toString()})});t.go("app.chat.realtime.queues",{})}},f.success=E,f.getChatQueues=function(){f.query.offset=(f.query.page-1)*f.query.limit,g.hasRole("admin")?f.promise=m.chatQueue.get(f.query,E).$promise:(f.query.id=f.userProfile.id,f.query.section="ChatQueues",f.promise=m.userProfile.getResources(f.query,E).$promise)},f.createOrEditChatQueue=function(e,n){i.show({controller:"CreateOrEditChatQueueDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatQueues/create/dialog.html",parent:angular.element(s.body),targetEvent:e,clickOutsideToClose:!0,locals:{chatQueue:n,chatQueues:f.chatQueues.rows,crudPermissions:f.crudPermissions}})},f.deleteChatQueue=y,f.exportSelectedChatQueues=function(){var e=angular.copy(f.selectedChatQueues);return f.selectedChatQueues=[],e},f.deleteSelectedChatQueues=function(e){var n=i.confirm().title("Are you sure want to delete the selected chatQueues?").htmlContent(""+f.selectedChatQueues.length+" selected will be deleted.").ariaLabel("delete ChatQueues").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){f.selectedChatQueues.forEach(function(e){y(e)}),f.selectedChatQueues=[]})},f.deselectChatQueues=function(){f.selectedChatQueues=[]},f.selectAllChatQueues=function(){f.selectedChatQueues=f.chatQueues.rows};var b=!0,A=1;function E(e){f.chatQueues=e||{count:0,rows:[]}}function y(e){m.chatQueue.delete({id:e.id}).$promise.then(function(){_.remove(f.chatQueues.rows,{id:e.id}),f.chatQueues.count-=1,f.chatQueues.rows.length||f.getChatQueues(),p.success({title:_.startCase("ChatQueue")+" deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){f.errors=e.data.errors||[{message:e.toString(),type:"SYSTEM:DELETEchatQueue"}];for(var n=0;n":"",n}),c.startingSelectedItems=angular.copy(c.selectedItems),c.dualMultiselectOptions.selectedItems=c.selectedItems,c.dualMultiselectOptions.items=_.differenceBy(c.allowedItems,c.dualMultiselectOptions.selectedItems,"id"),t()}).catch(function(e){n(e)})})}c.currentUser=l.getCurrentUser(),c.chatQueue=n,c.crudPermissions=d,c.realtime=o,c.items=[],c.allowedItems=[],c.selectedItems=[],c.startingAllowedItems=[],c.startingSelectedItems=[],c.pendingChanges=!1,c.onInit=function(){return l.hasRole("admin")?m().catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_AGENTS",msg:e.status?JSON.stringify(e.data):e.toString()})}):a(function(t,n){s.userProfileSection.get({userProfileId:c.currentUser.userProfileId,name:"Agents"}).$promise.then(function(e){var n=e&&e.rows?e.rows[0]:null;t(n)}).catch(function(e){n(e)})}).then(function(e){return c.section=e,m()}).catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_AGENTS",msg:e.status?JSON.stringify(e.data):e.toString()})})},c.saveAgents=function(){var e=_.differenceBy(c.startingSelectedItems,c.selectedItems,"id"),n=_.differenceBy(c.selectedItems,c.startingSelectedItems,"id");return(t=e,a(function(e,n){_.isEmpty(t)?e():s.chatQueue.removeAgents({id:c.chatQueue.id,ids:_.map(t,"id")}).$promise.then(function(){e()}).catch(function(e){n(e)})})).then(function(){return t=n,a(function(e,n){_.isEmpty(t)?e():s.chatQueue.addAgents({id:c.chatQueue.id,ids:_.map(t,"id")}).$promise.then(function(){e()}).catch(function(e){n(e)})});var t}).then(function(){c.pendingChanges=!1,c.startingAllowedItems=angular.copy(c.allowedItems),c.startingSelectedItems=angular.copy(c.selectedItems),i.success({title:"SUCCESS",msg:"Agents association has been updated!"})}).catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:LISTS_ASSOCIATION",msg:e.status?JSON.stringify(e.data):e.toString()})});var t},c.closeDialog=function(){e.hide()},c.dualMultiselectOptions={items:[],selectedItems:[],orderBy:"name",line1:"fullname",line2:["name","internal"],line3:"",labelAll:r.instant("CHAT.ALL_AGENTS"),labelSelected:r.instant("CHAT.SELECTED_AGENTS"),transferCallback:function(e,n){var t=_.xorBy(c.startingSelectedItems,c.selectedItems,"id");c.pendingChanges=!_.isEmpty(t)}}}e.$inject=["$mdDialog","$q","toasty","api","chatQueue","chatQueues","realtime","$translate","Auth","crudPermissions"],angular.module("app.chat").controller("ChatQueueagentaddController",e)}(),function(){"use strict";function e(e,a,i,s,n,t,o,r){var l=this;function d(){return a(function(t,n){return a(function(n,t){return s.team.get({fields:"id,name",nolimit:!0}).$promise.then(function(e){n(e)}).catch(function(e){t(e)})}).then(function(e){return l.items=e.rows?e.rows:[],o.hasRole("admin")?e:l.section?l.section.autoAssociation?e:a(function(n,t){return s.userProfileResource.get({sectionId:l.section.id,nolimit:!0}).$promise.then(function(e){n(e)}).catch(function(e){t(e)})}):null}).then(function(e){var n=e&&e.rows?e.rows:[];return l.allowedItems=_.map(n,function(e){return _.find(l.items,{id:o.hasRole("admin")||l.section.autoAssociation?e.id:e.resourceId})}),l.items.forEach(function(e){var n=_.find(l.allowedItems,{id:e.id});o.hasRole("admin")?e.isValid=!0:e.isValid=void 0!==n}),a(function(n,t){return s.chatQueue.getTeams({id:l.chatQueue.id,fields:"id,name",nolimit:!0}).$promise.then(function(e){n(e)}).catch(function(e){t(e)})})}).then(function(e){var n=e&&e.rows?e.rows:[];l.selectedItems=_.map(n,function(e){var n=_.find(l.items,{id:e.id});return n.penalty=e.TeamVoiceQueue?"penalty "+e.TeamVoiceQueue.penalty:"",n}),l.startingSelectedItems=angular.copy(l.selectedItems),l.dualMultiselectOptions.selectedItems=l.selectedItems,l.dualMultiselectOptions.items=_.differenceBy(l.allowedItems,l.dualMultiselectOptions.selectedItems,"id"),t()}).catch(function(e){n(e)})})}l.currentUser=o.getCurrentUser(),l.chatQueue=n,l.crudPermissions=r,l.items=[],l.allowedItems=[],l.selectedItems=[],l.startingAllowedItems=[],l.startingSelectedItems=[],l.pendingChanges=!1,l.dualMultiselectOptions={allowedItems:[],selectedItems:[],orderBy:"name",line1:"name",line2:"",line3:"",labelAll:t.instant("CHAT.ALL_TEAMS"),labelSelected:t.instant("CHAT.SELECTED_TEAMS"),transferCallback:function(e,n){var t=_.xorBy(l.startingSelectedItems,l.selectedItems,"id");l.pendingChanges=!_.isEmpty(t)}},l.onInit=function(){return o.hasRole("admin")?d().catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_TEAMS",msg:e.status?JSON.stringify(e.data):e.toString()})}):a(function(t,n){s.userProfileSection.get({userProfileId:l.currentUser.userProfileId,name:"Teams"}).$promise.then(function(e){var n=e&&e.rows?e.rows[0]:null;t(n)}).catch(function(e){n(e)})}).then(function(e){return l.section=e,d()}).catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_TEAMS",msg:e.status?JSON.stringify(e.data):e.toString()})})},l.saveTeams=function(){var e=_.differenceBy(l.startingSelectedItems,l.selectedItems,"id"),n=_.differenceBy(l.selectedItems,l.startingSelectedItems,"id");return(t=e,a(function(e,n){_.isEmpty(t)?e():s.chatQueue.removeTeams({id:l.chatQueue.id,ids:_.map(t,"id")}).$promise.then(function(){e()}).catch(function(e){n(e)})})).then(function(){return t=n,a(function(e,n){_.isEmpty(t)?e():s.chatQueue.addTeams({id:l.chatQueue.id,ids:_.map(t,"id")}).$promise.then(function(){e()}).catch(function(e){n(e)})});var t}).then(function(){l.pendingChanges=!1,l.startingAllowedItems=angular.copy(l.allowedItems),l.startingSelectedItems=angular.copy(l.selectedItems),i.success({title:"SUCCESS",msg:"Teams association has been updated!"})}).catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:LISTS_ASSOCIATION",msg:e.status?JSON.stringify(e.data):e.toString()})});var t},l.closeDialog=function(){e.hide()}}e.$inject=["$mdDialog","$q","toasty","api","chatQueue","$translate","Auth","crudPermissions"],angular.module("app.chat").controller("ChatQueueteamaddController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o,r,l,d,c,m){var u=this;u.currentUser=d.getCurrentUser(),u.license=s,u.setting=o,u.passwordPattern=u.setting.securePassword?/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[?!@#\$%\^&\*~\-_=+[{\]\}])(?=.{8,})/:"",u.location=n.protocol()+"://"+n.host(),u.chatQueue=c||e.params.chatQueue||{},u.userProfileSection=m&&1==m.count?m.rows[0]:null,u.crudPermissions=d.parseCrudPermissions(u.userProfileSection?u.userProfileSection.crudPermissions:null),u.hasModulePermissions={},u.selectedTab=e.params.tab||0,u.teamadddialog=function(e,n){t.show({controller:"ChatQueueteamaddController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatQueues/edit/teamadd/teamadd.html",parent:angular.element(a.body),targetEvent:n,clickOutsideToClose:!0,locals:{chatQueue:e,chatQueues:u.chatQueues?u.chatQueues.rows:[],crudPermissions:u.crudPermissions}})},u.agentadddialog=function(e,n){t.show({controller:"ChatQueueagentaddController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatQueues/edit/agentadd/agentadd.html",parent:angular.element(a.body),targetEvent:n,clickOutsideToClose:!0,locals:{chatQueue:e,chatQueues:u.chatQueues?u.chatQueues.rows:[],crudPermissions:u.crudPermissions,realtime:!1}})},u.alert=l.info,u.gotoChatQueues=function(){e.go("app.chat.chatQueues",{},{reload:"app.chat.chatQueues"})},u.saveChatQueue=function(){r.chatQueue.update({id:u.chatQueue.id},u.chatQueue).$promise.then(function(){l.success({title:"ChatQueue updated!",msg:u.chatQueue.name?u.chatQueue.name+" has been updated!":""})}).catch(function(e){l.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GETchatQueue",msg:e.data?JSON.stringify(e.data):e.toString()})})}}e.$inject=["$state","$location","$mdDialog","$document","$translate","license","setting","api","toasty","Auth","chatQueue","userProfileSection"],angular.module("app.chat").controller("ChatQueueController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o,r,l,d,c,m,u,p,g,v,h){var f=this;f.license=v,f.setting=h,f.currentUser=g.getCurrentUser(),f.chatWebsites=l||{count:0,rows:[]},f.userProfile=d,f.userProfileSection=c&&1==c.count?c.rows[0]:null,f.crudPermissions=g.parseCrudPermissions(f.userProfileSection?f.userProfileSection.crudPermissions:null),f.table="chatWebsites",f.listOrder="",f.listOrderAsc=null,f.selectedChatWebsites=[],f.query={fields:"createdAt,updatedAt,id,token,agentIdentifier,customerAlias,messageFontSize,name,key,address,remote,ListId,fidelity,timeout,agentAlias,closingQuestion,formSubmitSuccessMessage,formSubmitFailureMessage,color,color_focus,color_button,textColor,textButtonColor,backgroundColor,fontSize,header_shape,showAgentAvatar,showCustomerAvatar,alignment,verticalAlignment,labelText,messagesAlignment,defaultTitle,animation,defaultWhiteLabel,whiteLabel,defaultLogo,conditionAgreement,autoclose,enableUnmanagedNote,unmanagedMessage,skipUnmanaged,sendUnmanaged,enableCustomerWriting,waitingTitle,waitingMessage,closingMessage,noteTitle,placeholderMessage,skipMessageButton,enableRating,ratingType,ratingStarsNumber,enableFeedback,feedbackTitle,forwardTranscript,forwardTranscriptMessage,closingMessageButton,download_transcript,enableCustomerAttachment,enableSendButton,enableCustomerCheckmarks,systemAlias,enquiry_enable,enquiry_forwarding,enquiry_forwarding_address,name_title,username_placeholder,email_title,email_placeholder,header_online,hideWhenOffline,header_offline,start_chat_button,offline_chat_button,offlineMessageSubject,offlineMessageBody,offline_message,message_title,enquiry_message_placeholder,enquiry_button,rating_message,rating_send,rating_skip,onlineForm,offlineForm,mapKey,mapKeyOffline,forwardOffline,MailAccountId,openNewInteraction,forwardOfflineAddress,subjectOffline,IntervalId,timezone,waitForTheAssignedAgent,mandatoryDisposition,mandatoryDispositionPauseId,description,notificationSound,notificationShake,notificationTemplate,queueTransfer,queueTransferTimeout,agentTransfer,agentTransferTimeout,vidaooEscalation,vidaooApiKey,vidaooTopic,vidaooNote,vidaooMetadata",sort:"-updatedAt",limit:10,page:1},f.arrayagentIdentifier=_.keyBy([{option:"WebsiteAlias",value:"'website_alias'"},{option:"AgentAlias",value:"'agent_alias'"},{option:"AgentFullname",value:"'agent_fullname'"}],function(e){return _.replace(e.value,new RegExp("'","g"),"")}),f.arrayheader_shape=_.keyBy([{option:"Rounded",value:"'rounded'"},{option:"Squared",value:"'squared'"}],function(e){return _.replace(e.value,new RegExp("'","g"),"")}),f.arrayalignment=_.keyBy([{option:"bottom_right",value:"'bottom_right'"},{option:"right",value:"'right'"},{option:"left",value:"'left'"}],function(e){return _.replace(e.value,new RegExp("'","g"),"")}),f.arraymessagesAlignment=_.keyBy([{option:"alternate",value:"'alternate'"},{option:"centered",value:"'centered'"}],function(e){return _.replace(e.value,new RegExp("'","g"),"")}),f.arrayratingType=_.keyBy([{option:"Star",value:"'star'"},{option:"Thumb",value:"'thumb'"}],function(e){return _.replace(e.value,new RegExp("'","g"),"")}),f.editstate=function(e,n){t.go("app.chat.chatWebsites.edit",{id:e.id,chatWebsite:e,crudPermissions:f.crudPermissions})},f.interactionsgoto=function(e,n){t.go("app.chat.chatWebsites.edit",{id:e.id,tab:10})},f.offlinemessagesgoto=function(e,n){t.go("app.chat.chatWebsites.edit",{id:e.id,tab:11})},f.agentadddialog=function(e,n){i.show({controller:"ChatWebsiteagentaddController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/edit/agentadd/agentadd.html",parent:angular.element(s.body),targetEvent:n,clickOutsideToClose:!0,locals:{chatWebsite:e,chatWebsites:f.chatWebsites?f.chatWebsites.rows:[],crudPermissions:f.crudPermissions,realtime:!1}})},f.deleteconfirm=function(e,n){var t=i.confirm().title("Are you sure want to delete the "+_.startCase("chatWebsite")+"?").htmlContent(""+(e.name||"chatWebsite")+" will be deleted.").ariaLabel("delete chatWebsite").targetEvent(n).ok("OK").cancel("CANCEL");i.show(t).then(function(){y(e)},function(){console.log("CANCEL")})},f.success=E,f.getChatWebsites=function(){f.query.offset=(f.query.page-1)*f.query.limit,g.hasRole("admin")?f.promise=m.chatWebsite.get(f.query,E).$promise:(f.query.id=f.userProfile.id,f.query.section="ChatWebsites",f.promise=m.userProfile.getResources(f.query,E).$promise)},f.createOrEditChatWebsite=function(e,n){i.show({controller:"CreateOrEditChatWebsiteDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/create/dialog.html",parent:angular.element(s.body),targetEvent:e,clickOutsideToClose:!0,locals:{chatWebsite:n,chatWebsites:f.chatWebsites.rows,license:f.license,setting:f.setting,crudPermissions:f.crudPermissions}})},f.deleteChatWebsite=y,f.exportSelectedChatWebsites=function(){var e=angular.copy(f.selectedChatWebsites);return f.selectedChatWebsites=[],e},f.deleteSelectedChatWebsites=function(e){var n=i.confirm().title("Are you sure want to delete the selected chatWebsites?").htmlContent(""+f.selectedChatWebsites.length+" selected will be deleted.").ariaLabel("delete ChatWebsites").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){f.selectedChatWebsites.forEach(function(e){y(e)}),f.selectedChatWebsites=[]})},f.deselectChatWebsites=function(){f.selectedChatWebsites=[]},f.selectAllChatWebsites=function(){f.selectedChatWebsites=f.chatWebsites.rows},g.hasRole("admin")?m.cmList.get({fields:"id,name",sort:"name"}).$promise.then(function(e){f.lists=e.rows||[]}).catch(function(e){p.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_LISTS",msg:e.data?JSON.stringify(e.data):e.toString()})}):m.cmList.get({fields:"id,name",sort:"name"}).$promise.then(function(e){f.lists=e.rows||[]}).then(function(){return m.userProfileSection.get({userProfileId:f.currentUser.userProfileId,sectionId:301}).$promise}).then(function(e){var n=e&&e.rows?e.rows[0]:null;if(n){if(!n.autoAssociation)return m.userProfileResource.get({sectionId:n.id}).$promise.then(function(e){var n=_.map(e.rows,function(e){return _.find(f.lists,{id:e.resourceId})}),t=null;if(f.chatWebsite&&(t=_.find(f.lists,{id:Number(f.chatWebsite.ListId)})),t&&!_.some(n,["id",t.id])){var a=_.find(f.lists,{id:t.id});a.canSelect=!1,n.push(a)}f.lists=n})}else{var t=[],a=null;f.chatWebsite&&(a=_.find(f.lists,{id:Number(f.chatWebsite.ListId)}));for(var i=0;i"+e.app+" will be deleted.").ariaLabel("delete application").targetEvent(t).ok("OK").cancel("CANCEL");i.show(a).then(function(){r.chatWebsiteApps.rows.splice(n,1),l()},function(){console.log("CANCEL")})},r.getChatWebsiteApps=function(){r.promise=o.chatWebsite.getApplications(r.query,t).$promise},r.editChatWebsiteApp=n,r.editInterval=function(e,n){if(r.chatWebsiteApps.rows.length){var t=r.chatWebsiteApps.rows[n]?r.chatWebsiteApps.rows[n]:r.chatWebsiteApps.rows[0];i.show({controller:"EditChatWebsiteAppintervalDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/edit/apps/interval/dialog.html",parent:angular.element(a.body),targetEvent:e,clickOutsideToClose:!0,locals:{interval:{interval:t.interval,IntervalId:t.IntervalId,application:!0},intervals:[],crudPermissions:r.crudPermissions}}).then(function(e){e&&(t.interval=e.interval||"*,*,*,*",t.IntervalId=e.IntervalId||null,l())})}},r.deleteChatWebsiteApp=function(e){_.remove(r.chatWebsiteApps.rows,{id:e.id}),l(),s.success({title:"App deleted!",msg:e.app?e.app+" has been deleted!":""})},r.deleteSelectedChatWebsiteApps=function(e){var n=i.confirm().title("Are you sure want to delete the selected applications?").htmlContent(""+r.selectedChatWebsiteApps.length+" selected will be deleted.").ariaLabel("delete applications").targetEvent(e).ok("OK").cancel("CANCEL");i.show(n).then(function(){r.selectedChatWebsiteApps.forEach(function(e){_.remove(r.chatWebsiteApps.rows,{id:e.id})}),r.selectedChatWebsiteApps=[],l()})},r.rewriteRouting=l,r.getIntervals=function(){return o.interval.get({fields:"id,interval,IntervalId"}).$promise.then(function(e){r.intervals=e}).catch(function(e){console.error(e)})}}e.$inject=["api","$mdDialog","$document","toasty","Auth"],angular.module("app.chat").controller("ChatWebsiteActionsController",e)}(),function(){"use strict";function e(e,a,i,s,n,t,o,r,l,d){var c=this;function m(){return a(function(t,n){return a(function(n,t){return s.user.get({fields:"id,name,internal,fullname",nolimit:!0,role:"agent"}).$promise.then(function(e){n(e)}).catch(function(e){t(e)})}).then(function(e){return c.items=e.rows?e.rows:[],l.hasRole("admin")?e:c.section?c.section.autoAssociation?e:a(function(n,t){return s.userProfileResource.get({sectionId:c.section.id,nolimit:!0}).$promise.then(function(e){n(e)}).catch(function(e){t(e)})}):null}).then(function(e){var n=e&&e.rows?e.rows:[];return c.allowedItems=_.map(n,function(e){return _.find(c.items,{id:l.hasRole("admin")||c.section.autoAssociation?e.id:e.resourceId})}),c.startingAllowedItems=angular.copy(c.allowedItems),c.items.forEach(function(e){var n=_.find(c.allowedItems,{id:e.id});l.hasRole("admin")?e.isValid=!0:e.isValid=void 0!==n}),a(function(n,t){return s.chatWebsite.getAgents({id:c.chatWebsite.id,fields:"id,name,internal,fullname",nolimit:!0,role:"agent"}).$promise.then(function(e){n(e)}).catch(function(e){t(e)})})}).then(function(e){var n=e&&e.rows?e.rows:[];c.selectedItems=_.map(n,function(e){var n=_.find(c.items,{id:e.id});return n.penalty=e.UserChatWebsite?"penalty "+e.UserChatWebsite.penalty:"",n.internal=e.hasOwnProperty("internal")?"<"+e.internal+">":"",n}),c.startingSelectedItems=angular.copy(c.selectedItems),c.dualMultiselectOptions.selectedItems=c.selectedItems,c.dualMultiselectOptions.items=_.differenceBy(c.allowedItems,c.dualMultiselectOptions.selectedItems,"id"),t()}).catch(function(e){n(e)})})}c.currentUser=l.getCurrentUser(),c.chatWebsite=n,c.crudPermissions=d,c.realtime=o,c.items=[],c.allowedItems=[],c.selectedItems=[],c.startingAllowedItems=[],c.startingSelectedItems=[],c.pendingChanges=!1,c.onInit=function(){return l.hasRole("admin")?m().catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_AGENTS",msg:e.status?JSON.stringify(e.data):e.toString()})}):a(function(t,n){s.userProfileSection.get({userProfileId:c.currentUser.userProfileId,name:"Agents"}).$promise.then(function(e){var n=e&&e.rows?e.rows[0]:null;t(n)}).catch(function(e){n(e)})}).then(function(e){return c.section=e,m()}).catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:GET_AGENTS",msg:e.status?JSON.stringify(e.data):e.toString()})})},c.saveAgents=function(){var e=_.differenceBy(c.startingSelectedItems,c.selectedItems,"id"),n=_.differenceBy(c.selectedItems,c.startingSelectedItems,"id");return(t=e,a(function(e,n){_.isEmpty(t)?e():s.chatWebsite.removeAgents({id:c.chatWebsite.id,ids:_.map(t,"id")}).$promise.then(function(){e()}).catch(function(e){n(e)})})).then(function(){return t=n,a(function(e,n){_.isEmpty(t)?e():s.chatWebsite.addAgents({id:c.chatWebsite.id,ids:_.map(t,"id")}).$promise.then(function(){e()}).catch(function(e){n(e)})});var t}).then(function(){c.pendingChanges=!1,c.startingAllowedItems=angular.copy(c.allowedItems),c.startingSelectedItems=angular.copy(c.selectedItems),i.success({title:"SUCCESS",msg:"Agents association has been updated!"})}).catch(function(e){i.error({title:e.status?"API:"+e.status+" - "+e.statusText:"SYSTEM:LISTS_ASSOCIATION",msg:e.status?JSON.stringify(e.data):e.toString()})});var t},c.closeDialog=function(){e.hide()},c.dualMultiselectOptions={items:[],selectedItems:[],orderBy:"name",line1:"fullname",line2:["name","internal"],line3:"",labelAll:r.instant("CHAT.ALL_AGENTS"),labelSelected:r.instant("CHAT.SELECTED_AGENTS"),transferCallback:function(e,n){var t=_.xorBy(c.startingSelectedItems,c.selectedItems,"id");c.pendingChanges=!_.isEmpty(t)}}}e.$inject=["$mdDialog","$q","toasty","api","chatWebsite","chatWebsites","realtime","$translate","Auth","crudPermissions"],angular.module("app.chat").controller("ChatWebsiteagentaddController",e)}(),function(){"use strict";function e(e,a){var i=this;i.chatWebsite={},i.crudPermissions,i.ngFlowOptions={singleFile:!0,maxChunkRetries:1,chunkSize:8388608,simultaneousUploads:1,testChunks:!1,progressCallbacksInterval:1e3,allowDuplicateUploads:!0},i.ngFlow={flow:{}},i.dropping=!1,i.fileAdded=function(e){var n=["png","jpg"];if(!_.includes(n,e.getExtension()))return a.error({title:"Invalid extension: "+e.getExtension(),msg:"Supported extension: "+n.join()}),!1;if(8388608"+(e.name||e.id&&_.upperFirst("chatCannedAnswer #")+e.id||"chatCannedAnswer")+" will be deleted.").ariaLabel("delete chatCannedAnswer").targetEvent(n).ok("OK").cancel("CANCEL");o.show(t).then(function(){g(e)},function(){console.log("CANCEL")})},m.success=u,m.getChatWebsiteChatCannedAnswers=p,m.createOrEditChatWebsiteChatCannedAnswer=function(e,n){o.show({controller:"CreateOrEditChatCannedAnswerDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/edit/chatCannedAnswers/dialog.html",parent:angular.element(r.body),targetEvent:e,clickOutsideToClose:!0,locals:{chatWebsite:m.chatWebsite,chatCannedAnswer:n,chatCannedAnswers:m.chatWebsiteChatCannedAnswers.rows,license:null,setting:null,crudPermissions:m.crudPermissions}})},m.exportSelectedChatWebsiteChatCannedAnswers=function(){var e=angular.copy(m.selectedChatWebsiteChatCannedAnswers);return m.selectedChatWebsiteChatCannedAnswers=[],e},m.deleteChatWebsiteChatCannedAnswer=g,m.deleteSelectedChatWebsiteChatCannedAnswers=function(e){var n=o.confirm().title("Are you sure want to delete the selected chatCannedAnswers?").htmlContent(""+m.selectedChatWebsiteChatCannedAnswers.length+" selected will be deleted.").ariaLabel("delete chatCannedAnswers").targetEvent(e).ok("OK").cancel("CANCEL");o.show(n).then(function(){m.selectedChatWebsiteChatCannedAnswers.forEach(function(e){g(e)}),m.selectedChatWebsiteChatCannedAnswers=[]})}}e.$inject=["$cookies","$scope","$state","$q","$translate","$timeout","$mdDialog","$document","toasty","api","Auth"],angular.module("app.chat").controller("ChatWebsiteChatCannedAnswersController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o,r,l,d,c,m,u,p){var g=this;function v(e){a.hide(e)}g.currentUser=c.getCurrentUser(),g.errors=[],g.setting=u,g.license=m,g.crudPermissions=p,g.hasModulePermissions={},g.passwordPattern=g.setting&&g.setting.securePassword?/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[?!@#\$%\^&\*~\-_=+[{\]\}])(?=.{8,})/:"",g.title="CHAT.EDIT_CHATCANNEDANSWER",g.chatCannedAnswer=angular.copy(l),g.chatCannedAnswers=r,g.newChatCannedAnswer=!1,g.chatCannedAnswer||(g.chatCannedAnswer={},g.title="CHAT.NEW_CHATCANNEDANSWER",g.newChatCannedAnswer=!0),n.params.id&&(g.chatCannedAnswer.ChatWebsiteId=n.params.id),g.addNewChatCannedAnswer=function(){g.errors=[],d.cannedAnswer.save(g.chatCannedAnswer).$promise.then(function(e){g.chatCannedAnswers.unshift(e.toJSON()),o.success({title:"ChatCannedAnswer properly created",msg:g.chatCannedAnswer.name?g.chatCannedAnswer.name+" has been created!":""}),v(e)}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){g.errors=e.data.errors||[{message:e.toString(),type:"api.cannedAnswer.save"}];for(var n=0;n":i.instant("DASHBOARDS.NOT_ASSIGNED")}m.currentUser=c.getCurrentUser(),m.chatWebsite={},m.chatWebsiteInteractions={count:0,rows:[]},m.selectedChatWebsiteInteractions=[],m.crudPermissions,m.query={read:"null",closed:"null",sort:"-createdAt",includeAll:"true",limit:10,page:1},m.init=function(e,n,t){m.chatWebsite=e,m.crudPermissions=void 0!==n?n:{readOnly:!0,canEdit:!1,canDelete:!1},m.userProfile=t,m.query.ChatWebsiteId=m.chatWebsite.id,m.advancedSearch={fields:[{name:"Id",column:"id",type:"number"},{name:"Contact",column:"Contact",type:"autocomplete",options:{searchFields:["firstName","lastName","email"],route:{model:"cmContact",action:"get",params:{fields:"id,firstName,lastName,email",Contact:"@autocomplete",nolimit:!0}},extraOperators:["$substring"],excludedOperators:["$ne"]}},{name:"Body",column:"body",type:"text",options:{excludedOperators:["$eq","$ne"]}},{name:"Status",column:"closed",type:"select",values:[{id:0,translate:"DASHBOARDS.OPENED"},{id:1,translate:"DASHBOARDS.CLOSED"}],options:{excludedOperators:["$ne"]}},{name:"Customer Ip",column:"customerIp",type:"text",options:{excludedOperators:["$eq","$ne","$startsWith","$endsWith"]}},{name:"Agent",column:"User",type:"autocomplete",options:{table:"i",route:{model:"user",action:"get",params:{role:"agent",fields:"id,name,fullname",nolimit:!0}},searchFields:["fullname","name"],extraOperators:["$substring"],excludedOperators:["$ne"]}},{name:"Tags",column:"Tag",type:"multiselect",options:{route:{model:"tag",action:"get",params:{nolimit:!0}},excludedOperators:["$notIn"]}},{name:"Start Date",column:"createdAt",type:"date",options:{excludedOperators:["$ne"]}},{name:"Read",column:"unreadMessages",type:"select",values:[{id:1,translate:"DASHBOARDS.READ"},{id:0,translate:"DASHBOARDS.UNREAD"}],options:{excludedOperators:["$ne"]}},{name:"Disposition",column:"disposition",type:"multiselect",options:{routes:u("first")}},{name:"Second Disposition",column:"secondDisposition",type:"multiselect",options:{routes:u("second")}},{name:"Third Disposition",column:"thirdDisposition",type:"multiselect",options:{routes:u("third")}}]},d.tag.get({sort:"name"}).$promise.then(function(e){m.tags=e||{count:0,rows:[]}}).then(function(){m.quickFilters=[{name:"Start Date",key:"createdAt",type:"date",label:"DASHBOARDS.SELECT_DATE"},{name:"Messages",key:"read",type:"select",label:"DASHBOARDS.SELECT_READ_UNREAD",customOptions:[{value:0,translate:"DASHBOARDS.UNREAD"},{value:1,translate:"DASHBOARDS.READ"},{value:null,translate:"DASHBOARDS.ALL"}]},{name:"Status",key:"closed",type:"select",label:"DASHBOARDS.SELECT_STATUS",customOptions:[{value:0,translate:"DASHBOARDS.OPENED"},{value:1,translate:"DASHBOARDS.CLOSED"},{value:null,translate:"DASHBOARDS.ALL"}]},{name:"Agent",key:"UserId",type:"select",label:"DASHBOARDS.SELECT_AGENT",customOptions:[{value:"null",translate:"DASHBOARDS.NOT_ASSIGNED"},{value:void 0,translate:"DASHBOARDS.ALL"}]},{name:"Tag",key:"tag",type:"multiselect",label:"DASHBOARDS.SELECT_TAG",options:m.tags.rows,placeholder:"DASHBOARDS.TAGS"}]})},m.deleteConfirm=function(e,n){var t=o.confirm().title("Are you sure want to delete the interaction?").htmlContent(""+(e.name||e.id&&_.upperFirst("interaction #")+e.id||"interaction")+" will be deleted.").ariaLabel("delete interaction").targetEvent(n).ok("OK").cancel("CANCEL");o.show(t).then(function(){v(e)},function(){console.log("CANCEL")})},m.chatInteractionDownload=function(s,e,n){return d.chatInteraction.download({id:s.id,exists:!0,attachments:n}).$promise.then(function(e){var n=[e.buffer],t="interaction"+s.id,a=new Blob(n,{type:e.type});t="chat-interaction"+s.id+".zip";var i=window.document.createElement("a");i.setAttribute("href",URL.createObjectURL(a)),i.setAttribute("download",t),document.body.appendChild(i),i.click()}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length)for(var n=0;n"+m.selectedChatWebsiteInteractions.length+" selected will be deleted.").ariaLabel("delete interactions").targetEvent(e).ok("OK").cancel("CANCEL");o.show(n).then(function(){m.selectedChatWebsiteInteractions.forEach(function(e){v(e)}),m.selectedChatWebsiteInteractions=[]})}}e.$inject=["$cookies","$scope","$state","$q","$translate","$timeout","$mdDialog","$document","toasty","api","Auth"],angular.module("app.chat").controller("ChatWebsiteInteractionsController",e)}(),function(){"use strict";function e(e,a){var i=this;i.chatWebsite={},i.crudPermissions,i.ngFlowOptions={singleFile:!0,maxChunkRetries:1,chunkSize:8388608,simultaneousUploads:1,testChunks:!1,progressCallbacksInterval:1e3,allowDuplicateUploads:!0},i.ngFlow={flow:{}},i.dropping=!1,i.fileAdded=function(e){var n=["png","jpg"];if(!_.includes(n,e.getExtension()))return a.error({title:"Invalid extension: "+e.getExtension(),msg:"Supported extension: "+n.join()}),!1;if(8388608"+(e.name||e.id&&_.upperFirst("offlineMessage #")+e.id||"offlineMessage")+" will be deleted.").ariaLabel("delete offlineMessage").targetEvent(n).ok("OK").cancel("CANCEL");o.show(t).then(function(){g(e)},function(){console.log("CANCEL")})},m.success=u,m.getChatWebsiteOfflineMessages=p,m.createOrEditChatWebsiteOfflineMessage=function(e,n){o.show({controller:"CreateOrEditOfflineMessageDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/edit/offlineMessages/dialog.html",parent:angular.element(r.body),targetEvent:e,clickOutsideToClose:!0,locals:{chatWebsite:m.chatWebsite,offlineMessage:n,offlineMessages:m.chatWebsiteOfflineMessages.rows,license:null,setting:null,crudPermissions:m.crudPermissions}})},m.showOfflineMessageChatWebsiteOfflineMessage=function(e,t){o.show({controller:"ShowOfflineMessageOfflineMessageDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/edit/offlineMessages/dialog.html",parent:angular.element(r.body),targetEvent:e,clickOutsideToClose:!0,resolve:{message:["apiResolver","$stateParams",function(e,n){return e.resolve("chatOfflineMessage@get",{fields:"id,body",id:t.id})}],attachments:["apiResolver","$stateParams",function(e,n){return e.resolve("attachment@get",{fields:"id,name",ChatOfflineMessageId:t.id})}]}})},m.exportSelectedChatWebsiteOfflineMessages=function(){var e=angular.copy(m.selectedChatWebsiteOfflineMessages);return m.selectedChatWebsiteOfflineMessages=[],e},m.deleteChatWebsiteOfflineMessage=g,m.deleteSelectedChatWebsiteOfflineMessages=function(e){var n=o.confirm().title("Are you sure want to delete the selected offlineMessages?").htmlContent(""+m.selectedChatWebsiteOfflineMessages.length+" selected will be deleted.").ariaLabel("delete offlineMessages").targetEvent(e).ok("OK").cancel("CANCEL");o.show(n).then(function(){m.selectedChatWebsiteOfflineMessages.forEach(function(e){g(e)}),m.selectedChatWebsiteOfflineMessages=[]})}}e.$inject=["$cookies","$scope","$state","$q","$translate","$timeout","$mdDialog","$document","toasty","api","Auth"],angular.module("app.chat").controller("ChatWebsiteOfflineMessagesController",e)}(),function(){"use strict";function e(n,e,t,a,i){var s=this;s.title="CHAT.OFFLINE_MESSAGE",s.message=a,s.attachments=i.rows,s.closeDialog=function(e){n.hide(e)},s.download=function(i){return e.attachment.download({id:i.id}).$promise.then(function(e){if(e){var n=new Blob([e.buffer],{type:e.type}),t=document.createElement("a"),a=window.URL.createObjectURL(n);t.href=a,t.target="_self",t.download=i.name,document.body.appendChild(t),t.click(),setTimeout(function(){document.body.removeChild(t),window.URL.revokeObjectURL(a)},100)}}).catch(function(e){t.error({title:e.status?"API:"+e.status+" - "+e.statusText:"ATTACHMENTS:GET",msg:e.data?JSON.stringify(e.data):e.toString()})})},s.isHtml=_.isNil(s.message.body.match(/\s?|(]*>|]*>|]+>)+/i))}e.$inject=["$mdDialog","api","toasty","message","attachments"],angular.module("app.chat").controller("ShowOfflineMessageOfflineMessageDialogController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o,r,l,d,c){var m=this;function u(e){m.chatWebsiteProactiveActions=e||{count:0,rows:[]}}function p(){m.query.offset=(m.query.page-1)*m.query.limit,m.promise=d.chatWebsite.getProactiveActions(m.query,u).$promise}function g(e){d.chatProactiveAction.delete({id:e.id}).$promise.then(function(){_.remove(m.chatWebsiteProactiveActions.rows,{id:e.id}),m.chatWebsiteProactiveActions.count-=1,m.chatWebsiteProactiveActions.rows.length||p(),l.success({title:"ChatProactiveAction deleted!",msg:e.name?e.name+" has been deleted!":""})}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){m.errors=e.data.errors||[{message:e.toString(),type:"SYSTEM:GETchatWebsite"}];for(var n=0;n"+(e.name||e.id&&_.upperFirst("chatProactiveAction #")+e.id||"chatProactiveAction")+" will be deleted.").ariaLabel("delete chatProactiveAction").targetEvent(n).ok("OK").cancel("CANCEL");o.show(t).then(function(){g(e)},function(){console.log("CANCEL")})},m.success=u,m.getChatWebsiteProactiveActions=p,m.createOrEditChatWebsiteChatProactiveAction=function(e,n){o.show({controller:"CreateOrEditChatProactiveActionDialogController",controllerAs:"vm",templateUrl:"app/main/apps/chat/views/chatWebsites/edit/proactive/dialog.html",parent:angular.element(r.body),targetEvent:e,clickOutsideToClose:!0,locals:{chatWebsite:m.chatWebsite,chatProactiveAction:n,proactive:m.chatWebsiteProactiveActions.rows,license:null,setting:null,crudPermissions:m.crudPermissions}})},m.exportSelectedChatWebsiteProactiveActions=function(){var e=angular.copy(m.selectedChatWebsiteProactiveActions);return m.selectedChatWebsiteProactiveActions=[],e},m.deleteChatWebsiteChatProactiveAction=g,m.deleteSelectedChatWebsiteProactiveActions=function(e){var n=o.confirm().title("Are you sure want to delete the selected proactive?").htmlContent(""+m.selectedChatWebsiteProactiveActions.length+" selected will be deleted.").ariaLabel("delete proactive").targetEvent(e).ok("OK").cancel("CANCEL");o.show(n).then(function(){m.selectedChatWebsiteProactiveActions.forEach(function(e){g(e)}),m.selectedChatWebsiteProactiveActions=[]})}}e.$inject=["$cookies","$scope","$state","$q","$translate","$timeout","$mdDialog","$document","toasty","api","Auth"],angular.module("app.chat").controller("ChatWebsiteProactiveActionsController",e)}(),function(){"use strict";function e(e,n,t,a,i,s,o,r,l,d,c,m,u,p){var g=this;function v(e){a.hide(e)}g.currentUser=c.getCurrentUser(),g.errors=[],g.setting=u,g.license=m,g.crudPermissions=p,g.hasModulePermissions={},g.passwordPattern=g.setting&&g.setting.securePassword?/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[?!@#\$%\^&\*~\-_=+[{\]\}])(?=.{8,})/:"",g.title="CHAT.EDIT_CHATPROACTIVEACTION",g.chatProactiveAction=angular.copy(l),g.proactive=r,g.newChatProactiveAction=!1,g.chatProactiveAction||(g.chatProactiveAction={type:"mouseOver"},g.title="CHAT.NEW_CHATPROACTIVEACTION",g.newChatProactiveAction=!0),n.params.id&&(g.chatProactiveAction.ChatWebsiteId=n.params.id),g.addNewChatProactiveAction=function(){g.errors=[],d.chatProactiveAction.save(g.chatProactiveAction).$promise.then(function(e){g.proactive.unshift(e.toJSON()),o.success({title:"ChatProactiveAction properly created",msg:g.chatProactiveAction.name?g.chatProactiveAction.name+" has been created!":""}),v(e)}).catch(function(e){if(e.data&&e.data.errors&&e.data.errors.length){g.errors=e.data.errors||[{message:e.toString(),type:"api.chatProactiveAction.save"}];for(var n=0;n<\/script>',n.end="\n\x3c!-- START Motion Chat Script --\x3e"},n.info={},e.$watch("vm_ac.chatWebsite.remote",function(e){n.script='\n + + + + + + + +
+
+ + +
+
+ +
+ + + {{ actualTitle }} + + + + + + + +
+ +
+ + + +
+ +
+ +
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + diff --git a/snippet/3.2.0/scripts/app.js b/snippet/3.2.0/scripts/app.js new file mode 100644 index 0000000..7c3a8af --- /dev/null +++ b/snippet/3.2.0/scripts/app.js @@ -0,0 +1,42 @@ +/*! + * ************************************************************************* + * * * + * * xCALLY Motion - The Omnichannel Contact Center * + * * Copyright (c) Xenialab s.r.l. All Rights Reserved * + * * * + * ************************************************************************* + * * * + * * Email: info@xcally.com * + * * Website: https://www.xcally.com * + * * * + * ************************************************************************* + * * * + * * The SOFTWARE PRODUCT is protected by copyright laws and international * + * * copyright treaties, as well as other intellectual property laws and * + * * treaties. The SOFTWARE PRODUCT is licensed, not sold. * + * * * + * ************************************************************************* + */ +"use strict"; +(self["webpackChunkmotion_chat"] = self["webpackChunkmotion_chat"] || []).push([["app"],{ + +/***/ "./src/styles/app.scss": +/*!*****************************!*\ + !*** ./src/styles/app.scss ***! + \*****************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }) + +}, +/******/ __webpack_require__ => { // webpackRuntimeModules +/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) +/******/ __webpack_require__.O(0, ["chat"], () => (__webpack_exec__("./src/styles/app.scss"))); +/******/ var __webpack_exports__ = __webpack_require__.O(); +/******/ } +]); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/snippet/3.2.0/scripts/app.js.map b/snippet/3.2.0/scripts/app.js.map new file mode 100644 index 0000000..9b8ff55 --- /dev/null +++ b/snippet/3.2.0/scripts/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/app.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA","sources":["webpack://motion-chat/./src/styles/app.scss?077d"],"sourcesContent":["// extracted by mini-css-extract-plugin\nexport {};"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/snippet/3.2.0/scripts/chat.js b/snippet/3.2.0/scripts/chat.js new file mode 100644 index 0000000..fd8c1ab --- /dev/null +++ b/snippet/3.2.0/scripts/chat.js @@ -0,0 +1,149319 @@ +(self["webpackChunkmotion_chat"] = self["webpackChunkmotion_chat"] || []).push([["chat"],{ + +/***/ "./node_modules/@flowjs/flow.js/src/flow.js": +/*!**************************************************!*\ + !*** ./node_modules/@flowjs/flow.js/src/flow.js ***! + \**************************************************/ +/***/ ((module, exports, __webpack_require__) => { + +/* module decorator */ module = __webpack_require__.nmd(module); +var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/** + * @license MIT + */ +(function(window, document, undefined) {'use strict'; + if (!window || !document) { + console.warn('Flowjs needs window and document objects to work'); + return; + } + // ie10+ + var ie10plus = window.navigator.msPointerEnabled; + /** + * Flow.js is a library providing multiple simultaneous, stable and + * resumable uploads via the HTML5 File API. + * @param [opts] + * @param {number|Function} [opts.chunkSize] + * @param {bool} [opts.forceChunkSize] + * @param {number} [opts.simultaneousUploads] + * @param {bool} [opts.singleFile] + * @param {string} [opts.fileParameterName] + * @param {number} [opts.progressCallbacksInterval] + * @param {number} [opts.speedSmoothingFactor] + * @param {Object|Function} [opts.query] + * @param {Object|Function} [opts.headers] + * @param {bool} [opts.withCredentials] + * @param {Function} [opts.preprocess] + * @param {string} [opts.method] + * @param {string|Function} [opts.testMethod] + * @param {string|Function} [opts.uploadMethod] + * @param {bool} [opts.prioritizeFirstAndLastChunk] + * @param {bool} [opts.allowDuplicateUploads] + * @param {string|Function} [opts.target] + * @param {number} [opts.maxChunkRetries] + * @param {number} [opts.chunkRetryInterval] + * @param {Array.} [opts.permanentErrors] + * @param {Array.} [opts.successStatuses] + * @param {Function} [opts.initFileFn] + * @param {Function} [opts.readFileFn] + * @param {Function} [opts.generateUniqueIdentifier] + * @constructor + */ + function Flow(opts) { + /** + * Supported by browser? + * @type {boolean} + */ + this.support = ( + typeof File !== 'undefined' && + typeof Blob !== 'undefined' && + typeof FileList !== 'undefined' && + ( + !!Blob.prototype.slice || !!Blob.prototype.webkitSlice || !!Blob.prototype.mozSlice || + false + ) // slicing files support + ); + + if (!this.support) { + return ; + } + + /** + * Check if directory upload is supported + * @type {boolean} + */ + this.supportDirectory = ( + /Chrome/.test(window.navigator.userAgent) || + /Firefox/.test(window.navigator.userAgent) || + /Edge/.test(window.navigator.userAgent) + ); + + /** + * List of FlowFile objects + * @type {Array.} + */ + this.files = []; + + /** + * Default options for flow.js + * @type {Object} + */ + this.defaults = { + chunkSize: 1024 * 1024, + forceChunkSize: false, + simultaneousUploads: 3, + singleFile: false, + fileParameterName: 'file', + progressCallbacksInterval: 500, + speedSmoothingFactor: 0.1, + query: {}, + headers: {}, + withCredentials: false, + preprocess: null, + changeRawDataBeforeSend: null, + method: 'multipart', + testMethod: 'GET', + uploadMethod: 'POST', + prioritizeFirstAndLastChunk: false, + allowDuplicateUploads: false, + target: '/', + testChunks: true, + generateUniqueIdentifier: null, + maxChunkRetries: 0, + chunkRetryInterval: null, + permanentErrors: [404, 413, 415, 500, 501], + successStatuses: [200, 201, 202], + onDropStopPropagation: false, + initFileFn: null, + readFileFn: webAPIFileRead + }; + + /** + * Current options + * @type {Object} + */ + this.opts = {}; + + /** + * List of events: + * key stands for event name + * value array list of callbacks + * @type {} + */ + this.events = {}; + + var $ = this; + + /** + * On drop event + * @function + * @param {MouseEvent} event + */ + this.onDrop = function (event) { + if ($.opts.onDropStopPropagation) { + event.stopPropagation(); + } + event.preventDefault(); + var dataTransfer = event.dataTransfer; + if (dataTransfer.items && dataTransfer.items[0] && + dataTransfer.items[0].webkitGetAsEntry) { + $.webkitReadDataTransfer(event); + } else { + $.addFiles(dataTransfer.files, event); + } + }; + + /** + * Prevent default + * @function + * @param {MouseEvent} event + */ + this.preventEvent = function (event) { + event.preventDefault(); + }; + + + /** + * Current options + * @type {Object} + */ + this.opts = Flow.extend({}, this.defaults, opts || {}); + + } + + Flow.prototype = { + /** + * Set a callback for an event, possible events: + * fileSuccess(file), fileProgress(file), fileAdded(file, event), + * fileRemoved(file), fileRetry(file), fileError(file, message), + * complete(), progress(), error(message, file), pause() + * @function + * @param {string} event + * @param {Function} callback + */ + on: function (event, callback) { + event = event.toLowerCase(); + if (!this.events.hasOwnProperty(event)) { + this.events[event] = []; + } + this.events[event].push(callback); + }, + + /** + * Remove event callback + * @function + * @param {string} [event] removes all events if not specified + * @param {Function} [fn] removes all callbacks of event if not specified + */ + off: function (event, fn) { + if (event !== undefined) { + event = event.toLowerCase(); + if (fn !== undefined) { + if (this.events.hasOwnProperty(event)) { + arrayRemove(this.events[event], fn); + } + } else { + delete this.events[event]; + } + } else { + this.events = {}; + } + }, + + /** + * Fire an event + * @function + * @param {string} event event name + * @param {...} args arguments of a callback + * @return {bool} value is false if at least one of the event handlers which handled this event + * returned false. Otherwise it returns true. + */ + fire: function (event, args) { + // `arguments` is an object, not array, in FF, so: + args = Array.prototype.slice.call(arguments); + event = event.toLowerCase(); + var preventDefault = false; + if (this.events.hasOwnProperty(event)) { + each(this.events[event], function (callback) { + preventDefault = callback.apply(this, args.slice(1)) === false || preventDefault; + }, this); + } + if (event != 'catchall') { + args.unshift('catchAll'); + preventDefault = this.fire.apply(this, args) === false || preventDefault; + } + return !preventDefault; + }, + + /** + * Read webkit dataTransfer object + * @param event + */ + webkitReadDataTransfer: function (event) { + var $ = this; + var queue = event.dataTransfer.items.length; + var files = []; + each(event.dataTransfer.items, function (item) { + var entry = item.webkitGetAsEntry(); + if (!entry) { + decrement(); + return ; + } + if (entry.isFile) { + // due to a bug in Chrome's File System API impl - #149735 + fileReadSuccess(item.getAsFile(), entry.fullPath); + } else { + readDirectory(entry.createReader()); + } + }); + function readDirectory(reader) { + reader.readEntries(function (entries) { + if (entries.length) { + queue += entries.length; + each(entries, function(entry) { + if (entry.isFile) { + var fullPath = entry.fullPath; + entry.file(function (file) { + fileReadSuccess(file, fullPath); + }, readError); + } else if (entry.isDirectory) { + readDirectory(entry.createReader()); + } + }); + readDirectory(reader); + } else { + decrement(); + } + }, readError); + } + function fileReadSuccess(file, fullPath) { + // relative path should not start with "/" + file.relativePath = fullPath.substring(1); + files.push(file); + decrement(); + } + function readError(fileError) { + decrement(); + throw fileError; + } + function decrement() { + if (--queue == 0) { + $.addFiles(files, event); + } + } + }, + + /** + * Generate unique identifier for a file + * @function + * @param {FlowFile} file + * @returns {string} + */ + generateUniqueIdentifier: function (file) { + var custom = this.opts.generateUniqueIdentifier; + if (typeof custom === 'function') { + return custom(file); + } + // Some confusion in different versions of Firefox + var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name; + return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, ''); + }, + + /** + * Upload next chunk from the queue + * @function + * @returns {boolean} + * @private + */ + uploadNextChunk: function (preventEvents) { + // In some cases (such as videos) it's really handy to upload the first + // and last chunk of a file quickly; this let's the server check the file's + // metadata and determine if there's even a point in continuing. + var found = false; + if (this.opts.prioritizeFirstAndLastChunk) { + each(this.files, function (file) { + if (!file.paused && file.chunks.length && + file.chunks[0].status() === 'pending') { + file.chunks[0].send(); + found = true; + return false; + } + if (!file.paused && file.chunks.length > 1 && + file.chunks[file.chunks.length - 1].status() === 'pending') { + file.chunks[file.chunks.length - 1].send(); + found = true; + return false; + } + }); + if (found) { + return found; + } + } + + // Now, simply look for the next, best thing to upload + each(this.files, function (file) { + if (!file.paused) { + each(file.chunks, function (chunk) { + if (chunk.status() === 'pending') { + chunk.send(); + found = true; + return false; + } + }); + } + if (found) { + return false; + } + }); + if (found) { + return true; + } + + // The are no more outstanding chunks to upload, check is everything is done + var outstanding = false; + each(this.files, function (file) { + if (!file.isComplete()) { + outstanding = true; + return false; + } + }); + if (!outstanding && !preventEvents) { + // All chunks have been uploaded, complete + async(function () { + this.fire('complete'); + }, this); + } + return false; + }, + + + /** + * Assign a browse action to one or more DOM nodes. + * @function + * @param {Element|Array.} domNodes + * @param {boolean} isDirectory Pass in true to allow directories to + * @param {boolean} singleFile prevent multi file upload + * @param {Object} attributes set custom attributes: + * http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes + * eg: accept: 'image/*' + * be selected (Chrome only). + */ + assignBrowse: function (domNodes, isDirectory, singleFile, attributes) { + if (domNodes instanceof Element) { + domNodes = [domNodes]; + } + + each(domNodes, function (domNode) { + var input; + if (domNode.tagName === 'INPUT' && domNode.type === 'file') { + input = domNode; + } else { + input = document.createElement('input'); + input.setAttribute('type', 'file'); + // display:none - not working in opera 12 + extend(input.style, { + visibility: 'hidden', + position: 'absolute', + width: '1px', + height: '1px' + }); + // for opera 12 browser, input must be assigned to a document + domNode.appendChild(input); + // https://developer.mozilla.org/en/using_files_from_web_applications) + // event listener is executed two times + // first one - original mouse click event + // second - input.click(), input is inside domNode + domNode.addEventListener('click', function() { + input.click(); + }, false); + } + if (!this.opts.singleFile && !singleFile) { + input.setAttribute('multiple', 'multiple'); + } + if (isDirectory) { + input.setAttribute('webkitdirectory', 'webkitdirectory'); + } + each(attributes, function (value, key) { + input.setAttribute(key, value); + }); + // When new files are added, simply append them to the overall list + var $ = this; + input.addEventListener('change', function (e) { + if (e.target.value) { + $.addFiles(e.target.files, e); + e.target.value = ''; + } + }, false); + }, this); + }, + + /** + * Assign one or more DOM nodes as a drop target. + * @function + * @param {Element|Array.} domNodes + */ + assignDrop: function (domNodes) { + if (typeof domNodes.length === 'undefined') { + domNodes = [domNodes]; + } + each(domNodes, function (domNode) { + domNode.addEventListener('dragover', this.preventEvent, false); + domNode.addEventListener('dragenter', this.preventEvent, false); + domNode.addEventListener('drop', this.onDrop, false); + }, this); + }, + + /** + * Un-assign drop event from DOM nodes + * @function + * @param domNodes + */ + unAssignDrop: function (domNodes) { + if (typeof domNodes.length === 'undefined') { + domNodes = [domNodes]; + } + each(domNodes, function (domNode) { + domNode.removeEventListener('dragover', this.preventEvent); + domNode.removeEventListener('dragenter', this.preventEvent); + domNode.removeEventListener('drop', this.onDrop); + }, this); + }, + + /** + * Returns a boolean indicating whether or not the instance is currently + * uploading anything. + * @function + * @returns {boolean} + */ + isUploading: function () { + var uploading = false; + each(this.files, function (file) { + if (file.isUploading()) { + uploading = true; + return false; + } + }); + return uploading; + }, + + /** + * should upload next chunk + * @function + * @returns {boolean|number} + */ + _shouldUploadNext: function () { + var num = 0; + var should = true; + var simultaneousUploads = this.opts.simultaneousUploads; + each(this.files, function (file) { + each(file.chunks, function(chunk) { + if (chunk.status() === 'uploading') { + num++; + if (num >= simultaneousUploads) { + should = false; + return false; + } + } + }); + }); + // if should is true then return uploading chunks's length + return should && num; + }, + + /** + * Start or resume uploading. + * @function + */ + upload: function () { + // Make sure we don't start too many uploads at once + var ret = this._shouldUploadNext(); + if (ret === false) { + return; + } + // Kick off the queue + this.fire('uploadStart'); + var started = false; + for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) { + started = this.uploadNextChunk(true) || started; + } + if (!started) { + async(function () { + this.fire('complete'); + }, this); + } + }, + + /** + * Resume uploading. + * @function + */ + resume: function () { + each(this.files, function (file) { + if (!file.isComplete()) { + file.resume(); + } + }); + }, + + /** + * Pause uploading. + * @function + */ + pause: function () { + each(this.files, function (file) { + file.pause(); + }); + }, + + /** + * Cancel upload of all FlowFile objects and remove them from the list. + * @function + */ + cancel: function () { + for (var i = this.files.length - 1; i >= 0; i--) { + this.files[i].cancel(); + } + }, + + /** + * Returns a number between 0 and 1 indicating the current upload progress + * of all files. + * @function + * @returns {number} + */ + progress: function () { + var totalDone = 0; + var totalSize = 0; + // Resume all chunks currently being uploaded + each(this.files, function (file) { + totalDone += file.progress() * file.size; + totalSize += file.size; + }); + return totalSize > 0 ? totalDone / totalSize : 0; + }, + + /** + * Add a HTML5 File object to the list of files. + * @function + * @param {File} file + * @param {Event} [event] event is optional + */ + addFile: function (file, event) { + this.addFiles([file], event); + }, + + /** + * Add a HTML5 File object to the list of files. + * @function + * @param {FileList|Array} fileList + * @param {Event} [event] event is optional + */ + addFiles: function (fileList, event) { + var files = []; + each(fileList, function (file) { + // https://github.com/flowjs/flow.js/issues/55 + if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) { + var uniqueIdentifier = this.generateUniqueIdentifier(file); + if (this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(uniqueIdentifier)) { + var f = new FlowFile(this, file, uniqueIdentifier); + if (this.fire('fileAdded', f, event)) { + files.push(f); + } + } + } + }, this); + if (this.fire('filesAdded', files, event)) { + each(files, function (file) { + if (this.opts.singleFile && this.files.length > 0) { + this.removeFile(this.files[0]); + } + this.files.push(file); + }, this); + this.fire('filesSubmitted', files, event); + } + }, + + + /** + * Cancel upload of a specific FlowFile object from the list. + * @function + * @param {FlowFile} file + */ + removeFile: function (file) { + for (var i = this.files.length - 1; i >= 0; i--) { + if (this.files[i] === file) { + this.files.splice(i, 1); + file.abort(); + this.fire('fileRemoved', file); + } + } + }, + + /** + * Look up a FlowFile object by its unique identifier. + * @function + * @param {string} uniqueIdentifier + * @returns {boolean|FlowFile} false if file was not found + */ + getFromUniqueIdentifier: function (uniqueIdentifier) { + var ret = false; + each(this.files, function (file) { + if (file.uniqueIdentifier === uniqueIdentifier) { + ret = file; + } + }); + return ret; + }, + + /** + * Returns the total size of all files in bytes. + * @function + * @returns {number} + */ + getSize: function () { + var totalSize = 0; + each(this.files, function (file) { + totalSize += file.size; + }); + return totalSize; + }, + + /** + * Returns the total size uploaded of all files in bytes. + * @function + * @returns {number} + */ + sizeUploaded: function () { + var size = 0; + each(this.files, function (file) { + size += file.sizeUploaded(); + }); + return size; + }, + + /** + * Returns remaining time to upload all files in seconds. Accuracy is based on average speed. + * If speed is zero, time remaining will be equal to positive infinity `Number.POSITIVE_INFINITY` + * @function + * @returns {number} + */ + timeRemaining: function () { + var sizeDelta = 0; + var averageSpeed = 0; + each(this.files, function (file) { + if (!file.paused && !file.error) { + sizeDelta += file.size - file.sizeUploaded(); + averageSpeed += file.averageSpeed; + } + }); + if (sizeDelta && !averageSpeed) { + return Number.POSITIVE_INFINITY; + } + if (!sizeDelta && !averageSpeed) { + return 0; + } + return Math.floor(sizeDelta / averageSpeed); + } + }; + + + + + + + /** + * FlowFile class + * @name FlowFile + * @param {Flow} flowObj + * @param {File} file + * @param {string} uniqueIdentifier + * @constructor + */ + function FlowFile(flowObj, file, uniqueIdentifier) { + + /** + * Reference to parent Flow instance + * @type {Flow} + */ + this.flowObj = flowObj; + + /** + * Used to store the bytes read + * @type {Blob|string} + */ + this.bytes = null; + + /** + * Reference to file + * @type {File} + */ + this.file = file; + + /** + * File name. Some confusion in different versions of Firefox + * @type {string} + */ + this.name = file.fileName || file.name; + + /** + * File size + * @type {number} + */ + this.size = file.size; + + /** + * Relative file path + * @type {string} + */ + this.relativePath = file.relativePath || file.webkitRelativePath || this.name; + + /** + * File unique identifier + * @type {string} + */ + this.uniqueIdentifier = (uniqueIdentifier === undefined ? flowObj.generateUniqueIdentifier(file) : uniqueIdentifier); + + /** + * Size of Each Chunk + * @type {number} + */ + this.chunkSize = 0; + + /** + * List of chunks + * @type {Array.} + */ + this.chunks = []; + + /** + * Indicated if file is paused + * @type {boolean} + */ + this.paused = false; + + /** + * Indicated if file has encountered an error + * @type {boolean} + */ + this.error = false; + + /** + * Average upload speed + * @type {number} + */ + this.averageSpeed = 0; + + /** + * Current upload speed + * @type {number} + */ + this.currentSpeed = 0; + + /** + * Date then progress was called last time + * @type {number} + * @private + */ + this._lastProgressCallback = Date.now(); + + /** + * Previously uploaded file size + * @type {number} + * @private + */ + this._prevUploadedSize = 0; + + /** + * Holds previous progress + * @type {number} + * @private + */ + this._prevProgress = 0; + + this.bootstrap(); + } + + FlowFile.prototype = { + /** + * Update speed parameters + * @link http://stackoverflow.com/questions/2779600/how-to-estimate-download-time-remaining-accurately + * @function + */ + measureSpeed: function () { + var timeSpan = Date.now() - this._lastProgressCallback; + if (!timeSpan) { + return ; + } + var smoothingFactor = this.flowObj.opts.speedSmoothingFactor; + var uploaded = this.sizeUploaded(); + // Prevent negative upload speed after file upload resume + this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0); + this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed; + this._prevUploadedSize = uploaded; + }, + + /** + * For internal usage only. + * Callback when something happens within the chunk. + * @function + * @param {FlowChunk} chunk + * @param {string} event can be 'progress', 'success', 'error' or 'retry' + * @param {string} [message] + */ + chunkEvent: function (chunk, event, message) { + switch (event) { + case 'progress': + if (Date.now() - this._lastProgressCallback < + this.flowObj.opts.progressCallbacksInterval) { + break; + } + this.measureSpeed(); + this.flowObj.fire('fileProgress', this, chunk); + this.flowObj.fire('progress'); + this._lastProgressCallback = Date.now(); + break; + case 'error': + this.error = true; + this.abort(true); + this.flowObj.fire('fileError', this, message, chunk); + this.flowObj.fire('error', message, this, chunk); + break; + case 'success': + if (this.error) { + return; + } + this.measureSpeed(); + this.flowObj.fire('fileProgress', this, chunk); + this.flowObj.fire('progress'); + this._lastProgressCallback = Date.now(); + if (this.isComplete()) { + this.currentSpeed = 0; + this.averageSpeed = 0; + this.flowObj.fire('fileSuccess', this, message, chunk); + } + break; + case 'retry': + this.flowObj.fire('fileRetry', this, chunk); + break; + } + }, + + /** + * Pause file upload + * @function + */ + pause: function() { + this.paused = true; + this.abort(); + }, + + /** + * Resume file upload + * @function + */ + resume: function() { + this.paused = false; + this.flowObj.upload(); + }, + + /** + * Abort current upload + * @function + */ + abort: function (reset) { + this.currentSpeed = 0; + this.averageSpeed = 0; + var chunks = this.chunks; + if (reset) { + this.chunks = []; + } + each(chunks, function (c) { + if (c.status() === 'uploading') { + c.abort(); + this.flowObj.uploadNextChunk(); + } + }, this); + }, + + /** + * Cancel current upload and remove from a list + * @function + */ + cancel: function () { + this.flowObj.removeFile(this); + }, + + /** + * Retry aborted file upload + * @function + */ + retry: function () { + this.bootstrap(); + this.flowObj.upload(); + }, + + /** + * Clear current chunks and slice file again + * @function + */ + bootstrap: function () { + if (typeof this.flowObj.opts.initFileFn === "function") { + this.flowObj.opts.initFileFn(this); + } + + this.abort(true); + this.error = false; + // Rebuild stack of chunks from file + this._prevProgress = 0; + var round = this.flowObj.opts.forceChunkSize ? Math.ceil : Math.floor; + this.chunkSize = evalOpts(this.flowObj.opts.chunkSize, this); + var chunks = Math.max( + round(this.size / this.chunkSize), 1 + ); + for (var offset = 0; offset < chunks; offset++) { + this.chunks.push( + new FlowChunk(this.flowObj, this, offset) + ); + } + }, + + /** + * Get current upload progress status + * @function + * @returns {number} from 0 to 1 + */ + progress: function () { + if (this.error) { + return 1; + } + if (this.chunks.length === 1) { + this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress()); + return this._prevProgress; + } + // Sum up progress across everything + var bytesLoaded = 0; + each(this.chunks, function (c) { + // get chunk progress relative to entire file + bytesLoaded += c.progress() * (c.endByte - c.startByte); + }); + var percent = bytesLoaded / this.size; + // We don't want to lose percentages when an upload is paused + this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent); + return this._prevProgress; + }, + + /** + * Indicates if file is being uploaded at the moment + * @function + * @returns {boolean} + */ + isUploading: function () { + var uploading = false; + each(this.chunks, function (chunk) { + if (chunk.status() === 'uploading') { + uploading = true; + return false; + } + }); + return uploading; + }, + + /** + * Indicates if file is has finished uploading and received a response + * @function + * @returns {boolean} + */ + isComplete: function () { + var outstanding = false; + each(this.chunks, function (chunk) { + var status = chunk.status(); + if (status === 'pending' || status === 'uploading' || status === 'reading' || chunk.preprocessState === 1 || chunk.readState === 1) { + outstanding = true; + return false; + } + }); + return !outstanding; + }, + + /** + * Count total size uploaded + * @function + * @returns {number} + */ + sizeUploaded: function () { + var size = 0; + each(this.chunks, function (chunk) { + size += chunk.sizeUploaded(); + }); + return size; + }, + + /** + * Returns remaining time to finish upload file in seconds. Accuracy is based on average speed. + * If speed is zero, time remaining will be equal to positive infinity `Number.POSITIVE_INFINITY` + * @function + * @returns {number} + */ + timeRemaining: function () { + if (this.paused || this.error) { + return 0; + } + var delta = this.size - this.sizeUploaded(); + if (delta && !this.averageSpeed) { + return Number.POSITIVE_INFINITY; + } + if (!delta && !this.averageSpeed) { + return 0; + } + return Math.floor(delta / this.averageSpeed); + }, + + /** + * Get file type + * @function + * @returns {string} + */ + getType: function () { + return this.file.type && this.file.type.split('/')[1]; + }, + + /** + * Get file extension + * @function + * @returns {string} + */ + getExtension: function () { + return this.name.substr((~-this.name.lastIndexOf(".") >>> 0) + 2).toLowerCase(); + } + }; + + /** + * Default read function using the webAPI + * + * @function webAPIFileRead(fileObj, startByte, endByte, fileType, chunk) + * + */ + function webAPIFileRead(fileObj, startByte, endByte, fileType, chunk) { + var function_name = 'slice'; + + if (fileObj.file.slice) + function_name = 'slice'; + else if (fileObj.file.mozSlice) + function_name = 'mozSlice'; + else if (fileObj.file.webkitSlice) + function_name = 'webkitSlice'; + + chunk.readFinished(fileObj.file[function_name](startByte, endByte, fileType)); + } + + + /** + * Class for storing a single chunk + * @name FlowChunk + * @param {Flow} flowObj + * @param {FlowFile} fileObj + * @param {number} offset + * @constructor + */ + function FlowChunk(flowObj, fileObj, offset) { + + /** + * Reference to parent flow object + * @type {Flow} + */ + this.flowObj = flowObj; + + /** + * Reference to parent FlowFile object + * @type {FlowFile} + */ + this.fileObj = fileObj; + + /** + * File offset + * @type {number} + */ + this.offset = offset; + + /** + * Indicates if chunk existence was checked on the server + * @type {boolean} + */ + this.tested = false; + + /** + * Number of retries performed + * @type {number} + */ + this.retries = 0; + + /** + * Pending retry + * @type {boolean} + */ + this.pendingRetry = false; + + /** + * Preprocess state + * @type {number} 0 = unprocessed, 1 = processing, 2 = finished + */ + this.preprocessState = 0; + + /** + * Read state + * @type {number} 0 = not read, 1 = reading, 2 = finished + */ + this.readState = 0; + + + /** + * Bytes transferred from total request size + * @type {number} + */ + this.loaded = 0; + + /** + * Total request size + * @type {number} + */ + this.total = 0; + + /** + * Size of a chunk + * @type {number} + */ + this.chunkSize = this.fileObj.chunkSize; + + /** + * Chunk start byte in a file + * @type {number} + */ + this.startByte = this.offset * this.chunkSize; + + /** + * A specific filename for this chunk which otherwise default to the main name + * @type {string} + */ + this.filename = null; + + /** + * Compute the endbyte in a file + * + */ + this.computeEndByte = function() { + var endByte = Math.min(this.fileObj.size, (this.offset + 1) * this.chunkSize); + if (this.fileObj.size - endByte < this.chunkSize && !this.flowObj.opts.forceChunkSize) { + // The last chunk will be bigger than the chunk size, + // but less than 2 * this.chunkSize + endByte = this.fileObj.size; + } + return endByte; + } + + /** + * Chunk end byte in a file + * @type {number} + */ + this.endByte = this.computeEndByte(); + + /** + * XMLHttpRequest + * @type {XMLHttpRequest} + */ + this.xhr = null; + + var $ = this; + + /** + * Send chunk event + * @param event + * @param {...} args arguments of a callback + */ + this.event = function (event, args) { + args = Array.prototype.slice.call(arguments); + args.unshift($); + $.fileObj.chunkEvent.apply($.fileObj, args); + }; + /** + * Catch progress event + * @param {ProgressEvent} event + */ + this.progressHandler = function(event) { + if (event.lengthComputable) { + $.loaded = event.loaded ; + $.total = event.total; + } + $.event('progress', event); + }; + + /** + * Catch test event + * @param {Event} event + */ + this.testHandler = function(event) { + var status = $.status(true); + if (status === 'error') { + $.event(status, $.message()); + $.flowObj.uploadNextChunk(); + } else if (status === 'success') { + $.tested = true; + $.event(status, $.message()); + $.flowObj.uploadNextChunk(); + } else if (!$.fileObj.paused) { + // Error might be caused by file pause method + // Chunks does not exist on the server side + $.tested = true; + $.send(); + } + }; + + /** + * Upload has stopped + * @param {Event} event + */ + this.doneHandler = function(event) { + var status = $.status(); + if (status === 'success' || status === 'error') { + delete this.data; + $.event(status, $.message()); + $.flowObj.uploadNextChunk(); + } else if (!$.fileObj.paused) { + $.event('retry', $.message()); + $.pendingRetry = true; + $.abort(); + $.retries++; + var retryInterval = $.flowObj.opts.chunkRetryInterval; + if (retryInterval !== null) { + setTimeout(function () { + $.send(); + }, retryInterval); + } else { + $.send(); + } + } + }; + } + + FlowChunk.prototype = { + /** + * Get params for a request + * @function + */ + getParams: function () { + return { + flowChunkNumber: this.offset + 1, + flowChunkSize: this.chunkSize, + flowCurrentChunkSize: this.endByte - this.startByte, + flowTotalSize: this.fileObj.size, + flowIdentifier: this.fileObj.uniqueIdentifier, + flowFilename: this.fileObj.name, + flowRelativePath: this.fileObj.relativePath, + flowTotalChunks: this.fileObj.chunks.length + }; + }, + + /** + * Get target option with query params + * @function + * @param params + * @returns {string} + */ + getTarget: function(target, params){ + if (params.length == 0) { + return target; + } + + if(target.indexOf('?') < 0) { + target += '?'; + } else { + target += '&'; + } + return target + params.join('&'); + }, + + /** + * Makes a GET request without any data to see if the chunk has already + * been uploaded in a previous session + * @function + */ + test: function () { + // Set up request and listen for event + this.xhr = new XMLHttpRequest(); + this.xhr.addEventListener("load", this.testHandler, false); + this.xhr.addEventListener("error", this.testHandler, false); + var testMethod = evalOpts(this.flowObj.opts.testMethod, this.fileObj, this); + var data = this.prepareXhrRequest(testMethod, true); + this.xhr.send(data); + }, + + /** + * Finish preprocess state + * @function + */ + preprocessFinished: function () { + // Re-compute the endByte after the preprocess function to allow an + // implementer of preprocess to set the fileObj size + this.endByte = this.computeEndByte(); + + this.preprocessState = 2; + this.send(); + }, + + /** + * Finish read state + * @function + */ + readFinished: function (bytes) { + this.readState = 2; + this.bytes = bytes; + this.send(); + }, + + + /** + * Uploads the actual data in a POST call + * @function + */ + send: function () { + var preprocess = this.flowObj.opts.preprocess; + var read = this.flowObj.opts.readFileFn; + if (typeof preprocess === 'function') { + switch (this.preprocessState) { + case 0: + this.preprocessState = 1; + preprocess(this); + return; + case 1: + return; + } + } + switch (this.readState) { + case 0: + this.readState = 1; + read(this.fileObj, this.startByte, this.endByte, this.fileObj.file.type, this); + return; + case 1: + return; + } + if (this.flowObj.opts.testChunks && !this.tested) { + this.test(); + return; + } + + this.loaded = 0; + this.total = 0; + this.pendingRetry = false; + + // Set up request and listen for event + this.xhr = new XMLHttpRequest(); + this.xhr.upload.addEventListener('progress', this.progressHandler, false); + this.xhr.addEventListener("load", this.doneHandler, false); + this.xhr.addEventListener("error", this.doneHandler, false); + + var uploadMethod = evalOpts(this.flowObj.opts.uploadMethod, this.fileObj, this); + var data = this.prepareXhrRequest(uploadMethod, false, this.flowObj.opts.method, this.bytes); + var changeRawDataBeforeSend = this.flowObj.opts.changeRawDataBeforeSend; + if (typeof changeRawDataBeforeSend === 'function') { + data = changeRawDataBeforeSend(this, data); + } + this.xhr.send(data); + }, + + /** + * Abort current xhr request + * @function + */ + abort: function () { + // Abort and reset + var xhr = this.xhr; + this.xhr = null; + if (xhr) { + xhr.abort(); + } + }, + + /** + * Retrieve current chunk upload status + * @function + * @returns {string} 'pending', 'uploading', 'success', 'error' + */ + status: function (isTest) { + if (this.readState === 1) { + return 'reading'; + } else if (this.pendingRetry || this.preprocessState === 1) { + // if pending retry then that's effectively the same as actively uploading, + // there might just be a slight delay before the retry starts + return 'uploading'; + } else if (!this.xhr) { + return 'pending'; + } else if (this.xhr.readyState < 4) { + // Status is really 'OPENED', 'HEADERS_RECEIVED' + // or 'LOADING' - meaning that stuff is happening + return 'uploading'; + } else { + if (this.flowObj.opts.successStatuses.indexOf(this.xhr.status) > -1) { + // HTTP 200, perfect + // HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed. + return 'success'; + } else if (this.flowObj.opts.permanentErrors.indexOf(this.xhr.status) > -1 || + !isTest && this.retries >= this.flowObj.opts.maxChunkRetries) { + // HTTP 413/415/500/501, permanent error + return 'error'; + } else { + // this should never happen, but we'll reset and queue a retry + // a likely case for this would be 503 service unavailable + this.abort(); + return 'pending'; + } + } + }, + + /** + * Get response from xhr request + * @function + * @returns {String} + */ + message: function () { + return this.xhr ? this.xhr.responseText : ''; + }, + + /** + * Get upload progress + * @function + * @returns {number} + */ + progress: function () { + if (this.pendingRetry) { + return 0; + } + var s = this.status(); + if (s === 'success' || s === 'error') { + return 1; + } else if (s === 'pending') { + return 0; + } else { + return this.total > 0 ? this.loaded / this.total : 0; + } + }, + + /** + * Count total size uploaded + * @function + * @returns {number} + */ + sizeUploaded: function () { + var size = this.endByte - this.startByte; + // can't return only chunk.loaded value, because it is bigger than chunk size + if (this.status() !== 'success') { + size = this.progress() * size; + } + return size; + }, + + /** + * Prepare Xhr request. Set query, headers and data + * @param {string} method GET or POST + * @param {bool} isTest is this a test request + * @param {string} [paramsMethod] octet or form + * @param {Blob} [blob] to send + * @returns {FormData|Blob|Null} data to send + */ + prepareXhrRequest: function(method, isTest, paramsMethod, blob) { + // Add data from the query options + var query = evalOpts(this.flowObj.opts.query, this.fileObj, this, isTest); + query = extend(query || {}, this.getParams()); + + var target = evalOpts(this.flowObj.opts.target, this.fileObj, this, isTest); + var data = null; + if (method === 'GET' || paramsMethod === 'octet') { + // Add data from the query options + var params = []; + each(query, function (v, k) { + params.push([encodeURIComponent(k), encodeURIComponent(v)].join('=')); + }); + target = this.getTarget(target, params); + data = blob || null; + } else { + // Add data from the query options + data = new FormData(); + each(query, function (v, k) { + data.append(k, v); + }); + if (typeof blob !== "undefined") { + data.append(this.flowObj.opts.fileParameterName, blob, this.filename || this.fileObj.file.name); + } + } + + this.xhr.open(method, target, true); + this.xhr.withCredentials = this.flowObj.opts.withCredentials; + + // Add data from header options + each(evalOpts(this.flowObj.opts.headers, this.fileObj, this, isTest), function (v, k) { + this.xhr.setRequestHeader(k, v); + }, this); + + return data; + } + }; + + /** + * Remove value from array + * @param array + * @param value + */ + function arrayRemove(array, value) { + var index = array.indexOf(value); + if (index > -1) { + array.splice(index, 1); + } + } + + /** + * If option is a function, evaluate it with given params + * @param {*} data + * @param {...} args arguments of a callback + * @returns {*} + */ + function evalOpts(data, args) { + if (typeof data === "function") { + // `arguments` is an object, not array, in FF, so: + args = Array.prototype.slice.call(arguments); + data = data.apply(null, args.slice(1)); + } + return data; + } + Flow.evalOpts = evalOpts; + + /** + * Execute function asynchronously + * @param fn + * @param context + */ + function async(fn, context) { + setTimeout(fn.bind(context), 0); + } + + /** + * Extends the destination object `dst` by copying all of the properties from + * the `src` object(s) to `dst`. You can specify multiple `src` objects. + * @function + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ + function extend(dst, src) { + each(arguments, function(obj) { + if (obj !== dst) { + each(obj, function(value, key){ + dst[key] = value; + }); + } + }); + return dst; + } + Flow.extend = extend; + + /** + * Iterate each element of an object + * @function + * @param {Array|Object} obj object or an array to iterate + * @param {Function} callback first argument is a value and second is a key. + * @param {Object=} context Object to become context (`this`) for the iterator function. + */ + function each(obj, callback, context) { + if (!obj) { + return ; + } + var key; + // Is Array? + // Array.isArray won't work, not only arrays can be iterated by index https://github.com/flowjs/ng-flow/issues/236# + if (typeof(obj.length) !== 'undefined') { + for (key = 0; key < obj.length; key++) { + if (callback.call(context, obj[key], key) === false) { + return ; + } + } + } else { + for (key in obj) { + if (obj.hasOwnProperty(key) && callback.call(context, obj[key], key) === false) { + return ; + } + } + } + } + Flow.each = each; + + /** + * FlowFile constructor + * @type {FlowFile} + */ + Flow.FlowFile = FlowFile; + + /** + * FlowFile constructor + * @type {FlowChunk} + */ + Flow.FlowChunk = FlowChunk; + + /** + * Library version + * @type {string} + */ + Flow.version = '<%= version %>'; + + if ( true && module && typeof module.exports === "object" ) { + // Expose Flow as module.exports in loaders that implement the Node + // module pattern (including browserify). Do not create the global, since + // the user will be storing it themselves locally, and globals are frowned + // upon in the Node module world. + module.exports = Flow; + } else { + // Otherwise expose Flow to the global object as usual + window.Flow = Flow; + + // Register as a named AMD module, since Flow can be concatenated with other + // files that may use define, but not via a proper concatenation script that + // understands anonymous AMD modules. A named AMD is safest and most robust + // way to register. Lowercase flow is used because AMD module names are + // derived from file names, and Flow is normally delivered in a lowercase + // file name. Do this after creating the global so that if an AMD module wants + // to call noConflict to hide this version of Flow, it will work. + if ( true ) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () { return Flow; }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } + } +})(typeof window !== 'undefined' && window, typeof document !== 'undefined' && document); + + +/***/ }), + +/***/ "./node_modules/@flowjs/ng-flow/dist/ng-flow.js": +/*!******************************************************!*\ + !*** ./node_modules/@flowjs/ng-flow/dist/ng-flow.js ***! + \******************************************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +/* provided dependency */ var Flow = __webpack_require__(/*! @flowjs/flow.js */ "./node_modules/@flowjs/flow.js/src/flow.js"); +/** + * @description + * var app = angular.module('App', ['flow.provider'], function(flowFactoryProvider){ + * flowFactoryProvider.defaults = {target: '/'}; + * }); + * @name flowFactoryProvider + */ +angular.module('flow.provider', []) +.provider('flowFactory', function() { + 'use strict'; + /** + * Define the default properties for flow.js + * @name flowFactoryProvider.defaults + * @type {Object} + */ + this.defaults = {}; + + /** + * Flow, MaybeFlow or NotFlow + * @name flowFactoryProvider.factory + * @type {function} + * @return {Flow} + */ + this.factory = function (options) { + return new Flow(options); + }; + + /** + * Define the default events + * @name flowFactoryProvider.events + * @type {Array} + * @private + */ + this.events = []; + + /** + * Add default events + * @name flowFactoryProvider.on + * @function + * @param {string} event + * @param {Function} callback + */ + this.on = function (event, callback) { + this.events.push([event, callback]); + }; + + this.$get = function() { + var fn = this.factory; + var defaults = this.defaults; + var events = this.events; + return { + 'create': function(opts) { + // combine default options with global options and options + var flow = fn(angular.extend({}, defaults, opts)); + angular.forEach(events, function (event) { + flow.on(event[0], event[1]); + }); + return flow; + } + }; + }; +}); +angular.module('flow.init', ['flow.provider']) + .controller('flowCtrl', ['$scope', '$attrs', '$parse', 'flowFactory', + function ($scope, $attrs, $parse, flowFactory) { + + var options = angular.extend({}, $scope.$eval($attrs.flowInit)); + + // use existing flow object or create a new one + var flow = $scope.$eval($attrs.flowObject) || flowFactory.create(options); + + var catchAllHandler = function(eventName){ + var args = Array.prototype.slice.call(arguments); + args.shift(); + var event = $scope.$broadcast.apply($scope, ['flow::' + eventName, flow].concat(args)); + if ({ + 'progress':1, 'filesSubmitted':1, 'fileSuccess': 1, 'fileError': 1, 'complete': 1 + }[eventName]) { + $scope.$applyAsync(); + } + if (event.defaultPrevented) { + return false; + } + }; + + flow.on('catchAll', catchAllHandler); + $scope.$on('$destroy', function(){ + flow.off('catchAll', catchAllHandler); + }); + + $scope.$flow = flow; + + if ($attrs.hasOwnProperty('flowName')) { + $parse($attrs.flowName).assign($scope, flow); + $scope.$on('$destroy', function () { + $parse($attrs.flowName).assign($scope); + }); + } + }]) + .directive('flowInit', [function() { + return { + scope: true, + controller: 'flowCtrl' + }; + }]); +angular.module('flow.btn', ['flow.init']) +.directive('flowBtn', [function() { + return { + 'restrict': 'EA', + 'scope': false, + 'require': '^flowInit', + 'link': function(scope, element, attrs) { + var isDirectory = attrs.hasOwnProperty('flowDirectory'); + var isSingleFile = attrs.hasOwnProperty('flowSingleFile'); + var inputAttrs = attrs.hasOwnProperty('flowAttrs') && scope.$eval(attrs.flowAttrs); + scope.$flow.assignBrowse(element, isDirectory, isSingleFile, inputAttrs); + } + }; +}]); +angular.module('flow.dragEvents', ['flow.init']) +/** + * @name flowPreventDrop + * Prevent loading files then dropped on element + */ + .directive('flowPreventDrop', function() { + return { + 'scope': false, + 'link': function(scope, element, attrs) { + element.bind('drop dragover', function (event) { + event.preventDefault(); + }); + } + }; + }) +/** + * @name flowDragEnter + * executes `flowDragEnter` and `flowDragLeave` events + */ + .directive('flowDragEnter', ['$timeout', function($timeout) { + return { + 'scope': false, + 'link': function(scope, element, attrs) { + var promise; + var enter = false; + element.bind('dragover', function (event) { + if (!isFileDrag(event)) { + return ; + } + if (!enter) { + scope.$apply(attrs.flowDragEnter); + enter = true; + } + $timeout.cancel(promise); + event.preventDefault(); + }); + element.bind('dragleave drop', function (event) { + $timeout.cancel(promise); + promise = $timeout(function () { + scope.$eval(attrs.flowDragLeave); + promise = null; + enter = false; + }, 100); + }); + function isFileDrag(dragEvent) { + var fileDrag = false; + var dataTransfer = dragEvent.dataTransfer || dragEvent.originalEvent.dataTransfer; + angular.forEach(dataTransfer && dataTransfer.types, function(val) { + if (val === 'Files') { + fileDrag = true; + } + }); + return fileDrag; + } + } + }; + }]); + +angular.module('flow.drop', ['flow.init']) +.directive('flowDrop', function() { + return { + 'scope': false, + 'require': '^flowInit', + 'link': function(scope, element, attrs) { + if (attrs.flowDropEnabled) { + scope.$watch(attrs.flowDropEnabled, function (value) { + if (value) { + assignDrop(); + } else { + unAssignDrop(); + } + }); + } else { + assignDrop(); + } + function assignDrop() { + scope.$flow.assignDrop(element); + } + function unAssignDrop() { + scope.$flow.unAssignDrop(element); + } + } + }; +}); + +!function (angular) {'use strict'; + var module = angular.module('flow.events', ['flow.init']); + var events = { + fileSuccess: ['$file', '$message'], + fileProgress: ['$file'], + fileAdded: ['$file', '$event'], + filesAdded: ['$files', '$event'], + filesSubmitted: ['$files', '$event'], + fileRetry: ['$file'], + fileRemoved: ['$file'], + fileError: ['$file', '$message'], + uploadStart: [], + complete: [], + progress: [], + error: ['$message', '$file'] + }; + + angular.forEach(events, function (eventArgs, eventName) { + var name = 'flow' + capitaliseFirstLetter(eventName); + if (name == 'flowUploadStart') { + name = 'flowUploadStarted';// event alias + } + module.directive(name, [function() { + return { + require: '^flowInit', + controller: ['$scope', '$attrs', function ($scope, $attrs) { + $scope.$on('flow::' + eventName, function () { + var funcArgs = Array.prototype.slice.call(arguments); + var event = funcArgs.shift();// remove angular event + // remove flow object and ignore event if it is from parent directive + if ($scope.$flow !== funcArgs.shift()) { + return ; + } + var args = {}; + angular.forEach(eventArgs, function(value, key) { + args[value] = funcArgs[key]; + }); + if ($scope.$eval($attrs[name], args) === false) { + event.preventDefault(); + } + }); + }] + }; + }]); + }); + + function capitaliseFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + } +}(angular); + +angular.module('flow.img', ['flow.init']) +.directive('flowImg', [function() { + return { + 'scope': false, + 'require': '^flowInit', + 'link': function(scope, element, attrs) { + var file = attrs.flowImg; + scope.$watch(file, function (file) { + if (!file) { + return ; + } + var fileReader = new FileReader(); + fileReader.readAsDataURL(file.file); + fileReader.onload = function (event) { + scope.$apply(function () { + attrs.$set('src', event.target.result); + }); + }; + }); + } + }; +}]); +angular.module('flow.transfers', ['flow.init']) +.directive('flowTransfers', [function() { + return { + 'scope': true, + 'require': '^flowInit', + 'link': function(scope) { + scope.transfers = scope.$flow.files; + } + }; +}]); +angular.module('flow', ['flow.provider', 'flow.init', 'flow.events', 'flow.btn', + 'flow.drop', 'flow.transfers', 'flow.img', 'flow.dragEvents']); + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/angular.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/angular.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ng": () => (/* binding */ ng) +/* harmony export */ }); +/* harmony import */ var angular__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! angular */ "./node_modules/angular/index-exposed.js"); +/* harmony import */ var angular__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(angular__WEBPACK_IMPORTED_MODULE_0__); +/** @publicapi @module ng1 */ /** */ + +/** @hidden */ var ng_from_global = angular; +/** @hidden */ var ng = angular__WEBPACK_IMPORTED_MODULE_0__ && angular__WEBPACK_IMPORTED_MODULE_0__.module ? angular__WEBPACK_IMPORTED_MODULE_0__ : ng_from_global; +//# sourceMappingURL=angular.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/directives/stateDirectives.js": +/*!********************************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/directives/stateDirectives.js ***! + \********************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _angular__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../angular */ "./node_modules/@uirouter/angularjs/lib-esm/angular.js"); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/* eslint-disable @typescript-eslint/no-empty-interface */ +/* eslint-disable prefer-const */ +/** + * # Angular 1 Directives + * + * These are the directives included in UI-Router for Angular 1. + * These directives are used in templates to create viewports and link/navigate to states. + * + * @preferred @publicapi @module directives + */ /** */ + + +/** @hidden */ +function parseStateRef(ref) { + var paramsOnly = ref.match(/^\s*({[^}]*})\s*$/); + if (paramsOnly) + ref = '(' + paramsOnly[1] + ')'; + var parsed = ref.replace(/\n/g, ' ').match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/); + if (!parsed || parsed.length !== 4) + throw new Error("Invalid state ref '" + ref + "'"); + return { state: parsed[1] || null, paramExpr: parsed[3] || null }; +} +/** @hidden */ +function stateContext(el) { + var $uiView = el.parent().inheritedData('$uiView'); + var path = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.parse)('$cfg.path')($uiView); + return path ? (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.tail)(path).state.name : undefined; +} +/** @hidden */ +function processedDef($state, $element, def) { + var uiState = def.uiState || $state.current.name; + var uiStateOpts = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.extend)(defaultOpts($element, $state), def.uiStateOpts || {}); + var href = $state.href(uiState, def.uiStateParams, uiStateOpts); + return { uiState: uiState, uiStateParams: def.uiStateParams, uiStateOpts: uiStateOpts, href: href }; +} +/** @hidden */ +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, + }; +} +/** @hidden */ +function clickHook(el, $state, $timeout, type, getDef) { + return function (e) { + var button = e.which || e.button, target = getDef(); + if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || el.attr('target'))) { + // HACK: This is to allow ng-clicks to be processed before the transition is initiated: + var transition_1 = $timeout(function () { + if (!el.attr('disabled')) { + $state.go(target.uiState, target.uiStateParams, target.uiStateOpts); + } + }); + e.preventDefault(); + // if the state has no URL, ignore one preventDefault from the directive. + var ignorePreventDefaultCount_1 = type.isAnchor && !target.href ? 1 : 0; + e.preventDefault = function () { + if (ignorePreventDefaultCount_1-- <= 0) + $timeout.cancel(transition_1); + }; + } + }; +} +/** @hidden */ +function defaultOpts(el, $state) { + return { + relative: stateContext(el) || $state.$current, + inherit: true, + source: 'sref', + }; +} +/** @hidden */ +function bindEvents(element, scope, hookFn, uiStateOpts) { + var events; + if (uiStateOpts) { + events = uiStateOpts.events; + } + if (!(0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isArray)(events)) { + events = ['click']; + } + var on = element.on ? 'on' : 'bind'; + for (var _i = 0, events_1 = events; _i < events_1.length; _i++) { + var event_1 = events_1[_i]; + element[on](event_1, hookFn); + } + scope.$on('$destroy', function () { + var off = element.off ? 'off' : 'unbind'; + for (var _i = 0, events_2 = events; _i < events_2.length; _i++) { + var event_2 = events_2[_i]; + element[off](event_2, hookFn); + } + }); +} +/** + * `ui-sref`: A directive for linking to a state + * + * A directive which links to a state (and optionally, parameters). + * When clicked, this directive activates the linked state with the supplied parameter values. + * + * ### Linked State + * The attribute value of the `ui-sref` is the name of the state to link to. + * + * #### Example: + * This will activate the `home` state when the link is clicked. + * ```html + * Home + * ``` + * + * ### Relative Links + * You can also use relative state paths within `ui-sref`, just like a relative path passed to `$state.go()` ([[StateService.go]]). + * You just need to be aware that the path is relative to the state that *created* the link. + * This allows a state to create a relative `ui-sref` which always targets the same destination. + * + * #### Example: + * Both these links are relative to the parent state, even when a child state is currently active. + * ```html + * child 1 state + * child 2 state + * ``` + * + * This link activates the parent state. + * ```html + * Return + * ``` + * + * ### hrefs + * If the linked state has a URL, the directive will automatically generate and + * update the `href` attribute (using the [[StateService.href]] method). + * + * #### Example: + * Assuming the `users` state has a url of `/users/` + * ```html + * Users + * ``` + * + * ### Parameter Values + * In addition to the state name, a `ui-sref` can include parameter values which are applied when activating the state. + * Param values can be provided in the `ui-sref` value after the state name, enclosed by parentheses. + * The content inside the parentheses is an expression, evaluated to the parameter values. + * + * #### Example: + * This example renders a list of links to users. + * The state's `userId` parameter value comes from each user's `user.id` property. + * ```html + *
  • + * {{ user.displayName }} + *
  • + * ``` + * + * Note: + * The parameter values expression is `$watch`ed for updates. + * + * ### Transition Options + * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute. + * Options are restricted to `location`, `inherit`, and `reload`. + * + * #### Example: + * ```html + * Home + * ``` + * + * ### Other DOM Events + * + * You can also customize which DOM events to respond to (instead of `click`) by + * providing an `events` array in the `ui-sref-opts` attribute. + * + * #### Example: + * ```html + * + * ``` + * + * ### Highlighting the active link + * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link. + * + * ### Examples + * If you have the following template: + * + * ```html + * Home + * About + * Next page + * + * + * ``` + * + * Then (assuming the current state is `contacts`) the rendered html including hrefs would be: + * + * ```html + * Home + * About + * Next page + * + *
      + *
    • + * Joe + *
    • + *
    • + * Alice + *
    • + *
    • + * Bob + *
    • + *
    + * + * Home + * ``` + * + * ### Notes + * + * - You can use `ui-sref` to change **only the parameter values** by omitting the state name and parentheses. + * #### Example: + * Sets the `lang` parameter to `en` and remains on the same state. + * + * ```html + * English + * ``` + * + * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example. + * + * - Unlike the parameter values expression, the state name is not `$watch`ed (for performance reasons). + * If you need to dynamically update the state being linked to, use the fully dynamic [[uiState]] directive. + */ +var uiSrefDirective; +uiSrefDirective = [ + '$uiRouter', + '$timeout', + function $StateRefDirective($uiRouter, $timeout) { + var $state = $uiRouter.stateService; + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function (scope, element, attrs, uiSrefActive) { + var type = getTypeInfo(element); + var active = uiSrefActive[1] || uiSrefActive[0]; + var unlinkInfoFn = null; + var rawDef = {}; + var getDef = function () { return processedDef($state, element, rawDef); }; + var ref = parseStateRef(attrs.uiSref); + rawDef.uiState = ref.state; + rawDef.uiStateOpts = attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {}; + function update() { + var def = getDef(); + if (unlinkInfoFn) + unlinkInfoFn(); + if (active) + unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams); + if (def.href != null) + attrs.$set(type.attr, def.href); + } + if (ref.paramExpr) { + scope.$watch(ref.paramExpr, function (val) { + rawDef.uiStateParams = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.extend)({}, val); + update(); + }, true); + rawDef.uiStateParams = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.extend)({}, scope.$eval(ref.paramExpr)); + } + update(); + scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update)); + scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update)); + if (!type.clickable) + return; + var hookFn = clickHook(element, $state, $timeout, type, getDef); + bindEvents(element, scope, hookFn, rawDef.uiStateOpts); + }, + }; + }, +]; +/** + * `ui-state`: A fully dynamic directive for linking to a state + * + * A directive which links to a state (and optionally, parameters). + * When clicked, this directive activates the linked state with the supplied parameter values. + * + * **This directive is very similar to [[uiSref]], but it `$observe`s and `$watch`es/evaluates all its inputs.** + * + * A directive which links to a state (and optionally, parameters). + * When clicked, this directive activates the linked state with the supplied parameter values. + * + * ### Linked State + * The attribute value of `ui-state` is an expression which is `$watch`ed and evaluated as the state to link to. + * **This is in contrast with `ui-sref`, which takes a state name as a string literal.** + * + * #### Example: + * Create a list of links. + * ```html + *
  • + * {{ link.displayName }} + *
  • + * ``` + * + * ### Relative Links + * If the expression evaluates to a relative path, it is processed like [[uiSref]]. + * You just need to be aware that the path is relative to the state that *created* the link. + * This allows a state to create relative `ui-state` which always targets the same destination. + * + * ### hrefs + * If the linked state has a URL, the directive will automatically generate and + * update the `href` attribute (using the [[StateService.href]] method). + * + * ### Parameter Values + * In addition to the state name expression, a `ui-state` can include parameter values which are applied when activating the state. + * Param values should be provided using the `ui-state-params` attribute. + * The `ui-state-params` attribute value is `$watch`ed and evaluated as an expression. + * + * #### Example: + * This example renders a list of links with param values. + * The state's `userId` parameter value comes from each user's `user.id` property. + * ```html + *
  • + * {{ link.displayName }} + *
  • + * ``` + * + * ### Transition Options + * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute. + * Options are restricted to `location`, `inherit`, and `reload`. + * The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression. + * + * #### Example: + * ```html + * Home + * ``` + * + * ### Other DOM Events + * + * You can also customize which DOM events to respond to (instead of `click`) by + * providing an `events` array in the `ui-state-opts` attribute. + * + * #### Example: + * ```html + * + * ``` + * + * ### Highlighting the active link + * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link. + * + * ### Notes + * + * - You can use `ui-params` to change **only the parameter values** by omitting the state name and supplying only `ui-state-params`. + * However, it might be simpler to use [[uiSref]] parameter-only links. + * + * #### Example: + * Sets the `lang` parameter to `en` and remains on the same state. + * + * ```html + * English + * ``` + * + * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example. + * ``` + */ +var uiStateDirective; +uiStateDirective = [ + '$uiRouter', + '$timeout', + function $StateRefDynamicDirective($uiRouter, $timeout) { + var $state = $uiRouter.stateService; + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function (scope, element, attrs, uiSrefActive) { + var type = getTypeInfo(element); + var active = uiSrefActive[1] || uiSrefActive[0]; + var unlinkInfoFn = null; + var hookFn; + var rawDef = {}; + var getDef = function () { return processedDef($state, element, rawDef); }; + var inputAttrs = ['uiState', 'uiStateParams', 'uiStateOpts']; + var watchDeregFns = inputAttrs.reduce(function (acc, attr) { return ((acc[attr] = _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.noop), acc); }, {}); + function update() { + var def = getDef(); + if (unlinkInfoFn) + unlinkInfoFn(); + if (active) + unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams); + if (def.href != null) + attrs.$set(type.attr, def.href); + } + inputAttrs.forEach(function (field) { + rawDef[field] = attrs[field] ? scope.$eval(attrs[field]) : null; + attrs.$observe(field, function (expr) { + watchDeregFns[field](); + watchDeregFns[field] = scope.$watch(expr, function (newval) { + rawDef[field] = newval; + update(); + }, true); + }); + }); + update(); + scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update)); + scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update)); + if (!type.clickable) + return; + hookFn = clickHook(element, $state, $timeout, type, getDef); + bindEvents(element, scope, hookFn, rawDef.uiStateOpts); + }, + }; + }, +]; +/** + * `ui-sref-active` and `ui-sref-active-eq`: A directive that adds a CSS class when a `ui-sref` is active + * + * A directive working alongside [[uiSref]] and [[uiState]] to add classes to an element when the + * related directive's state is active (and remove them when it is inactive). + * + * The primary use-case is to highlight the active link in navigation menus, + * distinguishing it from the inactive menu items. + * + * ### Linking to a `ui-sref` or `ui-state` + * `ui-sref-active` can live on the same element as `ui-sref`/`ui-state`, or it can be on a parent element. + * If a `ui-sref-active` is a parent to more than one `ui-sref`/`ui-state`, it will apply the CSS class when **any of the links are active**. + * + * ### Matching + * + * The `ui-sref-active` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state **or any child state is active**. + * This is a "fuzzy match" which uses [[StateService.includes]]. + * + * The `ui-sref-active-eq` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state is directly active (not when child states are active). + * This is an "exact match" which uses [[StateService.is]]. + * + * ### Parameter values + * If the `ui-sref`/`ui-state` includes parameter values, the current parameter values must match the link's values for the link to be highlighted. + * This allows a list of links to the same state with different parameters to be rendered, and the correct one highlighted. + * + * #### Example: + * ```html + *
  • + * {{ user.lastName }} + *
  • + * ``` + * + * ### Examples + * + * Given the following template: + * #### Example: + * ```html + * + * ``` + * + * When the app state is `app.user` (or any child state), + * and contains the state parameter "user" with value "bilbobaggins", + * the resulting HTML will appear as (note the 'active' class): + * + * ```html + * + * ``` + * + * ### Glob mode + * + * It is possible to pass `ui-sref-active` an expression that evaluates to an object. + * The objects keys represent active class names and 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: + * ```html + *
    + * Roles + *
    + * ``` + * + * Arrays are also supported as values in the `ngClass`-like interface. + * This allows multiple states to add `active` class. + * + * #### Example: + * Given the following template, with "admin.roles" being the current state, the class will be added too: + * ```html + *
    + * 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` override any state provided by a linked `ui-sref`. + * + * ### Notes: + * + * - 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: `ui-sref-active='class1 class2 class3'` + */ +var uiSrefActiveDirective; +uiSrefActiveDirective = [ + '$state', + '$stateParams', + '$interpolate', + '$uiRouter', + function $StateRefActiveDirective($state, $stateParams, $interpolate, $uiRouter) { + return { + restrict: 'A', + controller: [ + '$scope', + '$element', + '$attrs', + function ($scope, $element, $attrs) { + var states = []; + var activeEqClass; + var 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); + setStatesFromDefinitionObject(uiSrefActive); + // 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 ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isObject)(uiSrefActive) && states.length > 0) { + return; + } + var deregister = addState(newState, newParams, uiSrefActive); + update(); + return deregister; + }; + function updateAfterTransition(trans) { + trans.promise.then(update, _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.noop); + } + $scope.$on('$destroy', setupEventListeners()); + if ($uiRouter.globals.transition) { + updateAfterTransition($uiRouter.globals.transition); + } + function setupEventListeners() { + var deregisterStatesChangedListener = $uiRouter.stateRegistry.onStatesChanged(handleStatesChanged); + var deregisterOnStartListener = $uiRouter.transitionService.onStart({}, updateAfterTransition); + var deregisterStateChangeSuccessListener = $scope.$on('$stateChangeSuccess', update); + return function cleanUp() { + deregisterStatesChangedListener(); + deregisterOnStartListener(); + deregisterStateChangeSuccessListener(); + }; + } + function handleStatesChanged() { + setStatesFromDefinitionObject(uiSrefActive); + } + function setStatesFromDefinitionObject(statesDefinition) { + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isObject)(statesDefinition)) { + states = []; + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.forEach)(statesDefinition, function (stateOrName, activeClass) { + // Helper function to abstract adding state. + var addStateForClass = function (stateOrName, activeClass) { + var ref = parseStateRef(stateOrName); + addState(ref.state, $scope.$eval(ref.paramExpr), activeClass); + }; + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isString)(stateOrName)) { + // If state is string, just add it. + addStateForClass(stateOrName, activeClass); + } + else if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isArray)(stateOrName)) { + // If state is an array, iterate over it and add each array item individually. + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.forEach)(stateOrName, function (stateOrName) { + addStateForClass(stateOrName, activeClass); + }); + } + }); + } + } + function addState(stateName, stateParams, activeClass) { + var state = $state.get(stateName, stateContext($element)); + var stateInfo = { + state: state || { name: stateName }, + params: stateParams, + activeClass: activeClass, + }; + states.push(stateInfo); + return function removeState() { + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.removeFrom)(states)(stateInfo); + }; + } + // Update route state + function update() { + var splitClasses = function (str) { return str.split(/\s/).filter(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.identity); }; + var getClasses = function (stateList) { + return stateList + .map(function (x) { return x.activeClass; }) + .map(splitClasses) + .reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.unnestR, []); + }; + var allClasses = getClasses(states).concat(splitClasses(activeEqClass)).reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.uniqR, []); + var fuzzyClasses = getClasses(states.filter(function (x) { return $state.includes(x.state.name, x.params); })); + var exactlyMatchesAny = !!states.filter(function (x) { return $state.is(x.state.name, x.params); }).length; + var exactClasses = exactlyMatchesAny ? splitClasses(activeEqClass) : []; + var addClasses = fuzzyClasses.concat(exactClasses).reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.uniqR, []); + var removeClasses = allClasses.filter(function (cls) { return !(0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.inArray)(addClasses, cls); }); + $scope.$evalAsync(function () { + addClasses.forEach(function (className) { return $element.addClass(className); }); + removeClasses.forEach(function (className) { return $element.removeClass(className); }); + }); + } + update(); + }, + ], + }; + }, +]; +_angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.state') + .directive('uiSref', uiSrefDirective) + .directive('uiSrefActive', uiSrefActiveDirective) + .directive('uiSrefActiveEq', uiSrefActiveDirective) + .directive('uiState', uiStateDirective); +//# sourceMappingURL=stateDirectives.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/directives/viewDirective.js": +/*!******************************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/directives/viewDirective.js ***! + \******************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "uiView": () => (/* binding */ uiView) +/* harmony export */ }); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/* harmony import */ var _angular__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../angular */ "./node_modules/@uirouter/angularjs/lib-esm/angular.js"); +/* harmony import */ var _services__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../services */ "./node_modules/@uirouter/angularjs/lib-esm/services.js"); +/* harmony import */ var _statebuilders_views__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../statebuilders/views */ "./node_modules/@uirouter/angularjs/lib-esm/statebuilders/views.js"); +/** @publicapi @module directives */ /** */ + + + + +/** + * `ui-view`: A viewport directive which is filled in by a view from the active state. + * + * ### Attributes + * + * - `name`: (Optional) 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. + * The ui-view can be targeted in a View using the name ([[Ng1StateDeclaration.views]]). + * + * - `autoscroll`: an expression. When it evaluates to true, the `ui-view` will be scrolled into view when it is activated. + * Uses [[$uiViewScroll]] to do the scrolling. + * + * - `onload`: Expression to evaluate whenever the view updates. + * + * #### Example: + * A view can be unnamed or named. + * ```html + * + *
    + * + * + *
    + * + * + * + * ``` + * + * 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: + * + * ```html + *
    + * $stateProvider.state("home", { + * template: "

    HELLO!

    " + * }) + * ``` + * + * The above is a convenient shortcut equivalent to specifying your view explicitly with the + * [[Ng1StateDeclaration.views]] config property, by name, in this case an empty name: + * + * ```js + * $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: + * + * ```html + *
    + * ``` + * + * ```js + * $stateProvider.state("home", { + * views: { + * "main": { + * template: "

    HELLO!

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

    HELLO!

    " + * }, + * "chart": { + * template: "" + * }, + * "data": { + * template: "" + * } + * } + * }) + * ``` + * + * #### Examples for `autoscroll`: + * ```html + * + * + * + * + * + * + * + * ``` + * + * Resolve data: + * + * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this + * can be customized using [[Ng1ViewDeclaration.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: + * ```js + * $stateProvider.state('home', { + * template: '', + * resolve: { + * user: function(UserService) { return UserService.fetchUser(); } + * } + * }); + * ``` + */ +var uiView; +// eslint-disable-next-line prefer-const +uiView = [ + '$view', + '$animate', + '$uiViewScroll', + '$interpolate', + '$q', + function $ViewDirective($view, $animate, $uiViewScroll, $interpolate, $q) { + function getRenderer() { + return { + enter: function (element, target, cb) { + if (_angular__WEBPACK_IMPORTED_MODULE_1__.ng.version.minor > 2) { + $animate.enter(element, null, target).then(cb); + } + else { + $animate.enter(element, null, target, cb); + } + }, + leave: function (element, cb) { + if (_angular__WEBPACK_IMPORTED_MODULE_1__.ng.version.minor > 2) { + $animate.leave(element).then(cb); + } + else { + $animate.leave(element, cb); + } + }, + }; + } + function configsEqual(config1, config2) { + return config1 === config2; + } + var rootData = { + $cfg: { viewDecl: { $context: $view._pluginapi._rootViewContext() } }, + $uiView: {}, + }; + var directive = { + count: 0, + restrict: 'ECA', + terminal: true, + priority: 400, + transclude: 'element', + compile: function (tElement, tAttrs, $transclude) { + return function (scope, $element, attrs) { + var onloadExp = attrs['onload'] || '', autoScrollExp = attrs['autoscroll'], renderer = getRenderer(), inherited = $element.inheritedData('$uiView') || rootData, name = $interpolate(attrs['uiView'] || attrs['name'] || '')(scope) || '$default'; + var previousEl, currentEl, currentScope, viewConfig; + var activeUIView = { + $type: 'ng1', + id: directive.count++, + name: name, + fqn: inherited.$uiView.fqn ? inherited.$uiView.fqn + '.' + name : name, + config: null, + configUpdated: configUpdatedCallback, + get creationContext() { + // The context in which this ui-view "tag" was created + var fromParentTagConfig = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.parse)('$cfg.viewDecl.$context')(inherited); + // Allow + // See https://github.com/angular-ui/ui-router/issues/3355 + var fromParentTag = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.parse)('$uiView.creationContext')(inherited); + return fromParentTagConfig || fromParentTag; + }, + }; + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewEvent('Linking', activeUIView); + function configUpdatedCallback(config) { + if (config && !(config instanceof _statebuilders_views__WEBPACK_IMPORTED_MODULE_3__.Ng1ViewConfig)) + return; + if (configsEqual(viewConfig, config)) + return; + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewConfigUpdated(activeUIView, config && config.viewDecl && config.viewDecl.$context); + viewConfig = config; + updateView(config); + } + $element.data('$uiView', { $uiView: activeUIView }); + updateView(); + var unregister = $view.registerUIView(activeUIView); + scope.$on('$destroy', function () { + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewEvent('Destroying/Unregistering', activeUIView); + unregister(); + }); + function cleanupLastView() { + if (previousEl) { + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewEvent('Removing (previous) el', previousEl.data('$uiView')); + previousEl.remove(); + previousEl = null; + } + if (currentScope) { + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewEvent('Destroying scope', activeUIView); + currentScope.$destroy(); + currentScope = null; + } + if (currentEl) { + var _viewData_1 = currentEl.data('$uiViewAnim'); + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewEvent('Animate out', _viewData_1); + renderer.leave(currentEl, function () { + _viewData_1.$$animLeave.resolve(); + previousEl = null; + }); + previousEl = currentEl; + currentEl = null; + } + } + function updateView(config) { + var newScope = scope.$new(); + var animEnter = $q.defer(), animLeave = $q.defer(); + var $uiViewData = { + $cfg: config, + $uiView: activeUIView, + }; + var $uiViewAnim = { + $animEnter: animEnter.promise, + $animLeave: animLeave.promise, + $$animLeave: animLeave, + }; + /** + * @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 cloned = $transclude(newScope, function (clone) { + clone.data('$uiViewAnim', $uiViewAnim); + clone.data('$uiView', $uiViewData); + renderer.enter(clone, $element, function onUIViewEnter() { + animEnter.resolve(); + if (currentScope) + currentScope.$emit('$viewContentAnimationEnded'); + if (((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isDefined)(autoScrollExp) && !autoScrollExp) || scope.$eval(autoScrollExp)) { + $uiViewScroll(clone); + } + }); + cleanupLastView(); + }); + currentEl = cloned; + currentScope = newScope; + /** + * @ngdoc event + * @name ui.router.state.directive:ui-view#$viewContentLoaded + * @eventOf ui.router.state.directive:ui-view + * @eventType emits on ui-view directive scope + * @description * + * Fired once the view is **loaded**, *after* the DOM is rendered. + * + * @param {Object} event Event object. + */ + currentScope.$emit('$viewContentLoaded', config || viewConfig); + currentScope.$eval(onloadExp); + } + }; + }, + }; + return directive; + }, +]; +$ViewDirectiveFill.$inject = ['$compile', '$controller', '$transitions', '$view', '$q']; +/** @hidden */ +function $ViewDirectiveFill($compile, $controller, $transitions, $view, $q) { + var getControllerAs = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.parse)('viewDecl.controllerAs'); + var getResolveAs = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.parse)('viewDecl.resolveAs'); + return { + restrict: 'ECA', + priority: -400, + compile: function (tElement) { + var initial = tElement.html(); + tElement.empty(); + return function (scope, $element) { + var data = $element.data('$uiView'); + if (!data) { + $element.html(initial); + $compile($element.contents())(scope); + return; + } + var cfg = data.$cfg || { viewDecl: {}, getTemplate: _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.noop }; + var resolveCtx = cfg.path && new _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.ResolveContext(cfg.path); + $element.html(cfg.getTemplate($element, resolveCtx) || initial); + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceUIViewFill(data.$uiView, $element.html()); + var link = $compile($element.contents()); + var controller = cfg.controller; + var controllerAs = getControllerAs(cfg); + var resolveAs = getResolveAs(cfg); + var locals = resolveCtx && (0,_services__WEBPACK_IMPORTED_MODULE_2__.getLocals)(resolveCtx); + scope[resolveAs] = locals; + if (controller) { + var controllerInstance = ($controller(controller, (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.extend)({}, locals, { $scope: scope, $element: $element }))); + if (controllerAs) { + scope[controllerAs] = controllerInstance; + scope[controllerAs][resolveAs] = locals; + } + // TODO: Use $view service as a central point for registering component-level hooks + // Then, when a component is created, tell the $view service, so it can invoke hooks + // $view.componentLoaded(controllerInstance, { $scope: scope, $element: $element }); + // scope.$on('$destroy', () => $view.componentUnloaded(controllerInstance, { $scope: scope, $element: $element })); + $element.data('$ngControllerController', controllerInstance); + $element.children().data('$ngControllerController', controllerInstance); + registerControllerCallbacks($q, $transitions, controllerInstance, scope, cfg); + } + // Wait for the component to appear in the DOM + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isString)(cfg.component)) { + var kebobName = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.kebobString)(cfg.component); + var tagRegexp_1 = new RegExp("^(x-|data-)?" + kebobName + "$", 'i'); + var getComponentController = function () { + var directiveEl = [].slice + .call($element[0].children) + .filter(function (el) { return el && el.tagName && tagRegexp_1.exec(el.tagName); }); + return directiveEl && _angular__WEBPACK_IMPORTED_MODULE_1__.ng.element(directiveEl).data("$" + cfg.component + "Controller"); + }; + var deregisterWatch_1 = scope.$watch(getComponentController, function (ctrlInstance) { + if (!ctrlInstance) + return; + registerControllerCallbacks($q, $transitions, ctrlInstance, scope, cfg); + deregisterWatch_1(); + }); + } + link(scope); + }; + }, + }; +} +/** @hidden */ +var hasComponentImpl = typeof _angular__WEBPACK_IMPORTED_MODULE_1__.ng.module('ui.router')['component'] === 'function'; +/** @hidden incrementing id */ +var _uiCanExitId = 0; +/** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */ +function registerControllerCallbacks($q, $transitions, controllerInstance, $scope, cfg) { + // Call $onInit() ASAP + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isFunction)(controllerInstance.$onInit) && + !((cfg.viewDecl.component || cfg.viewDecl.componentProvider) && hasComponentImpl)) { + controllerInstance.$onInit(); + } + var viewState = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.tail)(cfg.path).state.self; + var hookOptions = { bind: controllerInstance }; + // Add component-level hook for onUiParamsChanged + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isFunction)(controllerInstance.uiOnParamsChanged)) { + var resolveContext = new _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.ResolveContext(cfg.path); + var viewCreationTrans_1 = resolveContext.getResolvable('$transition$').data; + // Fire callback on any successful transition + var paramsUpdated = function ($transition$) { + // Exit early if the $transition$ is the same as the view was created within. + // Exit early if the $transition$ will exit the state the view is for. + if ($transition$ === viewCreationTrans_1 || $transition$.exiting().indexOf(viewState) !== -1) + return; + var toParams = $transition$.params('to'); + var fromParams = $transition$.params('from'); + var getNodeSchema = function (node) { return node.paramSchema; }; + var toSchema = $transition$.treeChanges('to').map(getNodeSchema).reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.unnestR, []); + var fromSchema = $transition$.treeChanges('from').map(getNodeSchema).reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.unnestR, []); + // Find the to params that have different values than the from params + var changedToParams = toSchema.filter(function (param) { + var idx = fromSchema.indexOf(param); + return idx === -1 || !fromSchema[idx].type.equals(toParams[param.id], fromParams[param.id]); + }); + // Only trigger callback if a to param has changed or is new + if (changedToParams.length) { + var changedKeys_1 = changedToParams.map(function (x) { return x.id; }); + // Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params. + var newValues = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.filter)(toParams, function (val, key) { return changedKeys_1.indexOf(key) !== -1; }); + controllerInstance.uiOnParamsChanged(newValues, $transition$); + } + }; + $scope.$on('$destroy', $transitions.onSuccess({}, paramsUpdated, hookOptions)); + } + // Add component-level hook for uiCanExit + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isFunction)(controllerInstance.uiCanExit)) { + var id_1 = _uiCanExitId++; + var cacheProp_1 = '_uiCanExitIds'; + // Returns true if a redirect transition already answered truthy + var prevTruthyAnswer_1 = function (trans) { + return !!trans && ((trans[cacheProp_1] && trans[cacheProp_1][id_1] === true) || prevTruthyAnswer_1(trans.redirectedFrom())); + }; + // If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition + var wrappedHook = function (trans) { + var promise; + var ids = (trans[cacheProp_1] = trans[cacheProp_1] || {}); + if (!prevTruthyAnswer_1(trans)) { + promise = $q.when(controllerInstance.uiCanExit(trans)); + promise.then(function (val) { return (ids[id_1] = val !== false); }); + } + return promise; + }; + var criteria = { exiting: viewState.name }; + $scope.$on('$destroy', $transitions.onBefore(criteria, wrappedHook, hookOptions)); + } +} +_angular__WEBPACK_IMPORTED_MODULE_1__.ng.module('ui.router.state').directive('uiView', uiView); +_angular__WEBPACK_IMPORTED_MODULE_1__.ng.module('ui.router.state').directive('uiView', $ViewDirectiveFill); +//# sourceMappingURL=viewDirective.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/index.js": +/*!***********************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/index.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "getLocals": () => (/* reexport safe */ _services__WEBPACK_IMPORTED_MODULE_1__.getLocals), +/* harmony export */ "watchDigests": () => (/* reexport safe */ _services__WEBPACK_IMPORTED_MODULE_1__.watchDigests), +/* harmony export */ "Ng1ViewConfig": () => (/* reexport safe */ _statebuilders_views__WEBPACK_IMPORTED_MODULE_2__.Ng1ViewConfig), +/* harmony export */ "getNg1ViewConfigFactory": () => (/* reexport safe */ _statebuilders_views__WEBPACK_IMPORTED_MODULE_2__.getNg1ViewConfigFactory), +/* harmony export */ "ng1ViewsBuilder": () => (/* reexport safe */ _statebuilders_views__WEBPACK_IMPORTED_MODULE_2__.ng1ViewsBuilder), +/* harmony export */ "StateProvider": () => (/* reexport safe */ _stateProvider__WEBPACK_IMPORTED_MODULE_3__.StateProvider), +/* harmony export */ "UrlRouterProvider": () => (/* reexport safe */ _urlRouterProvider__WEBPACK_IMPORTED_MODULE_4__.UrlRouterProvider), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__), +/* harmony export */ "core": () => (/* reexport module object */ _uirouter_core__WEBPACK_IMPORTED_MODULE_10__) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/angularjs/lib-esm/interface.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_interface__WEBPACK_IMPORTED_MODULE_0__); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _interface__WEBPACK_IMPORTED_MODULE_0__) if(["default","core"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _interface__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _services__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./services */ "./node_modules/@uirouter/angularjs/lib-esm/services.js"); +/* harmony import */ var _statebuilders_views__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./statebuilders/views */ "./node_modules/@uirouter/angularjs/lib-esm/statebuilders/views.js"); +/* harmony import */ var _stateProvider__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stateProvider */ "./node_modules/@uirouter/angularjs/lib-esm/stateProvider.js"); +/* harmony import */ var _urlRouterProvider__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./urlRouterProvider */ "./node_modules/@uirouter/angularjs/lib-esm/urlRouterProvider.js"); +/* harmony import */ var _injectables__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./injectables */ "./node_modules/@uirouter/angularjs/lib-esm/injectables.js"); +/* harmony import */ var _injectables__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_injectables__WEBPACK_IMPORTED_MODULE_5__); +/* harmony import */ var _directives_stateDirectives__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./directives/stateDirectives */ "./node_modules/@uirouter/angularjs/lib-esm/directives/stateDirectives.js"); +/* harmony import */ var _stateFilters__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./stateFilters */ "./node_modules/@uirouter/angularjs/lib-esm/stateFilters.js"); +/* harmony import */ var _directives_viewDirective__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./directives/viewDirective */ "./node_modules/@uirouter/angularjs/lib-esm/directives/viewDirective.js"); +/* harmony import */ var _viewScroll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./viewScroll */ "./node_modules/@uirouter/angularjs/lib-esm/viewScroll.js"); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _uirouter_core__WEBPACK_IMPORTED_MODULE_10__) if(["default","core","getLocals","watchDigests","Ng1ViewConfig","getNg1ViewConfigFactory","ng1ViewsBuilder","StateProvider","UrlRouterProvider"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _uirouter_core__WEBPACK_IMPORTED_MODULE_10__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/** + * Main entry point for angular 1.x build + * @publicapi @module ng1 + */ /** */ + + + + + + + + + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ('ui.router'); + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/injectables.js": +/*!*****************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/injectables.js ***! + \*****************************************************************/ +/***/ (() => { + +/** + * The current (or pending) State Parameters + * + * An injectable global **Service Object** which holds the state parameters for the latest **SUCCESSFUL** transition. + * + * The values are not updated until *after* a `Transition` successfully completes. + * + * **Also:** an injectable **Per-Transition Object** object which holds the pending state parameters for the pending `Transition` currently running. + * + * ### Deprecation warning: + * + * The value injected for `$stateParams` is different depending on where it is injected. + * + * - When injected into an angular service, the object injected is the global **Service Object** with the parameter values for the latest successful `Transition`. + * - When injected into transition hooks, resolves, or view controllers, the object is the **Per-Transition Object** with the parameter values for the running `Transition`. + * + * Because of these confusing details, this service is deprecated. + * + * ### Instead of using the global `$stateParams` service object, + * inject [[$uiRouterGlobals]] and use [[UIRouterGlobals.params]] + * + * ```js + * MyService.$inject = ['$uiRouterGlobals']; + * function MyService($uiRouterGlobals) { + * return { + * paramValues: function () { + * return $uiRouterGlobals.params; + * } + * } + * } + * ``` + * + * ### Instead of using the per-transition `$stateParams` object, + * inject the current `Transition` (as [[$transition$]]) and use [[Transition.params]] + * + * ```js + * MyController.$inject = ['$transition$']; + * function MyController($transition$) { + * var username = $transition$.params().username; + * // .. do something with username + * } + * ``` + * + * --- + * + * This object can be injected into other services. + * + * #### Deprecated Example: + * ```js + * SomeService.$inject = ['$http', '$stateParams']; + * function SomeService($http, $stateParams) { + * return { + * getUser: function() { + * return $http.get('/api/users/' + $stateParams.username); + * } + * } + * }; + * angular.service('SomeService', SomeService); + * ``` + * @deprecated + */ +var $stateParams; +/** + * Global UI-Router variables + * + * The router global state as a **Service Object** (injectable during runtime). + * + * This object contains globals such as the current state and current parameter values. + */ +var $uiRouterGlobals; +/** + * The UI-Router instance + * + * The [[UIRouter]] singleton (the router instance) as a **Service Object** (injectable during runtime). + * + * This object is the UI-Router singleton instance, created by angular dependency injection during application bootstrap. + * It has references to the other UI-Router services + * + * #### Note: This object is also exposed as [[$uiRouterProvider]] for injection during angular config time. + */ +var $uiRouter; +/** + * The UI-Router instance + * + * The [[UIRouter]] singleton (the router instance) as a **Provider Object** (injectable during config phase). + * + * This object is the UI-Router singleton instance, created by angular dependency injection during application bootstrap. + * It has references to the other UI-Router services + * + * #### Note: This object is also exposed as [[$uiRouter]] for injection during runtime. + */ +var $uiRouterProvider; +/** + * Transition debug/tracing + * + * The [[Trace]] singleton as a **Service Object** (injectable during runtime). + * + * Enables or disables Transition tracing which can help to debug issues. + */ +var $trace; +/** + * The Transition Service + * + * The [[TransitionService]] singleton as a **Service Object** (injectable during runtime). + * + * This angular service exposes the [[TransitionService]] singleton, which is primarily + * used to register global transition hooks. + * + * #### Note: This object is also exposed as [[$transitionsProvider]] for injection during the config phase. + */ +var $transitions; +/** + * The Transition Service + * + * The [[TransitionService]] singleton as a **Provider Object** (injectable during config phase) + * + * This angular service exposes the [[TransitionService]] singleton, which is primarily + * used to register global transition hooks. + * + * #### Note: This object is also exposed as [[$transitions]] for injection during runtime. + */ +var $transitionsProvider; +/** + * The current [[Transition]] object + * + * The current [[Transition]] object as a **Per-Transition Object** (injectable into Resolve, Hooks, Controllers) + * + * This object returns information about the current transition, including: + * + * - To/from states + * - To/from parameters + * - Transition options + * - States being entered, exited, and retained + * - Resolve data + * - A Promise for the transition + * - Any transition failure information + * - An injector for both Service and Per-Transition Objects + */ +var $transition$; +/** + * The State Service + * + * The [[StateService]] singleton as a **Service Object** (injectable during runtime). + * + * This service used to manage and query information on registered states. + * It exposes state related APIs including: + * + * - Start a [[Transition]] + * - Imperatively lazy load states + * - Check if a state is currently active + * - Look up states by name + * - Build URLs for a state+parameters + * - Configure the global Transition error handler + * + * This angular service exposes the [[StateService]] singleton. + */ +var $state; +/** + * The State Registry + * + * The [[StateRegistry]] singleton as a **Service Object** (injectable during runtime). + * + * This service is used to register/deregister states. + * It has state registration related APIs including: + * + * - Register/deregister states + * - Listen for state registration/deregistration + * - Get states by name + * - Add state decorators (to customize the state creation process) + * + * #### Note: This object is also exposed as [[$stateRegistryProvider]] for injection during the config phase. + */ +var $stateRegistry; +/** + * The State Registry + * + * The [[StateRegistry]] singleton as a **Provider Object** (injectable during config time). + * + * This service is used to register/deregister states. + * It has state registration related APIs including: + * + * - Register/deregister states + * - Listen for state registration/deregistration + * - Get states by name + * - Add state decorators (to customize the state creation process) + * + * #### Note: This object is also exposed as [[$stateRegistry]] for injection during runtime. + */ +var $stateRegistryProvider; +/** + * The View Scroll provider + * + * The [[UIViewScrollProvider]] as a **Provider Object** (injectable during config time). + * + * This angular service exposes the [[UIViewScrollProvider]] singleton and is + * used to disable UI-Router's scroll behavior. + */ +var $uiViewScrollProvider; +/** + * The View Scroll function + * + * The View Scroll function as a **Service Object** (injectable during runtime). + * + * This is a function that scrolls an element into view. + * The element is scrolled 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 [[UIViewScrollProvider.useAnchorScroll]]. + * + * Note: this function is used by the [[directives.uiView]] when the `autoscroll` expression evaluates to true. + */ +var $uiViewScroll; +/** + * The StateProvider + * + * An angular1-only [[StateProvider]] as a **Provider Object** (injectable during config time). + * + * This angular service exposes the [[StateProvider]] singleton. + * + * The `StateProvider` is primarily used to register states or add custom state decorators. + * + * ##### Note: This provider is a ng1 vestige. + * It is a passthrough to [[$stateRegistry]] and [[$state]]. + */ +var $stateProvider; +/** + * The URL Service Provider + * + * The [[UrlService]] singleton as a **Provider Object** (injectable during the angular config phase). + * + * A service used to configure and interact with the URL. + * It has URL related APIs including: + * + * - register custom Parameter types `UrlService.config.type` ([[UrlConfigApi.type]]) + * - add URL rules: `UrlService.rules.when` ([[UrlRulesApi.when]]) + * - configure behavior when no url matches: `UrlService.rules.otherwise` ([[UrlRulesApi.otherwise]]) + * - delay initial URL synchronization [[UrlService.deferIntercept]]. + * - get or set the current url: [[UrlService.url]] + * + * ##### Note: This service can also be injected during runtime as [[$urlService]]. + */ +var $urlServiceProvider; +/** + * The URL Service + * + * The [[UrlService]] singleton as a **Service Object** (injectable during runtime). + * + * Note: This service can also be injected during the config phase as [[$urlServiceProvider]]. + * + * Used to configure the URL. + * It has URL related APIs including: + * + * - register custom Parameter types `UrlService.config.type` ([[UrlConfigApi.type]]) + * - add URL rules: `UrlService.rules.when` ([[UrlRulesApi.when]]) + * - configure behavior when no url matches: `UrlService.rules.otherwise` ([[UrlRulesApi.otherwise]]) + * - delay initial URL synchronization [[UrlService.deferIntercept]]. + * - get or set the current url: [[UrlService.url]] + * + * ##### Note: This service can also be injected during the config phase as [[$urlServiceProvider]]. + */ +var $urlService; +/** + * The URL Router Provider + * + * ### Deprecation warning: This object is now considered internal. Use [[$urlServiceProvider]] instead. + * + * The [[UrlRouter]] singleton as a **Provider Object** (injectable during config time). + * + * #### Note: This object is also exposed as [[$urlRouter]] for injection during runtime. + * + * @deprecated + */ +var $urlRouterProvider; +/** + * The Url Router + * + * ### Deprecation warning: This object is now considered internal. Use [[$urlService]] instead. + * + * The [[UrlRouter]] singleton as a **Service Object** (injectable during runtime). + * + * #### Note: This object is also exposed as [[$urlRouterProvider]] for injection during angular config time. + * + * @deprecated + */ +var $urlRouter; +/** + * The URL Matcher Factory + * + * ### Deprecation warning: This object is now considered internal. Use [[$urlService]] instead. + * + * The [[UrlMatcherFactory]] singleton as a **Service Object** (injectable during runtime). + * + * This service is used to set url mapping options, define custom parameter types, and create [[UrlMatcher]] objects. + * + * #### Note: This object is also exposed as [[$urlMatcherFactoryProvider]] for injection during angular config time. + * + * @deprecated + */ +var $urlMatcherFactory; +/** + * The URL Matcher Factory + * + * ### Deprecation warning: This object is now considered internal. Use [[$urlService]] instead. + * + * The [[UrlMatcherFactory]] singleton as a **Provider Object** (injectable during config time). + * + * This service is used to set url mapping options, define custom parameter types, and create [[UrlMatcher]] objects. + * + * #### Note: This object is also exposed as [[$urlMatcherFactory]] for injection during runtime. + * + * @deprecated + */ +var $urlMatcherFactoryProvider; +//# sourceMappingURL=injectables.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/interface.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/interface.js ***! + \***************************************************************/ +/***/ (() => { + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/locationServices.js": +/*!**********************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/locationServices.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Ng1LocationServices": () => (/* binding */ Ng1LocationServices) +/* harmony export */ }); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/** @publicapi @module ng1 */ /** */ + + +/** + * Implements UI-Router LocationServices and LocationConfig using Angular 1's $location service + * @internalapi + */ +var Ng1LocationServices = /** @class */ (function () { + function Ng1LocationServices($locationProvider) { + // .onChange() registry + this._urlListeners = []; + this.$locationProvider = $locationProvider; + var _lp = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.val)($locationProvider); + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions)(_lp, this, _lp, ['hashPrefix']); + } + /** + * Applys ng1-specific path parameter encoding + * + * The Angular 1 `$location` service is a bit weird. + * It doesn't allow slashes to be encoded/decoded bi-directionally. + * + * See the writeup at https://github.com/angular-ui/ui-router/issues/2598 + * + * This code patches the `path` parameter type so it encoded/decodes slashes as ~2F + * + * @param router + */ + Ng1LocationServices.monkeyPatchPathParameterType = function (router) { + var pathType = router.urlMatcherFactory.type('path'); + pathType.encode = function (x) { + return x != null ? x.toString().replace(/(~|\/)/g, function (m) { return ({ '~': '~~', '/': '~2F' }[m]); }) : x; + }; + pathType.decode = function (x) { + return x != null ? x.toString().replace(/(~~|~2F)/g, function (m) { return ({ '~~': '~', '~2F': '/' }[m]); }) : x; + }; + }; + // eslint-disable-next-line @typescript-eslint/no-empty-function + Ng1LocationServices.prototype.dispose = function () { }; + Ng1LocationServices.prototype.onChange = function (callback) { + var _this = this; + this._urlListeners.push(callback); + return function () { return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(_this._urlListeners)(callback); }; + }; + Ng1LocationServices.prototype.html5Mode = function () { + var html5Mode = this.$locationProvider.html5Mode(); + html5Mode = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isObject)(html5Mode) ? html5Mode.enabled : html5Mode; + return html5Mode && this.$sniffer.history; + }; + Ng1LocationServices.prototype.baseHref = function () { + return this._baseHref || (this._baseHref = this.$browser.baseHref() || this.$window.location.pathname); + }; + Ng1LocationServices.prototype.url = function (newUrl, replace, state) { + if (replace === void 0) { replace = false; } + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isDefined)(newUrl)) + this.$location.url(newUrl); + if (replace) + this.$location.replace(); + if (state) + this.$location.state(state); + return this.$location.url(); + }; + Ng1LocationServices.prototype._runtimeServices = function ($rootScope, $location, $sniffer, $browser, $window) { + var _this = this; + this.$location = $location; + this.$sniffer = $sniffer; + this.$browser = $browser; + this.$window = $window; + // Bind $locationChangeSuccess to the listeners registered in LocationService.onChange + $rootScope.$on('$locationChangeSuccess', function (evt) { return _this._urlListeners.forEach(function (fn) { return fn(evt); }); }); + var _loc = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.val)($location); + // Bind these LocationService functions to $location + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions)(_loc, this, _loc, ['replace', 'path', 'search', 'hash']); + // Bind these LocationConfig functions to $location + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions)(_loc, this, _loc, ['port', 'protocol', 'host']); + }; + return Ng1LocationServices; +}()); + +//# sourceMappingURL=locationServices.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/services.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/services.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "watchDigests": () => (/* binding */ watchDigests), +/* harmony export */ "getLocals": () => (/* binding */ getLocals) +/* harmony export */ }); +/* harmony import */ var _angular__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./angular */ "./node_modules/@uirouter/angularjs/lib-esm/angular.js"); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/* harmony import */ var _statebuilders_views__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./statebuilders/views */ "./node_modules/@uirouter/angularjs/lib-esm/statebuilders/views.js"); +/* harmony import */ var _templateFactory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./templateFactory */ "./node_modules/@uirouter/angularjs/lib-esm/templateFactory.js"); +/* harmony import */ var _stateProvider__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./stateProvider */ "./node_modules/@uirouter/angularjs/lib-esm/stateProvider.js"); +/* harmony import */ var _statebuilders_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./statebuilders/onEnterExitRetain */ "./node_modules/@uirouter/angularjs/lib-esm/statebuilders/onEnterExitRetain.js"); +/* harmony import */ var _locationServices__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./locationServices */ "./node_modules/@uirouter/angularjs/lib-esm/locationServices.js"); +/* harmony import */ var _urlRouterProvider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./urlRouterProvider */ "./node_modules/@uirouter/angularjs/lib-esm/urlRouterProvider.js"); +/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * # Angular 1 types + * + * UI-Router core provides various Typescript types which you can use for code completion and validating parameter values, etc. + * The customizations to the core types for Angular UI-Router are documented here. + * + * The optional [[$resolve]] service is also documented here. + * + * @preferred @publicapi @module ng1 + */ /** */ + + + + + + + + +_angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.angular1', []); +var mod_init = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.init', ['ng']); +var mod_util = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.util', ['ui.router.init']); +var mod_rtr = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.router', ['ui.router.util']); +var mod_state = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.state', ['ui.router.router', 'ui.router.util', 'ui.router.angular1']); +var mod_main = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router', ['ui.router.init', 'ui.router.state', 'ui.router.angular1']); +var mod_cmpt = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.compat', ['ui.router']); +var router = null; +$uiRouterProvider.$inject = ['$locationProvider']; +/** This angular 1 provider instantiates a Router and exposes its services via the angular injector */ +function $uiRouterProvider($locationProvider) { + // Create a new instance of the Router when the $uiRouterProvider is initialized + router = this.router = new _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.UIRouter(); + router.stateProvider = new _stateProvider__WEBPACK_IMPORTED_MODULE_4__.StateProvider(router.stateRegistry, router.stateService); + // Apply ng1 specific StateBuilder code for `views`, `resolve`, and `onExit/Retain/Enter` properties + router.stateRegistry.decorator('views', _statebuilders_views__WEBPACK_IMPORTED_MODULE_2__.ng1ViewsBuilder); + router.stateRegistry.decorator('onExit', (0,_statebuilders_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__.getStateHookBuilder)('onExit')); + router.stateRegistry.decorator('onRetain', (0,_statebuilders_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__.getStateHookBuilder)('onRetain')); + router.stateRegistry.decorator('onEnter', (0,_statebuilders_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__.getStateHookBuilder)('onEnter')); + router.viewService._pluginapi._viewConfigFactory('ng1', (0,_statebuilders_views__WEBPACK_IMPORTED_MODULE_2__.getNg1ViewConfigFactory)()); + // Disable decoding of params by UrlMatcherFactory because $location already handles this + router.urlService.config._decodeParams = false; + var ng1LocationService = (router.locationService = router.locationConfig = new _locationServices__WEBPACK_IMPORTED_MODULE_6__.Ng1LocationServices($locationProvider)); + _locationServices__WEBPACK_IMPORTED_MODULE_6__.Ng1LocationServices.monkeyPatchPathParameterType(router); + // backwards compat: also expose router instance as $uiRouterProvider.router + router['router'] = router; + router['$get'] = $get; + $get.$inject = ['$location', '$browser', '$window', '$sniffer', '$rootScope', '$http', '$templateCache']; + function $get($location, $browser, $window, $sniffer, $rootScope, $http, $templateCache) { + ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser, $window); + delete router['router']; + delete router['$get']; + return router; + } + return router; +} +var getProviderFor = function (serviceName) { return [ + '$uiRouterProvider', + function ($urp) { + var service = $urp.router[serviceName]; + service['$get'] = function () { return service; }; + return service; + }, +]; }; +// This effectively calls $get() on `$uiRouterProvider` to trigger init (when ng enters runtime) +runBlock.$inject = ['$injector', '$q', '$uiRouter']; +function runBlock($injector, $q, $uiRouter) { + _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$injector = $injector; + _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$q = $q; + // https://github.com/angular-ui/ui-router/issues/3678 + if (!Object.prototype.hasOwnProperty.call($injector, 'strictDi')) { + try { + $injector.invoke(function (checkStrictDi) { }); + } + catch (error) { + $injector.strictDi = !!/strict mode/.exec(error && error.toString()); + } + } + // The $injector is now available. + // Find any resolvables that had dependency annotation deferred + $uiRouter.stateRegistry + .get() + .map(function (x) { return x.$$state().resolvables; }) + .reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.unnestR, []) + .filter(function (x) { return x.deps === 'deferred'; }) + .forEach(function (resolvable) { return (resolvable.deps = $injector.annotate(resolvable.resolveFn, $injector.strictDi)); }); +} +// $urlRouter service and $urlRouterProvider +var getUrlRouterProvider = function (uiRouter) { return (uiRouter.urlRouterProvider = new _urlRouterProvider__WEBPACK_IMPORTED_MODULE_7__.UrlRouterProvider(uiRouter)); }; +// $state service and $stateProvider +// $urlRouter service and $urlRouterProvider +var getStateProvider = function () { return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.extend)(router.stateProvider, { $get: function () { return router.stateService; } }); }; +watchDigests.$inject = ['$rootScope']; +function watchDigests($rootScope) { + $rootScope.$watch(function () { + _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.trace.approximateDigests++; + }); +} +mod_init.provider('$uiRouter', $uiRouterProvider); +mod_rtr.provider('$urlRouter', ['$uiRouterProvider', getUrlRouterProvider]); +mod_util.provider('$urlService', getProviderFor('urlService')); +mod_util.provider('$urlMatcherFactory', ['$uiRouterProvider', function () { return router.urlMatcherFactory; }]); +mod_util.provider('$templateFactory', function () { return new _templateFactory__WEBPACK_IMPORTED_MODULE_3__.TemplateFactory(); }); +mod_state.provider('$stateRegistry', getProviderFor('stateRegistry')); +mod_state.provider('$uiRouterGlobals', getProviderFor('globals')); +mod_state.provider('$transitions', getProviderFor('transitionService')); +mod_state.provider('$state', ['$uiRouterProvider', getStateProvider]); +mod_state.factory('$stateParams', ['$uiRouter', function ($uiRouter) { return $uiRouter.globals.params; }]); +mod_main.factory('$view', function () { return router.viewService; }); +mod_main.service('$trace', function () { return _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.trace; }); +mod_main.run(watchDigests); +mod_util.run(['$urlMatcherFactory', function ($urlMatcherFactory) { }]); +mod_state.run(['$state', function ($state) { }]); +mod_rtr.run(['$urlRouter', function ($urlRouter) { }]); +mod_init.run(runBlock); +/** @hidden TODO: find a place to move this */ +var getLocals = function (ctx) { + var tokens = ctx.getTokens().filter(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isString); + var tuples = tokens.map(function (key) { + var resolvable = ctx.getResolvable(key); + var waitPolicy = ctx.getPolicy(resolvable).async; + return [key, waitPolicy === 'NOWAIT' ? resolvable.promise : resolvable.data]; + }); + return tuples.reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.applyPairs, {}); +}; +//# sourceMappingURL=services.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/stateFilters.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/stateFilters.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "$IsStateFilter": () => (/* binding */ $IsStateFilter), +/* harmony export */ "$IncludedByStateFilter": () => (/* binding */ $IncludedByStateFilter) +/* harmony export */ }); +/* harmony import */ var _angular__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./angular */ "./node_modules/@uirouter/angularjs/lib-esm/angular.js"); +/** @publicapi @module ng1 */ /** */ + +/** + * `isState` Filter: truthy if the current state is the parameter + * + * Translates to [[StateService.is]] `$state.is("stateName")`. + * + * #### Example: + * ```html + *
    show if state is 'stateName'
    + * ``` + */ +$IsStateFilter.$inject = ['$state']; +function $IsStateFilter($state) { + var isFilter = function (state, params, options) { + return $state.is(state, params, options); + }; + isFilter.$stateful = true; + return isFilter; +} +/** + * `includedByState` Filter: truthy if the current state includes the parameter + * + * Translates to [[StateService.includes]]` $state.is("fullOrPartialStateName")`. + * + * #### Example: + * ```html + *
    show if 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__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.state').filter('isState', $IsStateFilter).filter('includedByState', $IncludedByStateFilter); + +//# sourceMappingURL=stateFilters.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/stateProvider.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/stateProvider.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateProvider": () => (/* binding */ StateProvider) +/* harmony export */ }); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/** @publicapi @module ng1 */ /** */ + +/** + * The Angular 1 `StateProvider` + * + * The `$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. + */ +var StateProvider = /** @class */ (function () { + function StateProvider(stateRegistry, stateService) { + this.stateRegistry = stateRegistry; + this.stateService = stateService; + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions)((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.val)(StateProvider.prototype), this, (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.val)(this)); + } + /** + * Decorates states when they are registered + * + * Allows you to extend (carefully) or override (at your own peril) the + * `stateBuilder` object used internally by [[StateRegistry]]. + * 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: + * ```js + * $stateProvider.decorator('views', function (state, parent) { + * let result = {}, + * views = parent(state); + * + * angular.forEach(views, function (config, name) { + * let 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' } + * } + * }); + * ``` + * + * + * ```js + * // Auto-populates list and item views with /partials/home/contact/list.html, + * // and /partials/home/contact/item.html, respectively. + * $state.go('home'); + * ``` + * + * @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 + */ + StateProvider.prototype.decorator = function (name, func) { + return this.stateRegistry.decorator(name, func) || this; + }; + StateProvider.prototype.state = function (name, definition) { + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isObject)(name)) { + definition = name; + } + else { + definition.name = name; + } + this.stateRegistry.register(definition); + return this; + }; + /** + * Registers an invalid state handler + * + * This is a passthrough to [[StateService.onInvalid]] for ng1. + */ + StateProvider.prototype.onInvalid = function (callback) { + return this.stateService.onInvalid(callback); + }; + return StateProvider; +}()); + +//# sourceMappingURL=stateProvider.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/statebuilders/onEnterExitRetain.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/statebuilders/onEnterExitRetain.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "getStateHookBuilder": () => (/* binding */ getStateHookBuilder) +/* harmony export */ }); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/* harmony import */ var _services__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../services */ "./node_modules/@uirouter/angularjs/lib-esm/services.js"); +/** @publicapi @module ng1 */ /** */ + + +/** + * This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`, + * `onRetain` callback hooks on a [[Ng1StateDeclaration]]. + * + * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder + * ensures that those hooks are injectable for @uirouter/angularjs (ng1). + * + * @internalapi + */ +var getStateHookBuilder = function (hookName) { + return function stateHookBuilder(stateObject) { + var hook = stateObject[hookName]; + var pathname = hookName === 'onExit' ? 'from' : 'to'; + function decoratedNg1Hook(trans, state) { + var resolveContext = new _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.ResolveContext(trans.treeChanges(pathname)); + var subContext = resolveContext.subContext(state.$$state()); + var locals = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.extend)((0,_services__WEBPACK_IMPORTED_MODULE_1__.getLocals)(subContext), { $state$: state, $transition$: trans }); + return _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$injector.invoke(hook, this, locals); + } + return hook ? decoratedNg1Hook : undefined; + }; +}; +//# sourceMappingURL=onEnterExitRetain.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/statebuilders/views.js": +/*!*************************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/statebuilders/views.js ***! + \*************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "getNg1ViewConfigFactory": () => (/* binding */ getNg1ViewConfigFactory), +/* harmony export */ "ng1ViewsBuilder": () => (/* binding */ ng1ViewsBuilder), +/* harmony export */ "Ng1ViewConfig": () => (/* binding */ Ng1ViewConfig) +/* harmony export */ }); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/** @publicapi @module ng1 */ /** */ + +/** @internalapi */ +function getNg1ViewConfigFactory() { + var templateFactory = null; + return function (path, view) { + templateFactory = templateFactory || _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$injector.get('$templateFactory'); + return [new Ng1ViewConfig(path, view, templateFactory)]; + }; +} +/** @internalapi */ +var hasAnyKey = function (keys, obj) { return keys.reduce(function (acc, key) { return acc || (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isDefined)(obj[key]); }, false); }; +/** + * This is a [[StateBuilder.builder]] function for angular1 `views`. + * + * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder + * handles the `views` property with logic specific to @uirouter/angularjs (ng1). + * + * If no `views: {}` property exists on the [[StateDeclaration]], then it creates the `views` object + * and applies the state-level configuration to a view named `$default`. + * + * @internalapi + */ +function ng1ViewsBuilder(state) { + // Do not process root state + if (!state.parent) + return {}; + var tplKeys = ['templateProvider', 'templateUrl', 'template', 'notify', 'async'], ctrlKeys = ['controller', 'controllerProvider', 'controllerAs', 'resolveAs'], compKeys = ['component', 'bindings', 'componentProvider'], nonCompKeys = tplKeys.concat(ctrlKeys), allViewKeys = compKeys.concat(nonCompKeys); + // Do not allow a state to have both state-level props and also a `views: {}` property. + // A state without a `views: {}` property can declare properties for the `$default` view as properties of the state. + // However, the `$default` approach should not be mixed with a separate `views: ` block. + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isDefined)(state.views) && hasAnyKey(allViewKeys, state)) { + throw new Error("State '" + state.name + "' has a 'views' object. " + + "It cannot also have \"view properties\" at the state level. " + + "Move the following properties into a view (in the 'views' object): " + + (" " + allViewKeys.filter(function (key) { return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isDefined)(state[key]); }).join(', '))); + } + var views = {}, viewsObject = state.views || { $default: (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.pick)(state, allViewKeys) }; + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.forEach)(viewsObject, function (config, name) { + // Account for views: { "": { template... } } + name = name || '$default'; + // Account for views: { header: "headerComponent" } + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isString)(config)) + config = { component: config }; + // Make a shallow copy of the config object + config = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.extend)({}, config); + // Do not allow a view to mix props for component-style view with props for template/controller-style view + if (hasAnyKey(compKeys, config) && hasAnyKey(nonCompKeys, config)) { + throw new Error("Cannot combine: " + compKeys.join('|') + " with: " + nonCompKeys.join('|') + " in stateview: '" + name + "@" + state.name + "'"); + } + config.resolveAs = config.resolveAs || '$resolve'; + config.$type = 'ng1'; + config.$context = state; + config.$name = name; + var normalized = _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.ViewService.normalizeUIViewTarget(config.$context, config.$name); + config.$uiViewName = normalized.uiViewName; + config.$uiViewContextAnchor = normalized.uiViewContextAnchor; + views[name] = config; + }); + return views; +} +/** @hidden */ +var id = 0; +/** @internalapi */ +var Ng1ViewConfig = /** @class */ (function () { + function Ng1ViewConfig(path, viewDecl, factory) { + var _this = this; + this.path = path; + this.viewDecl = viewDecl; + this.factory = factory; + this.$id = id++; + this.loaded = false; + this.getTemplate = function (uiView, context) { + return _this.component + ? _this.factory.makeComponentTemplate(uiView, context, _this.component, _this.viewDecl.bindings) + : _this.template; + }; + } + Ng1ViewConfig.prototype.load = function () { + var _this = this; + var $q = _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$q; + var context = new _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.ResolveContext(this.path); + var params = this.path.reduce(function (acc, node) { return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.extend)(acc, node.paramValues); }, {}); + var promises = { + template: $q.when(this.factory.fromConfig(this.viewDecl, params, context)), + controller: $q.when(this.getController(context)), + }; + return $q.all(promises).then(function (results) { + _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.trace.traceViewServiceEvent('Loaded', _this); + _this.controller = results.controller; + (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.extend)(_this, results.template); // Either { template: "tpl" } or { component: "cmpName" } + return _this; + }); + }; + /** + * Gets the controller for a view configuration. + * + * @returns {Function|Promise.} Returns a controller, or a promise that resolves to a controller. + */ + Ng1ViewConfig.prototype.getController = function (context) { + var provider = this.viewDecl.controllerProvider; + if (!(0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isInjectable)(provider)) + return this.viewDecl.controller; + var deps = _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$injector.annotate(provider); + var providerFn = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isArray)(provider) ? (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.tail)(provider) : provider; + var resolvable = new _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.Resolvable('', providerFn, deps); + return resolvable.get(context); + }; + return Ng1ViewConfig; +}()); + +//# sourceMappingURL=views.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/templateFactory.js": +/*!*********************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/templateFactory.js ***! + \*********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "TemplateFactory": () => (/* binding */ TemplateFactory) +/* harmony export */ }); +/* harmony import */ var _angular__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./angular */ "./node_modules/@uirouter/angularjs/lib-esm/angular.js"); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/** @publicapi @module view */ /** */ + + +/** + * Service which manages loading of templates from a ViewConfig. + */ +var TemplateFactory = /** @class */ (function () { + function TemplateFactory() { + var _this = this; + /** @hidden */ this._useHttp = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.version.minor < 3; + /** @hidden */ this.$get = [ + '$http', + '$templateCache', + '$injector', + function ($http, $templateCache, $injector) { + _this.$templateRequest = $injector.has && $injector.has('$templateRequest') && $injector.get('$templateRequest'); + _this.$http = $http; + _this.$templateCache = $templateCache; + return _this; + }, + ]; + } + /** @hidden */ + TemplateFactory.prototype.useHttpService = function (value) { + this._useHttp = value; + }; + /** + * Creates a template from a configuration object. + * + * @param 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 params Parameters to pass to the template function. + * @param context The resolve context associated with the template's view + * + * @return {string|object} The template html as a string, or a promise for + * that string,or `null` if no template is configured. + */ + TemplateFactory.prototype.fromConfig = function (config, params, context) { + var defaultTemplate = ''; + var asTemplate = function (result) { return _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$q.when(result).then(function (str) { return ({ template: str }); }); }; + var asComponent = function (result) { return _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$q.when(result).then(function (str) { return ({ component: str }); }); }; + return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isDefined)(config.template) + ? asTemplate(this.fromString(config.template, params)) + : (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isDefined)(config.templateUrl) + ? asTemplate(this.fromUrl(config.templateUrl, params)) + : (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isDefined)(config.templateProvider) + ? asTemplate(this.fromProvider(config.templateProvider, params, context)) + : (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isDefined)(config.component) + ? asComponent(config.component) + : (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isDefined)(config.componentProvider) + ? asComponent(this.fromComponentProvider(config.componentProvider, params, context)) + : asTemplate(defaultTemplate); + }; + /** + * Creates a template from a string or a function returning a string. + * + * @param template html template as a string or function that returns an html template as a string. + * @param params Parameters to pass to the template function. + * + * @return {string|object} The template html as a string, or a promise for that + * string. + */ + TemplateFactory.prototype.fromString = function (template, params) { + return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isFunction)(template) ? template(params) : template; + }; + /** + * 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. + */ + TemplateFactory.prototype.fromUrl = function (url, params) { + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isFunction)(url)) + url = url(params); + if (url == null) + return null; + if (this._useHttp) { + return this.$http + .get(url, { cache: this.$templateCache, headers: { Accept: 'text/html' } }) + .then(function (response) { + return response.data; + }); + } + return this.$templateRequest(url); + }; + /** + * Creates a template by invoking an injectable provider function. + * + * @param provider Function to invoke via `locals` + * @param {Function} injectFn a function used to invoke the template provider + * @return {string|Promise.} The template html as a string, or a promise + * for that string. + */ + TemplateFactory.prototype.fromProvider = function (provider, params, context) { + var deps = _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$injector.annotate(provider); + var providerFn = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isArray)(provider) ? (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.tail)(provider) : provider; + var resolvable = new _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.Resolvable('', providerFn, deps); + return resolvable.get(context); + }; + /** + * Creates a component's template by invoking an injectable provider function. + * + * @param provider Function to invoke via `locals` + * @param {Function} injectFn a function used to invoke the template provider + * @return {string} The template html as a string: "". + */ + TemplateFactory.prototype.fromComponentProvider = function (provider, params, context) { + var deps = _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$injector.annotate(provider); + var providerFn = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isArray)(provider) ? (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.tail)(provider) : provider; + var resolvable = new _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.Resolvable('', providerFn, deps); + return resolvable.get(context); + }; + /** + * Creates a template from a component's name + * + * This implements route-to-component. + * It works by retrieving the component (directive) metadata from the injector. + * It analyses the component's bindings, then constructs a template that instantiates the component. + * The template wires input and output bindings to resolves or from the parent component. + * + * @param uiView {object} The parent ui-view (for binding outputs to callbacks) + * @param context The ResolveContext (for binding outputs to callbacks returned from resolves) + * @param component {string} Component's name in camel case. + * @param bindings An object defining the component's bindings: {foo: '<'} + * @return {string} The template as a string: "". + */ + TemplateFactory.prototype.makeComponentTemplate = function (uiView, context, component, bindings) { + bindings = bindings || {}; + // Bind once prefix + var prefix = _angular__WEBPACK_IMPORTED_MODULE_0__.ng.version.minor >= 3 ? '::' : ''; + // Convert to kebob name. Add x- prefix if the string starts with `x-` or `data-` + var kebob = function (camelCase) { + var kebobed = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.kebobString)(camelCase); + return /^(x|data)-/.exec(kebobed) ? "x-" + kebobed : kebobed; + }; + var attributeTpl = function (input) { + var name = input.name, type = input.type; + var attrName = kebob(name); + // If the ui-view has an attribute which matches a binding on the routed component + // then pass that attribute through to the routed component template. + // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:` + if (uiView.attr(attrName) && !bindings[name]) + return attrName + "='" + uiView.attr(attrName) + "'"; + var resolveName = bindings[name] || name; + // Pre-evaluate the expression for "@" bindings by enclosing in {{ }} + // some-attr="{{ ::$resolve.someResolveName }}" + if (type === '@') + return attrName + "='{{" + prefix + "$resolve." + resolveName + "}}'"; + // Wire "&" callbacks to resolves that return a callback function + // Get the result of the resolve (should be a function) and annotate it to get its arguments. + // some-attr="$resolve.someResolveResultName(foo, bar)" + if (type === '&') { + var res = context.getResolvable(resolveName); + var fn = res && res.data; + var args = (fn && _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$injector.annotate(fn)) || []; + // account for array style injection, i.e., ['foo', function(foo) {}] + var arrayIdxStr = (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isArray)(fn) ? "[" + (fn.length - 1) + "]" : ''; + return attrName + "='$resolve." + resolveName + arrayIdxStr + "(" + args.join(',') + ")'"; + } + // some-attr="::$resolve.someResolveName" + return attrName + "='" + prefix + "$resolve." + resolveName + "'"; + }; + var attrs = getComponentBindings(component).map(attributeTpl).join(' '); + var kebobName = kebob(component); + return "<" + kebobName + " " + attrs + ">"; + }; + return TemplateFactory; +}()); + +// Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&') +function getComponentBindings(name) { + var cmpDefs = _uirouter_core__WEBPACK_IMPORTED_MODULE_1__.services.$injector.get(name + 'Directive'); // could be multiple + if (!cmpDefs || !cmpDefs.length) + throw new Error("Unable to find component named '" + name + "'"); + return cmpDefs.map(getBindings).reduce(_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.unnestR, []); +} +// Given a directive definition, find its object input attributes +// Use different properties, depending on the type of directive (component, bindToController, normal) +var getBindings = function (def) { + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isObject)(def.bindToController)) + return scopeBindings(def.bindToController); + return scopeBindings(def.scope); +}; +// for ng 1.2 style, process the scope: { input: "=foo" } +// for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object +var scopeBindings = function (bindingsObj) { + return Object.keys(bindingsObj || {}) + // [ 'input', [ '=foo', '=', 'foo' ] ] + .map(function (key) { return [key, /^([=<@&])[?]?(.*)/.exec(bindingsObj[key])]; }) + // skip malformed values + .filter(function (tuple) { return (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isDefined)(tuple) && (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_1__.isArray)(tuple[1]); }) + // { name: ('foo' || 'input'), type: '=' } + .map(function (tuple) { return ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }); }); +}; +//# sourceMappingURL=templateFactory.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/urlRouterProvider.js": +/*!***********************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/urlRouterProvider.js ***! + \***********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlRouterProvider": () => (/* binding */ UrlRouterProvider) +/* harmony export */ }); +/* harmony import */ var _uirouter_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @uirouter/core */ "./node_modules/@uirouter/core/lib-esm/index.js"); +/** @publicapi @module url */ /** */ + + +/** + * Manages rules for client-side URL + * + * ### Deprecation warning: + * This class is now considered to be an internal API + * Use the [[UrlService]] instead. + * For configuring URL rules, use the [[UrlRulesApi]] which can be found as [[UrlService.rules]]. + * + * This class manages the router rules for what to do when the URL changes. + * + * This provider remains for backwards compatibility. + * + * @internalapi + * @deprecated + */ +var UrlRouterProvider = /** @class */ (function () { + /** @hidden */ + function UrlRouterProvider(/** @hidden */ router) { + this.router = router; + } + UrlRouterProvider.injectableHandler = function (router, handler) { + return function (match) { return _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$injector.invoke(handler, null, { $match: match, $stateParams: router.globals.params }); }; + }; + /** @hidden */ + UrlRouterProvider.prototype.$get = function () { + var urlService = this.router.urlService; + this.router.urlRouter.update(true); + if (!urlService.interceptDeferred) + urlService.listen(); + return this.router.urlRouter; + }; + /** + * Registers a url handler function. + * + * Registers a low level url handler (a `rule`). + * A rule detects specific URL patterns and returns a redirect, or performs some action. + * + * If a rule returns a string, the URL is replaced with the string, and all rules are fired again. + * + * #### Example: + * ```js + * 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 ruleFn + * Handler function that takes `$injector` and `$location` services as arguments. + * You can use them to detect a url and return a different url as a string. + * + * @return [[UrlRouterProvider]] (`this`) + */ + UrlRouterProvider.prototype.rule = function (ruleFn) { + var _this = this; + if (!(0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isFunction)(ruleFn)) + throw new Error("'rule' must be a function"); + var match = function () { return ruleFn(_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$injector, _this.router.locationService); }; + var rule = new _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.BaseUrlRule(match, _uirouter_core__WEBPACK_IMPORTED_MODULE_0__.identity); + this.router.urlService.rules.rule(rule); + return this; + }; + /** + * Defines the path or behavior to use when no url can be matched. + * + * #### Example: + * ```js + * 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 rule + * The url path you want to redirect to or a function rule that returns the url path or performs a `$state.go()`. + * The function version is passed two params: `$injector` and `$location` services, and should return a url string. + * + * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance + */ + UrlRouterProvider.prototype.otherwise = function (rule) { + var _this = this; + var urlRules = this.router.urlService.rules; + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isString)(rule)) { + urlRules.otherwise(rule); + } + else if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isFunction)(rule)) { + urlRules.otherwise(function () { return rule(_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.services.$injector, _this.router.locationService); }); + } + else { + throw new Error("'rule' must be a string or function"); + } + return this; + }; + /** + * 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: + * ```js + * 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 what A pattern string to match, compiled as a [[UrlMatcher]]. + * @param handler The path (or function that returns a path) that you want to redirect your user to. + * @param ruleCallback [optional] A callback that receives the `rule` registered with [[UrlMatcher.rule]] + * + * Note: the handler may also invoke arbitrary code, such as `$state.go()` + */ + UrlRouterProvider.prototype.when = function (what, handler) { + if ((0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isArray)(handler) || (0,_uirouter_core__WEBPACK_IMPORTED_MODULE_0__.isFunction)(handler)) { + handler = UrlRouterProvider.injectableHandler(this.router, handler); + } + this.router.urlService.rules.when(what, handler); + return this; + }; + /** + * Disables monitoring of the URL. + * + * Call this method before UI-Router has bootstrapped. + * It will stop UI-Router from performing the initial url sync. + * + * This can be useful to perform some asynchronous initialization before the router starts. + * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL. + * + * #### Example: + * ```js + * var app = angular.module('app', ['ui.router']); + * + * app.config(function ($urlRouterProvider) { + * // Prevent $urlRouter from automatically intercepting URL changes; + * $urlRouterProvider.deferIntercept(); + * }) + * + * app.run(function (MyService, $urlRouter, $http) { + * $http.get("/stuff").then(function(resp) { + * MyService.doStuff(resp.data); + * $urlRouter.listen(); + * $urlRouter.sync(); + * }); + * }); + * ``` + * + * @param defer Indicates whether to defer location change interception. + * Passing no parameter is equivalent to `true`. + */ + UrlRouterProvider.prototype.deferIntercept = function (defer) { + this.router.urlService.deferIntercept(defer); + }; + return UrlRouterProvider; +}()); + +//# sourceMappingURL=urlRouterProvider.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/angularjs/lib-esm/viewScroll.js": +/*!****************************************************************!*\ + !*** ./node_modules/@uirouter/angularjs/lib-esm/viewScroll.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _angular__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./angular */ "./node_modules/@uirouter/angularjs/lib-esm/angular.js"); +/** @publicapi @module ng1 */ /** */ + +/** @hidden */ +function $ViewScrollProvider() { + var useAnchorScroll = false; + this.useAnchorScroll = function () { + useAnchorScroll = true; + }; + this.$get = [ + '$anchorScroll', + '$timeout', + function ($anchorScroll, $timeout) { + if (useAnchorScroll) { + return $anchorScroll; + } + return function ($element) { + return $timeout(function () { + $element[0].scrollIntoView(); + }, 0, false); + }; + }, + ]; +} +_angular__WEBPACK_IMPORTED_MODULE_0__.ng.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); +//# sourceMappingURL=viewScroll.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/common.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/common.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "root": () => (/* binding */ root), +/* harmony export */ "fromJson": () => (/* binding */ fromJson), +/* harmony export */ "toJson": () => (/* binding */ toJson), +/* harmony export */ "forEach": () => (/* binding */ forEach), +/* harmony export */ "extend": () => (/* binding */ extend), +/* harmony export */ "equals": () => (/* binding */ equals), +/* harmony export */ "identity": () => (/* binding */ identity), +/* harmony export */ "noop": () => (/* binding */ noop), +/* harmony export */ "createProxyFunctions": () => (/* binding */ createProxyFunctions), +/* harmony export */ "inherit": () => (/* binding */ inherit), +/* harmony export */ "inArray": () => (/* binding */ inArray), +/* harmony export */ "_inArray": () => (/* binding */ _inArray), +/* harmony export */ "removeFrom": () => (/* binding */ removeFrom), +/* harmony export */ "_removeFrom": () => (/* binding */ _removeFrom), +/* harmony export */ "pushTo": () => (/* binding */ pushTo), +/* harmony export */ "_pushTo": () => (/* binding */ _pushTo), +/* harmony export */ "deregAll": () => (/* binding */ deregAll), +/* harmony export */ "defaults": () => (/* binding */ defaults), +/* harmony export */ "mergeR": () => (/* binding */ mergeR), +/* harmony export */ "ancestors": () => (/* binding */ ancestors), +/* harmony export */ "pick": () => (/* binding */ pick), +/* harmony export */ "omit": () => (/* binding */ omit), +/* harmony export */ "pluck": () => (/* binding */ pluck), +/* harmony export */ "filter": () => (/* binding */ filter), +/* harmony export */ "find": () => (/* binding */ find), +/* harmony export */ "mapObj": () => (/* binding */ mapObj), +/* harmony export */ "map": () => (/* binding */ map), +/* harmony export */ "values": () => (/* binding */ values), +/* harmony export */ "allTrueR": () => (/* binding */ allTrueR), +/* harmony export */ "anyTrueR": () => (/* binding */ anyTrueR), +/* harmony export */ "unnestR": () => (/* binding */ unnestR), +/* harmony export */ "flattenR": () => (/* binding */ flattenR), +/* harmony export */ "pushR": () => (/* binding */ pushR), +/* harmony export */ "uniqR": () => (/* binding */ uniqR), +/* harmony export */ "unnest": () => (/* binding */ unnest), +/* harmony export */ "flatten": () => (/* binding */ flatten), +/* harmony export */ "assertPredicate": () => (/* binding */ assertPredicate), +/* harmony export */ "assertMap": () => (/* binding */ assertMap), +/* harmony export */ "assertFn": () => (/* binding */ assertFn), +/* harmony export */ "pairs": () => (/* binding */ pairs), +/* harmony export */ "arrayTuples": () => (/* binding */ arrayTuples), +/* harmony export */ "applyPairs": () => (/* binding */ applyPairs), +/* harmony export */ "tail": () => (/* binding */ tail), +/* harmony export */ "copy": () => (/* binding */ copy), +/* harmony export */ "_extend": () => (/* binding */ _extend), +/* harmony export */ "silenceUncaughtInPromise": () => (/* binding */ silenceUncaughtInPromise), +/* harmony export */ "silentRejection": () => (/* binding */ silentRejection) +/* harmony export */ }); +/* harmony import */ var _predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _coreservices__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +var __spreadArrays = (undefined && undefined.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +/** + * Random utility functions used in the UI-Router code + * + * These functions are exported, but are subject to change without notice. + * + * @packageDocumentation + * @preferred + */ + + + +var root = (typeof self === 'object' && self.self === self && self) || + (typeof __webpack_require__.g === 'object' && __webpack_require__.g.global === __webpack_require__.g && __webpack_require__.g) || + undefined; +var angular = root.angular || {}; +var fromJson = angular.fromJson || JSON.parse.bind(JSON); +var toJson = angular.toJson || JSON.stringify.bind(JSON); +var forEach = angular.forEach || _forEach; +var extend = Object.assign || _extend; +var equals = angular.equals || _equals; +function identity(x) { + return x; +} +function noop() { } +/** + * Builds proxy functions on the `to` object which pass through to the `from` object. + * + * For each key in `fnNames`, creates a proxy function on the `to` object. + * The proxy function calls the real function on the `from` object. + * + * + * #### Example: + * This example creates an new class instance whose functions are prebound to the new'd object. + * ```js + * class Foo { + * constructor(data) { + * // Binds all functions from Foo.prototype to 'this', + * // then copies them to 'this' + * bindFunctions(Foo.prototype, this, this); + * this.data = data; + * } + * + * log() { + * console.log(this.data); + * } + * } + * + * let myFoo = new Foo([1,2,3]); + * var logit = myFoo.log; + * logit(); // logs [1, 2, 3] from the myFoo 'this' instance + * ``` + * + * #### Example: + * This example creates a bound version of a service function, and copies it to another object + * ``` + * + * var SomeService = { + * this.data = [3, 4, 5]; + * this.log = function() { + * console.log(this.data); + * } + * } + * + * // Constructor fn + * function OtherThing() { + * // Binds all functions from SomeService to SomeService, + * // then copies them to 'this' + * bindFunctions(SomeService, this, SomeService); + * } + * + * let myOtherThing = new OtherThing(); + * myOtherThing.log(); // logs [3, 4, 5] from SomeService's 'this' + * ``` + * + * @param source A function that returns the source object which contains the original functions to be bound + * @param target A function that returns the target object which will receive the bound functions + * @param bind A function that returns the object which the functions will be bound to + * @param fnNames The function names which will be bound (Defaults to all the functions found on the 'from' object) + * @param latebind If true, the binding of the function is delayed until the first time it's invoked + */ +function createProxyFunctions(source, target, bind, fnNames, latebind) { + if (latebind === void 0) { latebind = false; } + var bindFunction = function (fnName) { return source()[fnName].bind(bind()); }; + var makeLateRebindFn = function (fnName) { + return function lateRebindFunction() { + target[fnName] = bindFunction(fnName); + return target[fnName].apply(null, arguments); + }; + }; + fnNames = fnNames || Object.keys(source()); + return fnNames.reduce(function (acc, name) { + acc[name] = latebind ? makeLateRebindFn(name) : bindFunction(name); + return acc; + }, target); +} +/** + * prototypal inheritance helper. + * Creates a new object which has `parent` object as its prototype, and then copies the properties from `extra` onto it + */ +var inherit = function (parent, extra) { return extend(Object.create(parent), extra); }; +/** Given an array, returns true if the object is found in the array, (using indexOf) */ +var inArray = (0,_hof__WEBPACK_IMPORTED_MODULE_1__.curry)(_inArray); +function _inArray(array, obj) { + return array.indexOf(obj) !== -1; +} +/** + * Given an array, and an item, if the item is found in the array, it removes it (in-place). + * The same array is returned + */ +var removeFrom = (0,_hof__WEBPACK_IMPORTED_MODULE_1__.curry)(_removeFrom); +function _removeFrom(array, obj) { + var idx = array.indexOf(obj); + if (idx >= 0) + array.splice(idx, 1); + return array; +} +/** pushes a values to an array and returns the value */ +var pushTo = (0,_hof__WEBPACK_IMPORTED_MODULE_1__.curry)(_pushTo); +function _pushTo(arr, val) { + return arr.push(val), val; +} +/** Given an array of (deregistration) functions, calls all functions and removes each one from the source array */ +var deregAll = function (functions) { + return functions.slice().forEach(function (fn) { + typeof fn === 'function' && fn(); + removeFrom(functions, fn); + }); +}; +/** + * Applies a set of defaults to an options object. The options object is filtered + * to only those properties of the objects in the defaultsList. + * Earlier objects in the defaultsList take precedence when applying defaults. + */ +function defaults(opts) { + var defaultsList = []; + for (var _i = 1; _i < arguments.length; _i++) { + defaultsList[_i - 1] = arguments[_i]; + } + var defaultVals = extend.apply(void 0, __spreadArrays([{}], defaultsList.reverse())); + return extend(defaultVals, pick(opts || {}, Object.keys(defaultVals))); +} +/** Reduce function that merges each element of the list into a single object, using extend */ +var mergeR = function (memo, item) { return extend(memo, item); }; +/** + * 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 = []; + // tslint:disable-next-line:forin + for (var n in first.path) { + if (first.path[n] !== second.path[n]) + break; + path.push(first.path[n]); + } + return path; +} +/** + * Return a copy of the object only containing the whitelisted properties. + * + * #### Example: + * ``` + * var foo = { a: 1, b: 2, c: 3 }; + * var ab = pick(foo, ['a', 'b']); // { a: 1, b: 2 } + * ``` + * @param obj the source object + * @param propNames an Array of strings, which are the whitelisted property names + */ +function pick(obj, propNames) { + var objCopy = {}; + for (var _prop in obj) { + if (propNames.indexOf(_prop) !== -1) { + objCopy[_prop] = obj[_prop]; + } + } + return objCopy; +} +/** + * Return a copy of the object omitting the blacklisted properties. + * + * @example + * ``` + * + * var foo = { a: 1, b: 2, c: 3 }; + * var ab = omit(foo, ['a', 'b']); // { c: 3 } + * ``` + * @param obj the source object + * @param propNames an Array of strings, which are the blacklisted property names + */ +function omit(obj, propNames) { + return Object.keys(obj) + .filter((0,_hof__WEBPACK_IMPORTED_MODULE_1__.not)(inArray(propNames))) + .reduce(function (acc, key) { return ((acc[key] = obj[key]), acc); }, {}); +} +/** + * Maps an array, or object to a property (by name) + */ +function pluck(collection, propName) { + return map(collection, (0,_hof__WEBPACK_IMPORTED_MODULE_1__.prop)(propName)); +} +/** Filters an Array or an Object's properties based on a predicate */ +function filter(collection, callback) { + var arr = (0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(collection), result = arr ? [] : {}; + var accept = arr ? function (x) { return result.push(x); } : function (x, key) { return (result[key] = x); }; + forEach(collection, function (item, i) { + if (callback(item, i)) + accept(item, i); + }); + return result; +} +/** Finds an object from an array, or a property of an object, that matches a predicate */ +function find(collection, callback) { + var result; + forEach(collection, function (item, i) { + if (result) + return; + if (callback(item, i)) + result = item; + }); + return result; +} +/** Given an object, returns a new object, where each property is transformed by the callback function */ +var mapObj = map; +/** Maps an array or object properties using a callback function */ +function map(collection, callback, target) { + target = target || ((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(collection) ? [] : {}); + forEach(collection, function (item, i) { return (target[i] = callback(item, i)); }); + return target; +} +/** + * Given an object, return its enumerable property values + * + * @example + * ``` + * + * let foo = { a: 1, b: 2, c: 3 } + * let vals = values(foo); // [ 1, 2, 3 ] + * ``` + */ +var values = function (obj) { return Object.keys(obj).map(function (key) { return obj[key]; }); }; +/** + * Reduce function that returns true if all of the values are truthy. + * + * @example + * ``` + * + * let vals = [ 1, true, {}, "hello world"]; + * vals.reduce(allTrueR, true); // true + * + * vals.push(0); + * vals.reduce(allTrueR, true); // false + * ``` + */ +var allTrueR = function (memo, elem) { return memo && elem; }; +/** + * Reduce function that returns true if any of the values are truthy. + * + * * @example + * ``` + * + * let vals = [ 0, null, undefined ]; + * vals.reduce(anyTrueR, true); // false + * + * vals.push("hello world"); + * vals.reduce(anyTrueR, true); // true + * ``` + */ +var anyTrueR = function (memo, elem) { return memo || elem; }; +/** + * Reduce function which un-nests a single level of arrays + * @example + * ``` + * + * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; + * input.reduce(unnestR, []) // [ "a", "b", "c", "d", [ "double, "nested" ] ] + * ``` + */ +var unnestR = function (memo, elem) { return memo.concat(elem); }; +/** + * Reduce function which recursively un-nests all arrays + * + * @example + * ``` + * + * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; + * input.reduce(unnestR, []) // [ "a", "b", "c", "d", "double, "nested" ] + * ``` + */ +var flattenR = function (memo, elem) { + return (0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(elem) ? memo.concat(elem.reduce(flattenR, [])) : pushR(memo, elem); +}; +/** + * Reduce function that pushes an object to an array, then returns the array. + * Mostly just for [[flattenR]] and [[uniqR]] + */ +function pushR(arr, obj) { + arr.push(obj); + return arr; +} +/** Reduce function that filters out duplicates */ +var uniqR = function (acc, token) { return (inArray(acc, token) ? acc : pushR(acc, token)); }; +/** + * Return a new array with a single level of arrays unnested. + * + * @example + * ``` + * + * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; + * unnest(input) // [ "a", "b", "c", "d", [ "double, "nested" ] ] + * ``` + */ +var unnest = function (arr) { return arr.reduce(unnestR, []); }; +/** + * Return a completely flattened version of an array. + * + * @example + * ``` + * + * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; + * flatten(input) // [ "a", "b", "c", "d", "double, "nested" ] + * ``` + */ +var flatten = function (arr) { return arr.reduce(flattenR, []); }; +/** + * Given a .filter Predicate, builds a .filter Predicate which throws an error if any elements do not pass. + * @example + * ``` + * + * let isNumber = (obj) => typeof(obj) === 'number'; + * let allNumbers = [ 1, 2, 3, 4, 5 ]; + * allNumbers.filter(assertPredicate(isNumber)); //OK + * + * let oneString = [ 1, 2, 3, 4, "5" ]; + * oneString.filter(assertPredicate(isNumber, "Not all numbers")); // throws Error(""Not all numbers""); + * ``` + */ +var assertPredicate = assertFn; +/** + * Given a .map function, builds a .map function which throws an error if any mapped elements do not pass a truthyness test. + * @example + * ``` + * + * var data = { foo: 1, bar: 2 }; + * + * let keys = [ 'foo', 'bar' ] + * let values = keys.map(assertMap(key => data[key], "Key not found")); + * // values is [1, 2] + * + * let keys = [ 'foo', 'bar', 'baz' ] + * let values = keys.map(assertMap(key => data[key], "Key not found")); + * // throws Error("Key not found") + * ``` + */ +var assertMap = assertFn; +function assertFn(predicateOrMap, errMsg) { + if (errMsg === void 0) { errMsg = 'assert failure'; } + return function (obj) { + var result = predicateOrMap(obj); + if (!result) { + throw new Error((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isFunction)(errMsg) ? errMsg(obj) : errMsg); + } + return result; + }; +} +/** + * Like _.pairs: Given an object, returns an array of key/value pairs + * + * @example + * ``` + * + * pairs({ foo: "FOO", bar: "BAR }) // [ [ "foo", "FOO" ], [ "bar": "BAR" ] ] + * ``` + */ +var pairs = function (obj) { return Object.keys(obj).map(function (key) { return [key, obj[key]]; }); }; +/** + * Given two or more parallel arrays, returns an array of tuples where + * each tuple is composed of [ a[i], b[i], ... z[i] ] + * + * @example + * ``` + * + * let foo = [ 0, 2, 4, 6 ]; + * let bar = [ 1, 3, 5, 7 ]; + * let baz = [ 10, 30, 50, 70 ]; + * arrayTuples(foo, bar); // [ [0, 1], [2, 3], [4, 5], [6, 7] ] + * arrayTuples(foo, bar, baz); // [ [0, 1, 10], [2, 3, 30], [4, 5, 50], [6, 7, 70] ] + * ``` + */ +function arrayTuples() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + if (args.length === 0) + return []; + var maxArrayLen = args.reduce(function (min, arr) { return Math.min(arr.length, min); }, 9007199254740991); // aka 2^53 − 1 aka Number.MAX_SAFE_INTEGER + var result = []; + var _loop_1 = function (i) { + // This is a hot function + // Unroll when there are 1-4 arguments + switch (args.length) { + case 1: + result.push([args[0][i]]); + break; + case 2: + result.push([args[0][i], args[1][i]]); + break; + case 3: + result.push([args[0][i], args[1][i], args[2][i]]); + break; + case 4: + result.push([args[0][i], args[1][i], args[2][i], args[3][i]]); + break; + default: + result.push(args.map(function (array) { return array[i]; })); + break; + } + }; + for (var i = 0; i < maxArrayLen; i++) { + _loop_1(i); + } + return result; +} +/** + * Reduce function which builds an object from an array of [key, value] pairs. + * + * Each iteration sets the key/val pair on the memo object, then returns the memo for the next iteration. + * + * Each keyValueTuple should be an array with values [ key: string, value: any ] + * + * @example + * ``` + * + * var pairs = [ ["fookey", "fooval"], ["barkey", "barval"] ] + * + * var pairsToObj = pairs.reduce((memo, pair) => applyPairs(memo, pair), {}) + * // pairsToObj == { fookey: "fooval", barkey: "barval" } + * + * // Or, more simply: + * var pairsToObj = pairs.reduce(applyPairs, {}) + * // pairsToObj == { fookey: "fooval", barkey: "barval" } + * ``` + */ +function applyPairs(memo, keyValTuple) { + var key, value; + if ((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(keyValTuple)) + key = keyValTuple[0], value = keyValTuple[1]; + if (!(0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)(key)) + throw new Error('invalid parameters to applyPairs'); + memo[key] = value; + return memo; +} +/** Get the last element of an array */ +function tail(arr) { + return (arr.length && arr[arr.length - 1]) || undefined; +} +/** + * shallow copy from src to dest + */ +function copy(src, dest) { + if (dest) + Object.keys(dest).forEach(function (key) { return delete dest[key]; }); + if (!dest) + dest = {}; + return extend(dest, src); +} +/** Naive forEach implementation works with Objects or Arrays */ +function _forEach(obj, cb, _this) { + if ((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(obj)) + return obj.forEach(cb, _this); + Object.keys(obj).forEach(function (key) { return cb(obj[key], key); }); +} +function _extend(toObj) { + for (var i = 1; i < arguments.length; i++) { + var obj = arguments[i]; + if (!obj) + continue; + var keys = Object.keys(obj); + for (var j = 0; j < keys.length; j++) { + toObj[keys[j]] = obj[keys[j]]; + } + } + return toObj; +} +function _equals(o1, o2) { + if (o1 === o2) + return true; + if (o1 === null || o2 === null) + return false; + if (o1 !== o1 && o2 !== o2) + return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2; + if (t1 !== t2 || t1 !== 'object') + return false; + var tup = [o1, o2]; + if ((0,_hof__WEBPACK_IMPORTED_MODULE_1__.all)(_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(tup)) + return _arraysEq(o1, o2); + if ((0,_hof__WEBPACK_IMPORTED_MODULE_1__.all)(_predicates__WEBPACK_IMPORTED_MODULE_0__.isDate)(tup)) + return o1.getTime() === o2.getTime(); + if ((0,_hof__WEBPACK_IMPORTED_MODULE_1__.all)(_predicates__WEBPACK_IMPORTED_MODULE_0__.isRegExp)(tup)) + return o1.toString() === o2.toString(); + if ((0,_hof__WEBPACK_IMPORTED_MODULE_1__.all)(_predicates__WEBPACK_IMPORTED_MODULE_0__.isFunction)(tup)) + return true; // meh + var predicates = [_predicates__WEBPACK_IMPORTED_MODULE_0__.isFunction, _predicates__WEBPACK_IMPORTED_MODULE_0__.isArray, _predicates__WEBPACK_IMPORTED_MODULE_0__.isDate, _predicates__WEBPACK_IMPORTED_MODULE_0__.isRegExp]; + if (predicates.map(_hof__WEBPACK_IMPORTED_MODULE_1__.any).reduce(function (b, fn) { return b || !!fn(tup); }, false)) + return false; + var keys = {}; + // tslint:disable-next-line:forin + for (var key in o1) { + if (!_equals(o1[key], o2[key])) + return false; + keys[key] = true; + } + for (var key in o2) { + if (!keys[key]) + return false; + } + return true; +} +function _arraysEq(a1, a2) { + if (a1.length !== a2.length) + return false; + return arrayTuples(a1, a2).reduce(function (b, t) { return b && _equals(t[0], t[1]); }, true); +} +// issue #2676 +var silenceUncaughtInPromise = function (promise) { return promise.catch(function (e) { return 0; }) && promise; }; +var silentRejection = function (error) { return silenceUncaughtInPromise(_coreservices__WEBPACK_IMPORTED_MODULE_2__.services.$q.reject(error)); }; +//# sourceMappingURL=common.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js": +/*!********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/coreservices.js ***! + \********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "makeStub": () => (/* binding */ makeStub), +/* harmony export */ "services": () => (/* binding */ services) +/* harmony export */ }); +var noImpl = function (fnname) { return function () { + throw new Error("No implementation for " + fnname + ". The framework specific code did not implement this method."); +}; }; +var makeStub = function (service, methods) { + return methods.reduce(function (acc, key) { return ((acc[key] = noImpl(service + "." + key + "()")), acc); }, {}); +}; +var services = { + $q: undefined, + $injector: undefined, +}; + +//# sourceMappingURL=coreservices.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/glob.js": +/*!************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/glob.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Glob": () => (/* binding */ Glob) +/* harmony export */ }); +/** + * Matches state names using glob-like pattern strings. + * + * Globs can be used in specific APIs including: + * + * - [[StateService.is]] + * - [[StateService.includes]] + * - The first argument to Hook Registration functions like [[TransitionService.onStart]] + * - [[HookMatchCriteria]] and [[HookMatchCriterion]] + * + * A `Glob` string is a pattern which matches state names. + * Nested state names are split into segments (separated by a dot) when processing. + * The state named `foo.bar.baz` is split into three segments ['foo', 'bar', 'baz'] + * + * Globs work according to the following rules: + * + * ### Exact match: + * + * The glob `'A.B'` matches the state named exactly `'A.B'`. + * + * | Glob |Matches states named|Does not match state named| + * |:------------|:--------------------|:---------------------| + * | `'A'` | `'A'` | `'B'` , `'A.C'` | + * | `'A.B'` | `'A.B'` | `'A'` , `'A.B.C'` | + * | `'foo'` | `'foo'` | `'FOO'` , `'foo.bar'`| + * + * ### Single star (`*`) + * + * A single star (`*`) is a wildcard that matches exactly one segment. + * + * | Glob |Matches states named |Does not match state named | + * |:------------|:---------------------|:--------------------------| + * | `'*'` | `'A'` , `'Z'` | `'A.B'` , `'Z.Y.X'` | + * | `'A.*'` | `'A.B'` , `'A.C'` | `'A'` , `'A.B.C'` | + * | `'A.*.*'` | `'A.B.C'` , `'A.X.Y'`| `'A'`, `'A.B'` , `'Z.Y.X'`| + * + * ### Double star (`**`) + * + * A double star (`'**'`) is a wildcard that matches *zero or more segments* + * + * | Glob |Matches states named |Does not match state named | + * |:------------|:----------------------------------------------|:----------------------------------| + * | `'**'` | `'A'` , `'A.B'`, `'Z.Y.X'` | (matches all states) | + * | `'A.**'` | `'A'` , `'A.B'` , `'A.C.X'` | `'Z.Y.X'` | + * | `'**.X'` | `'X'` , `'A.X'` , `'Z.Y.X'` | `'A'` , `'A.login.Z'` | + * | `'A.**.X'` | `'A.X'` , `'A.B.X'` , `'A.B.C.X'` | `'A'` , `'A.B.C'` | + * + * @packageDocumentation + */ +var Glob = /** @class */ (function () { + function Glob(text) { + this.text = text; + this.glob = text.split('.'); + var regexpString = this.text + .split('.') + .map(function (seg) { + if (seg === '**') + return '(?:|(?:\\.[^.]*)*)'; + if (seg === '*') + return '\\.[^.]*'; + return '\\.' + seg; + }) + .join(''); + this.regexp = new RegExp('^' + regexpString + '$'); + } + /** Returns true if the string has glob-like characters in it */ + Glob.is = function (text) { + return !!/[!,*]+/.exec(text); + }; + /** Returns a glob from the string, or null if the string isn't Glob-like */ + Glob.fromString = function (text) { + return Glob.is(text) ? new Glob(text) : null; + }; + Glob.prototype.matches = function (name) { + return this.regexp.test('.' + name); + }; + return Glob; +}()); + +//# sourceMappingURL=glob.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/hof.js": +/*!***********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/hof.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "curry": () => (/* binding */ curry), +/* harmony export */ "compose": () => (/* binding */ compose), +/* harmony export */ "pipe": () => (/* binding */ pipe), +/* harmony export */ "prop": () => (/* binding */ prop), +/* harmony export */ "propEq": () => (/* binding */ propEq), +/* harmony export */ "parse": () => (/* binding */ parse), +/* harmony export */ "not": () => (/* binding */ not), +/* harmony export */ "and": () => (/* binding */ and), +/* harmony export */ "or": () => (/* binding */ or), +/* harmony export */ "all": () => (/* binding */ all), +/* harmony export */ "any": () => (/* binding */ any), +/* harmony export */ "is": () => (/* binding */ is), +/* harmony export */ "eq": () => (/* binding */ eq), +/* harmony export */ "val": () => (/* binding */ val), +/* harmony export */ "invoke": () => (/* binding */ invoke), +/* harmony export */ "pattern": () => (/* binding */ pattern) +/* harmony export */ }); +/** + * Higher order functions + * + * These utility functions are exported, but are subject to change without notice. + * + * @packageDocumentation + */ +var __spreadArrays = (undefined && undefined.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +/** + * Returns a new function for [Partial Application](https://en.wikipedia.org/wiki/Partial_application) of the original function. + * + * Given a function with N parameters, returns a new function that supports partial application. + * The new function accepts anywhere from 1 to N parameters. When that function is called with M parameters, + * where M is less than N, it returns a new function that accepts the remaining parameters. It continues to + * accept more parameters until all N parameters have been supplied. + * + * + * This contrived example uses a partially applied function as an predicate, which returns true + * if an object is found in both arrays. + * @example + * ``` + * // returns true if an object is in both of the two arrays + * function inBoth(array1, array2, object) { + * return array1.indexOf(object) !== -1 && + * array2.indexOf(object) !== 1; + * } + * let obj1, obj2, obj3, obj4, obj5, obj6, obj7 + * let foos = [obj1, obj3] + * let bars = [obj3, obj4, obj5] + * + * // A curried "copy" of inBoth + * let curriedInBoth = curry(inBoth); + * // Partially apply both the array1 and array2 + * let inFoosAndBars = curriedInBoth(foos, bars); + * + * // Supply the final argument; since all arguments are + * // supplied, the original inBoth function is then called. + * let obj1InBoth = inFoosAndBars(obj1); // false + * + * // Use the inFoosAndBars as a predicate. + * // Filter, on each iteration, supplies the final argument + * let allObjs = [ obj1, obj2, obj3, obj4, obj5, obj6, obj7 ]; + * let foundInBoth = allObjs.filter(inFoosAndBars); // [ obj3 ] + * + * ``` + * + * @param fn + * @returns {*|function(): (*|any)} + */ +function curry(fn) { + return function curried() { + if (arguments.length >= fn.length) { + return fn.apply(this, arguments); + } + var args = Array.prototype.slice.call(arguments); + return curried.bind.apply(curried, __spreadArrays([this], args)); + }; +} +/** + * Given a varargs list of functions, returns a function that composes the argument functions, right-to-left + * given: f(x), g(x), h(x) + * let composed = compose(f,g,h) + * then, composed is: f(g(h(x))) + */ +function compose() { + var args = arguments; + var start = args.length - 1; + return function () { + var i = start, result = args[start].apply(this, arguments); + while (i--) + result = args[i].call(this, result); + return result; + }; +} +/** + * Given a varargs list of functions, returns a function that is composes the argument functions, left-to-right + * given: f(x), g(x), h(x) + * let piped = pipe(f,g,h); + * then, piped is: h(g(f(x))) + */ +function pipe() { + var funcs = []; + for (var _i = 0; _i < arguments.length; _i++) { + funcs[_i] = arguments[_i]; + } + return compose.apply(null, [].slice.call(arguments).reverse()); +} +/** + * Given a property name, returns a function that returns that property from an object + * let obj = { foo: 1, name: "blarg" }; + * let getName = prop("name"); + * getName(obj) === "blarg" + */ +var prop = function (name) { return function (obj) { return obj && obj[name]; }; }; +/** + * Given a property name and a value, returns a function that returns a boolean based on whether + * the passed object has a property that matches the value + * let obj = { foo: 1, name: "blarg" }; + * let getName = propEq("name", "blarg"); + * getName(obj) === true + */ +var propEq = curry(function (name, _val, obj) { return obj && obj[name] === _val; }); +/** + * Given a dotted property name, returns a function that returns a nested property from an object, or undefined + * let obj = { id: 1, nestedObj: { foo: 1, name: "blarg" }, }; + * let getName = prop("nestedObj.name"); + * getName(obj) === "blarg" + * let propNotFound = prop("this.property.doesnt.exist"); + * propNotFound(obj) === undefined + */ +var parse = function (name) { return pipe.apply(null, name.split('.').map(prop)); }; +/** + * Given a function that returns a truthy or falsey value, returns a + * function that returns the opposite (falsey or truthy) value given the same inputs + */ +var not = function (fn) { return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return !fn.apply(null, args); +}; }; +/** + * Given two functions that return truthy or falsey values, returns a function that returns truthy + * if both functions return truthy for the given arguments + */ +function and(fn1, fn2) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return fn1.apply(null, args) && fn2.apply(null, args); + }; +} +/** + * Given two functions that return truthy or falsey values, returns a function that returns truthy + * if at least one of the functions returns truthy for the given arguments + */ +function or(fn1, fn2) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return fn1.apply(null, args) || fn2.apply(null, args); + }; +} +/** + * Check if all the elements of an array match a predicate function + * + * @param fn1 a predicate function `fn1` + * @returns a function which takes an array and returns true if `fn1` is true for all elements of the array + */ +var all = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b && !!fn1(x); }, true); }; }; +// tslint:disable-next-line:variable-name +var any = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b || !!fn1(x); }, false); }; }; +/** Given a class, returns a Predicate function that returns true if the object is of that class */ +var is = function (ctor) { return function (obj) { + return (obj != null && obj.constructor === ctor) || obj instanceof ctor; +}; }; +/** Given a value, returns a Predicate function that returns true if another value is === equal to the original value */ +var eq = function (value) { return function (other) { return value === other; }; }; +/** Given a value, returns a function which returns the value */ +var val = function (v) { return function () { return v; }; }; +function invoke(fnName, args) { + return function (obj) { return obj[fnName].apply(obj, args); }; +} +/** + * Sorta like Pattern Matching (a functional programming conditional construct) + * + * See http://c2.com/cgi/wiki?PatternMatching + * + * This is a conditional construct which allows a series of predicates and output functions + * to be checked and then applied. Each predicate receives the input. If the predicate + * returns truthy, then its matching output function (mapping function) is provided with + * the input and, then the result is returned. + * + * Each combination (2-tuple) of predicate + output function should be placed in an array + * of size 2: [ predicate, mapFn ] + * + * These 2-tuples should be put in an outer array. + * + * @example + * ``` + * + * // Here's a 2-tuple where the first element is the isString predicate + * // and the second element is a function that returns a description of the input + * let firstTuple = [ angular.isString, (input) => `Heres your string ${input}` ]; + * + * // Second tuple: predicate "isNumber", mapfn returns a description + * let secondTuple = [ angular.isNumber, (input) => `(${input}) That's a number!` ]; + * + * let third = [ (input) => input === null, (input) => `Oh, null...` ]; + * + * let fourth = [ (input) => input === undefined, (input) => `notdefined` ]; + * + * let descriptionOf = pattern([ firstTuple, secondTuple, third, fourth ]); + * + * console.log(descriptionOf(undefined)); // 'notdefined' + * console.log(descriptionOf(55)); // '(55) That's a number!' + * console.log(descriptionOf("foo")); // 'Here's your string foo' + * ``` + * + * @param struct A 2D array. Each element of the array should be an array, a 2-tuple, + * with a Predicate and a mapping/output function + * @returns {function(any): *} + */ +function pattern(struct) { + return function (x) { + for (var i = 0; i < struct.length; i++) { + if (struct[i][0](x)) + return struct[i][1](x); + } + }; +} +//# sourceMappingURL=hof.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/index.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "_extend": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__._extend), +/* harmony export */ "_inArray": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__._inArray), +/* harmony export */ "_pushTo": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__._pushTo), +/* harmony export */ "_removeFrom": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__._removeFrom), +/* harmony export */ "allTrueR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.allTrueR), +/* harmony export */ "ancestors": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.ancestors), +/* harmony export */ "anyTrueR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.anyTrueR), +/* harmony export */ "applyPairs": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.applyPairs), +/* harmony export */ "arrayTuples": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.arrayTuples), +/* harmony export */ "assertFn": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.assertFn), +/* harmony export */ "assertMap": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.assertMap), +/* harmony export */ "assertPredicate": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.assertPredicate), +/* harmony export */ "copy": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.copy), +/* harmony export */ "createProxyFunctions": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions), +/* harmony export */ "defaults": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.defaults), +/* harmony export */ "deregAll": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.deregAll), +/* harmony export */ "equals": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.equals), +/* harmony export */ "extend": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.extend), +/* harmony export */ "filter": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.filter), +/* harmony export */ "find": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.find), +/* harmony export */ "flatten": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.flatten), +/* harmony export */ "flattenR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.flattenR), +/* harmony export */ "forEach": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.forEach), +/* harmony export */ "fromJson": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.fromJson), +/* harmony export */ "identity": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.identity), +/* harmony export */ "inArray": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.inArray), +/* harmony export */ "inherit": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.inherit), +/* harmony export */ "map": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.map), +/* harmony export */ "mapObj": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.mapObj), +/* harmony export */ "mergeR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.mergeR), +/* harmony export */ "noop": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.noop), +/* harmony export */ "omit": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.omit), +/* harmony export */ "pairs": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.pairs), +/* harmony export */ "pick": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.pick), +/* harmony export */ "pluck": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.pluck), +/* harmony export */ "pushR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.pushR), +/* harmony export */ "pushTo": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.pushTo), +/* harmony export */ "removeFrom": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.removeFrom), +/* harmony export */ "root": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.root), +/* harmony export */ "silenceUncaughtInPromise": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.silenceUncaughtInPromise), +/* harmony export */ "silentRejection": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.silentRejection), +/* harmony export */ "tail": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.tail), +/* harmony export */ "toJson": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.toJson), +/* harmony export */ "uniqR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.uniqR), +/* harmony export */ "unnest": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.unnest), +/* harmony export */ "unnestR": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.unnestR), +/* harmony export */ "values": () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_0__.values), +/* harmony export */ "makeStub": () => (/* reexport safe */ _coreservices__WEBPACK_IMPORTED_MODULE_1__.makeStub), +/* harmony export */ "services": () => (/* reexport safe */ _coreservices__WEBPACK_IMPORTED_MODULE_1__.services), +/* harmony export */ "Glob": () => (/* reexport safe */ _glob__WEBPACK_IMPORTED_MODULE_2__.Glob), +/* harmony export */ "all": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.all), +/* harmony export */ "and": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.and), +/* harmony export */ "any": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.any), +/* harmony export */ "compose": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.compose), +/* harmony export */ "curry": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.curry), +/* harmony export */ "eq": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.eq), +/* harmony export */ "invoke": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.invoke), +/* harmony export */ "is": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.is), +/* harmony export */ "not": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.not), +/* harmony export */ "or": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.or), +/* harmony export */ "parse": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.parse), +/* harmony export */ "pattern": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.pattern), +/* harmony export */ "pipe": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.pipe), +/* harmony export */ "prop": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.prop), +/* harmony export */ "propEq": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.propEq), +/* harmony export */ "val": () => (/* reexport safe */ _hof__WEBPACK_IMPORTED_MODULE_3__.val), +/* harmony export */ "isArray": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isArray), +/* harmony export */ "isDate": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isDate), +/* harmony export */ "isDefined": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isDefined), +/* harmony export */ "isFunction": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isFunction), +/* harmony export */ "isInjectable": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isInjectable), +/* harmony export */ "isNull": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isNull), +/* harmony export */ "isNullOrUndefined": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isNullOrUndefined), +/* harmony export */ "isNumber": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isNumber), +/* harmony export */ "isObject": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isObject), +/* harmony export */ "isPromise": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isPromise), +/* harmony export */ "isRegExp": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isRegExp), +/* harmony export */ "isString": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isString), +/* harmony export */ "isUndefined": () => (/* reexport safe */ _predicates__WEBPACK_IMPORTED_MODULE_4__.isUndefined), +/* harmony export */ "Queue": () => (/* reexport safe */ _queue__WEBPACK_IMPORTED_MODULE_5__.Queue), +/* harmony export */ "beforeAfterSubstr": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.beforeAfterSubstr), +/* harmony export */ "fnToString": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.fnToString), +/* harmony export */ "functionToString": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.functionToString), +/* harmony export */ "hostRegex": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.hostRegex), +/* harmony export */ "joinNeighborsR": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.joinNeighborsR), +/* harmony export */ "kebobString": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.kebobString), +/* harmony export */ "maxLength": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.maxLength), +/* harmony export */ "padString": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.padString), +/* harmony export */ "splitEqual": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.splitEqual), +/* harmony export */ "splitHash": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.splitHash), +/* harmony export */ "splitOnDelim": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.splitOnDelim), +/* harmony export */ "splitQuery": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.splitQuery), +/* harmony export */ "stringify": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.stringify), +/* harmony export */ "stripLastPathElement": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.stripLastPathElement), +/* harmony export */ "trimHashVal": () => (/* reexport safe */ _strings__WEBPACK_IMPORTED_MODULE_6__.trimHashVal), +/* harmony export */ "Category": () => (/* reexport safe */ _trace__WEBPACK_IMPORTED_MODULE_7__.Category), +/* harmony export */ "Trace": () => (/* reexport safe */ _trace__WEBPACK_IMPORTED_MODULE_7__.Trace), +/* harmony export */ "trace": () => (/* reexport safe */ _trace__WEBPACK_IMPORTED_MODULE_7__.trace) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _coreservices__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _glob__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./glob */ "./node_modules/@uirouter/core/lib-esm/common/glob.js"); +/* harmony import */ var _hof__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _predicates__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _queue__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./queue */ "./node_modules/@uirouter/core/lib-esm/common/queue.js"); +/* harmony import */ var _strings__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _trace__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); + + + + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/predicates.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/predicates.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "isUndefined": () => (/* binding */ isUndefined), +/* harmony export */ "isDefined": () => (/* binding */ isDefined), +/* harmony export */ "isNull": () => (/* binding */ isNull), +/* harmony export */ "isNullOrUndefined": () => (/* binding */ isNullOrUndefined), +/* harmony export */ "isFunction": () => (/* binding */ isFunction), +/* harmony export */ "isNumber": () => (/* binding */ isNumber), +/* harmony export */ "isString": () => (/* binding */ isString), +/* harmony export */ "isObject": () => (/* binding */ isObject), +/* harmony export */ "isArray": () => (/* binding */ isArray), +/* harmony export */ "isDate": () => (/* binding */ isDate), +/* harmony export */ "isRegExp": () => (/* binding */ isRegExp), +/* harmony export */ "isInjectable": () => (/* binding */ isInjectable), +/* harmony export */ "isPromise": () => (/* binding */ isPromise) +/* harmony export */ }); +/* harmony import */ var _hof__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/** + * Predicates + * + * These predicates return true/false based on the input. + * Although these functions are exported, they are subject to change without notice. + * + * @packageDocumentation + */ + +var toStr = Object.prototype.toString; +var tis = function (t) { return function (x) { return typeof x === t; }; }; +var isUndefined = tis('undefined'); +var isDefined = (0,_hof__WEBPACK_IMPORTED_MODULE_0__.not)(isUndefined); +var isNull = function (o) { return o === null; }; +var isNullOrUndefined = (0,_hof__WEBPACK_IMPORTED_MODULE_0__.or)(isNull, isUndefined); +var isFunction = tis('function'); +var isNumber = tis('number'); +var isString = tis('string'); +var isObject = function (x) { return x !== null && typeof x === 'object'; }; +var isArray = Array.isArray; +var isDate = (function (x) { return toStr.call(x) === '[object Date]'; }); +var isRegExp = (function (x) { return toStr.call(x) === '[object RegExp]'; }); +/** + * Predicate which checks if a value is injectable + * + * A value is "injectable" if it is a function, or if it is an ng1 array-notation-style array + * where all the elements in the array are Strings, except the last one, which is a Function + */ +function isInjectable(val) { + if (isArray(val) && val.length) { + var head = val.slice(0, -1), tail = val.slice(-1); + return !(head.filter((0,_hof__WEBPACK_IMPORTED_MODULE_0__.not)(isString)).length || tail.filter((0,_hof__WEBPACK_IMPORTED_MODULE_0__.not)(isFunction)).length); + } + return isFunction(val); +} +/** + * Predicate which checks if a value looks like a Promise + * + * It is probably a Promise if it's an object, and it has a `then` property which is a Function + */ +var isPromise = (0,_hof__WEBPACK_IMPORTED_MODULE_0__.and)(isObject, (0,_hof__WEBPACK_IMPORTED_MODULE_0__.pipe)((0,_hof__WEBPACK_IMPORTED_MODULE_0__.prop)('then'), isFunction)); +//# sourceMappingURL=predicates.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/queue.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/queue.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Queue": () => (/* binding */ Queue) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); + +var Queue = /** @class */ (function () { + function Queue(_items, _limit) { + if (_items === void 0) { _items = []; } + if (_limit === void 0) { _limit = null; } + this._items = _items; + this._limit = _limit; + this._evictListeners = []; + this.onEvict = (0,_common__WEBPACK_IMPORTED_MODULE_0__.pushTo)(this._evictListeners); + } + Queue.prototype.enqueue = function (item) { + var items = this._items; + items.push(item); + if (this._limit && items.length > this._limit) + this.evict(); + return item; + }; + Queue.prototype.evict = function () { + var item = this._items.shift(); + this._evictListeners.forEach(function (fn) { return fn(item); }); + return item; + }; + Queue.prototype.dequeue = function () { + if (this.size()) + return this._items.splice(0, 1)[0]; + }; + Queue.prototype.clear = function () { + var current = this._items; + this._items = []; + return current; + }; + Queue.prototype.size = function () { + return this._items.length; + }; + Queue.prototype.remove = function (item) { + var idx = this._items.indexOf(item); + return idx > -1 && this._items.splice(idx, 1)[0]; + }; + Queue.prototype.peekTail = function () { + return this._items[this._items.length - 1]; + }; + Queue.prototype.peekHead = function () { + if (this.size()) + return this._items[0]; + }; + return Queue; +}()); + +//# sourceMappingURL=queue.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/safeConsole.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/safeConsole.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "safeConsole": () => (/* binding */ safeConsole) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/** + * workaround for missing console object in IE9 when dev tools haven't been opened o_O + * @packageDocumentation + */ +/* tslint:disable:no-console */ + +var noopConsoleStub = { log: _common__WEBPACK_IMPORTED_MODULE_0__.noop, error: _common__WEBPACK_IMPORTED_MODULE_0__.noop, table: _common__WEBPACK_IMPORTED_MODULE_0__.noop }; +function ie9Console(console) { + var bound = function (fn) { return Function.prototype.bind.call(fn, console); }; + return { + log: bound(console.log), + error: bound(console.log), + table: bound(console.log), + }; +} +function fallbackConsole(console) { + var log = console.log.bind(console); + var error = console.error ? console.error.bind(console) : log; + var table = console.table ? console.table.bind(console) : log; + return { log: log, error: error, table: table }; +} +function getSafeConsole() { + // @ts-ignore + var isIE9 = typeof document !== 'undefined' && document.documentMode && document.documentMode === 9; + if (isIE9) { + return window && window.console ? ie9Console(window.console) : noopConsoleStub; + } + else if (!console.table || !console.error) { + return fallbackConsole(console); + } + else { + return console; + } +} +var safeConsole = getSafeConsole(); +//# sourceMappingURL=safeConsole.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/strings.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/strings.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "maxLength": () => (/* binding */ maxLength), +/* harmony export */ "padString": () => (/* binding */ padString), +/* harmony export */ "kebobString": () => (/* binding */ kebobString), +/* harmony export */ "functionToString": () => (/* binding */ functionToString), +/* harmony export */ "fnToString": () => (/* binding */ fnToString), +/* harmony export */ "stringify": () => (/* binding */ stringify), +/* harmony export */ "beforeAfterSubstr": () => (/* binding */ beforeAfterSubstr), +/* harmony export */ "hostRegex": () => (/* binding */ hostRegex), +/* harmony export */ "stripLastPathElement": () => (/* binding */ stripLastPathElement), +/* harmony export */ "splitHash": () => (/* binding */ splitHash), +/* harmony export */ "splitQuery": () => (/* binding */ splitQuery), +/* harmony export */ "splitEqual": () => (/* binding */ splitEqual), +/* harmony export */ "trimHashVal": () => (/* binding */ trimHashVal), +/* harmony export */ "splitOnDelim": () => (/* binding */ splitOnDelim), +/* harmony export */ "joinNeighborsR": () => (/* binding */ joinNeighborsR) +/* harmony export */ }); +/* harmony import */ var _predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../transition/rejectFactory */ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _hof__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/** + * Functions that manipulate strings + * + * Although these functions are exported, they are subject to change without notice. + * + * @packageDocumentation + */ + + + + +/** + * Returns a string shortened to a maximum length + * + * If the string is already less than the `max` length, return the string. + * Else return the string, shortened to `max - 3` and append three dots ("..."). + * + * @param max the maximum length of the string to return + * @param str the input string + */ +function maxLength(max, str) { + if (str.length <= max) + return str; + return str.substr(0, max - 3) + '...'; +} +/** + * Returns a string, with spaces added to the end, up to a desired str length + * + * If the string is already longer than the desired length, return the string. + * Else returns the string, with extra spaces on the end, such that it reaches `length` characters. + * + * @param length the desired length of the string to return + * @param str the input string + */ +function padString(length, str) { + while (str.length < length) + str += ' '; + return str; +} +function kebobString(camelCase) { + return camelCase + .replace(/^([A-Z])/, function ($1) { return $1.toLowerCase(); }) // replace first char + .replace(/([A-Z])/g, function ($1) { return '-' + $1.toLowerCase(); }); // replace rest +} +function functionToString(fn) { + var fnStr = fnToString(fn); + var namedFunctionMatch = fnStr.match(/^(function [^ ]+\([^)]*\))/); + var toStr = namedFunctionMatch ? namedFunctionMatch[1] : fnStr; + var fnName = fn['name'] || ''; + if (fnName && toStr.match(/function \(/)) { + return 'function ' + fnName + toStr.substr(9); + } + return toStr; +} +function fnToString(fn) { + var _fn = (0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(fn) ? fn.slice(-1)[0] : fn; + return (_fn && _fn.toString()) || 'undefined'; +} +var isRejection = _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_1__.Rejection.isRejectionPromise; +var hasToString = function (obj) { + return (0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isObject)(obj) && !(0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isArray)(obj) && obj.constructor !== Object && (0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isFunction)(obj.toString); +}; +var stringifyPattern = (0,_hof__WEBPACK_IMPORTED_MODULE_3__.pattern)([ + [_predicates__WEBPACK_IMPORTED_MODULE_0__.isUndefined, (0,_hof__WEBPACK_IMPORTED_MODULE_3__.val)('undefined')], + [_predicates__WEBPACK_IMPORTED_MODULE_0__.isNull, (0,_hof__WEBPACK_IMPORTED_MODULE_3__.val)('null')], + [_predicates__WEBPACK_IMPORTED_MODULE_0__.isPromise, (0,_hof__WEBPACK_IMPORTED_MODULE_3__.val)('[Promise]')], + [isRejection, function (x) { return x._transitionRejection.toString(); }], + [hasToString, function (x) { return x.toString(); }], + [_predicates__WEBPACK_IMPORTED_MODULE_0__.isInjectable, functionToString], + [(0,_hof__WEBPACK_IMPORTED_MODULE_3__.val)(true), _common__WEBPACK_IMPORTED_MODULE_2__.identity], +]); +function stringify(o) { + var seen = []; + function format(value) { + if ((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isObject)(value)) { + if (seen.indexOf(value) !== -1) + return '[circular ref]'; + seen.push(value); + } + return stringifyPattern(value); + } + if ((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isUndefined)(o)) { + // Workaround for IE & Edge Spec incompatibility where replacer function would not be called when JSON.stringify + // is given `undefined` as value. To work around that, we simply detect `undefined` and bail out early by + // manually stringifying it. + return format(o); + } + return JSON.stringify(o, function (key, value) { return format(value); }).replace(/\\"/g, '"'); +} +/** Returns a function that splits a string on a character or substring */ +var beforeAfterSubstr = function (char) { return function (str) { + if (!str) + return ['', '']; + var idx = str.indexOf(char); + if (idx === -1) + return [str, '']; + return [str.substr(0, idx), str.substr(idx + 1)]; +}; }; +var hostRegex = new RegExp('^(?:[a-z]+:)?//[^/]+/'); +var stripLastPathElement = function (str) { return str.replace(/\/[^/]*$/, ''); }; +var splitHash = beforeAfterSubstr('#'); +var splitQuery = beforeAfterSubstr('?'); +var splitEqual = beforeAfterSubstr('='); +var trimHashVal = function (str) { return (str ? str.replace(/^#/, '') : ''); }; +/** + * Splits on a delimiter, but returns the delimiters in the array + * + * #### Example: + * ```js + * var splitOnSlashes = splitOnDelim('/'); + * splitOnSlashes("/foo"); // ["/", "foo"] + * splitOnSlashes("/foo/"); // ["/", "foo", "/"] + * ``` + */ +function splitOnDelim(delim) { + var re = new RegExp('(' + delim + ')', 'g'); + return function (str) { return str.split(re).filter(_common__WEBPACK_IMPORTED_MODULE_2__.identity); }; +} +/** + * Reduce fn that joins neighboring strings + * + * Given an array of strings, returns a new array + * where all neighboring strings have been joined. + * + * #### Example: + * ```js + * let arr = ["foo", "bar", 1, "baz", "", "qux" ]; + * arr.reduce(joinNeighborsR, []) // ["foobar", 1, "bazqux" ] + * ``` + */ +function joinNeighborsR(acc, x) { + if ((0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)((0,_common__WEBPACK_IMPORTED_MODULE_2__.tail)(acc)) && (0,_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)(x)) + return acc.slice(0, -1).concat((0,_common__WEBPACK_IMPORTED_MODULE_2__.tail)(acc) + x); + return (0,_common__WEBPACK_IMPORTED_MODULE_2__.pushR)(acc, x); +} +//# sourceMappingURL=strings.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/common/trace.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/common/trace.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Category": () => (/* binding */ Category), +/* harmony export */ "Trace": () => (/* binding */ Trace), +/* harmony export */ "trace": () => (/* binding */ trace) +/* harmony export */ }); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _safeConsole__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./safeConsole */ "./node_modules/@uirouter/core/lib-esm/common/safeConsole.js"); +/** + * # Transition tracing (debug) + * + * Enable transition tracing to print transition information to the console, + * in order to help debug your application. + * Tracing logs detailed information about each Transition to your console. + * + * To enable tracing, import the [[Trace]] singleton and enable one or more categories. + * + * ### ES6 + * ```js + * import {trace} from "@uirouter/core"; + * trace.enable(1, 5); // TRANSITION and VIEWCONFIG + * ``` + * + * ### CJS + * ```js + * let trace = require("@uirouter/core").trace; + * trace.enable("TRANSITION", "VIEWCONFIG"); + * ``` + * + * ### Globals + * ```js + * let trace = window["@uirouter/core"].trace; + * trace.enable(); // Trace everything (very verbose) + * ``` + * + * ### Angular 1: + * ```js + * app.run($trace => $trace.enable()); + * ``` + * + * @packageDocumentation + */ + + + + +function uiViewString(uiview) { + if (!uiview) + return 'ui-view (defunct)'; + var state = uiview.creationContext ? uiview.creationContext.name || '(root)' : '(none)'; + return "[ui-view#" + uiview.id + " " + uiview.$type + ":" + uiview.fqn + " (" + uiview.name + "@" + state + ")]"; +} +var viewConfigString = function (viewConfig) { + var view = viewConfig.viewDecl; + var state = view.$context.name || '(root)'; + return "[View#" + viewConfig.$id + " from '" + state + "' state]: target ui-view: '" + view.$uiViewName + "@" + view.$uiViewContextAnchor + "'"; +}; +function normalizedCat(input) { + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isNumber)(input) ? Category[input] : Category[Category[input]]; +} +/** + * Trace categories Enum + * + * Enable or disable a category using [[Trace.enable]] or [[Trace.disable]] + * + * `trace.enable(Category.TRANSITION)` + * + * These can also be provided using a matching string, or position ordinal + * + * `trace.enable("TRANSITION")` + * + * `trace.enable(1)` + */ +var Category; +(function (Category) { + Category[Category["RESOLVE"] = 0] = "RESOLVE"; + Category[Category["TRANSITION"] = 1] = "TRANSITION"; + Category[Category["HOOK"] = 2] = "HOOK"; + Category[Category["UIVIEW"] = 3] = "UIVIEW"; + Category[Category["VIEWCONFIG"] = 4] = "VIEWCONFIG"; +})(Category || (Category = {})); + +var _tid = (0,_common_hof__WEBPACK_IMPORTED_MODULE_0__.parse)('$id'); +var _rid = (0,_common_hof__WEBPACK_IMPORTED_MODULE_0__.parse)('router.$id'); +var transLbl = function (trans) { return "Transition #" + _tid(trans) + "-" + _rid(trans); }; +/** + * Prints UI-Router Transition trace information to the console. + */ +var Trace = /** @class */ (function () { + /** @internal */ + function Trace() { + /** @internal */ + this._enabled = {}; + this.approximateDigests = 0; + } + /** @internal */ + Trace.prototype._set = function (enabled, categories) { + var _this = this; + if (!categories.length) { + categories = Object.keys(Category) + .map(function (k) { return parseInt(k, 10); }) + .filter(function (k) { return !isNaN(k); }) + .map(function (key) { return Category[key]; }); + } + categories.map(normalizedCat).forEach(function (category) { return (_this._enabled[category] = enabled); }); + }; + Trace.prototype.enable = function () { + var categories = []; + for (var _i = 0; _i < arguments.length; _i++) { + categories[_i] = arguments[_i]; + } + this._set(true, categories); + }; + Trace.prototype.disable = function () { + var categories = []; + for (var _i = 0; _i < arguments.length; _i++) { + categories[_i] = arguments[_i]; + } + this._set(false, categories); + }; + /** + * Retrieves the enabled stateus of a [[Category]] + * + * ```js + * trace.enabled("VIEWCONFIG"); // true or false + * ``` + * + * @returns boolean true if the category is enabled + */ + Trace.prototype.enabled = function (category) { + return !!this._enabled[normalizedCat(category)]; + }; + /** @internal called by ui-router code */ + Trace.prototype.traceTransitionStart = function (trans) { + if (!this.enabled(Category.TRANSITION)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": Started -> " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(trans)); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceTransitionIgnored = function (trans) { + if (!this.enabled(Category.TRANSITION)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": Ignored <> " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(trans)); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceHookInvocation = function (step, trans, options) { + if (!this.enabled(Category.HOOK)) + return; + var event = (0,_common_hof__WEBPACK_IMPORTED_MODULE_0__.parse)('traceData.hookType')(options) || 'internal', context = (0,_common_hof__WEBPACK_IMPORTED_MODULE_0__.parse)('traceData.context.state.name')(options) || (0,_common_hof__WEBPACK_IMPORTED_MODULE_0__.parse)('traceData.context')(options) || 'unknown', name = (0,_strings__WEBPACK_IMPORTED_MODULE_2__.functionToString)(step.registeredHook.callback); + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": Hook -> " + event + " context: " + context + ", " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.maxLength)(200, name)); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceHookResult = function (hookResult, trans, transitionOptions) { + if (!this.enabled(Category.HOOK)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": <- Hook returned: " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.maxLength)(200, (0,_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(hookResult))); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceResolvePath = function (path, when, trans) { + if (!this.enabled(Category.RESOLVE)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": Resolving " + path + " (" + when + ")"); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceResolvableResolved = function (resolvable, trans) { + if (!this.enabled(Category.RESOLVE)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": <- Resolved " + resolvable + " to: " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.maxLength)(200, (0,_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(resolvable.data))); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceError = function (reason, trans) { + if (!this.enabled(Category.TRANSITION)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": <- Rejected " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(trans) + ", reason: " + reason); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceSuccess = function (finalState, trans) { + if (!this.enabled(Category.TRANSITION)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log(transLbl(trans) + ": <- Success " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(trans) + ", final state: " + finalState.name); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceUIViewEvent = function (event, viewData, extra) { + if (extra === void 0) { extra = ''; } + if (!this.enabled(Category.UIVIEW)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log("ui-view: " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.padString)(30, event) + " " + uiViewString(viewData) + extra); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceUIViewConfigUpdated = function (viewData, context) { + if (!this.enabled(Category.UIVIEW)) + return; + this.traceUIViewEvent('Updating', viewData, " with ViewConfig from context='" + context + "'"); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceUIViewFill = function (viewData, html) { + if (!this.enabled(Category.UIVIEW)) + return; + this.traceUIViewEvent('Fill', viewData, " with: " + (0,_strings__WEBPACK_IMPORTED_MODULE_2__.maxLength)(200, html)); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceViewSync = function (pairs) { + if (!this.enabled(Category.VIEWCONFIG)) + return; + var uivheader = 'uiview component fqn'; + var cfgheader = 'view config state (view name)'; + var mapping = pairs + .map(function (_a) { + var _b; + var uiView = _a.uiView, viewConfig = _a.viewConfig; + var uiv = uiView && uiView.fqn; + var cfg = viewConfig && viewConfig.viewDecl.$context.name + ": (" + viewConfig.viewDecl.$name + ")"; + return _b = {}, _b[uivheader] = uiv, _b[cfgheader] = cfg, _b; + }) + .sort(function (a, b) { return (a[uivheader] || '').localeCompare(b[uivheader] || ''); }); + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.table(mapping); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceViewServiceEvent = function (event, viewConfig) { + if (!this.enabled(Category.VIEWCONFIG)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log("VIEWCONFIG: " + event + " " + viewConfigString(viewConfig)); + }; + /** @internal called by ui-router code */ + Trace.prototype.traceViewServiceUIViewEvent = function (event, viewData) { + if (!this.enabled(Category.VIEWCONFIG)) + return; + _safeConsole__WEBPACK_IMPORTED_MODULE_3__.safeConsole.log("VIEWCONFIG: " + event + " " + uiViewString(viewData)); + }; + return Trace; +}()); + +/** + * The [[Trace]] singleton + * + * #### Example: + * ```js + * import {trace} from "@uirouter/core"; + * trace.enable(1, 5); + * ``` + */ +var trace = new Trace(); + +//# sourceMappingURL=trace.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/globals.js": +/*!********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/globals.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UIRouterGlobals": () => (/* binding */ UIRouterGlobals) +/* harmony export */ }); +/* harmony import */ var _params_stateParams__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./params/stateParams */ "./node_modules/@uirouter/core/lib-esm/params/stateParams.js"); +/* harmony import */ var _common_queue__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./common/queue */ "./node_modules/@uirouter/core/lib-esm/common/queue.js"); + + +/** + * Global router state + * + * This is where we hold the global mutable state such as current state, current + * params, current transition, etc. + */ +var UIRouterGlobals = /** @class */ (function () { + function UIRouterGlobals() { + /** + * Current parameter values + * + * The parameter values from the latest successful transition + */ + this.params = new _params_stateParams__WEBPACK_IMPORTED_MODULE_0__.StateParams(); + /** @internal */ + this.lastStartedTransitionId = -1; + /** @internal */ + this.transitionHistory = new _common_queue__WEBPACK_IMPORTED_MODULE_1__.Queue([], 1); + /** @internal */ + this.successfulTransitions = new _common_queue__WEBPACK_IMPORTED_MODULE_1__.Queue([], 1); + } + UIRouterGlobals.prototype.dispose = function () { + this.transitionHistory.clear(); + this.successfulTransitions.clear(); + this.transition = null; + }; + return UIRouterGlobals; +}()); + +//# sourceMappingURL=globals.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/coreResolvables.js": +/*!**********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/coreResolvables.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerAddCoreResolvables": () => (/* binding */ registerAddCoreResolvables), +/* harmony export */ "treeChangesCleanup": () => (/* binding */ treeChangesCleanup) +/* harmony export */ }); +/* harmony import */ var _transition_transition__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../transition/transition */ "./node_modules/@uirouter/core/lib-esm/transition/transition.js"); +/* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../router */ "./node_modules/@uirouter/core/lib-esm/router.js"); +/* harmony import */ var _resolve__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../resolve */ "./node_modules/@uirouter/core/lib-esm/resolve/index.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + + + +function addCoreResolvables(trans) { + trans.addResolvable(_resolve__WEBPACK_IMPORTED_MODULE_2__.Resolvable.fromData(_router__WEBPACK_IMPORTED_MODULE_1__.UIRouter, trans.router), ''); + trans.addResolvable(_resolve__WEBPACK_IMPORTED_MODULE_2__.Resolvable.fromData(_transition_transition__WEBPACK_IMPORTED_MODULE_0__.Transition, trans), ''); + trans.addResolvable(_resolve__WEBPACK_IMPORTED_MODULE_2__.Resolvable.fromData('$transition$', trans), ''); + trans.addResolvable(_resolve__WEBPACK_IMPORTED_MODULE_2__.Resolvable.fromData('$stateParams', trans.params()), ''); + trans.entering().forEach(function (state) { + trans.addResolvable(_resolve__WEBPACK_IMPORTED_MODULE_2__.Resolvable.fromData('$state$', state), state); + }); +} +var registerAddCoreResolvables = function (transitionService) { + return transitionService.onCreate({}, addCoreResolvables); +}; +var TRANSITION_TOKENS = ['$transition$', _transition_transition__WEBPACK_IMPORTED_MODULE_0__.Transition]; +var isTransition = (0,_common__WEBPACK_IMPORTED_MODULE_3__.inArray)(TRANSITION_TOKENS); +// References to Transition in the treeChanges pathnodes makes all +// previous Transitions reachable in memory, causing a memory leak +// This function removes resolves for '$transition$' and `Transition` from the treeChanges. +// Do not use this on current transitions, only on old ones. +var treeChangesCleanup = function (trans) { + var nodes = (0,_common__WEBPACK_IMPORTED_MODULE_3__.values)(trans.treeChanges()).reduce(_common__WEBPACK_IMPORTED_MODULE_3__.unnestR, []).reduce(_common__WEBPACK_IMPORTED_MODULE_3__.uniqR, []); + // If the resolvable is a Transition, return a new resolvable with null data + var replaceTransitionWithNull = function (r) { + return isTransition(r.token) ? _resolve__WEBPACK_IMPORTED_MODULE_2__.Resolvable.fromData(r.token, null) : r; + }; + nodes.forEach(function (node) { + node.resolvables = node.resolvables.map(replaceTransitionWithNull); + }); +}; +//# sourceMappingURL=coreResolvables.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/ignoredTransition.js": +/*!************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/ignoredTransition.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerIgnoredTransitionHook": () => (/* binding */ registerIgnoredTransitionHook) +/* harmony export */ }); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); +/* harmony import */ var _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../transition/rejectFactory */ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js"); + + +/** + * A [[TransitionHookFn]] that skips a transition if it should be ignored + * + * This hook is invoked at the end of the onBefore phase. + * + * If the transition should be ignored (because no parameter or states changed) + * then the transition is ignored and not processed. + */ +function ignoredHook(trans) { + var ignoredReason = trans._ignoredReason(); + if (!ignoredReason) + return; + _common_trace__WEBPACK_IMPORTED_MODULE_0__.trace.traceTransitionIgnored(trans); + var pending = trans.router.globals.transition; + // The user clicked a link going back to the *current state* ('A') + // However, there is also a pending transition in flight (to 'B') + // Abort the transition to 'B' because the user now wants to be back at 'A'. + if (ignoredReason === 'SameAsCurrent' && pending) { + pending.abort(); + } + return _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_1__.Rejection.ignored().toPromise(); +} +var registerIgnoredTransitionHook = function (transitionService) { + return transitionService.onBefore({}, ignoredHook, { priority: -9999 }); +}; +//# sourceMappingURL=ignoredTransition.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/invalidTransition.js": +/*!************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/invalidTransition.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerInvalidTransitionHook": () => (/* binding */ registerInvalidTransitionHook) +/* harmony export */ }); +/** + * A [[TransitionHookFn]] that rejects the Transition if it is invalid + * + * This hook is invoked at the end of the onBefore phase. + * If the transition is invalid (for example, param values do not validate) + * then the transition is rejected. + */ +function invalidTransitionHook(trans) { + if (!trans.valid()) { + throw new Error(trans.error().toString()); + } +} +var registerInvalidTransitionHook = function (transitionService) { + return transitionService.onBefore({}, invalidTransitionHook, { priority: -10000 }); +}; +//# sourceMappingURL=invalidTransition.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/lazyLoad.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/lazyLoad.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerLazyLoadHook": () => (/* binding */ registerLazyLoadHook), +/* harmony export */ "lazyLoadState": () => (/* binding */ lazyLoadState) +/* harmony export */ }); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); + +/** + * A [[TransitionHookFn]] that performs lazy loading + * + * When entering a state "abc" which has a `lazyLoad` function defined: + * - Invoke the `lazyLoad` function (unless it is already in process) + * - Flag the hook function as "in process" + * - The function should return a promise (that resolves when lazy loading is complete) + * - Wait for the promise to settle + * - If the promise resolves to a [[LazyLoadResult]], then register those states + * - Flag the hook function as "not in process" + * - If the hook was successful + * - Remove the `lazyLoad` function from the state declaration + * - If all the hooks were successful + * - Retry the transition (by returning a TargetState) + * + * ``` + * .state('abc', { + * component: 'fooComponent', + * lazyLoad: () => import('./fooComponent') + * }); + * ``` + * + * See [[StateDeclaration.lazyLoad]] + */ +var lazyLoadHook = function (transition) { + var router = transition.router; + function retryTransition() { + if (transition.originalTransition().options().source !== 'url') { + // The original transition was not triggered via url sync + // The lazy state should be loaded now, so re-try the original transition + var orig = transition.targetState(); + return router.stateService.target(orig.identifier(), orig.params(), orig.options()); + } + // The original transition was triggered via url sync + // Run the URL rules and find the best match + var $url = router.urlService; + var result = $url.match($url.parts()); + var rule = result && result.rule; + // If the best match is a state, redirect the transition (instead + // of calling sync() which supersedes the current transition) + if (rule && rule.type === 'STATE') { + var state = rule.state; + var params = result.match; + return router.stateService.target(state, params, transition.options()); + } + // No matching state found, so let .sync() choose the best non-state match/otherwise + router.urlService.sync(); + } + var promises = transition + .entering() + .filter(function (state) { return !!state.$$state().lazyLoad; }) + .map(function (state) { return lazyLoadState(transition, state); }); + return _common_coreservices__WEBPACK_IMPORTED_MODULE_0__.services.$q.all(promises).then(retryTransition); +}; +var registerLazyLoadHook = function (transitionService) { + return transitionService.onBefore({ entering: function (state) { return !!state.lazyLoad; } }, lazyLoadHook); +}; +/** + * Invokes a state's lazy load function + * + * @param transition a Transition context + * @param state the state to lazy load + * @returns A promise for the lazy load result + */ +function lazyLoadState(transition, state) { + var lazyLoadFn = state.$$state().lazyLoad; + // Store/get the lazy load promise on/from the hookfn so it doesn't get re-invoked + var promise = lazyLoadFn['_promise']; + if (!promise) { + var success = function (result) { + delete state.lazyLoad; + delete state.$$state().lazyLoad; + delete lazyLoadFn['_promise']; + return result; + }; + var error = function (err) { + delete lazyLoadFn['_promise']; + return _common_coreservices__WEBPACK_IMPORTED_MODULE_0__.services.$q.reject(err); + }; + promise = lazyLoadFn['_promise'] = _common_coreservices__WEBPACK_IMPORTED_MODULE_0__.services.$q.when(lazyLoadFn(transition, state)) + .then(updateStateRegistry) + .then(success, error); + } + /** Register any lazy loaded state definitions */ + function updateStateRegistry(result) { + if (result && Array.isArray(result.states)) { + result.states.forEach(function (_state) { return transition.router.stateRegistry.register(_state); }); + } + return result; + } + return promise; +} +//# sourceMappingURL=lazyLoad.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/onEnterExitRetain.js": +/*!************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/onEnterExitRetain.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerOnExitHook": () => (/* binding */ registerOnExitHook), +/* harmony export */ "registerOnRetainHook": () => (/* binding */ registerOnRetainHook), +/* harmony export */ "registerOnEnterHook": () => (/* binding */ registerOnEnterHook) +/* harmony export */ }); +/** + * A factory which creates an onEnter, onExit or onRetain transition hook function + * + * The returned function invokes the (for instance) state.onEnter hook when the + * state is being entered. + */ +function makeEnterExitRetainHook(hookName) { + return function (transition, state) { + var _state = state.$$state(); + var hookFn = _state[hookName]; + return hookFn(transition, state); + }; +} +/** + * The [[TransitionStateHookFn]] for onExit + * + * When the state is being exited, the state's .onExit function is invoked. + * + * Registered using `transitionService.onExit({ exiting: (state) => !!state.onExit }, onExitHook);` + * + * See: [[IHookRegistry.onExit]] + */ +var onExitHook = makeEnterExitRetainHook('onExit'); +var registerOnExitHook = function (transitionService) { + return transitionService.onExit({ exiting: function (state) { return !!state.onExit; } }, onExitHook); +}; +/** + * The [[TransitionStateHookFn]] for onRetain + * + * When the state was already entered, and is not being exited or re-entered, the state's .onRetain function is invoked. + * + * Registered using `transitionService.onRetain({ retained: (state) => !!state.onRetain }, onRetainHook);` + * + * See: [[IHookRegistry.onRetain]] + */ +var onRetainHook = makeEnterExitRetainHook('onRetain'); +var registerOnRetainHook = function (transitionService) { + return transitionService.onRetain({ retained: function (state) { return !!state.onRetain; } }, onRetainHook); +}; +/** + * The [[TransitionStateHookFn]] for onEnter + * + * When the state is being entered, the state's .onEnter function is invoked. + * + * Registered using `transitionService.onEnter({ entering: (state) => !!state.onEnter }, onEnterHook);` + * + * See: [[IHookRegistry.onEnter]] + */ +var onEnterHook = makeEnterExitRetainHook('onEnter'); +var registerOnEnterHook = function (transitionService) { + return transitionService.onEnter({ entering: function (state) { return !!state.onEnter; } }, onEnterHook); +}; +//# sourceMappingURL=onEnterExitRetain.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/redirectTo.js": +/*!*****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/redirectTo.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerRedirectToHook": () => (/* binding */ registerRedirectToHook) +/* harmony export */ }); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _state_targetState__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../state/targetState */ "./node_modules/@uirouter/core/lib-esm/state/targetState.js"); + + + +/** + * A [[TransitionHookFn]] that redirects to a different state or params + * + * Registered using `transitionService.onStart({ to: (state) => !!state.redirectTo }, redirectHook);` + * + * See [[StateDeclaration.redirectTo]] + */ +var redirectToHook = function (trans) { + var redirect = trans.to().redirectTo; + if (!redirect) + return; + var $state = trans.router.stateService; + function handleResult(result) { + if (!result) + return; + if (result instanceof _state_targetState__WEBPACK_IMPORTED_MODULE_2__.TargetState) + return result; + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)(result)) + return $state.target(result, trans.params(), trans.options()); + if (result['state'] || result['params']) + return $state.target(result['state'] || trans.to(), result['params'] || trans.params(), trans.options()); + } + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isFunction)(redirect)) { + return _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q.when(redirect(trans)).then(handleResult); + } + return handleResult(redirect); +}; +var registerRedirectToHook = function (transitionService) { + return transitionService.onStart({ to: function (state) { return !!state.redirectTo; } }, redirectToHook); +}; +//# sourceMappingURL=redirectTo.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/resolve.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/resolve.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "RESOLVE_HOOK_PRIORITY": () => (/* binding */ RESOLVE_HOOK_PRIORITY), +/* harmony export */ "registerEagerResolvePath": () => (/* binding */ registerEagerResolvePath), +/* harmony export */ "registerLazyResolveState": () => (/* binding */ registerLazyResolveState), +/* harmony export */ "registerResolveRemaining": () => (/* binding */ registerResolveRemaining) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../resolve/resolveContext */ "./node_modules/@uirouter/core/lib-esm/resolve/resolveContext.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); + + + +var RESOLVE_HOOK_PRIORITY = 1000; +/** + * A [[TransitionHookFn]] which resolves all EAGER Resolvables in the To Path + * + * Registered using `transitionService.onStart({}, eagerResolvePath, { priority: 1000 });` + * + * When a Transition starts, this hook resolves all the EAGER Resolvables, which the transition then waits for. + * + * See [[StateDeclaration.resolve]] + */ +var eagerResolvePath = function (trans) { + return new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_1__.ResolveContext(trans.treeChanges().to).resolvePath('EAGER', trans).then(_common_common__WEBPACK_IMPORTED_MODULE_0__.noop); +}; +var registerEagerResolvePath = function (transitionService) { + return transitionService.onStart({}, eagerResolvePath, { priority: RESOLVE_HOOK_PRIORITY }); +}; +/** + * A [[TransitionHookFn]] which resolves all LAZY Resolvables for the state (and all its ancestors) in the To Path + * + * Registered using `transitionService.onEnter({ entering: () => true }, lazyResolveState, { priority: 1000 });` + * + * When a State is being entered, this hook resolves all the Resolvables for this state, which the transition then waits for. + * + * See [[StateDeclaration.resolve]] + */ +var lazyResolveState = function (trans, state) { + return new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_1__.ResolveContext(trans.treeChanges().to).subContext(state.$$state()).resolvePath('LAZY', trans).then(_common_common__WEBPACK_IMPORTED_MODULE_0__.noop); +}; +var registerLazyResolveState = function (transitionService) { + return transitionService.onEnter({ entering: (0,_common_hof__WEBPACK_IMPORTED_MODULE_2__.val)(true) }, lazyResolveState, { priority: RESOLVE_HOOK_PRIORITY }); +}; +/** + * A [[TransitionHookFn]] which resolves any dynamically added (LAZY or EAGER) Resolvables. + * + * Registered using `transitionService.onFinish({}, eagerResolvePath, { priority: 1000 });` + * + * After all entering states have been entered, this hook resolves any remaining Resolvables. + * These are typically dynamic resolves which were added by some Transition Hook using [[Transition.addResolvable]]. + * + * See [[StateDeclaration.resolve]] + */ +var resolveRemaining = function (trans) { + return new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_1__.ResolveContext(trans.treeChanges().to).resolvePath('LAZY', trans).then(_common_common__WEBPACK_IMPORTED_MODULE_0__.noop); +}; +var registerResolveRemaining = function (transitionService) { + return transitionService.onFinish({}, resolveRemaining, { priority: RESOLVE_HOOK_PRIORITY }); +}; +//# sourceMappingURL=resolve.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/updateGlobals.js": +/*!********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/updateGlobals.js ***! + \********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerUpdateGlobalState": () => (/* binding */ registerUpdateGlobalState) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); + +/** + * A [[TransitionHookFn]] which updates global UI-Router state + * + * Registered using `transitionService.onBefore({}, updateGlobalState);` + * + * Before a [[Transition]] starts, updates the global value of "the current transition" ([[Globals.transition]]). + * After a successful [[Transition]], updates the global values of "the current state" + * ([[Globals.current]] and [[Globals.$current]]) and "the current param values" ([[Globals.params]]). + * + * See also the deprecated properties: + * [[StateService.transition]], [[StateService.current]], [[StateService.params]] + */ +var updateGlobalState = function (trans) { + var globals = trans.router.globals; + var transitionSuccessful = function () { + globals.successfulTransitions.enqueue(trans); + globals.$current = trans.$to(); + globals.current = globals.$current.self; + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.copy)(trans.params(), globals.params); + }; + var clearCurrentTransition = function () { + // Do not clear globals.transition if a different transition has started in the meantime + if (globals.transition === trans) + globals.transition = null; + }; + trans.onSuccess({}, transitionSuccessful, { priority: 10000 }); + trans.promise.then(clearCurrentTransition, clearCurrentTransition); +}; +var registerUpdateGlobalState = function (transitionService) { + return transitionService.onCreate({}, updateGlobalState); +}; +//# sourceMappingURL=updateGlobals.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/url.js": +/*!**********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/url.js ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerUpdateUrl": () => (/* binding */ registerUpdateUrl) +/* harmony export */ }); +/** + * A [[TransitionHookFn]] which updates the URL after a successful transition + * + * Registered using `transitionService.onSuccess({}, updateUrl);` + */ +var updateUrl = function (transition) { + var options = transition.options(); + var $state = transition.router.stateService; + var $urlRouter = transition.router.urlRouter; + // Dont update the url in these situations: + // The transition was triggered by a URL sync (options.source === 'url') + // The user doesn't want the url to update (options.location === false) + // The destination state, and all parents have no navigable url + if (options.source !== 'url' && options.location && $state.$current.navigable) { + var urlOptions = { replace: options.location === 'replace' }; + $urlRouter.push($state.$current.navigable.url, $state.params, urlOptions); + } + $urlRouter.update(true); +}; +var registerUpdateUrl = function (transitionService) { + return transitionService.onSuccess({}, updateUrl, { priority: 9999 }); +}; +//# sourceMappingURL=url.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/hooks/views.js": +/*!************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/hooks/views.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerLoadEnteringViews": () => (/* binding */ registerLoadEnteringViews), +/* harmony export */ "registerActivateViews": () => (/* binding */ registerActivateViews) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); + + +/** + * A [[TransitionHookFn]] which waits for the views to load + * + * Registered using `transitionService.onStart({}, loadEnteringViews);` + * + * Allows the views to do async work in [[ViewConfig.load]] before the transition continues. + * In angular 1, this includes loading the templates. + */ +var loadEnteringViews = function (transition) { + var $q = _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q; + var enteringViews = transition.views('entering'); + if (!enteringViews.length) + return; + return $q.all(enteringViews.map(function (view) { return $q.when(view.load()); })).then(_common_common__WEBPACK_IMPORTED_MODULE_0__.noop); +}; +var registerLoadEnteringViews = function (transitionService) { + return transitionService.onFinish({}, loadEnteringViews); +}; +/** + * A [[TransitionHookFn]] which activates the new views when a transition is successful. + * + * Registered using `transitionService.onSuccess({}, activateViews);` + * + * After a transition is complete, this hook deactivates the old views from the previous state, + * and activates the new views from the destination state. + * + * See [[ViewService]] + */ +var activateViews = function (transition) { + var enteringViews = transition.views('entering'); + var exitingViews = transition.views('exiting'); + if (!enteringViews.length && !exitingViews.length) + return; + var $view = transition.router.viewService; + exitingViews.forEach(function (vc) { return $view.deactivateViewConfig(vc); }); + enteringViews.forEach(function (vc) { return $view.activateViewConfig(vc); }); + $view.sync(); +}; +var registerActivateViews = function (transitionService) { + return transitionService.onSuccess({}, activateViews); +}; +//# sourceMappingURL=views.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/index.js": +/*!******************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/index.js ***! + \******************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Category": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.Category), +/* harmony export */ "Glob": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.Glob), +/* harmony export */ "Queue": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.Queue), +/* harmony export */ "Trace": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.Trace), +/* harmony export */ "_extend": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__._extend), +/* harmony export */ "_inArray": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__._inArray), +/* harmony export */ "_pushTo": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__._pushTo), +/* harmony export */ "_removeFrom": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__._removeFrom), +/* harmony export */ "all": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.all), +/* harmony export */ "allTrueR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.allTrueR), +/* harmony export */ "ancestors": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.ancestors), +/* harmony export */ "and": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.and), +/* harmony export */ "any": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.any), +/* harmony export */ "anyTrueR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.anyTrueR), +/* harmony export */ "applyPairs": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.applyPairs), +/* harmony export */ "arrayTuples": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.arrayTuples), +/* harmony export */ "assertFn": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.assertFn), +/* harmony export */ "assertMap": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.assertMap), +/* harmony export */ "assertPredicate": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.assertPredicate), +/* harmony export */ "beforeAfterSubstr": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.beforeAfterSubstr), +/* harmony export */ "compose": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.compose), +/* harmony export */ "copy": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.copy), +/* harmony export */ "createProxyFunctions": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions), +/* harmony export */ "curry": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.curry), +/* harmony export */ "defaults": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.defaults), +/* harmony export */ "deregAll": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.deregAll), +/* harmony export */ "eq": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.eq), +/* harmony export */ "equals": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.equals), +/* harmony export */ "extend": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.extend), +/* harmony export */ "filter": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.filter), +/* harmony export */ "find": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.find), +/* harmony export */ "flatten": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.flatten), +/* harmony export */ "flattenR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.flattenR), +/* harmony export */ "fnToString": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.fnToString), +/* harmony export */ "forEach": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.forEach), +/* harmony export */ "fromJson": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.fromJson), +/* harmony export */ "functionToString": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.functionToString), +/* harmony export */ "hostRegex": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.hostRegex), +/* harmony export */ "identity": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.identity), +/* harmony export */ "inArray": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.inArray), +/* harmony export */ "inherit": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.inherit), +/* harmony export */ "invoke": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.invoke), +/* harmony export */ "is": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.is), +/* harmony export */ "isArray": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isArray), +/* harmony export */ "isDate": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isDate), +/* harmony export */ "isDefined": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isDefined), +/* harmony export */ "isFunction": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isFunction), +/* harmony export */ "isInjectable": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isInjectable), +/* harmony export */ "isNull": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isNull), +/* harmony export */ "isNullOrUndefined": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isNullOrUndefined), +/* harmony export */ "isNumber": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isNumber), +/* harmony export */ "isObject": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isObject), +/* harmony export */ "isPromise": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isPromise), +/* harmony export */ "isRegExp": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isRegExp), +/* harmony export */ "isString": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isString), +/* harmony export */ "isUndefined": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.isUndefined), +/* harmony export */ "joinNeighborsR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.joinNeighborsR), +/* harmony export */ "kebobString": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.kebobString), +/* harmony export */ "makeStub": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.makeStub), +/* harmony export */ "map": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.map), +/* harmony export */ "mapObj": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.mapObj), +/* harmony export */ "maxLength": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.maxLength), +/* harmony export */ "mergeR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.mergeR), +/* harmony export */ "noop": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.noop), +/* harmony export */ "not": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.not), +/* harmony export */ "omit": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.omit), +/* harmony export */ "or": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.or), +/* harmony export */ "padString": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.padString), +/* harmony export */ "pairs": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pairs), +/* harmony export */ "parse": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.parse), +/* harmony export */ "pattern": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pattern), +/* harmony export */ "pick": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pick), +/* harmony export */ "pipe": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pipe), +/* harmony export */ "pluck": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pluck), +/* harmony export */ "prop": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.prop), +/* harmony export */ "propEq": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.propEq), +/* harmony export */ "pushR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pushR), +/* harmony export */ "pushTo": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.pushTo), +/* harmony export */ "removeFrom": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.removeFrom), +/* harmony export */ "root": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.root), +/* harmony export */ "services": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.services), +/* harmony export */ "silenceUncaughtInPromise": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.silenceUncaughtInPromise), +/* harmony export */ "silentRejection": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.silentRejection), +/* harmony export */ "splitEqual": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.splitEqual), +/* harmony export */ "splitHash": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.splitHash), +/* harmony export */ "splitOnDelim": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.splitOnDelim), +/* harmony export */ "splitQuery": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.splitQuery), +/* harmony export */ "stringify": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.stringify), +/* harmony export */ "stripLastPathElement": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.stripLastPathElement), +/* harmony export */ "tail": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.tail), +/* harmony export */ "toJson": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.toJson), +/* harmony export */ "trace": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.trace), +/* harmony export */ "trimHashVal": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.trimHashVal), +/* harmony export */ "uniqR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.uniqR), +/* harmony export */ "unnest": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.unnest), +/* harmony export */ "unnestR": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.unnestR), +/* harmony export */ "val": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.val), +/* harmony export */ "values": () => (/* reexport safe */ _common_index__WEBPACK_IMPORTED_MODULE_0__.values), +/* harmony export */ "PathNode": () => (/* reexport safe */ _path_index__WEBPACK_IMPORTED_MODULE_2__.PathNode), +/* harmony export */ "PathUtils": () => (/* reexport safe */ _path_index__WEBPACK_IMPORTED_MODULE_2__.PathUtils), +/* harmony export */ "NATIVE_INJECTOR_TOKEN": () => (/* reexport safe */ _resolve_index__WEBPACK_IMPORTED_MODULE_3__.NATIVE_INJECTOR_TOKEN), +/* harmony export */ "Resolvable": () => (/* reexport safe */ _resolve_index__WEBPACK_IMPORTED_MODULE_3__.Resolvable), +/* harmony export */ "ResolveContext": () => (/* reexport safe */ _resolve_index__WEBPACK_IMPORTED_MODULE_3__.ResolveContext), +/* harmony export */ "defaultResolvePolicy": () => (/* reexport safe */ _resolve_index__WEBPACK_IMPORTED_MODULE_3__.defaultResolvePolicy), +/* harmony export */ "resolvePolicies": () => (/* reexport safe */ _resolve_index__WEBPACK_IMPORTED_MODULE_3__.resolvePolicies), +/* harmony export */ "HookBuilder": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.HookBuilder), +/* harmony export */ "RegisteredHook": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.RegisteredHook), +/* harmony export */ "RejectType": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.RejectType), +/* harmony export */ "Rejection": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.Rejection), +/* harmony export */ "Transition": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.Transition), +/* harmony export */ "TransitionEventType": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.TransitionEventType), +/* harmony export */ "TransitionHook": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.TransitionHook), +/* harmony export */ "TransitionHookPhase": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.TransitionHookPhase), +/* harmony export */ "TransitionHookScope": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.TransitionHookScope), +/* harmony export */ "TransitionService": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.TransitionService), +/* harmony export */ "defaultTransOpts": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.defaultTransOpts), +/* harmony export */ "makeEvent": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.makeEvent), +/* harmony export */ "matchState": () => (/* reexport safe */ _transition_index__WEBPACK_IMPORTED_MODULE_5__.matchState), +/* harmony export */ "UIRouterGlobals": () => (/* reexport safe */ _globals__WEBPACK_IMPORTED_MODULE_8__.UIRouterGlobals), +/* harmony export */ "UIRouter": () => (/* reexport safe */ _router__WEBPACK_IMPORTED_MODULE_9__.UIRouter), +/* harmony export */ "UIRouterPluginBase": () => (/* reexport safe */ _interface__WEBPACK_IMPORTED_MODULE_11__.UIRouterPluginBase) +/* harmony export */ }); +/* harmony import */ var _common_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common/index */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _params_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./params/index */ "./node_modules/@uirouter/core/lib-esm/params/index.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _params_index__WEBPACK_IMPORTED_MODULE_1__) if(["default","Category","Glob","Queue","Trace","_extend","_inArray","_pushTo","_removeFrom","all","allTrueR","ancestors","and","any","anyTrueR","applyPairs","arrayTuples","assertFn","assertMap","assertPredicate","beforeAfterSubstr","compose","copy","createProxyFunctions","curry","defaults","deregAll","eq","equals","extend","filter","find","flatten","flattenR","fnToString","forEach","fromJson","functionToString","hostRegex","identity","inArray","inherit","invoke","is","isArray","isDate","isDefined","isFunction","isInjectable","isNull","isNullOrUndefined","isNumber","isObject","isPromise","isRegExp","isString","isUndefined","joinNeighborsR","kebobString","makeStub","map","mapObj","maxLength","mergeR","noop","not","omit","or","padString","pairs","parse","pattern","pick","pipe","pluck","prop","propEq","pushR","pushTo","removeFrom","root","services","silenceUncaughtInPromise","silentRejection","splitEqual","splitHash","splitOnDelim","splitQuery","stringify","stripLastPathElement","tail","toJson","trace","trimHashVal","uniqR","unnest","unnestR","val","values"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _params_index__WEBPACK_IMPORTED_MODULE_1__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _path_index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./path/index */ "./node_modules/@uirouter/core/lib-esm/path/index.js"); +/* harmony import */ var _resolve_index__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./resolve/index */ "./node_modules/@uirouter/core/lib-esm/resolve/index.js"); +/* harmony import */ var _state_index__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./state/index */ "./node_modules/@uirouter/core/lib-esm/state/index.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _state_index__WEBPACK_IMPORTED_MODULE_4__) if(["default","Category","Glob","Queue","Trace","_extend","_inArray","_pushTo","_removeFrom","all","allTrueR","ancestors","and","any","anyTrueR","applyPairs","arrayTuples","assertFn","assertMap","assertPredicate","beforeAfterSubstr","compose","copy","createProxyFunctions","curry","defaults","deregAll","eq","equals","extend","filter","find","flatten","flattenR","fnToString","forEach","fromJson","functionToString","hostRegex","identity","inArray","inherit","invoke","is","isArray","isDate","isDefined","isFunction","isInjectable","isNull","isNullOrUndefined","isNumber","isObject","isPromise","isRegExp","isString","isUndefined","joinNeighborsR","kebobString","makeStub","map","mapObj","maxLength","mergeR","noop","not","omit","or","padString","pairs","parse","pattern","pick","pipe","pluck","prop","propEq","pushR","pushTo","removeFrom","root","services","silenceUncaughtInPromise","silentRejection","splitEqual","splitHash","splitOnDelim","splitQuery","stringify","stripLastPathElement","tail","toJson","trace","trimHashVal","uniqR","unnest","unnestR","val","values","DefType","Param","ParamType","ParamTypes","StateParams","PathNode","PathUtils","NATIVE_INJECTOR_TOKEN","Resolvable","ResolveContext","defaultResolvePolicy","resolvePolicies"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _state_index__WEBPACK_IMPORTED_MODULE_4__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./transition/index */ "./node_modules/@uirouter/core/lib-esm/transition/index.js"); +/* harmony import */ var _url_index__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./url/index */ "./node_modules/@uirouter/core/lib-esm/url/index.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _url_index__WEBPACK_IMPORTED_MODULE_6__) if(["default","Category","Glob","Queue","Trace","_extend","_inArray","_pushTo","_removeFrom","all","allTrueR","ancestors","and","any","anyTrueR","applyPairs","arrayTuples","assertFn","assertMap","assertPredicate","beforeAfterSubstr","compose","copy","createProxyFunctions","curry","defaults","deregAll","eq","equals","extend","filter","find","flatten","flattenR","fnToString","forEach","fromJson","functionToString","hostRegex","identity","inArray","inherit","invoke","is","isArray","isDate","isDefined","isFunction","isInjectable","isNull","isNullOrUndefined","isNumber","isObject","isPromise","isRegExp","isString","isUndefined","joinNeighborsR","kebobString","makeStub","map","mapObj","maxLength","mergeR","noop","not","omit","or","padString","pairs","parse","pattern","pick","pipe","pluck","prop","propEq","pushR","pushTo","removeFrom","root","services","silenceUncaughtInPromise","silentRejection","splitEqual","splitHash","splitOnDelim","splitQuery","stringify","stripLastPathElement","tail","toJson","trace","trimHashVal","uniqR","unnest","unnestR","val","values","DefType","Param","ParamType","ParamTypes","StateParams","PathNode","PathUtils","NATIVE_INJECTOR_TOKEN","Resolvable","ResolveContext","defaultResolvePolicy","resolvePolicies","StateBuilder","StateMatcher","StateObject","StateQueueManager","StateRegistry","StateService","TargetState","resolvablesBuilder","HookBuilder","RegisteredHook","RejectType","Rejection","Transition","TransitionEventType","TransitionHook","TransitionHookPhase","TransitionHookScope","TransitionService","defaultTransOpts","makeEvent","matchState"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _url_index__WEBPACK_IMPORTED_MODULE_6__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _view_index__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./view/index */ "./node_modules/@uirouter/core/lib-esm/view/index.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _view_index__WEBPACK_IMPORTED_MODULE_7__) if(["default","Category","Glob","Queue","Trace","_extend","_inArray","_pushTo","_removeFrom","all","allTrueR","ancestors","and","any","anyTrueR","applyPairs","arrayTuples","assertFn","assertMap","assertPredicate","beforeAfterSubstr","compose","copy","createProxyFunctions","curry","defaults","deregAll","eq","equals","extend","filter","find","flatten","flattenR","fnToString","forEach","fromJson","functionToString","hostRegex","identity","inArray","inherit","invoke","is","isArray","isDate","isDefined","isFunction","isInjectable","isNull","isNullOrUndefined","isNumber","isObject","isPromise","isRegExp","isString","isUndefined","joinNeighborsR","kebobString","makeStub","map","mapObj","maxLength","mergeR","noop","not","omit","or","padString","pairs","parse","pattern","pick","pipe","pluck","prop","propEq","pushR","pushTo","removeFrom","root","services","silenceUncaughtInPromise","silentRejection","splitEqual","splitHash","splitOnDelim","splitQuery","stringify","stripLastPathElement","tail","toJson","trace","trimHashVal","uniqR","unnest","unnestR","val","values","DefType","Param","ParamType","ParamTypes","StateParams","PathNode","PathUtils","NATIVE_INJECTOR_TOKEN","Resolvable","ResolveContext","defaultResolvePolicy","resolvePolicies","StateBuilder","StateMatcher","StateObject","StateQueueManager","StateRegistry","StateService","TargetState","resolvablesBuilder","HookBuilder","RegisteredHook","RejectType","Rejection","Transition","TransitionEventType","TransitionHook","TransitionHookPhase","TransitionHookScope","TransitionService","defaultTransOpts","makeEvent","matchState","BaseUrlRule","ParamFactory","UrlConfig","UrlMatcher","UrlMatcherFactory","UrlRouter","UrlRuleFactory","UrlRules","UrlService"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _view_index__WEBPACK_IMPORTED_MODULE_7__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _globals__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./globals */ "./node_modules/@uirouter/core/lib-esm/globals.js"); +/* harmony import */ var _router__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./router */ "./node_modules/@uirouter/core/lib-esm/router.js"); +/* harmony import */ var _vanilla__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./vanilla */ "./node_modules/@uirouter/core/lib-esm/vanilla.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _vanilla__WEBPACK_IMPORTED_MODULE_10__) if(["default","Category","Glob","Queue","Trace","_extend","_inArray","_pushTo","_removeFrom","all","allTrueR","ancestors","and","any","anyTrueR","applyPairs","arrayTuples","assertFn","assertMap","assertPredicate","beforeAfterSubstr","compose","copy","createProxyFunctions","curry","defaults","deregAll","eq","equals","extend","filter","find","flatten","flattenR","fnToString","forEach","fromJson","functionToString","hostRegex","identity","inArray","inherit","invoke","is","isArray","isDate","isDefined","isFunction","isInjectable","isNull","isNullOrUndefined","isNumber","isObject","isPromise","isRegExp","isString","isUndefined","joinNeighborsR","kebobString","makeStub","map","mapObj","maxLength","mergeR","noop","not","omit","or","padString","pairs","parse","pattern","pick","pipe","pluck","prop","propEq","pushR","pushTo","removeFrom","root","services","silenceUncaughtInPromise","silentRejection","splitEqual","splitHash","splitOnDelim","splitQuery","stringify","stripLastPathElement","tail","toJson","trace","trimHashVal","uniqR","unnest","unnestR","val","values","DefType","Param","ParamType","ParamTypes","StateParams","PathNode","PathUtils","NATIVE_INJECTOR_TOKEN","Resolvable","ResolveContext","defaultResolvePolicy","resolvePolicies","StateBuilder","StateMatcher","StateObject","StateQueueManager","StateRegistry","StateService","TargetState","resolvablesBuilder","HookBuilder","RegisteredHook","RejectType","Rejection","Transition","TransitionEventType","TransitionHook","TransitionHookPhase","TransitionHookScope","TransitionService","defaultTransOpts","makeEvent","matchState","BaseUrlRule","ParamFactory","UrlConfig","UrlMatcher","UrlMatcherFactory","UrlRouter","UrlRuleFactory","UrlRules","UrlService","ViewService","UIRouterGlobals","UIRouter"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _vanilla__WEBPACK_IMPORTED_MODULE_10__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/interface.js"); + + + + + + + + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/interface.js": +/*!**********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/interface.js ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UIRouterPluginBase": () => (/* binding */ UIRouterPluginBase) +/* harmony export */ }); +var UIRouterPluginBase = /** @class */ (function () { + function UIRouterPluginBase() { + } + UIRouterPluginBase.prototype.dispose = function (router) { }; + return UIRouterPluginBase; +}()); + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/params/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/params/index.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "DefType": () => (/* reexport safe */ _param__WEBPACK_IMPORTED_MODULE_1__.DefType), +/* harmony export */ "Param": () => (/* reexport safe */ _param__WEBPACK_IMPORTED_MODULE_1__.Param), +/* harmony export */ "ParamTypes": () => (/* reexport safe */ _paramTypes__WEBPACK_IMPORTED_MODULE_2__.ParamTypes), +/* harmony export */ "StateParams": () => (/* reexport safe */ _stateParams__WEBPACK_IMPORTED_MODULE_3__.StateParams), +/* harmony export */ "ParamType": () => (/* reexport safe */ _paramType__WEBPACK_IMPORTED_MODULE_4__.ParamType) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/params/interface.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_interface__WEBPACK_IMPORTED_MODULE_0__); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _interface__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== "default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _interface__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _param__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./param */ "./node_modules/@uirouter/core/lib-esm/params/param.js"); +/* harmony import */ var _paramTypes__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./paramTypes */ "./node_modules/@uirouter/core/lib-esm/params/paramTypes.js"); +/* harmony import */ var _stateParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stateParams */ "./node_modules/@uirouter/core/lib-esm/params/stateParams.js"); +/* harmony import */ var _paramType__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./paramType */ "./node_modules/@uirouter/core/lib-esm/params/paramType.js"); +/** + * This module contains code for State Parameters. + * + * See [[ParamDeclaration]] + * + * @packageDocumentation @preferred + */ + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/params/interface.js": +/*!*****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/params/interface.js ***! + \*****************************************************************/ +/***/ (() => { + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/params/param.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/params/param.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "DefType": () => (/* binding */ DefType), +/* harmony export */ "Param": () => (/* binding */ Param) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _paramType__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./paramType */ "./node_modules/@uirouter/core/lib-esm/params/paramType.js"); + + + + + +var hasOwn = Object.prototype.hasOwnProperty; +var isShorthand = function (cfg) { + return ['value', 'type', 'squash', 'array', 'dynamic'].filter(hasOwn.bind(cfg || {})).length === 0; +}; +var DefType; +(function (DefType) { + DefType[DefType["PATH"] = 0] = "PATH"; + DefType[DefType["SEARCH"] = 1] = "SEARCH"; + DefType[DefType["CONFIG"] = 2] = "CONFIG"; +})(DefType || (DefType = {})); + +function getParamDeclaration(paramName, location, state) { + var noReloadOnSearch = (state.reloadOnSearch === false && location === DefType.SEARCH) || undefined; + var dynamic = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)([state.dynamic, noReloadOnSearch], _common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined); + var defaultConfig = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(dynamic) ? { dynamic: dynamic } : {}; + var paramConfig = unwrapShorthand(state && state.params && state.params[paramName]); + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(defaultConfig, paramConfig); +} +function unwrapShorthand(cfg) { + cfg = isShorthand(cfg) ? { value: cfg } : cfg; + getStaticDefaultValue['__cacheable'] = true; + function getStaticDefaultValue() { + return cfg.value; + } + var $$fn = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isInjectable)(cfg.value) ? cfg.value : getStaticDefaultValue; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(cfg, { $$fn: $$fn }); +} +function getType(cfg, urlType, location, id, paramTypes) { + if (cfg.type && urlType && urlType.name !== 'string') + throw new Error("Param '" + id + "' has two type configurations."); + if (cfg.type && urlType && urlType.name === 'string' && paramTypes.type(cfg.type)) + return paramTypes.type(cfg.type); + if (urlType) + return urlType; + if (!cfg.type) { + var type = location === DefType.CONFIG + ? 'any' + : location === DefType.PATH + ? 'path' + : location === DefType.SEARCH + ? 'query' + : 'string'; + return paramTypes.type(type); + } + return cfg.type instanceof _paramType__WEBPACK_IMPORTED_MODULE_4__.ParamType ? cfg.type : paramTypes.type(cfg.type); +} +/** returns false, true, or the squash value to indicate the "default parameter url squash policy". */ +function getSquashPolicy(config, isOptional, defaultPolicy) { + var squash = config.squash; + if (!isOptional || squash === false) + return false; + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(squash) || squash == null) + return defaultPolicy; + if (squash === true || (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.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 defaultPolicy = [ + { from: '', to: isOptional || arrayMode ? undefined : '' }, + { from: null, to: isOptional || arrayMode ? undefined : '' }, + ]; + var replace = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isArray)(config.replace) ? config.replace : []; + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(squash)) + replace.push({ from: squash, to: undefined }); + var configuredKeys = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(replace, (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('from')); + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.filter)(defaultPolicy, function (item) { return configuredKeys.indexOf(item.from) === -1; }).concat(replace); +} +var Param = /** @class */ (function () { + function Param(id, type, location, urlConfig, state) { + var config = getParamDeclaration(id, location, state); + type = getType(config, type, location, id, urlConfig.paramTypes); + var arrayMode = getArrayMode(); + type = arrayMode ? type.$asArray(arrayMode, location === DefType.SEARCH) : type; + var isOptional = config.value !== undefined || location === DefType.SEARCH; + var dynamic = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(config.dynamic) ? !!config.dynamic : !!type.dynamic; + var raw = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(config.raw) ? !!config.raw : !!type.raw; + var squash = getSquashPolicy(config, isOptional, urlConfig.defaultSquashPolicy()); + var replace = getReplace(config, arrayMode, isOptional, squash); + var inherit = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(config.inherit) ? !!config.inherit : !!type.inherit; + // array config: param name (param[]) overrides default settings. explicit config overrides param name. + function getArrayMode() { + var arrayDefaults = { array: location === DefType.SEARCH ? 'auto' : false }; + var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {}; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(arrayDefaults, arrayParamNomenclature, config).array; + } + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this, { id: id, type: type, location: location, isOptional: isOptional, dynamic: dynamic, raw: raw, squash: squash, replace: replace, inherit: inherit, array: arrayMode, config: config }); + } + Param.values = function (params, values) { + if (values === void 0) { values = {}; } + var paramValues = {}; + for (var _i = 0, params_1 = params; _i < params_1.length; _i++) { + var param = params_1[_i]; + paramValues[param.id] = param.value(values[param.id]); + } + return paramValues; + }; + /** + * Finds [[Param]] objects which have different param values + * + * Filters a list of [[Param]] objects to only those whose parameter values differ in two param value objects + * + * @param params: The list of Param objects to filter + * @param values1: The first set of parameter values + * @param values2: the second set of parameter values + * + * @returns any Param objects whose values were different between values1 and values2 + */ + Param.changed = function (params, values1, values2) { + if (values1 === void 0) { values1 = {}; } + if (values2 === void 0) { values2 = {}; } + return params.filter(function (param) { return !param.type.equals(values1[param.id], values2[param.id]); }); + }; + /** + * Checks if two param value objects are equal (for a set of [[Param]] objects) + * + * @param params The list of [[Param]] objects to check + * @param values1 The first set of param values + * @param values2 The second set of param values + * + * @returns true if the param values in values1 and values2 are equal + */ + Param.equals = function (params, values1, values2) { + if (values1 === void 0) { values1 = {}; } + if (values2 === void 0) { values2 = {}; } + return Param.changed(params, values1, values2).length === 0; + }; + /** Returns true if a the parameter values are valid, according to the Param definitions */ + Param.validates = function (params, values) { + if (values === void 0) { values = {}; } + return params.map(function (param) { return param.validates(values[param.id]); }).reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.allTrueR, true); + }; + Param.prototype.isDefaultValue = function (value) { + return this.isOptional && this.type.equals(this.value(), value); + }; + /** + * [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. + */ + Param.prototype.value = function (value) { + var _this = this; + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + var getDefaultValue = function () { + if (_this._defaultValueCache) + return _this._defaultValueCache.defaultValue; + if (!_common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$injector) + throw new Error('Injectable functions cannot be called at configuration time'); + var defaultValue = _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$injector.invoke(_this.config.$$fn); + if (defaultValue !== null && defaultValue !== undefined && !_this.type.is(defaultValue)) + throw new Error("Default value (" + defaultValue + ") for parameter '" + _this.id + "' is not an instance of ParamType (" + _this.type.name + ")"); + if (_this.config.$$fn['__cacheable']) { + _this._defaultValueCache = { defaultValue: defaultValue }; + } + return defaultValue; + }; + var replaceSpecialValues = function (val) { + for (var _i = 0, _a = _this.replace; _i < _a.length; _i++) { + var tuple = _a[_i]; + if (tuple.from === val) + return tuple.to; + } + return val; + }; + value = replaceSpecialValues(value); + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isUndefined)(value) ? getDefaultValue() : this.type.$normalize(value); + }; + Param.prototype.isSearch = function () { + return this.location === DefType.SEARCH; + }; + Param.prototype.validates = function (value) { + // There was no parameter value, but the param is optional + if (((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isUndefined)(value) || value === null) && this.isOptional) + return true; + // The value was not of the correct ParamType, and could not be decoded to the correct ParamType + var normalized = this.type.$normalize(value); + if (!this.type.is(normalized)) + return false; + // The value was of the correct type, but when encoded, did not match the ParamType's regexp + var encoded = this.type.encode(normalized); + return !((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(encoded) && !this.type.pattern.exec(encoded)); + }; + Param.prototype.toString = function () { + return "{Param:" + this.id + " " + this.type + " squash: '" + this.squash + "' optional: " + this.isOptional + "}"; + }; + return Param; +}()); + +//# sourceMappingURL=param.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/params/paramType.js": +/*!*****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/params/paramType.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ParamType": () => (/* binding */ ParamType) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); + + +/** + * An internal class which implements [[ParamTypeDefinition]]. + * + * A [[ParamTypeDefinition]] is a plain javascript object used to register custom parameter types. + * When a param type definition is registered, an instance of this class is created internally. + * + * This class has naive implementations for all the [[ParamTypeDefinition]] methods. + * + * Used by [[UrlMatcher]] when matching or formatting URLs, or comparing and validating parameter values. + * + * #### Example: + * ```js + * var paramTypeDef = { + * 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+/ + * } + * + * var paramType = new ParamType(paramTypeDef); + * ``` + */ +var ParamType = /** @class */ (function () { + /** + * @param def A configuration object which contains the custom type definition. The object's + * properties will override the default methods and/or pattern in `ParamType`'s public interface. + * @returns a new ParamType object + */ + function ParamType(def) { + /** @inheritdoc */ + this.pattern = /.*/; + /** @inheritdoc */ + this.inherit = true; + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this, def); + } + // consider these four methods to be "abstract methods" that should be overridden + /** @inheritdoc */ + ParamType.prototype.is = function (val, key) { + return true; + }; + /** @inheritdoc */ + ParamType.prototype.encode = function (val, key) { + return val; + }; + /** @inheritdoc */ + ParamType.prototype.decode = function (val, key) { + return val; + }; + /** @inheritdoc */ + ParamType.prototype.equals = function (a, b) { + // tslint:disable-next-line:triple-equals + return a == b; + }; + ParamType.prototype.$subPattern = function () { + var sub = this.pattern.toString(); + return sub.substr(1, sub.length - 2); + }; + ParamType.prototype.toString = function () { + return "{ParamType:" + this.name + "}"; + }; + /** Given an encoded string, or a decoded object, returns a decoded object */ + ParamType.prototype.$normalize = function (val) { + return this.is(val) ? val : this.decode(val); + }; + /** + * Wraps an existing custom ParamType as an array of ParamType, 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] + */ + ParamType.prototype.$asArray = function (mode, isSearch) { + if (!mode) + return this; + if (mode === 'auto' && !isSearch) + throw new Error("'auto' array mode is for query parameters only"); + return new ArrayType(this, mode); + }; + return ParamType; +}()); + +/** Wraps up a `ParamType` object to handle array values. */ +function ArrayType(type, mode) { + var _this = this; + // Wrap non-array value as array + function arrayWrap(val) { + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isArray)(val) ? val : (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.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; + } + } + // Wraps type (.is/.encode/.decode) functions to operate on each value of an array + function arrayHandler(callback, allTruthyMode) { + return function handleArray(val) { + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isArray)(val) && val.length === 0) + return val; + var arr = arrayWrap(val); + var result = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(arr, callback); + return allTruthyMode === true ? (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.filter)(result, function (x) { return !x; }).length === 0 : 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; + }; + } + ['encode', 'decode', 'equals', '$normalize'].forEach(function (name) { + var paramTypeFn = type[name].bind(type); + var wrapperFn = name === 'equals' ? arrayEqualsHandler : arrayHandler; + _this[name] = wrapperFn(paramTypeFn); + }); + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this, { + dynamic: type.dynamic, + name: type.name, + pattern: type.pattern, + inherit: type.inherit, + raw: type.raw, + is: arrayHandler(type.is.bind(type), true), + $arrayMode: mode, + }); +} +//# sourceMappingURL=paramType.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/params/paramTypes.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/params/paramTypes.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ParamTypes": () => (/* binding */ ParamTypes) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _paramType__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./paramType */ "./node_modules/@uirouter/core/lib-esm/params/paramType.js"); + + + + + +/** + * A registry for parameter types. + * + * This registry manages the built-in (and custom) parameter types. + * + * The built-in parameter types are: + * + * - [[string]] + * - [[path]] + * - [[query]] + * - [[hash]] + * - [[int]] + * - [[bool]] + * - [[date]] + * - [[json]] + * - [[any]] + * + * To register custom parameter types, use [[UrlConfig.type]], i.e., + * + * ```js + * router.urlService.config.type(customType) + * ``` + */ +var ParamTypes = /** @class */ (function () { + function ParamTypes() { + this.enqueue = true; + this.typeQueue = []; + this.defaultTypes = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.pick)(ParamTypes.prototype, [ + 'hash', + 'string', + 'query', + 'path', + 'int', + 'bool', + 'date', + 'json', + 'any', + ]); + // Register default types. Store them in the prototype of this.types. + var makeType = function (definition, name) { return new _paramType__WEBPACK_IMPORTED_MODULE_4__.ParamType((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({ name: name }, definition)); }; + this.types = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inherit)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(this.defaultTypes, makeType), {}); + } + ParamTypes.prototype.dispose = function () { + this.types = {}; + }; + /** + * Registers a parameter type + * + * End users should call [[UrlMatcherFactory.type]], which delegates to this method. + */ + ParamTypes.prototype.type = function (name, definition, definitionFn) { + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(definition)) + return this.types[name]; + if (this.types.hasOwnProperty(name)) + throw new Error("A type named '" + name + "' has already been defined."); + this.types[name] = new _paramType__WEBPACK_IMPORTED_MODULE_4__.ParamType((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({ name: name }, definition)); + if (definitionFn) { + this.typeQueue.push({ name: name, def: definitionFn }); + if (!this.enqueue) + this._flushTypeQueue(); + } + return this; + }; + ParamTypes.prototype._flushTypeQueue = function () { + while (this.typeQueue.length) { + var type = this.typeQueue.shift(); + if (type.pattern) + throw new Error("You cannot override a type's .pattern at runtime."); + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this.types[type.name], _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$injector.invoke(type.def)); + } + }; + return ParamTypes; +}()); + +function initDefaultTypes() { + var makeDefaultType = function (def) { + var valToString = function (val) { return (val != null ? val.toString() : val); }; + var defaultTypeBase = { + encode: valToString, + decode: valToString, + is: (0,_common_hof__WEBPACK_IMPORTED_MODULE_2__.is)(String), + pattern: /.*/, + // tslint:disable-next-line:triple-equals + equals: function (a, b) { return a == b; }, + }; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({}, defaultTypeBase, def); + }; + // Default Parameter Type Definitions + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(ParamTypes.prototype, { + string: makeDefaultType({}), + path: makeDefaultType({ + pattern: /[^/]*/, + }), + query: makeDefaultType({}), + hash: makeDefaultType({ + inherit: false, + }), + int: makeDefaultType({ + decode: function (val) { return parseInt(val, 10); }, + is: function (val) { + return !(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isNullOrUndefined)(val) && this.decode(val.toString()) === val; + }, + pattern: /-?\d+/, + }), + bool: makeDefaultType({ + encode: function (val) { return (val && 1) || 0; }, + decode: function (val) { return parseInt(val, 10) !== 0; }, + is: (0,_common_hof__WEBPACK_IMPORTED_MODULE_2__.is)(Boolean), + pattern: /0|1/, + }), + date: makeDefaultType({ + encode: function (val) { + return !this.is(val) + ? undefined + : [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 (l, r) { + return ['getFullYear', 'getMonth', 'getDate'].reduce(function (acc, fn) { return acc && l[fn]() === r[fn](); }, true); + }, + 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: makeDefaultType({ + encode: _common_common__WEBPACK_IMPORTED_MODULE_0__.toJson, + decode: _common_common__WEBPACK_IMPORTED_MODULE_0__.fromJson, + is: (0,_common_hof__WEBPACK_IMPORTED_MODULE_2__.is)(Object), + equals: _common_common__WEBPACK_IMPORTED_MODULE_0__.equals, + pattern: /[^/]*/, + }), + // does not encode/decode + any: makeDefaultType({ + encode: _common_common__WEBPACK_IMPORTED_MODULE_0__.identity, + decode: _common_common__WEBPACK_IMPORTED_MODULE_0__.identity, + is: function () { return true; }, + equals: _common_common__WEBPACK_IMPORTED_MODULE_0__.equals, + }), + }); +} +initDefaultTypes(); +//# sourceMappingURL=paramTypes.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/params/stateParams.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/params/stateParams.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateParams": () => (/* binding */ StateParams) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); + +var StateParams = /** @class */ (function () { + function StateParams(params) { + if (params === void 0) { params = {}; } + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this, params); + } + /** + * Merges a set of parameters with all parameters inherited between the common parents of the + * current state and a given destination state. + * + * @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. + */ + StateParams.prototype.$inherit = function (newParams, $current, $to) { + var parentParams; + var parents = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.ancestors)($current, $to), inherited = {}, inheritList = []; + for (var i in parents) { + if (!parents[i] || !parents[i].params) + continue; + parentParams = Object.keys(parents[i].params); + if (!parentParams.length) + continue; + for (var j in parentParams) { + if (inheritList.indexOf(parentParams[j]) >= 0) + continue; + inheritList.push(parentParams[j]); + inherited[parentParams[j]] = this[parentParams[j]]; + } + } + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({}, inherited, newParams); + }; + return StateParams; +}()); + +//# sourceMappingURL=stateParams.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/path/index.js": +/*!***********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/path/index.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "PathNode": () => (/* reexport safe */ _pathNode__WEBPACK_IMPORTED_MODULE_0__.PathNode), +/* harmony export */ "PathUtils": () => (/* reexport safe */ _pathUtils__WEBPACK_IMPORTED_MODULE_1__.PathUtils) +/* harmony export */ }); +/* harmony import */ var _pathNode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pathNode */ "./node_modules/@uirouter/core/lib-esm/path/pathNode.js"); +/* harmony import */ var _pathUtils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./pathUtils */ "./node_modules/@uirouter/core/lib-esm/path/pathUtils.js"); + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/path/pathNode.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/path/pathNode.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "PathNode": () => (/* binding */ PathNode) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _params_param__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../params/param */ "./node_modules/@uirouter/core/lib-esm/params/param.js"); + + + +/** + * A node in a [[TreeChanges]] path + * + * For a [[TreeChanges]] path, this class holds the stateful information for a single node in the path. + * Each PathNode corresponds to a state being entered, exited, or retained. + * The stateful information includes parameter values and resolve data. + */ +var PathNode = /** @class */ (function () { + function PathNode(stateOrNode) { + if (stateOrNode instanceof PathNode) { + var node = stateOrNode; + this.state = node.state; + this.paramSchema = node.paramSchema.slice(); + this.paramValues = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({}, node.paramValues); + this.resolvables = node.resolvables.slice(); + this.views = node.views && node.views.slice(); + } + else { + var state = stateOrNode; + this.state = state; + this.paramSchema = state.parameters({ inherit: false }); + this.paramValues = {}; + this.resolvables = state.resolvables.map(function (res) { return res.clone(); }); + } + } + PathNode.prototype.clone = function () { + return new PathNode(this); + }; + /** Sets [[paramValues]] for the node, from the values of an object hash */ + PathNode.prototype.applyRawParams = function (params) { + var getParamVal = function (paramDef) { return [paramDef.id, paramDef.value(params[paramDef.id])]; }; + this.paramValues = this.paramSchema.reduce(function (memo, pDef) { return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.applyPairs)(memo, getParamVal(pDef)); }, {}); + return this; + }; + /** Gets a specific [[Param]] metadata that belongs to the node */ + PathNode.prototype.parameter = function (name) { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(this.paramSchema, (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.propEq)('id', name)); + }; + /** + * @returns true if the state and parameter values for another PathNode are + * equal to the state and param values for this PathNode + */ + PathNode.prototype.equals = function (node, paramsFn) { + var diff = this.diff(node, paramsFn); + return diff && diff.length === 0; + }; + /** + * Finds Params with different parameter values on another PathNode. + * + * Given another node (of the same state), finds the parameter values which differ. + * Returns the [[Param]] (schema objects) whose parameter values differ. + * + * Given another node for a different state, returns `false` + * + * @param node The node to compare to + * @param paramsFn A function that returns which parameters should be compared. + * @returns The [[Param]]s which differ, or null if the two nodes are for different states + */ + PathNode.prototype.diff = function (node, paramsFn) { + if (this.state !== node.state) + return false; + var params = paramsFn ? paramsFn(this) : this.paramSchema; + return _params_param__WEBPACK_IMPORTED_MODULE_2__.Param.changed(params, this.paramValues, node.paramValues); + }; + /** + * Returns a clone of the PathNode + * @deprecated use instance method `node.clone()` + */ + PathNode.clone = function (node) { return node.clone(); }; + return PathNode; +}()); + +//# sourceMappingURL=pathNode.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/path/pathUtils.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/path/pathUtils.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "PathUtils": () => (/* binding */ PathUtils) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _state_targetState__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../state/targetState */ "./node_modules/@uirouter/core/lib-esm/state/targetState.js"); +/* harmony import */ var _pathNode__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./pathNode */ "./node_modules/@uirouter/core/lib-esm/path/pathNode.js"); + + + + +/** + * This class contains functions which convert TargetStates, Nodes and paths from one type to another. + */ +var PathUtils = /** @class */ (function () { + function PathUtils() { + } + /** Given a PathNode[], create an TargetState */ + PathUtils.makeTargetState = function (registry, path) { + var state = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.tail)(path).state; + return new _state_targetState__WEBPACK_IMPORTED_MODULE_2__.TargetState(registry, state, path.map((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('paramValues')).reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.mergeR, {}), {}); + }; + PathUtils.buildPath = function (targetState) { + var toParams = targetState.params(); + return targetState.$state().path.map(function (state) { return new _pathNode__WEBPACK_IMPORTED_MODULE_3__.PathNode(state).applyRawParams(toParams); }); + }; + /** Given a fromPath: PathNode[] and a TargetState, builds a toPath: PathNode[] */ + PathUtils.buildToPath = function (fromPath, targetState) { + var toPath = PathUtils.buildPath(targetState); + if (targetState.options().inherit) { + return PathUtils.inheritParams(fromPath, toPath, Object.keys(targetState.params())); + } + return toPath; + }; + /** + * Creates ViewConfig objects and adds to nodes. + * + * On each [[PathNode]], creates ViewConfig objects from the views: property of the node's state + */ + PathUtils.applyViewConfigs = function ($view, path, states) { + // Only apply the viewConfigs to the nodes for the given states + path + .filter(function (node) { return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(states, node.state); }) + .forEach(function (node) { + var viewDecls = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.values)(node.state.views || {}); + var subPath = PathUtils.subPath(path, function (n) { return n === node; }); + var viewConfigs = viewDecls.map(function (view) { return $view.createViewConfig(subPath, view); }); + node.views = viewConfigs.reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []); + }); + }; + /** + * Given a fromPath and a toPath, returns a new to path which inherits parameters from the fromPath + * + * For a parameter in a node to be inherited from the from path: + * - The toPath's node must have a matching node in the fromPath (by state). + * - The parameter name must not be found in the toKeys parameter array. + * + * Note: the keys provided in toKeys are intended to be those param keys explicitly specified by some + * caller, for instance, $state.transitionTo(..., toParams). If a key was found in toParams, + * it is not inherited from the fromPath. + */ + PathUtils.inheritParams = function (fromPath, toPath, toKeys) { + if (toKeys === void 0) { toKeys = []; } + function nodeParamVals(path, state) { + var node = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(path, (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.propEq)('state', state)); + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({}, node && node.paramValues); + } + var noInherit = fromPath + .map(function (node) { return node.paramSchema; }) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .filter(function (param) { return !param.inherit; }) + .map((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('id')); + /** + * Given an [[PathNode]] "toNode", return a new [[PathNode]] with param values inherited from the + * matching node in fromPath. Only inherit keys that aren't found in "toKeys" from the node in "fromPath"" + */ + function makeInheritedParamsNode(toNode) { + // All param values for the node (may include default key/vals, when key was not found in toParams) + var toParamVals = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({}, toNode && toNode.paramValues); + // limited to only those keys found in toParams + var incomingParamVals = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.pick)(toParamVals, toKeys); + toParamVals = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.omit)(toParamVals, toKeys); + var fromParamVals = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.omit)(nodeParamVals(fromPath, toNode.state) || {}, noInherit); + // extend toParamVals with any fromParamVals, then override any of those those with incomingParamVals + var ownParamVals = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(toParamVals, fromParamVals, incomingParamVals); + return new _pathNode__WEBPACK_IMPORTED_MODULE_3__.PathNode(toNode.state).applyRawParams(ownParamVals); + } + // The param keys specified by the incoming toParams + return toPath.map(makeInheritedParamsNode); + }; + /** + * Computes the tree changes (entering, exiting) between a fromPath and toPath. + */ + PathUtils.treeChanges = function (fromPath, toPath, reloadState) { + var max = Math.min(fromPath.length, toPath.length); + var keep = 0; + var nodesMatch = function (node1, node2) { return node1.equals(node2, PathUtils.nonDynamicParams); }; + while (keep < max && fromPath[keep].state !== reloadState && nodesMatch(fromPath[keep], toPath[keep])) { + keep++; + } + /** Given a retained node, return a new node which uses the to node's param values */ + function applyToParams(retainedNode, idx) { + var cloned = retainedNode.clone(); + cloned.paramValues = toPath[idx].paramValues; + return cloned; + } + var from, retained, exiting, entering, to; + from = fromPath; + retained = from.slice(0, keep); + exiting = from.slice(keep); + // Create a new retained path (with shallow copies of nodes) which have the params of the toPath mapped + var retainedWithToParams = retained.map(applyToParams); + entering = toPath.slice(keep); + to = retainedWithToParams.concat(entering); + return { from: from, to: to, retained: retained, retainedWithToParams: retainedWithToParams, exiting: exiting, entering: entering }; + }; + /** + * Returns a new path which is: the subpath of the first path which matches the second path. + * + * The new path starts from root and contains any nodes that match the nodes in the second path. + * It stops before the first non-matching node. + * + * Nodes are compared using their state property and their parameter values. + * If a `paramsFn` is provided, only the [[Param]] returned by the function will be considered when comparing nodes. + * + * @param pathA the first path + * @param pathB the second path + * @param paramsFn a function which returns the parameters to consider when comparing + * + * @returns an array of PathNodes from the first path which match the nodes in the second path + */ + PathUtils.matching = function (pathA, pathB, paramsFn) { + var done = false; + var tuples = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.arrayTuples)(pathA, pathB); + return tuples.reduce(function (matching, _a) { + var nodeA = _a[0], nodeB = _a[1]; + done = done || !nodeA.equals(nodeB, paramsFn); + return done ? matching : matching.concat(nodeA); + }, []); + }; + /** + * Returns true if two paths are identical. + * + * @param pathA + * @param pathB + * @param paramsFn a function which returns the parameters to consider when comparing + * @returns true if the the states and parameter values for both paths are identical + */ + PathUtils.equals = function (pathA, pathB, paramsFn) { + return pathA.length === pathB.length && PathUtils.matching(pathA, pathB, paramsFn).length === pathA.length; + }; + /** + * Return a subpath of a path, which stops at the first matching node + * + * Given an array of nodes, returns a subset of the array starting from the first node, + * stopping when the first node matches the predicate. + * + * @param path a path of [[PathNode]]s + * @param predicate a [[Predicate]] fn that matches [[PathNode]]s + * @returns a subpath up to the matching node, or undefined if no match is found + */ + PathUtils.subPath = function (path, predicate) { + var node = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(path, predicate); + var elementIdx = path.indexOf(node); + return elementIdx === -1 ? undefined : path.slice(0, elementIdx + 1); + }; + PathUtils.nonDynamicParams = function (node) { + return node.state.parameters({ inherit: false }).filter(function (param) { return !param.dynamic; }); + }; + /** Gets the raw parameter values from a path */ + PathUtils.paramValues = function (path) { return path.reduce(function (acc, node) { return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(acc, node.paramValues); }, {}); }; + return PathUtils; +}()); + +//# sourceMappingURL=pathUtils.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/resolve/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/resolve/index.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "resolvePolicies": () => (/* reexport safe */ _interface__WEBPACK_IMPORTED_MODULE_0__.resolvePolicies), +/* harmony export */ "Resolvable": () => (/* reexport safe */ _resolvable__WEBPACK_IMPORTED_MODULE_1__.Resolvable), +/* harmony export */ "defaultResolvePolicy": () => (/* reexport safe */ _resolvable__WEBPACK_IMPORTED_MODULE_1__.defaultResolvePolicy), +/* harmony export */ "NATIVE_INJECTOR_TOKEN": () => (/* reexport safe */ _resolveContext__WEBPACK_IMPORTED_MODULE_2__.NATIVE_INJECTOR_TOKEN), +/* harmony export */ "ResolveContext": () => (/* reexport safe */ _resolveContext__WEBPACK_IMPORTED_MODULE_2__.ResolveContext) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/resolve/interface.js"); +/* harmony import */ var _resolvable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./resolvable */ "./node_modules/@uirouter/core/lib-esm/resolve/resolvable.js"); +/* harmony import */ var _resolveContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./resolveContext */ "./node_modules/@uirouter/core/lib-esm/resolve/resolveContext.js"); + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/resolve/interface.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/resolve/interface.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "resolvePolicies": () => (/* binding */ resolvePolicies) +/* harmony export */ }); +var resolvePolicies = { + when: { + LAZY: 'LAZY', + EAGER: 'EAGER', + }, + async: { + WAIT: 'WAIT', + NOWAIT: 'NOWAIT', + }, +}; +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/resolve/resolvable.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/resolve/resolvable.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "defaultResolvePolicy": () => (/* binding */ defaultResolvePolicy), +/* harmony export */ "Resolvable": () => (/* binding */ Resolvable) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); + + + + + + +// TODO: explicitly make this user configurable +var defaultResolvePolicy = { + when: 'LAZY', + async: 'WAIT', +}; +/** + * The basic building block for the resolve system. + * + * Resolvables encapsulate a state's resolve's resolveFn, the resolveFn's declared dependencies, the wrapped (.promise), + * and the unwrapped-when-complete (.data) result of the resolveFn. + * + * Resolvable.get() either retrieves the Resolvable's existing promise, or else invokes resolve() (which invokes the + * resolveFn) and returns the resulting promise. + * + * Resolvable.get() and Resolvable.resolve() both execute within a context path, which is passed as the first + * parameter to those fns. + */ +var Resolvable = /** @class */ (function () { + function Resolvable(arg1, resolveFn, deps, policy, data) { + this.resolved = false; + this.promise = undefined; + if (arg1 instanceof Resolvable) { + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this, arg1); + } + else if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isFunction)(resolveFn)) { + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isNullOrUndefined)(arg1)) + throw new Error('new Resolvable(): token argument is required'); + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isFunction)(resolveFn)) + throw new Error('new Resolvable(): resolveFn argument must be a function'); + this.token = arg1; + this.policy = policy; + this.resolveFn = resolveFn; + this.deps = deps || []; + this.data = data; + this.resolved = data !== undefined; + this.promise = this.resolved ? _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q.when(this.data) : undefined; + } + else if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isObject)(arg1) && arg1.token && (arg1.hasOwnProperty('resolveFn') || arg1.hasOwnProperty('data'))) { + var literal = arg1; + return new Resolvable(literal.token, literal.resolveFn, literal.deps, literal.policy, literal.data); + } + } + Resolvable.prototype.getPolicy = function (state) { + var thisPolicy = this.policy || {}; + var statePolicy = (state && state.resolvePolicy) || {}; + return { + when: thisPolicy.when || statePolicy.when || defaultResolvePolicy.when, + async: thisPolicy.async || statePolicy.async || defaultResolvePolicy.async, + }; + }; + /** + * Asynchronously resolve this Resolvable's data + * + * Given a ResolveContext that this Resolvable is found in: + * Wait for this Resolvable's dependencies, then invoke this Resolvable's function + * and update the Resolvable's state + */ + Resolvable.prototype.resolve = function (resolveContext, trans) { + var _this = this; + var $q = _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q; + // Gets all dependencies from ResolveContext and wait for them to be resolved + var getResolvableDependencies = function () { + return $q.all(resolveContext.getDependencies(_this).map(function (resolvable) { return resolvable.get(resolveContext, trans); })); + }; + // Invokes the resolve function passing the resolved dependencies as arguments + var invokeResolveFn = function (resolvedDeps) { return _this.resolveFn.apply(null, resolvedDeps); }; + var node = resolveContext.findNode(this); + var state = node && node.state; + var asyncPolicy = this.getPolicy(state).async; + var customAsyncPolicy = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isFunction)(asyncPolicy) ? asyncPolicy : _common_common__WEBPACK_IMPORTED_MODULE_0__.identity; + // After the final value has been resolved, update the state of the Resolvable + var applyResolvedValue = function (resolvedValue) { + _this.data = resolvedValue; + _this.resolved = true; + _this.resolveFn = null; + _common_trace__WEBPACK_IMPORTED_MODULE_2__.trace.traceResolvableResolved(_this, trans); + return _this.data; + }; + // Sets the promise property first, then getsResolvableDependencies in the context of the promise chain. Always waits one tick. + return (this.promise = $q + .when() + .then(getResolvableDependencies) + .then(invokeResolveFn) + .then(customAsyncPolicy) + .then(applyResolvedValue)); + }; + /** + * Gets a promise for this Resolvable's data. + * + * Fetches the data and returns a promise. + * Returns the existing promise if it has already been fetched once. + */ + Resolvable.prototype.get = function (resolveContext, trans) { + return this.promise || this.resolve(resolveContext, trans); + }; + Resolvable.prototype.toString = function () { + return "Resolvable(token: " + (0,_common_strings__WEBPACK_IMPORTED_MODULE_3__.stringify)(this.token) + ", requires: [" + this.deps.map(_common_strings__WEBPACK_IMPORTED_MODULE_3__.stringify) + "])"; + }; + Resolvable.prototype.clone = function () { + return new Resolvable(this); + }; + Resolvable.fromData = function (token, data) { return new Resolvable(token, function () { return data; }, null, null, data); }; + return Resolvable; +}()); + +//# sourceMappingURL=resolvable.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/resolve/resolveContext.js": +/*!***********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/resolve/resolveContext.js ***! + \***********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "NATIVE_INJECTOR_TOKEN": () => (/* binding */ NATIVE_INJECTOR_TOKEN), +/* harmony export */ "ResolveContext": () => (/* binding */ ResolveContext) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/resolve/interface.js"); +/* harmony import */ var _resolvable__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./resolvable */ "./node_modules/@uirouter/core/lib-esm/resolve/resolvable.js"); +/* harmony import */ var _path_pathUtils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../path/pathUtils */ "./node_modules/@uirouter/core/lib-esm/path/pathUtils.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + + + + + + + + +var whens = _interface__WEBPACK_IMPORTED_MODULE_4__.resolvePolicies.when; +var ALL_WHENS = [whens.EAGER, whens.LAZY]; +var EAGER_WHENS = [whens.EAGER]; +// tslint:disable-next-line:no-inferrable-types +var NATIVE_INJECTOR_TOKEN = 'Native Injector'; +/** + * Encapsulates Dependency Injection for a path of nodes + * + * UI-Router states are organized as a tree. + * A nested state has a path of ancestors to the root of the tree. + * When a state is being activated, each element in the path is wrapped as a [[PathNode]]. + * A `PathNode` is a stateful object that holds things like parameters and resolvables for the state being activated. + * + * The ResolveContext closes over the [[PathNode]]s, and provides DI for the last node in the path. + */ +var ResolveContext = /** @class */ (function () { + function ResolveContext(_path) { + this._path = _path; + } + /** Gets all the tokens found in the resolve context, de-duplicated */ + ResolveContext.prototype.getTokens = function () { + return this._path.reduce(function (acc, node) { return acc.concat(node.resolvables.map(function (r) { return r.token; })); }, []).reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.uniqR, []); + }; + /** + * Gets the Resolvable that matches the token + * + * Gets the last Resolvable that matches the token in this context, or undefined. + * Throws an error if it doesn't exist in the ResolveContext + */ + ResolveContext.prototype.getResolvable = function (token) { + var matching = this._path + .map(function (node) { return node.resolvables; }) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .filter(function (r) { return r.token === token; }); + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.tail)(matching); + }; + /** Returns the [[ResolvePolicy]] for the given [[Resolvable]] */ + ResolveContext.prototype.getPolicy = function (resolvable) { + var node = this.findNode(resolvable); + return resolvable.getPolicy(node.state); + }; + /** + * Returns a ResolveContext that includes a portion of this one + * + * Given a state, this method creates a new ResolveContext from this one. + * The new context starts at the first node (root) and stops at the node for the `state` parameter. + * + * #### Why + * + * When a transition is created, the nodes in the "To Path" are injected from a ResolveContext. + * A ResolveContext closes over a path of [[PathNode]]s and processes the resolvables. + * The "To State" can inject values from its own resolvables, as well as those from all its ancestor state's (node's). + * This method is used to create a narrower context when injecting ancestor nodes. + * + * @example + * `let ABCD = new ResolveContext([A, B, C, D]);` + * + * Given a path `[A, B, C, D]`, where `A`, `B`, `C` and `D` are nodes for states `a`, `b`, `c`, `d`: + * When injecting `D`, `D` should have access to all resolvables from `A`, `B`, `C`, `D`. + * However, `B` should only be able to access resolvables from `A`, `B`. + * + * When resolving for the `B` node, first take the full "To Path" Context `[A,B,C,D]` and limit to the subpath `[A,B]`. + * `let AB = ABCD.subcontext(a)` + */ + ResolveContext.prototype.subContext = function (state) { + return new ResolveContext(_path_pathUtils__WEBPACK_IMPORTED_MODULE_6__.PathUtils.subPath(this._path, function (node) { return node.state === state; })); + }; + /** + * Adds Resolvables to the node that matches the state + * + * This adds a [[Resolvable]] (generally one created on the fly; not declared on a [[StateDeclaration.resolve]] block). + * The resolvable is added to the node matching the `state` parameter. + * + * These new resolvables are not automatically fetched. + * The calling code should either fetch them, fetch something that depends on them, + * or rely on [[resolvePath]] being called when some state is being entered. + * + * Note: each resolvable's [[ResolvePolicy]] is merged with the state's policy, and the global default. + * + * @param newResolvables the new Resolvables + * @param state Used to find the node to put the resolvable on + */ + ResolveContext.prototype.addResolvables = function (newResolvables, state) { + var node = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(this._path, (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.propEq)('state', state)); + var keys = newResolvables.map(function (r) { return r.token; }); + node.resolvables = node.resolvables.filter(function (r) { return keys.indexOf(r.token) === -1; }).concat(newResolvables); + }; + /** + * Returns a promise for an array of resolved path Element promises + * + * @param when + * @param trans + * @returns {Promise|any} + */ + ResolveContext.prototype.resolvePath = function (when, trans) { + var _this = this; + if (when === void 0) { when = 'LAZY'; } + // This option determines which 'when' policy Resolvables we are about to fetch. + var whenOption = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(ALL_WHENS, when) ? when : 'LAZY'; + // If the caller specified EAGER, only the EAGER Resolvables are fetched. + // if the caller specified LAZY, both EAGER and LAZY Resolvables are fetched.` + var matchedWhens = whenOption === _interface__WEBPACK_IMPORTED_MODULE_4__.resolvePolicies.when.EAGER ? EAGER_WHENS : ALL_WHENS; + // get the subpath to the state argument, if provided + _common_trace__WEBPACK_IMPORTED_MODULE_2__.trace.traceResolvePath(this._path, when, trans); + var matchesPolicy = function (acceptedVals, whenOrAsync) { return function (resolvable) { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(acceptedVals, _this.getPolicy(resolvable)[whenOrAsync]); + }; }; + // Trigger all the (matching) Resolvables in the path + // Reduce all the "WAIT" Resolvables into an array + var promises = this._path.reduce(function (acc, node) { + var nodeResolvables = node.resolvables.filter(matchesPolicy(matchedWhens, 'when')); + var nowait = nodeResolvables.filter(matchesPolicy(['NOWAIT'], 'async')); + var wait = nodeResolvables.filter((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.not)(matchesPolicy(['NOWAIT'], 'async'))); + // For the matching Resolvables, start their async fetch process. + var subContext = _this.subContext(node.state); + var getResult = function (r) { + return r + .get(subContext, trans) + // Return a tuple that includes the Resolvable's token + .then(function (value) { return ({ token: r.token, value: value }); }); + }; + nowait.forEach(getResult); + return acc.concat(wait.map(getResult)); + }, []); + // Wait for all the "WAIT" resolvables + return _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$q.all(promises); + }; + ResolveContext.prototype.injector = function () { + return this._injector || (this._injector = new UIInjectorImpl(this)); + }; + ResolveContext.prototype.findNode = function (resolvable) { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(this._path, function (node) { return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(node.resolvables, resolvable); }); + }; + /** + * Gets the async dependencies of a Resolvable + * + * Given a Resolvable, returns its dependencies as a Resolvable[] + */ + ResolveContext.prototype.getDependencies = function (resolvable) { + var _this = this; + var node = this.findNode(resolvable); + // Find which other resolvables are "visible" to the `resolvable` argument + // subpath stopping at resolvable's node, or the whole path (if the resolvable isn't in the path) + var subPath = _path_pathUtils__WEBPACK_IMPORTED_MODULE_6__.PathUtils.subPath(this._path, function (x) { return x === node; }) || this._path; + var availableResolvables = subPath + .reduce(function (acc, _node) { return acc.concat(_node.resolvables); }, []) // all of subpath's resolvables + .filter(function (res) { return res !== resolvable; }); // filter out the `resolvable` argument + var getDependency = function (token) { + var matching = availableResolvables.filter(function (r) { return r.token === token; }); + if (matching.length) + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.tail)(matching); + var fromInjector = _this.injector().getNative(token); + if ((0,_common__WEBPACK_IMPORTED_MODULE_8__.isUndefined)(fromInjector)) { + throw new Error('Could not find Dependency Injection token: ' + (0,_common_strings__WEBPACK_IMPORTED_MODULE_7__.stringify)(token)); + } + return new _resolvable__WEBPACK_IMPORTED_MODULE_5__.Resolvable(token, function () { return fromInjector; }, [], fromInjector); + }; + return resolvable.deps.map(getDependency); + }; + return ResolveContext; +}()); + +/** @internal */ +var UIInjectorImpl = /** @class */ (function () { + function UIInjectorImpl(context) { + this.context = context; + this.native = this.get(NATIVE_INJECTOR_TOKEN) || _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$injector; + } + UIInjectorImpl.prototype.get = function (token) { + var resolvable = this.context.getResolvable(token); + if (resolvable) { + if (this.context.getPolicy(resolvable).async === 'NOWAIT') { + return resolvable.get(this.context); + } + if (!resolvable.resolved) { + throw new Error('Resolvable async .get() not complete:' + (0,_common_strings__WEBPACK_IMPORTED_MODULE_7__.stringify)(resolvable.token)); + } + return resolvable.data; + } + return this.getNative(token); + }; + UIInjectorImpl.prototype.getAsync = function (token) { + var resolvable = this.context.getResolvable(token); + if (resolvable) + return resolvable.get(this.context); + return _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$q.when(this.native.get(token)); + }; + UIInjectorImpl.prototype.getNative = function (token) { + return this.native && this.native.get(token); + }; + return UIInjectorImpl; +}()); +//# sourceMappingURL=resolveContext.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/router.js": +/*!*******************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/router.js ***! + \*******************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UIRouter": () => (/* binding */ UIRouter) +/* harmony export */ }); +/* harmony import */ var _url_urlMatcherFactory__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./url/urlMatcherFactory */ "./node_modules/@uirouter/core/lib-esm/url/urlMatcherFactory.js"); +/* harmony import */ var _url_urlRouter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./url/urlRouter */ "./node_modules/@uirouter/core/lib-esm/url/urlRouter.js"); +/* harmony import */ var _transition_transitionService__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./transition/transitionService */ "./node_modules/@uirouter/core/lib-esm/transition/transitionService.js"); +/* harmony import */ var _view_view__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./view/view */ "./node_modules/@uirouter/core/lib-esm/view/view.js"); +/* harmony import */ var _state_stateRegistry__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./state/stateRegistry */ "./node_modules/@uirouter/core/lib-esm/state/stateRegistry.js"); +/* harmony import */ var _state_stateService__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./state/stateService */ "./node_modules/@uirouter/core/lib-esm/state/stateService.js"); +/* harmony import */ var _globals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./globals */ "./node_modules/@uirouter/core/lib-esm/globals.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _url_urlService__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./url/urlService */ "./node_modules/@uirouter/core/lib-esm/url/urlService.js"); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + + + + + + + + + + + +/** @internal */ +var _routerInstance = 0; +/** @internal */ +var locSvcFns = ['url', 'path', 'search', 'hash', 'onChange']; +/** @internal */ +var locCfgFns = ['port', 'protocol', 'host', 'baseHref', 'html5Mode', 'hashPrefix']; +/** @internal */ +var locationServiceStub = (0,_common__WEBPACK_IMPORTED_MODULE_11__.makeStub)('LocationServices', locSvcFns); +/** @internal */ +var locationConfigStub = (0,_common__WEBPACK_IMPORTED_MODULE_11__.makeStub)('LocationConfig', locCfgFns); +/** + * An instance of UI-Router. + * + * This object contains references to service APIs which define your application's routing behavior. + */ +var UIRouter = /** @class */ (function () { + /** + * Creates a new `UIRouter` object + * + * @param locationService a [[LocationServices]] implementation + * @param locationConfig a [[LocationConfig]] implementation + * @internal + */ + function UIRouter(locationService, locationConfig) { + if (locationService === void 0) { locationService = locationServiceStub; } + if (locationConfig === void 0) { locationConfig = locationConfigStub; } + this.locationService = locationService; + this.locationConfig = locationConfig; + /** @internal */ this.$id = _routerInstance++; + /** @internal */ this._disposed = false; + /** @internal */ this._disposables = []; + /** Enable/disable tracing to the javascript console */ + this.trace = _common_trace__WEBPACK_IMPORTED_MODULE_10__.trace; + /** Provides services related to ui-view synchronization */ + this.viewService = new _view_view__WEBPACK_IMPORTED_MODULE_3__.ViewService(this); + /** An object that contains global router state, such as the current state and params */ + this.globals = new _globals__WEBPACK_IMPORTED_MODULE_6__.UIRouterGlobals(); + /** A service that exposes global Transition Hooks */ + this.transitionService = new _transition_transitionService__WEBPACK_IMPORTED_MODULE_2__.TransitionService(this); + /** + * Deprecated for public use. Use [[urlService]] instead. + * @deprecated Use [[urlService]] instead + */ + this.urlMatcherFactory = new _url_urlMatcherFactory__WEBPACK_IMPORTED_MODULE_0__.UrlMatcherFactory(this); + /** + * Deprecated for public use. Use [[urlService]] instead. + * @deprecated Use [[urlService]] instead + */ + this.urlRouter = new _url_urlRouter__WEBPACK_IMPORTED_MODULE_1__.UrlRouter(this); + /** Provides services related to the URL */ + this.urlService = new _url_urlService__WEBPACK_IMPORTED_MODULE_9__.UrlService(this); + /** Provides a registry for states, and related registration services */ + this.stateRegistry = new _state_stateRegistry__WEBPACK_IMPORTED_MODULE_4__.StateRegistry(this); + /** Provides services related to states */ + this.stateService = new _state_stateService__WEBPACK_IMPORTED_MODULE_5__.StateService(this); + /** @internal plugin instances are registered here */ + this._plugins = {}; + this.viewService._pluginapi._rootViewContext(this.stateRegistry.root()); + this.globals.$current = this.stateRegistry.root(); + this.globals.current = this.globals.$current.self; + this.disposable(this.globals); + this.disposable(this.stateService); + this.disposable(this.stateRegistry); + this.disposable(this.transitionService); + this.disposable(this.urlService); + this.disposable(locationService); + this.disposable(locationConfig); + } + /** Registers an object to be notified when the router is disposed */ + UIRouter.prototype.disposable = function (disposable) { + this._disposables.push(disposable); + }; + /** + * Disposes this router instance + * + * When called, clears resources retained by the router by calling `dispose(this)` on all + * registered [[disposable]] objects. + * + * Or, if a `disposable` object is provided, calls `dispose(this)` on that object only. + * + * @internal + * @param disposable (optional) the disposable to dispose + */ + UIRouter.prototype.dispose = function (disposable) { + var _this = this; + if (disposable && (0,_common_predicates__WEBPACK_IMPORTED_MODULE_8__.isFunction)(disposable.dispose)) { + disposable.dispose(this); + return undefined; + } + this._disposed = true; + this._disposables.slice().forEach(function (d) { + try { + typeof d.dispose === 'function' && d.dispose(_this); + (0,_common_common__WEBPACK_IMPORTED_MODULE_7__.removeFrom)(_this._disposables, d); + } + catch (ignored) { } + }); + }; + /** + * Adds a plugin to UI-Router + * + * This method adds a UI-Router Plugin. + * A plugin can enhance or change UI-Router behavior using any public API. + * + * #### Example: + * ```js + * import { MyCoolPlugin } from "ui-router-cool-plugin"; + * + * var plugin = router.addPlugin(MyCoolPlugin); + * ``` + * + * ### Plugin authoring + * + * A plugin is simply a class (or constructor function) which accepts a [[UIRouter]] instance and (optionally) an options object. + * + * The plugin can implement its functionality using any of the public APIs of [[UIRouter]]. + * For example, it may configure router options or add a Transition Hook. + * + * The plugin can then be published as a separate module. + * + * #### Example: + * ```js + * export class MyAuthPlugin implements UIRouterPlugin { + * constructor(router: UIRouter, options: any) { + * this.name = "MyAuthPlugin"; + * let $transitions = router.transitionService; + * let $state = router.stateService; + * + * let authCriteria = { + * to: (state) => state.data && state.data.requiresAuth + * }; + * + * function authHook(transition: Transition) { + * let authService = transition.injector().get('AuthService'); + * if (!authService.isAuthenticated()) { + * return $state.target('login'); + * } + * } + * + * $transitions.onStart(authCriteria, authHook); + * } + * } + * ``` + * + * @param plugin one of: + * - a plugin class which implements [[UIRouterPlugin]] + * - a constructor function for a [[UIRouterPlugin]] which accepts a [[UIRouter]] instance + * - a factory function which accepts a [[UIRouter]] instance and returns a [[UIRouterPlugin]] instance + * @param options options to pass to the plugin class/factory + * @returns the registered plugin instance + */ + UIRouter.prototype.plugin = function (plugin, options) { + if (options === void 0) { options = {}; } + var pluginInstance = new plugin(this, options); + if (!pluginInstance.name) + throw new Error('Required property `name` missing on plugin: ' + pluginInstance); + this._disposables.push(pluginInstance); + return (this._plugins[pluginInstance.name] = pluginInstance); + }; + UIRouter.prototype.getPlugin = function (pluginName) { + return pluginName ? this._plugins[pluginName] : (0,_common_common__WEBPACK_IMPORTED_MODULE_7__.values)(this._plugins); + }; + return UIRouter; +}()); + +//# sourceMappingURL=router.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/index.js": +/*!************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/index.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateBuilder": () => (/* reexport safe */ _stateBuilder__WEBPACK_IMPORTED_MODULE_1__.StateBuilder), +/* harmony export */ "resolvablesBuilder": () => (/* reexport safe */ _stateBuilder__WEBPACK_IMPORTED_MODULE_1__.resolvablesBuilder), +/* harmony export */ "StateObject": () => (/* reexport safe */ _stateObject__WEBPACK_IMPORTED_MODULE_2__.StateObject), +/* harmony export */ "StateMatcher": () => (/* reexport safe */ _stateMatcher__WEBPACK_IMPORTED_MODULE_3__.StateMatcher), +/* harmony export */ "StateQueueManager": () => (/* reexport safe */ _stateQueueManager__WEBPACK_IMPORTED_MODULE_4__.StateQueueManager), +/* harmony export */ "StateRegistry": () => (/* reexport safe */ _stateRegistry__WEBPACK_IMPORTED_MODULE_5__.StateRegistry), +/* harmony export */ "StateService": () => (/* reexport safe */ _stateService__WEBPACK_IMPORTED_MODULE_6__.StateService), +/* harmony export */ "TargetState": () => (/* reexport safe */ _targetState__WEBPACK_IMPORTED_MODULE_7__.TargetState) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/state/interface.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_interface__WEBPACK_IMPORTED_MODULE_0__); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _interface__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== "default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _interface__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _stateBuilder__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./stateBuilder */ "./node_modules/@uirouter/core/lib-esm/state/stateBuilder.js"); +/* harmony import */ var _stateObject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stateObject */ "./node_modules/@uirouter/core/lib-esm/state/stateObject.js"); +/* harmony import */ var _stateMatcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stateMatcher */ "./node_modules/@uirouter/core/lib-esm/state/stateMatcher.js"); +/* harmony import */ var _stateQueueManager__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./stateQueueManager */ "./node_modules/@uirouter/core/lib-esm/state/stateQueueManager.js"); +/* harmony import */ var _stateRegistry__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./stateRegistry */ "./node_modules/@uirouter/core/lib-esm/state/stateRegistry.js"); +/* harmony import */ var _stateService__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./stateService */ "./node_modules/@uirouter/core/lib-esm/state/stateService.js"); +/* harmony import */ var _targetState__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./targetState */ "./node_modules/@uirouter/core/lib-esm/state/targetState.js"); +/** + * # The state subsystem + * + * This subsystem implements the ui-router state tree + * + * - The [[StateService]] has state-related service methods such as: + * - [[StateService.get]]: Get a registered [[StateDeclaration]] object + * - [[StateService.go]]: Transition from the current state to a new state + * - [[StateService.reload]]: Reload the current state + * - [[StateService.target]]: Get a [[TargetState]] (useful when redirecting from a Transition Hook) + * - [[StateService.onInvalid]]: Register a callback for when a transition to an invalid state is started + * - [[StateService.defaultErrorHandler]]: Register a global callback for when a transition errors + * - The [[StateDeclaration]] interface defines the shape of a state declaration + * - The [[StateRegistry]] contains all the registered states + * - States can be added/removed using the [[StateRegistry.register]] and [[StateRegistry.deregister]] + * - Note: Bootstrap state registration differs by front-end framework. + * - Get notified of state registration/deregistration using [[StateRegistry.onStatesChanged]]. + * + * @packageDocumentation + */ + + + + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/interface.js": +/*!****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/interface.js ***! + \****************************************************************/ +/***/ (() => { + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/stateBuilder.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/stateBuilder.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "resolvablesBuilder": () => (/* binding */ resolvablesBuilder), +/* harmony export */ "StateBuilder": () => (/* binding */ StateBuilder) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../resolve/resolvable */ "./node_modules/@uirouter/core/lib-esm/resolve/resolvable.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); + + + + + + +var parseUrl = function (url) { + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(url)) + return false; + var root = url.charAt(0) === '^'; + return { val: root ? url.substring(1) : url, root: root }; +}; +function nameBuilder(state) { + return state.name; +} +function selfBuilder(state) { + state.self.$$state = function () { return state; }; + return state.self; +} +function dataBuilder(state) { + if (state.parent && state.parent.data) { + state.data = state.self.data = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inherit)(state.parent.data, state.data); + } + return state.data; +} +var getUrlBuilder = function ($urlMatcherFactoryProvider, root) { + return function urlBuilder(stateObject) { + var stateDec = stateObject.self; + // For future states, i.e., states whose name ends with `.**`, + // match anything that starts with the url prefix + if (stateDec && stateDec.url && stateDec.name && stateDec.name.match(/\.\*\*$/)) { + var newStateDec = {}; + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.copy)(stateDec, newStateDec); + newStateDec.url += '{remainder:any}'; // match any path (.*) + stateDec = newStateDec; + } + var parent = stateObject.parent; + var parsed = parseUrl(stateDec.url); + var url = !parsed ? stateDec.url : $urlMatcherFactoryProvider.compile(parsed.val, { state: stateDec }); + if (!url) + return null; + if (!$urlMatcherFactoryProvider.isMatcher(url)) + throw new Error("Invalid url '" + url + "' in state '" + stateObject + "'"); + return parsed && parsed.root ? url : ((parent && parent.navigable) || root()).url.append(url); + }; +}; +var getNavigableBuilder = function (isRoot) { + return function navigableBuilder(state) { + return !isRoot(state) && state.url ? state : state.parent ? state.parent.navigable : null; + }; +}; +var getParamsBuilder = function (paramFactory) { + return function paramsBuilder(state) { + var makeConfigParam = function (config, id) { return paramFactory.fromConfig(id, null, state.self); }; + var urlParams = (state.url && state.url.parameters({ inherit: false })) || []; + var nonUrlParams = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.values)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.mapObj)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.omit)(state.params || {}, urlParams.map((0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('id'))), makeConfigParam)); + return urlParams + .concat(nonUrlParams) + .map(function (p) { return [p.id, p]; }) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.applyPairs, {}); + }; +}; +function pathBuilder(state) { + return state.parent ? state.parent.path.concat(state) : /*root*/ [state]; +} +function includesBuilder(state) { + var includes = state.parent ? (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({}, state.parent.includes) : {}; + includes[state.name] = true; + return includes; +} +/** + * This is a [[StateBuilder.builder]] function for the `resolve:` block on a [[StateDeclaration]]. + * + * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder + * validates the `resolve` property and converts it to a [[Resolvable]] array. + * + * resolve: input value can be: + * + * { + * // analyzed but not injected + * myFooResolve: function() { return "myFooData"; }, + * + * // function.toString() parsed, "DependencyName" dep as string (not min-safe) + * myBarResolve: function(DependencyName) { return DependencyName.fetchSomethingAsPromise() }, + * + * // Array split; "DependencyName" dep as string + * myBazResolve: [ "DependencyName", function(dep) { return dep.fetchSomethingAsPromise() }, + * + * // Array split; DependencyType dep as token (compared using ===) + * myQuxResolve: [ DependencyType, function(dep) { return dep.fetchSometingAsPromise() }, + * + * // val.$inject used as deps + * // where: + * // corgeResolve.$inject = ["DependencyName"]; + * // function corgeResolve(dep) { dep.fetchSometingAsPromise() } + * // then "DependencyName" dep as string + * myCorgeResolve: corgeResolve, + * + * // inject service by name + * // When a string is found, desugar creating a resolve that injects the named service + * myGraultResolve: "SomeService" + * } + * + * or: + * + * [ + * new Resolvable("myFooResolve", function() { return "myFooData" }), + * new Resolvable("myBarResolve", function(dep) { return dep.fetchSomethingAsPromise() }, [ "DependencyName" ]), + * { provide: "myBazResolve", useFactory: function(dep) { dep.fetchSomethingAsPromise() }, deps: [ "DependencyName" ] } + * ] + */ +function resolvablesBuilder(state) { + /** convert resolve: {} and resolvePolicy: {} objects to an array of tuples */ + var objects2Tuples = function (resolveObj, resolvePolicies) { + return Object.keys(resolveObj || {}).map(function (token) { return ({ + token: token, + val: resolveObj[token], + deps: undefined, + policy: resolvePolicies[token], + }); }); + }; + /** fetch DI annotations from a function or ng1-style array */ + var annotate = function (fn) { + var $injector = _common_coreservices__WEBPACK_IMPORTED_MODULE_5__.services.$injector; + // ng1 doesn't have an $injector until runtime. + // If the $injector doesn't exist, use "deferred" literal as a + // marker indicating they should be annotated when runtime starts + return fn['$inject'] || ($injector && $injector.annotate(fn, $injector.strictDi)) || 'deferred'; + }; + /** true if the object has both `token` and `resolveFn`, and is probably a [[ResolveLiteral]] */ + var isResolveLiteral = function (obj) { return !!(obj.token && obj.resolveFn); }; + /** true if the object looks like a provide literal, or a ng2 Provider */ + var isLikeNg2Provider = function (obj) { + return !!((obj.provide || obj.token) && (obj.useValue || obj.useFactory || obj.useExisting || obj.useClass)); + }; + /** true if the object looks like a tuple from obj2Tuples */ + var isTupleFromObj = function (obj) { + return !!(obj && obj.val && ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(obj.val) || (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isArray)(obj.val) || (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isFunction)(obj.val))); + }; + /** extracts the token from a Provider or provide literal */ + var getToken = function (p) { return p.provide || p.token; }; + // prettier-ignore: Given a literal resolve or provider object, returns a Resolvable + var literal2Resolvable = (0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pattern)([ + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('resolveFn'), function (p) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(getToken(p), p.resolveFn, p.deps, p.policy); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('useFactory'), function (p) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(getToken(p), p.useFactory, p.deps || p.dependencies, p.policy); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('useClass'), function (p) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(getToken(p), function () { return new p.useClass(); }, [], p.policy); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('useValue'), function (p) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(getToken(p), function () { return p.useValue; }, [], p.policy, p.useValue); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('useExisting'), function (p) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(getToken(p), _common_common__WEBPACK_IMPORTED_MODULE_0__.identity, [p.useExisting], p.policy); }], + ]); + // prettier-ignore + var tuple2Resolvable = (0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pattern)([ + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pipe)((0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('val'), _common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString), function (tuple) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(tuple.token, _common_common__WEBPACK_IMPORTED_MODULE_0__.identity, [tuple.val], tuple.policy); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pipe)((0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('val'), _common_predicates__WEBPACK_IMPORTED_MODULE_1__.isArray), function (tuple) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(tuple.token, (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.tail)(tuple.val), tuple.val.slice(0, -1), tuple.policy); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pipe)((0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.prop)('val'), _common_predicates__WEBPACK_IMPORTED_MODULE_1__.isFunction), function (tuple) { return new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable(tuple.token, tuple.val, annotate(tuple.val), tuple.policy); }], + ]); + // prettier-ignore + var item2Resolvable = (0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pattern)([ + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.is)(_resolve_resolvable__WEBPACK_IMPORTED_MODULE_4__.Resolvable), function (r) { return r; }], + [isResolveLiteral, literal2Resolvable], + [isLikeNg2Provider, literal2Resolvable], + [isTupleFromObj, tuple2Resolvable], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.val)(true), function (obj) { throw new Error('Invalid resolve value: ' + (0,_common_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(obj)); },], + ]); + // If resolveBlock is already an array, use it as-is. + // Otherwise, assume it's an object and convert to an Array of tuples + var decl = state.resolve; + var items = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isArray)(decl) ? decl : objects2Tuples(decl, state.resolvePolicy || {}); + return items.map(item2Resolvable); +} +/** + * A internal global service + * + * StateBuilder is a factory for the internal [[StateObject]] objects. + * + * When you register a state with the [[StateRegistry]], you register a plain old javascript object which + * conforms to the [[StateDeclaration]] interface. This factory takes that object and builds the corresponding + * [[StateObject]] object, which has an API and is used internally. + * + * Custom properties or API may be added to the internal [[StateObject]] object by registering a decorator function + * using the [[builder]] method. + */ +var StateBuilder = /** @class */ (function () { + function StateBuilder(matcher, urlMatcherFactory) { + this.matcher = matcher; + var self = this; + var root = function () { return matcher.find(''); }; + var isRoot = function (state) { return state.name === ''; }; + function parentBuilder(state) { + if (isRoot(state)) + return null; + return matcher.find(self.parentName(state)) || root(); + } + this.builders = { + name: [nameBuilder], + self: [selfBuilder], + parent: [parentBuilder], + data: [dataBuilder], + // Build a URLMatcher if necessary, either via a relative or absolute URL + url: [getUrlBuilder(urlMatcherFactory, root)], + // Keep track of the closest ancestor state that has a URL (i.e. is navigable) + navigable: [getNavigableBuilder(isRoot)], + params: [getParamsBuilder(urlMatcherFactory.paramFactory)], + // Each framework-specific ui-router implementation should define its own `views` builder + // e.g., src/ng1/statebuilders/views.ts + views: [], + // Keep a full path from the root down to this state as this is needed for state activation. + path: [pathBuilder], + // Speed up $state.includes() as it's used a lot + includes: [includesBuilder], + resolvables: [resolvablesBuilder], + }; + } + StateBuilder.prototype.builder = function (name, fn) { + var builders = this.builders; + var array = builders[name] || []; + // Backwards compat: if only one builder exists, return it, else return whole arary. + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(name) && !(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(fn)) + return array.length > 1 ? array : array[0]; + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(name) || !(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isFunction)(fn)) + return; + builders[name] = array; + builders[name].push(fn); + return function () { return builders[name].splice(builders[name].indexOf(fn, 1)) && null; }; + }; + /** + * Builds all of the properties on an essentially blank State object, returning a State object which has all its + * properties and API built. + * + * @param state an uninitialized State object + * @returns the built State object + */ + StateBuilder.prototype.build = function (state) { + var _a = this, matcher = _a.matcher, builders = _a.builders; + var parent = this.parentName(state); + if (parent && !matcher.find(parent, undefined, false)) { + return null; + } + for (var key in builders) { + if (!builders.hasOwnProperty(key)) + continue; + var chain = builders[key].reduce(function (parentFn, step) { return function (_state) { return step(_state, parentFn); }; }, _common_common__WEBPACK_IMPORTED_MODULE_0__.noop); + state[key] = chain(state); + } + return state; + }; + StateBuilder.prototype.parentName = function (state) { + // name = 'foo.bar.baz.**' + var name = state.name || ''; + // segments = ['foo', 'bar', 'baz', '.**'] + var segments = name.split('.'); + // segments = ['foo', 'bar', 'baz'] + var lastSegment = segments.pop(); + // segments = ['foo', 'bar'] (ignore .** segment for future states) + if (lastSegment === '**') + segments.pop(); + if (segments.length) { + if (state.parent) { + throw new Error("States that specify the 'parent:' property should not have a '.' in their name (" + name + ")"); + } + // 'foo.bar' + return segments.join('.'); + } + if (!state.parent) + return ''; + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(state.parent) ? state.parent : state.parent.name; + }; + StateBuilder.prototype.name = function (state) { + var name = state.name; + if (name.indexOf('.') !== -1 || !state.parent) + return name; + var parentName = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(state.parent) ? state.parent : state.parent.name; + return parentName ? parentName + '.' + name : name; + }; + return StateBuilder; +}()); + +//# sourceMappingURL=stateBuilder.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/stateMatcher.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/stateMatcher.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateMatcher": () => (/* binding */ StateMatcher) +/* harmony export */ }); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_safeConsole__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/safeConsole */ "./node_modules/@uirouter/core/lib-esm/common/safeConsole.js"); + + + +var StateMatcher = /** @class */ (function () { + function StateMatcher(_states) { + this._states = _states; + } + StateMatcher.prototype.isRelative = function (stateName) { + stateName = stateName || ''; + return stateName.indexOf('.') === 0 || stateName.indexOf('^') === 0; + }; + StateMatcher.prototype.find = function (stateOrName, base, matchGlob) { + if (matchGlob === void 0) { matchGlob = true; } + if (!stateOrName && stateOrName !== '') + return undefined; + var isStr = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)(stateOrName); + var name = isStr ? stateOrName : stateOrName.name; + if (this.isRelative(name)) + name = this.resolvePath(name, base); + var state = this._states[name]; + if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) { + return state; + } + else if (isStr && matchGlob) { + var _states = (0,_common_common__WEBPACK_IMPORTED_MODULE_1__.values)(this._states); + var matches = _states.filter(function (_state) { return _state.__stateObjectCache.nameGlob && _state.__stateObjectCache.nameGlob.matches(name); }); + if (matches.length > 1) { + _common_safeConsole__WEBPACK_IMPORTED_MODULE_2__.safeConsole.error("stateMatcher.find: Found multiple matches for " + name + " using glob: ", matches.map(function (match) { return match.name; })); + } + return matches[0]; + } + return undefined; + }; + StateMatcher.prototype.resolvePath = function (name, base) { + if (!base) + throw new Error("No reference point given for path '" + name + "'"); + var baseState = this.find(base); + var splitName = name.split('.'); + var pathLength = splitName.length; + var i = 0, current = baseState; + for (; i < pathLength; i++) { + if (splitName[i] === '' && i === 0) { + current = baseState; + continue; + } + if (splitName[i] === '^') { + if (!current.parent) + throw new Error("Path '" + name + "' not valid for state '" + baseState.name + "'"); + current = current.parent; + continue; + } + break; + } + var relName = splitName.slice(i).join('.'); + return current.name + (current.name && relName ? '.' : '') + relName; + }; + return StateMatcher; +}()); + +//# sourceMappingURL=stateMatcher.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/stateObject.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/stateObject.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateObject": () => (/* binding */ StateObject) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_glob__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/glob */ "./node_modules/@uirouter/core/lib-esm/common/glob.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); + + + + +/** + * Internal representation of a UI-Router state. + * + * Instances of this class are created when a [[StateDeclaration]] is registered with the [[StateRegistry]]. + * + * A registered [[StateDeclaration]] is augmented with a getter ([[StateDeclaration.$$state]]) which returns the corresponding [[StateObject]] object. + * + * This class prototypally inherits from the corresponding [[StateDeclaration]]. + * Each of its own properties (i.e., `hasOwnProperty`) are built using builders from the [[StateBuilder]]. + */ +var StateObject = /** @class */ (function () { + /** @deprecated use State.create() */ + function StateObject(config) { + return StateObject.create(config || {}); + } + /** + * Create a state object to put the private/internal implementation details onto. + * The object's prototype chain looks like: + * (Internal State Object) -> (Copy of State.prototype) -> (State Declaration object) -> (State Declaration's prototype...) + * + * @param stateDecl the user-supplied State Declaration + * @returns {StateObject} an internal State object + */ + StateObject.create = function (stateDecl) { + stateDecl = StateObject.isStateClass(stateDecl) ? new stateDecl() : stateDecl; + var state = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inherit)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inherit)(stateDecl, StateObject.prototype)); + stateDecl.$$state = function () { return state; }; + state.self = stateDecl; + state.__stateObjectCache = { + nameGlob: _common_glob__WEBPACK_IMPORTED_MODULE_2__.Glob.fromString(state.name), + }; + return state; + }; + /** + * Returns true if the provided parameter is the same state. + * + * Compares the identity of the state against the passed value, which is either an object + * reference to the actual `State` instance, the original definition object passed to + * `$stateProvider.state()`, or the fully-qualified name. + * + * @param ref Can be one of (a) a `State` instance, (b) an object that was passed + * into `$stateProvider.state()`, (c) the fully-qualified name of a state as a string. + * @returns Returns `true` if `ref` matches the current `State` instance. + */ + StateObject.prototype.is = function (ref) { + return this === ref || this.self === ref || this.fqn() === ref; + }; + /** + * @deprecated this does not properly handle dot notation + * @returns Returns a dot-separated name of the state. + */ + StateObject.prototype.fqn = function () { + if (!this.parent || !(this.parent instanceof this.constructor)) + return this.name; + var name = this.parent.fqn(); + return name ? name + '.' + this.name : this.name; + }; + /** + * Returns the root node of this state's tree. + * + * @returns The root of this state's tree. + */ + StateObject.prototype.root = function () { + return (this.parent && this.parent.root()) || this; + }; + /** + * Gets the state's `Param` objects + * + * Gets the list of [[Param]] objects owned by the state. + * If `opts.inherit` is true, it also includes the ancestor states' [[Param]] objects. + * If `opts.matchingKeys` exists, returns only `Param`s whose `id` is a key on the `matchingKeys` object + * + * @param opts options + */ + StateObject.prototype.parameters = function (opts) { + opts = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.defaults)(opts, { inherit: true, matchingKeys: null }); + var inherited = (opts.inherit && this.parent && this.parent.parameters()) || []; + return inherited + .concat((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.values)(this.params)) + .filter(function (param) { return !opts.matchingKeys || opts.matchingKeys.hasOwnProperty(param.id); }); + }; + /** + * Returns a single [[Param]] that is owned by the state + * + * If `opts.inherit` is true, it also searches the ancestor states` [[Param]]s. + * @param id the name of the [[Param]] to return + * @param opts options + */ + StateObject.prototype.parameter = function (id, opts) { + if (opts === void 0) { opts = {}; } + return ((this.url && this.url.parameter(id, opts)) || + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.values)(this.params), (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.propEq)('id', id)) || + (opts.inherit && this.parent && this.parent.parameter(id))); + }; + StateObject.prototype.toString = function () { + return this.fqn(); + }; + /** Predicate which returns true if the object is an class with @State() decorator */ + StateObject.isStateClass = function (stateDecl) { + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isFunction)(stateDecl) && stateDecl['__uiRouterState'] === true; + }; + /** Predicate which returns true if the object is a [[StateDeclaration]] object */ + StateObject.isStateDeclaration = function (obj) { return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isFunction)(obj['$$state']); }; + /** Predicate which returns true if the object is an internal [[StateObject]] object */ + StateObject.isState = function (obj) { return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isObject)(obj['__stateObjectCache']); }; + return StateObject; +}()); + +//# sourceMappingURL=stateObject.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/stateQueueManager.js": +/*!************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/stateQueueManager.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateQueueManager": () => (/* binding */ StateQueueManager) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _stateObject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./stateObject */ "./node_modules/@uirouter/core/lib-esm/state/stateObject.js"); + + +var StateQueueManager = /** @class */ (function () { + function StateQueueManager(router, states, builder, listeners) { + this.router = router; + this.states = states; + this.builder = builder; + this.listeners = listeners; + this.queue = []; + } + StateQueueManager.prototype.dispose = function () { + this.queue = []; + }; + StateQueueManager.prototype.register = function (stateDecl) { + var queue = this.queue; + var state = _stateObject__WEBPACK_IMPORTED_MODULE_1__.StateObject.create(stateDecl); + var name = state.name; + if (!(0,_common__WEBPACK_IMPORTED_MODULE_0__.isString)(name)) + throw new Error('State must have a valid name'); + if (this.states.hasOwnProperty(name) || (0,_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(queue.map((0,_common__WEBPACK_IMPORTED_MODULE_0__.prop)('name')), name)) + throw new Error("State '" + name + "' is already defined"); + queue.push(state); + this.flush(); + return state; + }; + StateQueueManager.prototype.flush = function () { + var _this = this; + var _a = this, queue = _a.queue, states = _a.states, builder = _a.builder; + var registered = [], // states that got registered + orphans = [], // states that don't yet have a parent registered + previousQueueLength = {}; // keep track of how long the queue when an orphan was first encountered + var getState = function (name) { return _this.states.hasOwnProperty(name) && _this.states[name]; }; + var notifyListeners = function () { + if (registered.length) { + _this.listeners.forEach(function (listener) { + return listener('registered', registered.map(function (s) { return s.self; })); + }); + } + }; + while (queue.length > 0) { + var state = queue.shift(); + var name_1 = state.name; + var result = builder.build(state); + var orphanIdx = orphans.indexOf(state); + if (result) { + var existingState = getState(name_1); + if (existingState && existingState.name === name_1) { + throw new Error("State '" + name_1 + "' is already defined"); + } + var existingFutureState = getState(name_1 + '.**'); + if (existingFutureState) { + // Remove future state of the same name + this.router.stateRegistry.deregister(existingFutureState); + } + states[name_1] = state; + this.attachRoute(state); + if (orphanIdx >= 0) + orphans.splice(orphanIdx, 1); + registered.push(state); + continue; + } + var prev = previousQueueLength[name_1]; + previousQueueLength[name_1] = queue.length; + if (orphanIdx >= 0 && prev === queue.length) { + // Wait until two consecutive iterations where no additional states were dequeued successfully. + // throw new Error(`Cannot register orphaned state '${name}'`); + queue.push(state); + notifyListeners(); + return states; + } + else if (orphanIdx < 0) { + orphans.push(state); + } + queue.push(state); + } + notifyListeners(); + return states; + }; + StateQueueManager.prototype.attachRoute = function (state) { + if (state.abstract || !state.url) + return; + var rulesApi = this.router.urlService.rules; + rulesApi.rule(rulesApi.urlRuleFactory.create(state)); + }; + return StateQueueManager; +}()); + +//# sourceMappingURL=stateQueueManager.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/stateRegistry.js": +/*!********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/stateRegistry.js ***! + \********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateRegistry": () => (/* binding */ StateRegistry) +/* harmony export */ }); +/* harmony import */ var _stateMatcher__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./stateMatcher */ "./node_modules/@uirouter/core/lib-esm/state/stateMatcher.js"); +/* harmony import */ var _stateBuilder__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./stateBuilder */ "./node_modules/@uirouter/core/lib-esm/state/stateBuilder.js"); +/* harmony import */ var _stateQueueManager__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stateQueueManager */ "./node_modules/@uirouter/core/lib-esm/state/stateQueueManager.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); + + + + + +/** + * A registry for all of the application's [[StateDeclaration]]s + * + * This API is found at `router.stateRegistry` ([[UIRouter.stateRegistry]]) + */ +var StateRegistry = /** @class */ (function () { + /** @internal */ + function StateRegistry(router) { + this.router = router; + this.states = {}; + /** @internal */ + this.listeners = []; + this.matcher = new _stateMatcher__WEBPACK_IMPORTED_MODULE_0__.StateMatcher(this.states); + this.builder = new _stateBuilder__WEBPACK_IMPORTED_MODULE_1__.StateBuilder(this.matcher, router.urlMatcherFactory); + this.stateQueue = new _stateQueueManager__WEBPACK_IMPORTED_MODULE_2__.StateQueueManager(router, this.states, this.builder, this.listeners); + this._registerRoot(); + } + /** @internal */ + StateRegistry.prototype._registerRoot = function () { + var rootStateDef = { + name: '', + url: '^', + views: null, + params: { + '#': { value: null, type: 'hash', dynamic: true }, + }, + abstract: true, + }; + var _root = (this._root = this.stateQueue.register(rootStateDef)); + _root.navigable = null; + }; + /** @internal */ + StateRegistry.prototype.dispose = function () { + var _this = this; + this.stateQueue.dispose(); + this.listeners = []; + this.get().forEach(function (state) { return _this.get(state) && _this.deregister(state); }); + }; + /** + * Listen for a State Registry events + * + * Adds a callback that is invoked when states are registered or deregistered with the StateRegistry. + * + * #### Example: + * ```js + * let allStates = registry.get(); + * + * // Later, invoke deregisterFn() to remove the listener + * let deregisterFn = registry.onStatesChanged((event, states) => { + * switch(event) { + * case: 'registered': + * states.forEach(state => allStates.push(state)); + * break; + * case: 'deregistered': + * states.forEach(state => { + * let idx = allStates.indexOf(state); + * if (idx !== -1) allStates.splice(idx, 1); + * }); + * break; + * } + * }); + * ``` + * + * @param listener a callback function invoked when the registered states changes. + * The function receives two parameters, `event` and `state`. + * See [[StateRegistryListener]] + * @return a function that deregisters the listener + */ + StateRegistry.prototype.onStatesChanged = function (listener) { + this.listeners.push(listener); + return function deregisterListener() { + (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.removeFrom)(this.listeners)(listener); + }.bind(this); + }; + /** + * Gets the implicit root state + * + * Gets the root of the state tree. + * The root state is implicitly created by UI-Router. + * Note: this returns the internal [[StateObject]] representation, not a [[StateDeclaration]] + * + * @return the root [[StateObject]] + */ + StateRegistry.prototype.root = function () { + return this._root; + }; + /** + * Adds a state to the registry + * + * Registers a [[StateDeclaration]] or queues it for registration. + * + * Note: a state will be queued if the state's parent isn't yet registered. + * + * @param stateDefinition the definition of the state to register. + * @returns the internal [[StateObject]] object. + * If the state was successfully registered, then the object is fully built (See: [[StateBuilder]]). + * If the state was only queued, then the object is not fully built. + */ + StateRegistry.prototype.register = function (stateDefinition) { + return this.stateQueue.register(stateDefinition); + }; + /** @internal */ + StateRegistry.prototype._deregisterTree = function (state) { + var _this = this; + var all = this.get().map(function (s) { return s.$$state(); }); + var getChildren = function (states) { + var _children = all.filter(function (s) { return states.indexOf(s.parent) !== -1; }); + return _children.length === 0 ? _children : _children.concat(getChildren(_children)); + }; + var children = getChildren([state]); + var deregistered = [state].concat(children).reverse(); + deregistered.forEach(function (_state) { + var rulesApi = _this.router.urlService.rules; + // Remove URL rule + rulesApi + .rules() + .filter((0,_common_hof__WEBPACK_IMPORTED_MODULE_4__.propEq)('state', _state)) + .forEach(function (rule) { return rulesApi.removeRule(rule); }); + // Remove state from registry + delete _this.states[_state.name]; + }); + return deregistered; + }; + /** + * Removes a state from the registry + * + * This removes a state from the registry. + * If the state has children, they are are also removed from the registry. + * + * @param stateOrName the state's name or object representation + * @returns {StateObject[]} a list of removed states + */ + StateRegistry.prototype.deregister = function (stateOrName) { + var _state = this.get(stateOrName); + if (!_state) + throw new Error("Can't deregister state; not found: " + stateOrName); + var deregisteredStates = this._deregisterTree(_state.$$state()); + this.listeners.forEach(function (listener) { + return listener('deregistered', deregisteredStates.map(function (s) { return s.self; })); + }); + return deregisteredStates; + }; + StateRegistry.prototype.get = function (stateOrName, base) { + var _this = this; + if (arguments.length === 0) + return Object.keys(this.states).map(function (name) { return _this.states[name].self; }); + var found = this.matcher.find(stateOrName, base); + return (found && found.self) || null; + }; + /** + * Registers a [[BuilderFunction]] for a specific [[StateObject]] property (e.g., `parent`, `url`, or `path`). + * More than one BuilderFunction can be registered for a given property. + * + * The BuilderFunction(s) will be used to define the property on any subsequently built [[StateObject]] objects. + * + * @param property The name of the State property being registered for. + * @param builderFunction The BuilderFunction which will be used to build the State property + * @returns a function which deregisters the BuilderFunction + */ + StateRegistry.prototype.decorator = function (property, builderFunction) { + return this.builder.builder(property, builderFunction); + }; + return StateRegistry; +}()); + +//# sourceMappingURL=stateRegistry.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/stateService.js": +/*!*******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/stateService.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "StateService": () => (/* binding */ StateService) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_queue__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/queue */ "./node_modules/@uirouter/core/lib-esm/common/queue.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _path_pathUtils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../path/pathUtils */ "./node_modules/@uirouter/core/lib-esm/path/pathUtils.js"); +/* harmony import */ var _path_pathNode__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../path/pathNode */ "./node_modules/@uirouter/core/lib-esm/path/pathNode.js"); +/* harmony import */ var _transition_transitionService__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../transition/transitionService */ "./node_modules/@uirouter/core/lib-esm/transition/transitionService.js"); +/* harmony import */ var _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../transition/rejectFactory */ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js"); +/* harmony import */ var _targetState__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./targetState */ "./node_modules/@uirouter/core/lib-esm/state/targetState.js"); +/* harmony import */ var _params_param__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../params/param */ "./node_modules/@uirouter/core/lib-esm/params/param.js"); +/* harmony import */ var _common_glob__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../common/glob */ "./node_modules/@uirouter/core/lib-esm/common/glob.js"); +/* harmony import */ var _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../resolve/resolveContext */ "./node_modules/@uirouter/core/lib-esm/resolve/resolveContext.js"); +/* harmony import */ var _hooks_lazyLoad__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../hooks/lazyLoad */ "./node_modules/@uirouter/core/lib-esm/hooks/lazyLoad.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); + + + + + + + + + + + + + + +/** + * Provides services related to ui-router states. + * + * This API is located at `router.stateService` ([[UIRouter.stateService]]) + */ +var StateService = /** @class */ (function () { + /** @internal */ + function StateService(/** @internal */ router) { + this.router = router; + /** @internal */ + this.invalidCallbacks = []; + /** @internal */ + this._defaultErrorHandler = function $defaultErrorHandler($error$) { + if ($error$ instanceof Error && $error$.stack) { + console.error($error$); + console.error($error$.stack); + } + else if ($error$ instanceof _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection) { + console.error($error$.toString()); + if ($error$.detail && $error$.detail.stack) + console.error($error$.detail.stack); + } + else { + console.error($error$); + } + }; + var getters = ['current', '$current', 'params', 'transition']; + var boundFns = Object.keys(StateService.prototype).filter((0,_common_hof__WEBPACK_IMPORTED_MODULE_13__.not)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(getters))); + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.createProxyFunctions)((0,_common_hof__WEBPACK_IMPORTED_MODULE_13__.val)(StateService.prototype), this, (0,_common_hof__WEBPACK_IMPORTED_MODULE_13__.val)(this), boundFns); + } + Object.defineProperty(StateService.prototype, "transition", { + /** + * The [[Transition]] currently in progress (or null) + * + * @deprecated This is a passthrough through to [[UIRouterGlobals.transition]] + */ + get: function () { + return this.router.globals.transition; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(StateService.prototype, "params", { + /** + * The latest successful state parameters + * + * @deprecated This is a passthrough through to [[UIRouterGlobals.params]] + */ + get: function () { + return this.router.globals.params; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(StateService.prototype, "current", { + /** + * The current [[StateDeclaration]] + * + * @deprecated This is a passthrough through to [[UIRouterGlobals.current]] + */ + get: function () { + return this.router.globals.current; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(StateService.prototype, "$current", { + /** + * The current [[StateObject]] (an internal API) + * + * @deprecated This is a passthrough through to [[UIRouterGlobals.$current]] + */ + get: function () { + return this.router.globals.$current; + }, + enumerable: false, + configurable: true + }); + /** @internal */ + StateService.prototype.dispose = function () { + this.defaultErrorHandler(_common_common__WEBPACK_IMPORTED_MODULE_0__.noop); + this.invalidCallbacks = []; + }; + /** + * Handler for when [[transitionTo]] is called with an invalid state. + * + * Invokes the [[onInvalid]] callbacks, in natural order. + * Each callback's return value is checked in sequence until one of them returns an instance of TargetState. + * The results of the callbacks are wrapped in $q.when(), so the callbacks may return promises. + * + * If a callback returns an TargetState, then it is used as arguments to $state.transitionTo() and the result returned. + * + * @internal + */ + StateService.prototype._handleInvalidTargetState = function (fromPath, toState) { + var _this = this; + var fromState = _path_pathUtils__WEBPACK_IMPORTED_MODULE_4__.PathUtils.makeTargetState(this.router.stateRegistry, fromPath); + var globals = this.router.globals; + var latestThing = function () { return globals.transitionHistory.peekTail(); }; + var latest = latestThing(); + var callbackQueue = new _common_queue__WEBPACK_IMPORTED_MODULE_2__.Queue(this.invalidCallbacks.slice()); + var injector = new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_11__.ResolveContext(fromPath).injector(); + var checkForRedirect = function (result) { + if (!(result instanceof _targetState__WEBPACK_IMPORTED_MODULE_8__.TargetState)) { + return; + } + var target = result; + // Recreate the TargetState, in case the state is now defined. + target = _this.target(target.identifier(), target.params(), target.options()); + if (!target.valid()) { + return _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.invalid(target.error()).toPromise(); + } + if (latestThing() !== latest) { + return _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.superseded().toPromise(); + } + return _this.transitionTo(target.identifier(), target.params(), target.options()); + }; + function invokeNextCallback() { + var nextCallback = callbackQueue.dequeue(); + if (nextCallback === undefined) + return _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.invalid(toState.error()).toPromise(); + var callbackResult = _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$q.when(nextCallback(toState, fromState, injector)); + return callbackResult.then(checkForRedirect).then(function (result) { return result || invokeNextCallback(); }); + } + return invokeNextCallback(); + }; + /** + * Registers an Invalid State handler + * + * Registers a [[OnInvalidCallback]] function to be invoked when [[StateService.transitionTo]] + * has been called with an invalid state reference parameter + * + * Example: + * ```js + * stateService.onInvalid(function(to, from, injector) { + * if (to.name() === 'foo') { + * let lazyLoader = injector.get('LazyLoadService'); + * return lazyLoader.load('foo') + * .then(() => stateService.target('foo')); + * } + * }); + * ``` + * + * @param {function} callback invoked when the toState is invalid + * This function receives the (invalid) toState, the fromState, and an injector. + * The function may optionally return a [[TargetState]] or a Promise for a TargetState. + * If one is returned, it is treated as a redirect. + * + * @returns a function which deregisters the callback + */ + StateService.prototype.onInvalid = function (callback) { + this.invalidCallbacks.push(callback); + return function deregisterListener() { + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(this.invalidCallbacks)(callback); + }.bind(this); + }; + /** + * Reloads the current state + * + * A method that force reloads the current state, or a partial state hierarchy. + * All resolves are re-resolved, and components reinstantiated. + * + * #### Example: + * ```js + * let app angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.reload = function(){ + * $state.reload(); + * } + * }); + * ``` + * + * Note: `reload()` is just an alias for: + * + * ```js + * $state.transitionTo($state.current, $state.params, { + * reload: true, inherit: false + * }); + * ``` + * + * @param reloadState A state name or a state object. + * If present, this state and all its children will be reloaded, but ancestors will not reload. + * + * #### Example: + * ```js + * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' + * //and current state is 'contacts.detail.item' + * let app angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.reload = function(){ + * //will reload 'contact.detail' and nested 'contact.detail.item' states + * $state.reload('contact.detail'); + * } + * }); + * ``` + * + * @returns A promise representing the state of the new transition. See [[StateService.go]] + */ + StateService.prototype.reload = function (reloadState) { + return this.transitionTo(this.current, this.params, { + reload: (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(reloadState) ? reloadState : true, + inherit: false, + notify: false, + }); + }; + /** + * Transition to a different state and/or parameters + * + * Convenience method for transitioning to a new state. + * + * `$state.go` calls `$state.transitionTo` internally but automatically sets options to + * `{ location: true, inherit: true, relative: router.globals.$current, notify: true }`. + * This allows you to use either an absolute or relative `to` argument (because of `relative: router.globals.$current`). + * It also allows you to specify * only the parameters you'd like to update, while letting unspecified parameters + * inherit from the current parameter values (because of `inherit: true`). + * + * #### Example: + * ```js + * let app = angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.changeState = function () { + * $state.go('contact.detail'); + * }; + * }); + * ``` + * + * @param to Absolute state name, state object, or relative state path (relative to current state). + * + * Some examples: + * + * - `$state.go('contact.detail')` - will go to the `contact.detail` state + * - `$state.go('^')` - will go to the parent state + * - `$state.go('^.sibling')` - if current state is `home.child`, will go to the `home.sibling` state + * - `$state.go('.child.grandchild')` - if current state is home, will go to the `home.child.grandchild` state + * + * @param 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 current parameter values (because of `inherit: true`). + * This allows, for example, going to a sibling state that shares parameters defined by a parent state. + * + * @param options Transition options + * + * @returns {promise} A promise representing the state of the new transition. + */ + StateService.prototype.go = function (to, params, options) { + var defautGoOpts = { relative: this.$current, inherit: true }; + var transOpts = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.defaults)(options, defautGoOpts, _transition_transitionService__WEBPACK_IMPORTED_MODULE_6__.defaultTransOpts); + return this.transitionTo(to, params, transOpts); + }; + /** + * Creates a [[TargetState]] + * + * This is a factory method for creating a TargetState + * + * This may be returned from a Transition Hook to redirect a transition, for example. + */ + StateService.prototype.target = function (identifier, params, options) { + if (options === void 0) { options = {}; } + // If we're reloading, find the state object to reload from + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isObject)(options.reload) && !options.reload.name) + throw new Error('Invalid reload state object'); + var reg = this.router.stateRegistry; + options.reloadState = + options.reload === true ? reg.root() : reg.matcher.find(options.reload, options.relative); + if (options.reload && !options.reloadState) + throw new Error("No such reload state '" + ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(options.reload) ? options.reload : options.reload.name) + "'"); + return new _targetState__WEBPACK_IMPORTED_MODULE_8__.TargetState(this.router.stateRegistry, identifier, params, options); + }; + /** @internal */ + StateService.prototype.getCurrentPath = function () { + var _this = this; + var globals = this.router.globals; + var latestSuccess = globals.successfulTransitions.peekTail(); + var rootPath = function () { return [new _path_pathNode__WEBPACK_IMPORTED_MODULE_5__.PathNode(_this.router.stateRegistry.root())]; }; + return latestSuccess ? latestSuccess.treeChanges().to : rootPath(); + }; + /** + * Low-level method for transitioning to a new state. + * + * The [[go]] method (which uses `transitionTo` internally) is recommended in most situations. + * + * #### Example: + * ```js + * let app = angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.changeState = function () { + * $state.transitionTo('contact.detail'); + * }; + * }); + * ``` + * + * @param to State name or state object. + * @param toParams A map of the parameters that will be sent to the state, + * will populate $stateParams. + * @param options Transition options + * + * @returns A promise representing the state of the new transition. See [[go]] + */ + StateService.prototype.transitionTo = function (to, toParams, options) { + var _this = this; + if (toParams === void 0) { toParams = {}; } + if (options === void 0) { options = {}; } + var router = this.router; + var globals = router.globals; + options = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.defaults)(options, _transition_transitionService__WEBPACK_IMPORTED_MODULE_6__.defaultTransOpts); + var getCurrent = function () { return globals.transition; }; + options = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(options, { current: getCurrent }); + var ref = this.target(to, toParams, options); + var currentPath = this.getCurrentPath(); + if (!ref.exists()) + return this._handleInvalidTargetState(currentPath, ref); + if (!ref.valid()) + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.silentRejection)(ref.error()); + if (options.supercede === false && getCurrent()) { + return (_transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.ignored('Another transition is in progress and supercede has been set to false in TransitionOptions for the transition. So the transition was ignored in favour of the existing one in progress.').toPromise()); + } + /** + * Special handling for Ignored, Aborted, and Redirected transitions + * + * The semantics for the transition.run() promise and the StateService.transitionTo() + * promise differ. For instance, the run() promise may be rejected because it was + * IGNORED, but the transitionTo() promise is resolved because from the user perspective + * no error occurred. Likewise, the transition.run() promise may be rejected because of + * a Redirect, but the transitionTo() promise is chained to the new Transition's promise. + */ + var rejectedTransitionHandler = function (trans) { return function (error) { + if (error instanceof _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection) { + var isLatest = router.globals.lastStartedTransitionId <= trans.$id; + if (error.type === _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.RejectType.IGNORED) { + isLatest && router.urlRouter.update(); + // Consider ignored `Transition.run()` as a successful `transitionTo` + return _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$q.when(globals.current); + } + var detail = error.detail; + if (error.type === _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.RejectType.SUPERSEDED && error.redirected && detail instanceof _targetState__WEBPACK_IMPORTED_MODULE_8__.TargetState) { + // If `Transition.run()` was redirected, allow the `transitionTo()` promise to resolve successfully + // by returning the promise for the new (redirect) `Transition.run()`. + var redirect = trans.redirect(detail); + return redirect.run().catch(rejectedTransitionHandler(redirect)); + } + if (error.type === _transition_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.RejectType.ABORTED) { + isLatest && router.urlRouter.update(); + return _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$q.reject(error); + } + } + var errorHandler = _this.defaultErrorHandler(); + errorHandler(error); + return _common_coreservices__WEBPACK_IMPORTED_MODULE_3__.services.$q.reject(error); + }; }; + var transition = this.router.transitionService.create(currentPath, ref); + var transitionToPromise = transition.run().catch(rejectedTransitionHandler(transition)); + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.silenceUncaughtInPromise)(transitionToPromise); // issue #2676 + // Return a promise for the transition, which also has the transition object on it. + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)(transitionToPromise, { transition: transition }); + }; + /** + * Checks if the current state *is* the provided state + * + * Similar to [[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: + * ```js + * $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 + * ```html + *
    Item
    + * ``` + * + * @param stateOrName The state name (absolute or relative) or state object you'd like to check. + * @param params A param object, e.g. `{sectionId: section.id}`, that you'd like + * to test against the current active state. + * @param options An options object. The options are: + * - `relative`: If `stateOrName` is a relative state name and `options.relative` is set, .is will + * test relative to `options.relative` state (or name). + * + * @returns Returns true if it is the state. + */ + StateService.prototype.is = function (stateOrName, params, options) { + options = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.defaults)(options, { relative: this.$current }); + var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative); + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(state)) + return undefined; + if (this.$current !== state) + return false; + if (!params) + return true; + var schema = state.parameters({ inherit: true, matchingKeys: params }); + return _params_param__WEBPACK_IMPORTED_MODULE_9__.Param.equals(schema, _params_param__WEBPACK_IMPORTED_MODULE_9__.Param.values(schema, params), this.params); + }; + /** + * Checks if the current state *includes* the provided state + * + * 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 when `$state.$current.name === 'contacts.details.item'` + * ```js + * // 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 + * ``` + * + * #### Glob Examples when `* $state.$current.name === 'contacts.details.item.url'`: + * ```js + * $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 stateOrName A partial name, relative name, glob pattern, + * or state object to be searched for within the current state name. + * @param params A param object, e.g. `{sectionId: section.id}`, + * that you'd like to test against the current active state. + * @param options An options object. The options are: + * - `relative`: 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 does include the state + */ + StateService.prototype.includes = function (stateOrName, params, options) { + options = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.defaults)(options, { relative: this.$current }); + var glob = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(stateOrName) && _common_glob__WEBPACK_IMPORTED_MODULE_10__.Glob.fromString(stateOrName); + if (glob) { + if (!glob.matches(this.$current.name)) + return false; + stateOrName = this.$current.name; + } + var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative), include = this.$current.includes; + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(state)) + return undefined; + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(include[state.name])) + return false; + if (!params) + return true; + var schema = state.parameters({ inherit: true, matchingKeys: params }); + return _params_param__WEBPACK_IMPORTED_MODULE_9__.Param.equals(schema, _params_param__WEBPACK_IMPORTED_MODULE_9__.Param.values(schema, params), this.params); + }; + /** + * Generates a URL for a state and parameters + * + * Returns the url for the given state populated with the given params. + * + * #### Example: + * ```js + * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob"); + * ``` + * + * @param stateOrName The state name or state object you'd like to generate a url from. + * @param params An object of parameter values to fill the state's required parameters. + * @param options Options object. The options are: + * + * @returns {string} compiled state url + */ + StateService.prototype.href = function (stateOrName, params, options) { + var defaultHrefOpts = { + lossy: true, + inherit: true, + absolute: false, + relative: this.$current, + }; + options = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.defaults)(options, defaultHrefOpts); + params = params || {}; + var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative); + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(state)) + return null; + if (options.inherit) + params = this.params.$inherit(params, this.$current, state); + var nav = state && options.lossy ? state.navigable : state; + if (!nav || nav.url === undefined || nav.url === null) { + return null; + } + return this.router.urlRouter.href(nav.url, params, { absolute: options.absolute }); + }; + /** + * Sets or gets the default [[transitionTo]] error handler. + * + * The error handler is called when a [[Transition]] is rejected or when any error occurred during the Transition. + * This includes errors caused by resolves and transition hooks. + * + * Note: + * This handler does not receive certain Transition rejections. + * Redirected and Ignored Transitions are not considered to be errors by [[StateService.transitionTo]]. + * + * The built-in default error handler logs the error to the console. + * + * You can provide your own custom handler. + * + * #### Example: + * ```js + * stateService.defaultErrorHandler(function() { + * // Do not log transitionTo errors + * }); + * ``` + * + * @param handler a global error handler function + * @returns the current global error handler + */ + StateService.prototype.defaultErrorHandler = function (handler) { + return (this._defaultErrorHandler = handler || this._defaultErrorHandler); + }; + StateService.prototype.get = function (stateOrName, base) { + var reg = this.router.stateRegistry; + if (arguments.length === 0) + return reg.get(); + return reg.get(stateOrName, base || this.$current); + }; + /** + * Lazy loads a state + * + * Explicitly runs a state's [[StateDeclaration.lazyLoad]] function. + * + * @param stateOrName the state that should be lazy loaded + * @param transition the optional Transition context to use (if the lazyLoad function requires an injector, etc) + * Note: If no transition is provided, a noop transition is created using the from the current state to the current state. + * This noop transition is not actually run. + * + * @returns a promise to lazy load + */ + StateService.prototype.lazyLoad = function (stateOrName, transition) { + var state = this.get(stateOrName); + if (!state || !state.lazyLoad) + throw new Error('Can not lazy load ' + stateOrName); + var currentPath = this.getCurrentPath(); + var target = _path_pathUtils__WEBPACK_IMPORTED_MODULE_4__.PathUtils.makeTargetState(this.router.stateRegistry, currentPath); + transition = transition || this.router.transitionService.create(currentPath, target); + return (0,_hooks_lazyLoad__WEBPACK_IMPORTED_MODULE_12__.lazyLoadState)(transition, state); + }; + return StateService; +}()); + +//# sourceMappingURL=stateService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/state/targetState.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/state/targetState.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "TargetState": () => (/* binding */ TargetState) +/* harmony export */ }); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + + +/** + * Encapsulate the target (destination) state/params/options of a [[Transition]]. + * + * This class is frequently used to redirect a transition to a new destination. + * + * See: + * + * - [[HookResult]] + * - [[TransitionHookFn]] + * - [[TransitionService.onStart]] + * + * To create a `TargetState`, use [[StateService.target]]. + * + * --- + * + * This class wraps: + * + * 1) an identifier for a state + * 2) a set of parameters + * 3) and transition options + * 4) the registered state object (the [[StateDeclaration]]) + * + * Many UI-Router APIs such as [[StateService.go]] take a [[StateOrName]] argument which can + * either be a *state object* (a [[StateDeclaration]] or [[StateObject]]) or a *state name* (a string). + * The `TargetState` class normalizes those options. + * + * A `TargetState` may be valid (the state being targeted exists in the registry) + * or invalid (the state being targeted is not registered). + */ +var TargetState = /** @class */ (function () { + /** + * The TargetState constructor + * + * Note: Do not construct a `TargetState` manually. + * To create a `TargetState`, use the [[StateService.target]] factory method. + * + * @param _stateRegistry The StateRegistry to use to look up the _definition + * @param _identifier An identifier for a state. + * Either a fully-qualified state name, or the object used to define the state. + * @param _params Parameters for the target state + * @param _options Transition options. + * + * @internal + */ + function TargetState(_stateRegistry, _identifier, _params, _options) { + this._stateRegistry = _stateRegistry; + this._identifier = _identifier; + this._identifier = _identifier; + this._params = (0,_common__WEBPACK_IMPORTED_MODULE_2__.extend)({}, _params || {}); + this._options = (0,_common__WEBPACK_IMPORTED_MODULE_2__.extend)({}, _options || {}); + this._definition = _stateRegistry.matcher.find(_identifier, this._options.relative); + } + /** The name of the state this object targets */ + TargetState.prototype.name = function () { + return (this._definition && this._definition.name) || this._identifier; + }; + /** The identifier used when creating this TargetState */ + TargetState.prototype.identifier = function () { + return this._identifier; + }; + /** The target parameter values */ + TargetState.prototype.params = function () { + return this._params; + }; + /** The internal state object (if it was found) */ + TargetState.prototype.$state = function () { + return this._definition; + }; + /** The internal state declaration (if it was found) */ + TargetState.prototype.state = function () { + return this._definition && this._definition.self; + }; + /** The target options */ + TargetState.prototype.options = function () { + return this._options; + }; + /** True if the target state was found */ + TargetState.prototype.exists = function () { + return !!(this._definition && this._definition.self); + }; + /** True if the object is valid */ + TargetState.prototype.valid = function () { + return !this.error(); + }; + /** If the object is invalid, returns the reason why */ + TargetState.prototype.error = function () { + var base = this.options().relative; + if (!this._definition && !!base) { + var stateName = base.name ? base.name : base; + return "Could not resolve '" + this.name() + "' from state '" + stateName + "'"; + } + if (!this._definition) + return "No such state '" + this.name() + "'"; + if (!this._definition.self) + return "State '" + this.name() + "' has an invalid definition"; + }; + TargetState.prototype.toString = function () { + return "'" + this.name() + "'" + (0,_common_strings__WEBPACK_IMPORTED_MODULE_1__.stringify)(this.params()); + }; + /** + * Returns a copy of this TargetState which targets a different state. + * The new TargetState has the same parameter values and transition options. + * + * @param state The new state that should be targeted + */ + TargetState.prototype.withState = function (state) { + return new TargetState(this._stateRegistry, state, this._params, this._options); + }; + /** + * Returns a copy of this TargetState, using the specified parameter values. + * + * @param params the new parameter values to use + * @param replace When false (default) the new parameter values will be merged with the current values. + * When true the parameter values will be used instead of the current values. + */ + TargetState.prototype.withParams = function (params, replace) { + if (replace === void 0) { replace = false; } + var newParams = replace ? params : (0,_common__WEBPACK_IMPORTED_MODULE_2__.extend)({}, this._params, params); + return new TargetState(this._stateRegistry, this._identifier, newParams, this._options); + }; + /** + * Returns a copy of this TargetState, using the specified Transition Options. + * + * @param options the new options to use + * @param replace When false (default) the new options will be merged with the current options. + * When true the options will be used instead of the current options. + */ + TargetState.prototype.withOptions = function (options, replace) { + if (replace === void 0) { replace = false; } + var newOpts = replace ? options : (0,_common__WEBPACK_IMPORTED_MODULE_2__.extend)({}, this._options, options); + return new TargetState(this._stateRegistry, this._identifier, this._params, newOpts); + }; + /** Returns true if the object has a state property that might be a state or state name */ + TargetState.isDef = function (obj) { + return obj && obj.state && ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)(obj.state) || ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isObject)(obj.state) && (0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isString)(obj.state.name))); + }; + return TargetState; +}()); + +//# sourceMappingURL=targetState.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/hookBuilder.js": +/*!***********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/hookBuilder.js ***! + \***********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "HookBuilder": () => (/* binding */ HookBuilder) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/transition/interface.js"); +/* harmony import */ var _transitionHook__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./transitionHook */ "./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js"); + + + + +/** + * This class returns applicable TransitionHooks for a specific Transition instance. + * + * Hooks ([[RegisteredHook]]) may be registered globally, e.g., $transitions.onEnter(...), or locally, e.g. + * myTransition.onEnter(...). The HookBuilder finds matching RegisteredHooks (where the match criteria is + * determined by the type of hook) + * + * The HookBuilder also converts RegisteredHooks objects to TransitionHook objects, which are used to run a Transition. + * + * The HookBuilder constructor is given the $transitions service and a Transition instance. Thus, a HookBuilder + * instance may only be used for one specific Transition object. (side note: the _treeChanges accessor is private + * in the Transition class, so we must also provide the Transition's _treeChanges) + */ +var HookBuilder = /** @class */ (function () { + function HookBuilder(transition) { + this.transition = transition; + } + HookBuilder.prototype.buildHooksForPhase = function (phase) { + var _this = this; + var $transitions = this.transition.router.transitionService; + return $transitions._pluginapi + ._getEvents(phase) + .map(function (type) { return _this.buildHooks(type); }) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .filter(_common_common__WEBPACK_IMPORTED_MODULE_0__.identity); + }; + /** + * Returns an array of newly built TransitionHook objects. + * + * - Finds all RegisteredHooks registered for the given `hookType` which matched the transition's [[TreeChanges]]. + * - Finds [[PathNode]] (or `PathNode[]`) to use as the TransitionHook context(s) + * - For each of the [[PathNode]]s, creates a TransitionHook + * + * @param hookType the type of the hook registration function, e.g., 'onEnter', 'onFinish'. + */ + HookBuilder.prototype.buildHooks = function (hookType) { + var transition = this.transition; + var treeChanges = transition.treeChanges(); + // Find all the matching registered hooks for a given hook type + var matchingHooks = this.getMatchingHooks(hookType, treeChanges, transition); + if (!matchingHooks) + return []; + var baseHookOptions = { + transition: transition, + current: transition.options().current, + }; + var makeTransitionHooks = function (hook) { + // Fetch the Nodes that caused this hook to match. + var matches = hook.matches(treeChanges, transition); + // Select the PathNode[] that will be used as TransitionHook context objects + var matchingNodes = matches[hookType.criteriaMatchPath.name]; + // Return an array of HookTuples + return matchingNodes.map(function (node) { + var _options = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)({ + bind: hook.bind, + traceData: { hookType: hookType.name, context: node }, + }, baseHookOptions); + var state = hookType.criteriaMatchPath.scope === _interface__WEBPACK_IMPORTED_MODULE_2__.TransitionHookScope.STATE ? node.state.self : null; + var transitionHook = new _transitionHook__WEBPACK_IMPORTED_MODULE_3__.TransitionHook(transition, state, hook, _options); + return { hook: hook, node: node, transitionHook: transitionHook }; + }); + }; + return matchingHooks + .map(makeTransitionHooks) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .sort(tupleSort(hookType.reverseSort)) + .map(function (tuple) { return tuple.transitionHook; }); + }; + /** + * Finds all RegisteredHooks from: + * - The Transition object instance hook registry + * - The TransitionService ($transitions) global hook registry + * + * which matched: + * - the eventType + * - the matchCriteria (to, from, exiting, retained, entering) + * + * @returns an array of matched [[RegisteredHook]]s + */ + HookBuilder.prototype.getMatchingHooks = function (hookType, treeChanges, transition) { + var isCreate = hookType.hookPhase === _interface__WEBPACK_IMPORTED_MODULE_2__.TransitionHookPhase.CREATE; + // Instance and Global hook registries + var $transitions = this.transition.router.transitionService; + var registries = isCreate ? [$transitions] : [this.transition, $transitions]; + return registries + .map(function (reg) { return reg.getHooks(hookType.name); }) // Get named hooks from registries + .filter((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.assertPredicate)(_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isArray, "broken event named: " + hookType.name)) // Sanity check + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) // Un-nest RegisteredHook[][] to RegisteredHook[] array + .filter(function (hook) { return hook.matches(treeChanges, transition); }); // Only those satisfying matchCriteria + }; + return HookBuilder; +}()); + +/** + * A factory for a sort function for HookTuples. + * + * The sort function first compares the PathNode depth (how deep in the state tree a node is), then compares + * the EventHook priority. + * + * @param reverseDepthSort a boolean, when true, reverses the sort order for the node depth + * @returns a tuple sort function + */ +function tupleSort(reverseDepthSort) { + if (reverseDepthSort === void 0) { reverseDepthSort = false; } + return function nodeDepthThenPriority(l, r) { + var factor = reverseDepthSort ? -1 : 1; + var depthDelta = (l.node.state.path.length - r.node.state.path.length) * factor; + return depthDelta !== 0 ? depthDelta : r.hook.priority - l.hook.priority; + }; +} +//# sourceMappingURL=hookBuilder.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/hookRegistry.js": +/*!************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/hookRegistry.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "matchState": () => (/* binding */ matchState), +/* harmony export */ "RegisteredHook": () => (/* binding */ RegisteredHook), +/* harmony export */ "makeEvent": () => (/* binding */ makeEvent) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/transition/interface.js"); + + +/** + * Determines if the given state matches the matchCriteria + * + * @internal + * + * @param state a State Object to test against + * @param criterion + * - If a string, matchState uses the string as a glob-matcher against the state name + * - If an array (of strings), matchState uses each string in the array as a glob-matchers against the state name + * and returns a positive match if any of the globs match. + * - If a function, matchState calls the function with the state and returns true if the function's result is truthy. + * @returns {boolean} + */ +function matchState(state, criterion, transition) { + var toMatch = (0,_common__WEBPACK_IMPORTED_MODULE_0__.isString)(criterion) ? [criterion] : criterion; + function matchGlobs(_state) { + var globStrings = toMatch; + for (var i = 0; i < globStrings.length; i++) { + var glob = new _common__WEBPACK_IMPORTED_MODULE_0__.Glob(globStrings[i]); + if ((glob && glob.matches(_state.name)) || (!glob && globStrings[i] === _state.name)) { + return true; + } + } + return false; + } + var matchFn = ((0,_common__WEBPACK_IMPORTED_MODULE_0__.isFunction)(toMatch) ? toMatch : matchGlobs); + return !!matchFn(state, transition); +} +/** + * The registration data for a registered transition hook + */ +var RegisteredHook = /** @class */ (function () { + function RegisteredHook(tranSvc, eventType, callback, matchCriteria, removeHookFromRegistry, options) { + if (options === void 0) { options = {}; } + this.tranSvc = tranSvc; + this.eventType = eventType; + this.callback = callback; + this.matchCriteria = matchCriteria; + this.removeHookFromRegistry = removeHookFromRegistry; + this.invokeCount = 0; + this._deregistered = false; + this.priority = options.priority || 0; + this.bind = options.bind || null; + this.invokeLimit = options.invokeLimit; + } + /** + * Gets the matching [[PathNode]]s + * + * Given an array of [[PathNode]]s, and a [[HookMatchCriterion]], returns an array containing + * the [[PathNode]]s that the criteria matches, or `null` if there were no matching nodes. + * + * Returning `null` is significant to distinguish between the default + * "match-all criterion value" of `true` compared to a `() => true` function, + * when the nodes is an empty array. + * + * This is useful to allow a transition match criteria of `entering: true` + * to still match a transition, even when `entering === []`. Contrast that + * with `entering: (state) => true` which only matches when a state is actually + * being entered. + */ + RegisteredHook.prototype._matchingNodes = function (nodes, criterion, transition) { + if (criterion === true) + return nodes; + var matching = nodes.filter(function (node) { return matchState(node.state, criterion, transition); }); + return matching.length ? matching : null; + }; + /** + * Gets the default match criteria (all `true`) + * + * Returns an object which has all the criteria match paths as keys and `true` as values, i.e.: + * + * ```js + * { + * to: true, + * from: true, + * entering: true, + * exiting: true, + * retained: true, + * } + */ + RegisteredHook.prototype._getDefaultMatchCriteria = function () { + return (0,_common__WEBPACK_IMPORTED_MODULE_0__.mapObj)(this.tranSvc._pluginapi._getPathTypes(), function () { return true; }); + }; + /** + * Gets matching nodes as [[IMatchingNodes]] + * + * Create a IMatchingNodes object from the TransitionHookTypes that is roughly equivalent to: + * + * ```js + * let matches: IMatchingNodes = { + * to: _matchingNodes([tail(treeChanges.to)], mc.to), + * from: _matchingNodes([tail(treeChanges.from)], mc.from), + * exiting: _matchingNodes(treeChanges.exiting, mc.exiting), + * retained: _matchingNodes(treeChanges.retained, mc.retained), + * entering: _matchingNodes(treeChanges.entering, mc.entering), + * }; + * ``` + */ + RegisteredHook.prototype._getMatchingNodes = function (treeChanges, transition) { + var _this = this; + var criteria = (0,_common__WEBPACK_IMPORTED_MODULE_0__.extend)(this._getDefaultMatchCriteria(), this.matchCriteria); + var paths = (0,_common__WEBPACK_IMPORTED_MODULE_0__.values)(this.tranSvc._pluginapi._getPathTypes()); + return paths.reduce(function (mn, pathtype) { + // STATE scope criteria matches against every node in the path. + // TRANSITION scope criteria matches against only the last node in the path + var isStateHook = pathtype.scope === _interface__WEBPACK_IMPORTED_MODULE_1__.TransitionHookScope.STATE; + var path = treeChanges[pathtype.name] || []; + var nodes = isStateHook ? path : [(0,_common__WEBPACK_IMPORTED_MODULE_0__.tail)(path)]; + mn[pathtype.name] = _this._matchingNodes(nodes, criteria[pathtype.name], transition); + return mn; + }, {}); + }; + /** + * Determines if this hook's [[matchCriteria]] match the given [[TreeChanges]] + * + * @returns an IMatchingNodes object, or null. If an IMatchingNodes object is returned, its values + * are the matching [[PathNode]]s for each [[HookMatchCriterion]] (to, from, exiting, retained, entering) + */ + RegisteredHook.prototype.matches = function (treeChanges, transition) { + var matches = this._getMatchingNodes(treeChanges, transition); + // Check if all the criteria matched the TreeChanges object + var allMatched = (0,_common__WEBPACK_IMPORTED_MODULE_0__.values)(matches).every(_common__WEBPACK_IMPORTED_MODULE_0__.identity); + return allMatched ? matches : null; + }; + RegisteredHook.prototype.deregister = function () { + this.removeHookFromRegistry(this); + this._deregistered = true; + }; + return RegisteredHook; +}()); + +/** Return a registration function of the requested type. */ +function makeEvent(registry, transitionService, eventType) { + // Create the object which holds the registered transition hooks. + var _registeredHooks = (registry._registeredHooks = registry._registeredHooks || {}); + var hooks = (_registeredHooks[eventType.name] = []); + var removeHookFn = (0,_common__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(hooks); + // Create hook registration function on the IHookRegistry for the event + registry[eventType.name] = hookRegistrationFn; + function hookRegistrationFn(matchObject, callback, options) { + if (options === void 0) { options = {}; } + var registeredHook = new RegisteredHook(transitionService, eventType, callback, matchObject, removeHookFn, options); + hooks.push(registeredHook); + return registeredHook.deregister.bind(registeredHook); + } + return hookRegistrationFn; +} +//# sourceMappingURL=hookRegistry.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/index.js": +/*!*****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/index.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "TransitionHookPhase": () => (/* reexport safe */ _interface__WEBPACK_IMPORTED_MODULE_0__.TransitionHookPhase), +/* harmony export */ "TransitionHookScope": () => (/* reexport safe */ _interface__WEBPACK_IMPORTED_MODULE_0__.TransitionHookScope), +/* harmony export */ "HookBuilder": () => (/* reexport safe */ _hookBuilder__WEBPACK_IMPORTED_MODULE_1__.HookBuilder), +/* harmony export */ "RegisteredHook": () => (/* reexport safe */ _hookRegistry__WEBPACK_IMPORTED_MODULE_2__.RegisteredHook), +/* harmony export */ "makeEvent": () => (/* reexport safe */ _hookRegistry__WEBPACK_IMPORTED_MODULE_2__.makeEvent), +/* harmony export */ "matchState": () => (/* reexport safe */ _hookRegistry__WEBPACK_IMPORTED_MODULE_2__.matchState), +/* harmony export */ "RejectType": () => (/* reexport safe */ _rejectFactory__WEBPACK_IMPORTED_MODULE_3__.RejectType), +/* harmony export */ "Rejection": () => (/* reexport safe */ _rejectFactory__WEBPACK_IMPORTED_MODULE_3__.Rejection), +/* harmony export */ "Transition": () => (/* reexport safe */ _transition__WEBPACK_IMPORTED_MODULE_4__.Transition), +/* harmony export */ "TransitionHook": () => (/* reexport safe */ _transitionHook__WEBPACK_IMPORTED_MODULE_5__.TransitionHook), +/* harmony export */ "TransitionEventType": () => (/* reexport safe */ _transitionEventType__WEBPACK_IMPORTED_MODULE_6__.TransitionEventType), +/* harmony export */ "TransitionService": () => (/* reexport safe */ _transitionService__WEBPACK_IMPORTED_MODULE_7__.TransitionService), +/* harmony export */ "defaultTransOpts": () => (/* reexport safe */ _transitionService__WEBPACK_IMPORTED_MODULE_7__.defaultTransOpts) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/transition/interface.js"); +/* harmony import */ var _hookBuilder__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hookBuilder */ "./node_modules/@uirouter/core/lib-esm/transition/hookBuilder.js"); +/* harmony import */ var _hookRegistry__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./hookRegistry */ "./node_modules/@uirouter/core/lib-esm/transition/hookRegistry.js"); +/* harmony import */ var _rejectFactory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./rejectFactory */ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js"); +/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./transition */ "./node_modules/@uirouter/core/lib-esm/transition/transition.js"); +/* harmony import */ var _transitionHook__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./transitionHook */ "./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js"); +/* harmony import */ var _transitionEventType__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./transitionEventType */ "./node_modules/@uirouter/core/lib-esm/transition/transitionEventType.js"); +/* harmony import */ var _transitionService__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./transitionService */ "./node_modules/@uirouter/core/lib-esm/transition/transitionService.js"); +/** + * # Transition subsystem + * + * This module contains APIs related to a Transition. + * + * See: + * - [[TransitionService]] + * - [[Transition]] + * - [[HookFn]], [[TransitionHookFn]], [[TransitionStateHookFn]], [[HookMatchCriteria]], [[HookResult]] + * + * @packageDocumentation @preferred + */ + + + + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/interface.js": +/*!*********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/interface.js ***! + \*********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "TransitionHookPhase": () => (/* binding */ TransitionHookPhase), +/* harmony export */ "TransitionHookScope": () => (/* binding */ TransitionHookScope) +/* harmony export */ }); +var TransitionHookPhase; +(function (TransitionHookPhase) { + TransitionHookPhase[TransitionHookPhase["CREATE"] = 0] = "CREATE"; + TransitionHookPhase[TransitionHookPhase["BEFORE"] = 1] = "BEFORE"; + TransitionHookPhase[TransitionHookPhase["RUN"] = 2] = "RUN"; + TransitionHookPhase[TransitionHookPhase["SUCCESS"] = 3] = "SUCCESS"; + TransitionHookPhase[TransitionHookPhase["ERROR"] = 4] = "ERROR"; +})(TransitionHookPhase || (TransitionHookPhase = {})); +var TransitionHookScope; +(function (TransitionHookScope) { + TransitionHookScope[TransitionHookScope["TRANSITION"] = 0] = "TRANSITION"; + TransitionHookScope[TransitionHookScope["STATE"] = 1] = "STATE"; +})(TransitionHookScope || (TransitionHookScope = {})); + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js": +/*!*************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js ***! + \*************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "RejectType": () => (/* binding */ RejectType), +/* harmony export */ "Rejection": () => (/* binding */ Rejection) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); + + + + +/** An enum for Transition Rejection reasons */ +var RejectType; +(function (RejectType) { + /** + * A new transition superseded this one. + * + * While this transition was running, a new transition started. + * This transition is cancelled because it was superseded by new transition. + */ + RejectType[RejectType["SUPERSEDED"] = 2] = "SUPERSEDED"; + /** + * The transition was aborted + * + * The transition was aborted by a hook which returned `false` + */ + RejectType[RejectType["ABORTED"] = 3] = "ABORTED"; + /** + * The transition was invalid + * + * The transition was never started because it was invalid + */ + RejectType[RejectType["INVALID"] = 4] = "INVALID"; + /** + * The transition was ignored + * + * The transition was ignored because it would have no effect. + * + * Either: + * + * - The transition is targeting the current state and parameter values + * - The transition is targeting the same state and parameter values as the currently running transition. + */ + RejectType[RejectType["IGNORED"] = 5] = "IGNORED"; + /** + * The transition errored. + * + * This generally means a hook threw an error or returned a rejected promise + */ + RejectType[RejectType["ERROR"] = 6] = "ERROR"; +})(RejectType || (RejectType = {})); + +/** @internal */ +var id = 0; +var Rejection = /** @class */ (function () { + function Rejection(type, message, detail) { + /** @internal */ + this.$id = id++; + this.type = type; + this.message = message; + this.detail = detail; + } + /** Returns true if the obj is a rejected promise created from the `asPromise` factory */ + Rejection.isRejectionPromise = function (obj) { + return obj && typeof obj.then === 'function' && (0,_common_hof__WEBPACK_IMPORTED_MODULE_2__.is)(Rejection)(obj._transitionRejection); + }; + /** Returns a Rejection due to transition superseded */ + Rejection.superseded = function (detail, options) { + var message = 'The transition has been superseded by a different transition'; + var rejection = new Rejection(RejectType.SUPERSEDED, message, detail); + if (options && options.redirected) { + rejection.redirected = true; + } + return rejection; + }; + /** Returns a Rejection due to redirected transition */ + Rejection.redirected = function (detail) { + return Rejection.superseded(detail, { redirected: true }); + }; + /** Returns a Rejection due to invalid transition */ + Rejection.invalid = function (detail) { + var message = 'This transition is invalid'; + return new Rejection(RejectType.INVALID, message, detail); + }; + /** Returns a Rejection due to ignored transition */ + Rejection.ignored = function (detail) { + var message = 'The transition was ignored'; + return new Rejection(RejectType.IGNORED, message, detail); + }; + /** Returns a Rejection due to aborted transition */ + Rejection.aborted = function (detail) { + var message = 'The transition has been aborted'; + return new Rejection(RejectType.ABORTED, message, detail); + }; + /** Returns a Rejection due to aborted transition */ + Rejection.errored = function (detail) { + var message = 'The transition errored'; + return new Rejection(RejectType.ERROR, message, detail); + }; + /** + * Returns a Rejection + * + * Normalizes a value as a Rejection. + * If the value is already a Rejection, returns it. + * Otherwise, wraps and returns the value as a Rejection (Rejection type: ERROR). + * + * @returns `detail` if it is already a `Rejection`, else returns an ERROR Rejection. + */ + Rejection.normalize = function (detail) { + return (0,_common_hof__WEBPACK_IMPORTED_MODULE_2__.is)(Rejection)(detail) ? detail : Rejection.errored(detail); + }; + Rejection.prototype.toString = function () { + var detailString = function (d) { return (d && d.toString !== Object.prototype.toString ? d.toString() : (0,_common_strings__WEBPACK_IMPORTED_MODULE_1__.stringify)(d)); }; + var detail = detailString(this.detail); + var _a = this, $id = _a.$id, type = _a.type, message = _a.message; + return "Transition Rejection($id: " + $id + " type: " + type + ", message: " + message + ", detail: " + detail + ")"; + }; + Rejection.prototype.toPromise = function () { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.extend)((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.silentRejection)(this), { _transitionRejection: this }); + }; + return Rejection; +}()); + +//# sourceMappingURL=rejectFactory.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/transition.js": +/*!**********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/transition.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Transition": () => (/* binding */ Transition) +/* harmony export */ }); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/transition/interface.js"); +/* harmony import */ var _transitionHook__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./transitionHook */ "./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js"); +/* harmony import */ var _hookRegistry__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./hookRegistry */ "./node_modules/@uirouter/core/lib-esm/transition/hookRegistry.js"); +/* harmony import */ var _hookBuilder__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./hookBuilder */ "./node_modules/@uirouter/core/lib-esm/transition/hookBuilder.js"); +/* harmony import */ var _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../path/pathUtils */ "./node_modules/@uirouter/core/lib-esm/path/pathUtils.js"); +/* harmony import */ var _params_param__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../params/param */ "./node_modules/@uirouter/core/lib-esm/params/param.js"); +/* harmony import */ var _resolve_resolvable__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../resolve/resolvable */ "./node_modules/@uirouter/core/lib-esm/resolve/resolvable.js"); +/* harmony import */ var _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../resolve/resolveContext */ "./node_modules/@uirouter/core/lib-esm/resolve/resolveContext.js"); +/* harmony import */ var _rejectFactory__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./rejectFactory */ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + + + + + + // has or is using + + + + + + + + + +/** @internal */ +var stateSelf = (0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('self'); +/** + * Represents a transition between two states. + * + * When navigating to a state, we are transitioning **from** the current state **to** the new state. + * + * This object contains all contextual information about the to/from states, parameters, resolves. + * It has information about all states being entered and exited as a result of the transition. + */ +var Transition = /** @class */ (function () { + /** + * Creates a new Transition object. + * + * If the target state is not valid, an error is thrown. + * + * @internal + * + * @param fromPath The path of [[PathNode]]s from which the transition is leaving. The last node in the `fromPath` + * encapsulates the "from state". + * @param targetState The target state and parameters being transitioned to (also, the transition options) + * @param router The [[UIRouter]] instance + * @internal + */ + function Transition(fromPath, targetState, router) { + var _this = this; + /** @internal */ + this._deferred = _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q.defer(); + /** + * This promise is resolved or rejected based on the outcome of the Transition. + * + * When the transition is successful, the promise is resolved + * When the transition is unsuccessful, the promise is rejected with the [[Rejection]] or javascript error + */ + this.promise = this._deferred.promise; + /** @internal Holds the hook registration functions such as those passed to Transition.onStart() */ + this._registeredHooks = {}; + /** @internal */ + this._hookBuilder = new _hookBuilder__WEBPACK_IMPORTED_MODULE_9__.HookBuilder(this); + /** Checks if this transition is currently active/running. */ + this.isActive = function () { return _this.router.globals.transition === _this; }; + this.router = router; + this._targetState = targetState; + if (!targetState.valid()) { + throw new Error(targetState.error()); + } + // current() is assumed to come from targetState.options, but provide a naive implementation otherwise. + this._options = (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.extend)({ current: (0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.val)(this) }, targetState.options()); + this.$id = router.transitionService._transitionCount++; + var toPath = _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.buildToPath(fromPath, targetState); + this._treeChanges = _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.treeChanges(fromPath, toPath, this._options.reloadState); + this.createTransitionHookRegFns(); + var onCreateHooks = this._hookBuilder.buildHooksForPhase(_interface__WEBPACK_IMPORTED_MODULE_6__.TransitionHookPhase.CREATE); + _transitionHook__WEBPACK_IMPORTED_MODULE_7__.TransitionHook.invokeHooks(onCreateHooks, function () { return null; }); + this.applyViewConfigs(router); + } + /** @internal */ + Transition.prototype.onBefore = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onStart = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onExit = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onRetain = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onEnter = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onFinish = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onSuccess = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + Transition.prototype.onError = function (criteria, callback, options) { + return; + }; + /** @internal + * Creates the transition-level hook registration functions + * (which can then be used to register hooks) + */ + Transition.prototype.createTransitionHookRegFns = function () { + var _this = this; + this.router.transitionService._pluginapi + ._getEvents() + .filter(function (type) { return type.hookPhase !== _interface__WEBPACK_IMPORTED_MODULE_6__.TransitionHookPhase.CREATE; }) + .forEach(function (type) { return (0,_hookRegistry__WEBPACK_IMPORTED_MODULE_8__.makeEvent)(_this, _this.router.transitionService, type); }); + }; + /** @internal */ + Transition.prototype.getHooks = function (hookName) { + return this._registeredHooks[hookName]; + }; + Transition.prototype.applyViewConfigs = function (router) { + var enteringStates = this._treeChanges.entering.map(function (node) { return node.state; }); + _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.applyViewConfigs(router.transitionService.$view, this._treeChanges.to, enteringStates); + }; + /** + * @internal + * @returns the internal from [State] object + */ + Transition.prototype.$from = function () { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.tail)(this._treeChanges.from).state; + }; + /** + * @internal + * @returns the internal to [State] object + */ + Transition.prototype.$to = function () { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.tail)(this._treeChanges.to).state; + }; + /** + * Returns the "from state" + * + * Returns the state that the transition is coming *from*. + * + * @returns The state declaration object for the Transition's ("from state"). + */ + Transition.prototype.from = function () { + return this.$from().self; + }; + /** + * Returns the "to state" + * + * Returns the state that the transition is going *to*. + * + * @returns The state declaration object for the Transition's target state ("to state"). + */ + Transition.prototype.to = function () { + return this.$to().self; + }; + /** + * Gets the Target State + * + * A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]] as a single object. + * + * @returns the [[TargetState]] of this Transition + */ + Transition.prototype.targetState = function () { + return this._targetState; + }; + /** + * Determines whether two transitions are equivalent. + * @deprecated + */ + Transition.prototype.is = function (compare) { + if (compare instanceof Transition) { + // TODO: Also compare parameters + return this.is({ to: compare.$to().name, from: compare.$from().name }); + } + return !((compare.to && !(0,_hookRegistry__WEBPACK_IMPORTED_MODULE_8__.matchState)(this.$to(), compare.to, this)) || + (compare.from && !(0,_hookRegistry__WEBPACK_IMPORTED_MODULE_8__.matchState)(this.$from(), compare.from, this))); + }; + Transition.prototype.params = function (pathname) { + if (pathname === void 0) { pathname = 'to'; } + return Object.freeze(this._treeChanges[pathname].map((0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('paramValues')).reduce(_common_common__WEBPACK_IMPORTED_MODULE_3__.mergeR, {})); + }; + Transition.prototype.paramsChanged = function () { + var fromParams = this.params('from'); + var toParams = this.params('to'); + // All the parameters declared on both the "to" and "from" paths + var allParamDescriptors = [] + .concat(this._treeChanges.to) + .concat(this._treeChanges.from) + .map(function (pathNode) { return pathNode.paramSchema; }) + .reduce(_common__WEBPACK_IMPORTED_MODULE_15__.flattenR, []) + .reduce(_common__WEBPACK_IMPORTED_MODULE_15__.uniqR, []); + var changedParamDescriptors = _params_param__WEBPACK_IMPORTED_MODULE_11__.Param.changed(allParamDescriptors, fromParams, toParams); + return changedParamDescriptors.reduce(function (changedValues, descriptor) { + changedValues[descriptor.id] = toParams[descriptor.id]; + return changedValues; + }, {}); + }; + /** + * Creates a [[UIInjector]] Dependency Injector + * + * Returns a Dependency Injector for the Transition's target state (to state). + * The injector provides resolve values which the target state has access to. + * + * The `UIInjector` can also provide values from the native root/global injector (ng1/ng2). + * + * #### Example: + * ```js + * .onEnter({ entering: 'myState' }, trans => { + * var myResolveValue = trans.injector().get('myResolve'); + * // Inject a global service from the global/native injector (if it exists) + * var MyService = trans.injector().get('MyService'); + * }) + * ``` + * + * In some cases (such as `onBefore`), you may need access to some resolve data but it has not yet been fetched. + * You can use [[UIInjector.getAsync]] to get a promise for the data. + * #### Example: + * ```js + * .onBefore({}, trans => { + * return trans.injector().getAsync('myResolve').then(myResolveValue => + * return myResolveValue !== 'ABORT'; + * }); + * }); + * ``` + * + * If a `state` is provided, the injector that is returned will be limited to resolve values that the provided state has access to. + * This can be useful if both a parent state `foo` and a child state `foo.bar` have both defined a resolve such as `data`. + * #### Example: + * ```js + * .onEnter({ to: 'foo.bar' }, trans => { + * // returns result of `foo` state's `myResolve` resolve + * // even though `foo.bar` also has a `myResolve` resolve + * var fooData = trans.injector('foo').get('myResolve'); + * }); + * ``` + * + * If you need resolve data from the exiting states, pass `'from'` as `pathName`. + * The resolve data from the `from` path will be returned. + * #### Example: + * ```js + * .onExit({ exiting: 'foo.bar' }, trans => { + * // Gets the resolve value of `myResolve` from the state being exited + * var fooData = trans.injector(null, 'from').get('myResolve'); + * }); + * ``` + * + * + * @param state Limits the resolves provided to only the resolves the provided state has access to. + * @param pathName Default: `'to'`: Chooses the path for which to create the injector. Use this to access resolves for `exiting` states. + * + * @returns a [[UIInjector]] + */ + Transition.prototype.injector = function (state, pathName) { + if (pathName === void 0) { pathName = 'to'; } + var path = this._treeChanges[pathName]; + if (state) + path = _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.subPath(path, function (node) { return node.state === state || node.state.name === state; }); + return new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_13__.ResolveContext(path).injector(); + }; + /** + * Gets all available resolve tokens (keys) + * + * This method can be used in conjunction with [[injector]] to inspect the resolve values + * available to the Transition. + * + * This returns all the tokens defined on [[StateDeclaration.resolve]] blocks, for the states + * in the Transition's [[TreeChanges.to]] path. + * + * #### Example: + * This example logs all resolve values + * ```js + * let tokens = trans.getResolveTokens(); + * tokens.forEach(token => console.log(token + " = " + trans.injector().get(token))); + * ``` + * + * #### Example: + * This example creates promises for each resolve value. + * This triggers fetches of resolves (if any have not yet been fetched). + * When all promises have all settled, it logs the resolve values. + * ```js + * let tokens = trans.getResolveTokens(); + * let promise = tokens.map(token => trans.injector().getAsync(token)); + * Promise.all(promises).then(values => console.log("Resolved values: " + values)); + * ``` + * + * Note: Angular 1 users whould use `$q.all()` + * + * @param pathname resolve context's path name (e.g., `to` or `from`) + * + * @returns an array of resolve tokens (keys) + */ + Transition.prototype.getResolveTokens = function (pathname) { + if (pathname === void 0) { pathname = 'to'; } + return new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_13__.ResolveContext(this._treeChanges[pathname]).getTokens(); + }; + /** + * Dynamically adds a new [[Resolvable]] (i.e., [[StateDeclaration.resolve]]) to this transition. + * + * Allows a transition hook to dynamically add a Resolvable to this Transition. + * + * Use the [[Transition.injector]] to retrieve the resolved data in subsequent hooks ([[UIInjector.get]]). + * + * If a `state` argument is provided, the Resolvable is processed when that state is being entered. + * If no `state` is provided then the root state is used. + * If the given `state` has already been entered, the Resolvable is processed when any child state is entered. + * If no child states will be entered, the Resolvable is processed during the `onFinish` phase of the Transition. + * + * The `state` argument also scopes the resolved data. + * The resolved data is available from the injector for that `state` and any children states. + * + * #### Example: + * ```js + * transitionService.onBefore({}, transition => { + * transition.addResolvable({ + * token: 'myResolve', + * deps: ['MyService'], + * resolveFn: myService => myService.getData() + * }); + * }); + * ``` + * + * @param resolvable a [[ResolvableLiteral]] object (or a [[Resolvable]]) + * @param state the state in the "to path" which should receive the new resolve (otherwise, the root state) + */ + Transition.prototype.addResolvable = function (resolvable, state) { + if (state === void 0) { state = ''; } + resolvable = (0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.is)(_resolve_resolvable__WEBPACK_IMPORTED_MODULE_12__.Resolvable)(resolvable) ? resolvable : new _resolve_resolvable__WEBPACK_IMPORTED_MODULE_12__.Resolvable(resolvable); + var stateName = typeof state === 'string' ? state : state.name; + var topath = this._treeChanges.to; + var targetNode = (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.find)(topath, function (node) { return node.state.name === stateName; }); + var resolveContext = new _resolve_resolveContext__WEBPACK_IMPORTED_MODULE_13__.ResolveContext(topath); + resolveContext.addResolvables([resolvable], targetNode.state); + }; + /** + * Gets the transition from which this transition was redirected. + * + * If the current transition is a redirect, this method returns the transition that was redirected. + * + * #### Example: + * ```js + * let transitionA = $state.go('A').transition + * transitionA.onStart({}, () => $state.target('B')); + * $transitions.onSuccess({ to: 'B' }, (trans) => { + * trans.to().name === 'B'; // true + * trans.redirectedFrom() === transitionA; // true + * }); + * ``` + * + * @returns The previous Transition, or null if this Transition is not the result of a redirection + */ + Transition.prototype.redirectedFrom = function () { + return this._options.redirectedFrom || null; + }; + /** + * Gets the original transition in a redirect chain + * + * A transition might belong to a long chain of multiple redirects. + * This method walks the [[redirectedFrom]] chain back to the original (first) transition in the chain. + * + * #### Example: + * ```js + * // states + * registry.register({ name: 'A', redirectTo: 'B' }); + * registry.register({ name: 'B', redirectTo: 'C' }); + * registry.register({ name: 'C', redirectTo: 'D' }); + * registry.register({ name: 'D' }); + * + * let transitionA = $state.go('A').transition + * + * $transitions.onSuccess({ to: 'D' }, (trans) => { + * trans.to().name === 'D'; // true + * trans.redirectedFrom().to().name === 'C'; // true + * trans.originalTransition() === transitionA; // true + * trans.originalTransition().to().name === 'A'; // true + * }); + * ``` + * + * @returns The original Transition that started a redirect chain + */ + Transition.prototype.originalTransition = function () { + var rf = this.redirectedFrom(); + return (rf && rf.originalTransition()) || this; + }; + /** + * Get the transition options + * + * @returns the options for this Transition. + */ + Transition.prototype.options = function () { + return this._options; + }; + /** + * Gets the states being entered. + * + * @returns an array of states that will be entered during this transition. + */ + Transition.prototype.entering = function () { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.map)(this._treeChanges.entering, (0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('state')).map(stateSelf); + }; + /** + * Gets the states being exited. + * + * @returns an array of states that will be exited during this transition. + */ + Transition.prototype.exiting = function () { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.map)(this._treeChanges.exiting, (0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('state')).map(stateSelf).reverse(); + }; + /** + * Gets the states being retained. + * + * @returns an array of states that are already entered from a previous Transition, that will not be + * exited during this Transition + */ + Transition.prototype.retained = function () { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.map)(this._treeChanges.retained, (0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('state')).map(stateSelf); + }; + /** + * Get the [[ViewConfig]]s associated with this Transition + * + * Each state can define one or more views (template/controller), which are encapsulated as `ViewConfig` objects. + * This method fetches the `ViewConfigs` for a given path in the Transition (e.g., "to" or "entering"). + * + * @param pathname the name of the path to fetch views for: + * (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`) + * @param state If provided, only returns the `ViewConfig`s for a single state in the path + * + * @returns a list of ViewConfig objects for the given path. + */ + Transition.prototype.views = function (pathname, state) { + if (pathname === void 0) { pathname = 'entering'; } + var path = this._treeChanges[pathname]; + path = !state ? path : path.filter((0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.propEq)('state', state)); + return path.map((0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('views')).filter(_common_common__WEBPACK_IMPORTED_MODULE_3__.identity).reduce(_common_common__WEBPACK_IMPORTED_MODULE_3__.unnestR, []); + }; + Transition.prototype.treeChanges = function (pathname) { + return pathname ? this._treeChanges[pathname] : this._treeChanges; + }; + /** + * Creates a new transition that is a redirection of the current one. + * + * This transition can be returned from a [[TransitionService]] hook to + * redirect a transition to a new state and/or set of parameters. + * + * @internal + * + * @returns Returns a new [[Transition]] instance. + */ + Transition.prototype.redirect = function (targetState) { + var redirects = 1, trans = this; + // tslint:disable-next-line:no-conditional-assignment + while ((trans = trans.redirectedFrom()) != null) { + if (++redirects > 20) + throw new Error("Too many consecutive Transition redirects (20+)"); + } + var redirectOpts = { redirectedFrom: this, source: 'redirect' }; + // If the original transition was caused by URL sync, then use { location: 'replace' } + // on the new transition (unless the target state explicitly specifies location: false). + // This causes the original url to be replaced with the url for the redirect target + // so the original url disappears from the browser history. + if (this.options().source === 'url' && targetState.options().location !== false) { + redirectOpts.location = 'replace'; + } + var newOptions = (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.extend)({}, this.options(), targetState.options(), redirectOpts); + targetState = targetState.withOptions(newOptions, true); + var newTransition = this.router.transitionService.create(this._treeChanges.from, targetState); + var originalEnteringNodes = this._treeChanges.entering; + var redirectEnteringNodes = newTransition._treeChanges.entering; + // --- Re-use resolve data from original transition --- + // When redirecting from a parent state to a child state where the parent parameter values haven't changed + // (because of the redirect), the resolves fetched by the original transition are still valid in the + // redirected transition. + // + // This allows you to define a redirect on a parent state which depends on an async resolve value. + // You can wait for the resolve, then redirect to a child state based on the result. + // The redirected transition does not have to re-fetch the resolve. + // --------------------------------------------------------- + var nodeIsReloading = function (reloadState) { return function (node) { + return reloadState && node.state.includes[reloadState.name]; + }; }; + // Find any "entering" nodes in the redirect path that match the original path and aren't being reloaded + var matchingEnteringNodes = _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.matching(redirectEnteringNodes, originalEnteringNodes, _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.nonDynamicParams).filter((0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.not)(nodeIsReloading(targetState.options().reloadState))); + // Use the existing (possibly pre-resolved) resolvables for the matching entering nodes. + matchingEnteringNodes.forEach(function (node, idx) { + node.resolvables = originalEnteringNodes[idx].resolvables; + }); + return newTransition; + }; + /** @internal If a transition doesn't exit/enter any states, returns any [[Param]] whose value changed */ + Transition.prototype._changedParams = function () { + var tc = this._treeChanges; + /** Return undefined if it's not a "dynamic" transition, for the following reasons */ + // If user explicitly wants a reload + if (this._options.reload) + return undefined; + // If any states are exiting or entering + if (tc.exiting.length || tc.entering.length) + return undefined; + // If to/from path lengths differ + if (tc.to.length !== tc.from.length) + return undefined; + // If the to/from paths are different + var pathsDiffer = (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.arrayTuples)(tc.to, tc.from) + .map(function (tuple) { return tuple[0].state !== tuple[1].state; }) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_3__.anyTrueR, false); + if (pathsDiffer) + return undefined; + // Find any parameter values that differ + var nodeSchemas = tc.to.map(function (node) { return node.paramSchema; }); + var _a = [tc.to, tc.from].map(function (path) { return path.map(function (x) { return x.paramValues; }); }), toValues = _a[0], fromValues = _a[1]; + var tuples = (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.arrayTuples)(nodeSchemas, toValues, fromValues); + return tuples.map(function (_a) { + var schema = _a[0], toVals = _a[1], fromVals = _a[2]; + return _params_param__WEBPACK_IMPORTED_MODULE_11__.Param.changed(schema, toVals, fromVals); + }).reduce(_common_common__WEBPACK_IMPORTED_MODULE_3__.unnestR, []); + }; + /** + * Returns true if the transition is dynamic. + * + * A transition is dynamic if no states are entered nor exited, but at least one dynamic parameter has changed. + * + * @returns true if the Transition is dynamic + */ + Transition.prototype.dynamic = function () { + var changes = this._changedParams(); + return !changes ? false : changes.map(function (x) { return x.dynamic; }).reduce(_common_common__WEBPACK_IMPORTED_MODULE_3__.anyTrueR, false); + }; + /** + * Returns true if the transition is ignored. + * + * A transition is ignored if no states are entered nor exited, and no parameter values have changed. + * + * @returns true if the Transition is ignored. + */ + Transition.prototype.ignored = function () { + return !!this._ignoredReason(); + }; + /** @internal */ + Transition.prototype._ignoredReason = function () { + var pending = this.router.globals.transition; + var reloadState = this._options.reloadState; + var same = function (pathA, pathB) { + if (pathA.length !== pathB.length) + return false; + var matching = _path_pathUtils__WEBPACK_IMPORTED_MODULE_10__.PathUtils.matching(pathA, pathB); + return pathA.length === matching.filter(function (node) { return !reloadState || !node.state.includes[reloadState.name]; }).length; + }; + var newTC = this.treeChanges(); + var pendTC = pending && pending.treeChanges(); + if (pendTC && same(pendTC.to, newTC.to) && same(pendTC.exiting, newTC.exiting)) + return 'SameAsPending'; + if (newTC.exiting.length === 0 && newTC.entering.length === 0 && same(newTC.from, newTC.to)) + return 'SameAsCurrent'; + }; + /** + * Runs the transition + * + * This method is generally called from the [[StateService.transitionTo]] + * + * @internal + * + * @returns a promise for a successful transition. + */ + Transition.prototype.run = function () { + var _this = this; + var runAllHooks = _transitionHook__WEBPACK_IMPORTED_MODULE_7__.TransitionHook.runAllHooks; + // Gets transition hooks array for the given phase + var getHooksFor = function (phase) { return _this._hookBuilder.buildHooksForPhase(phase); }; + // When the chain is complete, then resolve or reject the deferred + var transitionSuccess = function () { + _common_trace__WEBPACK_IMPORTED_MODULE_0__.trace.traceSuccess(_this.$to(), _this); + _this.success = true; + _this._deferred.resolve(_this.to()); + runAllHooks(getHooksFor(_interface__WEBPACK_IMPORTED_MODULE_6__.TransitionHookPhase.SUCCESS)); + }; + var transitionError = function (reason) { + _common_trace__WEBPACK_IMPORTED_MODULE_0__.trace.traceError(reason, _this); + _this.success = false; + _this._deferred.reject(reason); + _this._error = reason; + runAllHooks(getHooksFor(_interface__WEBPACK_IMPORTED_MODULE_6__.TransitionHookPhase.ERROR)); + }; + var runTransition = function () { + // Wait to build the RUN hook chain until the BEFORE hooks are done + // This allows a BEFORE hook to dynamically add additional RUN hooks via the Transition object. + var allRunHooks = getHooksFor(_interface__WEBPACK_IMPORTED_MODULE_6__.TransitionHookPhase.RUN); + var done = function () { return _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q.when(undefined); }; + return _transitionHook__WEBPACK_IMPORTED_MODULE_7__.TransitionHook.invokeHooks(allRunHooks, done); + }; + var startTransition = function () { + var globals = _this.router.globals; + globals.lastStartedTransitionId = _this.$id; + globals.transition = _this; + globals.transitionHistory.enqueue(_this); + _common_trace__WEBPACK_IMPORTED_MODULE_0__.trace.traceTransitionStart(_this); + return _common_coreservices__WEBPACK_IMPORTED_MODULE_1__.services.$q.when(undefined); + }; + var allBeforeHooks = getHooksFor(_interface__WEBPACK_IMPORTED_MODULE_6__.TransitionHookPhase.BEFORE); + _transitionHook__WEBPACK_IMPORTED_MODULE_7__.TransitionHook.invokeHooks(allBeforeHooks, startTransition) + .then(runTransition) + .then(transitionSuccess, transitionError); + return this.promise; + }; + /** + * Checks if the Transition is valid + * + * @returns true if the Transition is valid + */ + Transition.prototype.valid = function () { + return !this.error() || this.success !== undefined; + }; + /** + * Aborts this transition + * + * Imperative API to abort a Transition. + * This only applies to Transitions that are not yet complete. + */ + Transition.prototype.abort = function () { + // Do not set flag if the transition is already complete + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isUndefined)(this.success)) { + this._aborted = true; + } + }; + /** + * The Transition error reason. + * + * If the transition is invalid (and could not be run), returns the reason the transition is invalid. + * If the transition was valid and ran, but was not successful, returns the reason the transition failed. + * + * @returns a transition rejection explaining why the transition is invalid, or the reason the transition failed. + */ + Transition.prototype.error = function () { + var state = this.$to(); + if (state.self.abstract) { + return _rejectFactory__WEBPACK_IMPORTED_MODULE_14__.Rejection.invalid("Cannot transition to abstract state '" + state.name + "'"); + } + var paramDefs = state.parameters(); + var values = this.params(); + var invalidParams = paramDefs.filter(function (param) { return !param.validates(values[param.id]); }); + if (invalidParams.length) { + var invalidValues = invalidParams.map(function (param) { return "[" + param.id + ":" + (0,_common_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(values[param.id]) + "]"; }).join(', '); + var detail = "The following parameter values are not valid for state '" + state.name + "': " + invalidValues; + return _rejectFactory__WEBPACK_IMPORTED_MODULE_14__.Rejection.invalid(detail); + } + if (this.success === false) + return this._error; + }; + /** + * A string representation of the Transition + * + * @returns A string representation of the Transition + */ + Transition.prototype.toString = function () { + var fromStateOrName = this.from(); + var toStateOrName = this.to(); + var avoidEmptyHash = function (params) { + return params['#'] !== null && params['#'] !== undefined ? params : (0,_common_common__WEBPACK_IMPORTED_MODULE_3__.omit)(params, ['#']); + }; + // (X) means the to state is invalid. + var id = this.$id, from = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isObject)(fromStateOrName) ? fromStateOrName.name : fromStateOrName, fromParams = (0,_common_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(avoidEmptyHash(this._treeChanges.from.map((0,_common_hof__WEBPACK_IMPORTED_MODULE_5__.prop)('paramValues')).reduce(_common_common__WEBPACK_IMPORTED_MODULE_3__.mergeR, {}))), toValid = this.valid() ? '' : '(X) ', to = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_4__.isObject)(toStateOrName) ? toStateOrName.name : toStateOrName, toParams = (0,_common_strings__WEBPACK_IMPORTED_MODULE_2__.stringify)(avoidEmptyHash(this.params())); + return "Transition#" + id + "( '" + from + "'" + fromParams + " -> " + toValid + "'" + to + "'" + toParams + " )"; + }; + /** @internal */ + Transition.diToken = Transition; + return Transition; +}()); + +//# sourceMappingURL=transition.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/transitionEventType.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/transitionEventType.js ***! + \*******************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "TransitionEventType": () => (/* binding */ TransitionEventType) +/* harmony export */ }); +/* harmony import */ var _transitionHook__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transitionHook */ "./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js"); + +/** + * This class defines a type of hook, such as `onBefore` or `onEnter`. + * Plugins can define custom hook types, such as sticky states does for `onInactive`. + */ +var TransitionEventType = /** @class */ (function () { + /* tslint:disable:no-inferrable-types */ + function TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous) { + if (reverseSort === void 0) { reverseSort = false; } + if (getResultHandler === void 0) { getResultHandler = _transitionHook__WEBPACK_IMPORTED_MODULE_0__.TransitionHook.HANDLE_RESULT; } + if (getErrorHandler === void 0) { getErrorHandler = _transitionHook__WEBPACK_IMPORTED_MODULE_0__.TransitionHook.REJECT_ERROR; } + if (synchronous === void 0) { synchronous = false; } + this.name = name; + this.hookPhase = hookPhase; + this.hookOrder = hookOrder; + this.criteriaMatchPath = criteriaMatchPath; + this.reverseSort = reverseSort; + this.getResultHandler = getResultHandler; + this.getErrorHandler = getErrorHandler; + this.synchronous = synchronous; + } + return TransitionEventType; +}()); + +//# sourceMappingURL=transitionEventType.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js": +/*!**************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js ***! + \**************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "TransitionHook": () => (/* binding */ TransitionHook) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/transition/interface.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); +/* harmony import */ var _rejectFactory__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./rejectFactory */ "./node_modules/@uirouter/core/lib-esm/transition/rejectFactory.js"); +/* harmony import */ var _state_targetState__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../state/targetState */ "./node_modules/@uirouter/core/lib-esm/state/targetState.js"); + + + + + + + + + +var defaultOptions = { + current: _common_common__WEBPACK_IMPORTED_MODULE_1__.noop, + transition: null, + traceData: {}, + bind: null, +}; +var TransitionHook = /** @class */ (function () { + function TransitionHook(transition, stateContext, registeredHook, options) { + var _this = this; + this.transition = transition; + this.stateContext = stateContext; + this.registeredHook = registeredHook; + this.options = options; + this.isSuperseded = function () { return _this.type.hookPhase === _interface__WEBPACK_IMPORTED_MODULE_0__.TransitionHookPhase.RUN && !_this.options.transition.isActive(); }; + this.options = (0,_common_common__WEBPACK_IMPORTED_MODULE_1__.defaults)(options, defaultOptions); + this.type = registeredHook.eventType; + } + /** + * Chains together an array of TransitionHooks. + * + * Given a list of [[TransitionHook]] objects, chains them together. + * Each hook is invoked after the previous one completes. + * + * #### Example: + * ```js + * var hooks: TransitionHook[] = getHooks(); + * let promise: Promise = TransitionHook.chain(hooks); + * + * promise.then(handleSuccess, handleError); + * ``` + * + * @param hooks the list of hooks to chain together + * @param waitFor if provided, the chain is `.then()`'ed off this promise + * @returns a `Promise` for sequentially invoking the hooks (in order) + */ + TransitionHook.chain = function (hooks, waitFor) { + // Chain the next hook off the previous + var createHookChainR = function (prev, nextHook) { return prev.then(function () { return nextHook.invokeHook(); }); }; + return hooks.reduce(createHookChainR, waitFor || _common_coreservices__WEBPACK_IMPORTED_MODULE_6__.services.$q.when()); + }; + /** + * Invokes all the provided TransitionHooks, in order. + * Each hook's return value is checked. + * If any hook returns a promise, then the rest of the hooks are chained off that promise, and the promise is returned. + * If no hook returns a promise, then all hooks are processed synchronously. + * + * @param hooks the list of TransitionHooks to invoke + * @param doneCallback a callback that is invoked after all the hooks have successfully completed + * + * @returns a promise for the async result, or the result of the callback + */ + TransitionHook.invokeHooks = function (hooks, doneCallback) { + for (var idx = 0; idx < hooks.length; idx++) { + var hookResult = hooks[idx].invokeHook(); + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isPromise)(hookResult)) { + var remainingHooks = hooks.slice(idx + 1); + return TransitionHook.chain(remainingHooks, hookResult).then(doneCallback); + } + } + return doneCallback(); + }; + /** + * Run all TransitionHooks, ignoring their return value. + */ + TransitionHook.runAllHooks = function (hooks) { + hooks.forEach(function (hook) { return hook.invokeHook(); }); + }; + TransitionHook.prototype.logError = function (err) { + this.transition.router.stateService.defaultErrorHandler()(err); + }; + TransitionHook.prototype.invokeHook = function () { + var _this = this; + var hook = this.registeredHook; + if (hook._deregistered) + return; + var notCurrent = this.getNotCurrentRejection(); + if (notCurrent) + return notCurrent; + var options = this.options; + _common_trace__WEBPACK_IMPORTED_MODULE_5__.trace.traceHookInvocation(this, this.transition, options); + var invokeCallback = function () { return hook.callback.call(options.bind, _this.transition, _this.stateContext); }; + var normalizeErr = function (err) { return _rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.normalize(err).toPromise(); }; + var handleError = function (err) { return hook.eventType.getErrorHandler(_this)(err); }; + var handleResult = function (result) { return hook.eventType.getResultHandler(_this)(result); }; + try { + var result = invokeCallback(); + if (!this.type.synchronous && (0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isPromise)(result)) { + return result.catch(normalizeErr).then(handleResult, handleError); + } + else { + return handleResult(result); + } + } + catch (err) { + // If callback throws (synchronously) + return handleError(_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.normalize(err)); + } + finally { + if (hook.invokeLimit && ++hook.invokeCount >= hook.invokeLimit) { + hook.deregister(); + } + } + }; + /** + * This method handles the return value of a Transition Hook. + * + * A hook can return false (cancel), a TargetState (redirect), + * or a promise (which may later resolve to false or a redirect) + * + * This also handles "transition superseded" -- when a new transition + * was started while the hook was still running + */ + TransitionHook.prototype.handleHookResult = function (result) { + var _this = this; + var notCurrent = this.getNotCurrentRejection(); + if (notCurrent) + return notCurrent; + // Hook returned a promise + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isPromise)(result)) { + // Wait for the promise, then reprocess with the resulting value + return result.then(function (val) { return _this.handleHookResult(val); }); + } + _common_trace__WEBPACK_IMPORTED_MODULE_5__.trace.traceHookResult(result, this.transition, this.options); + // Hook returned false + if (result === false) { + // Abort this Transition + return _rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.aborted('Hook aborted transition').toPromise(); + } + var isTargetState = (0,_common_hof__WEBPACK_IMPORTED_MODULE_4__.is)(_state_targetState__WEBPACK_IMPORTED_MODULE_8__.TargetState); + // hook returned a TargetState + if (isTargetState(result)) { + // Halt the current Transition and redirect (a new Transition) to the TargetState. + return _rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.redirected(result).toPromise(); + } + }; + /** + * Return a Rejection promise if the transition is no longer current due + * to a stopped router (disposed), or a new transition has started and superseded this one. + */ + TransitionHook.prototype.getNotCurrentRejection = function () { + var router = this.transition.router; + // The router is stopped + if (router._disposed) { + return _rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.aborted("UIRouter instance #" + router.$id + " has been stopped (disposed)").toPromise(); + } + if (this.transition._aborted) { + return _rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.aborted().toPromise(); + } + // This transition is no longer current. + // Another transition started while this hook was still running. + if (this.isSuperseded()) { + // Abort this transition + return _rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.superseded(this.options.current()).toPromise(); + } + }; + TransitionHook.prototype.toString = function () { + var _a = this, options = _a.options, registeredHook = _a.registeredHook; + var event = (0,_common_hof__WEBPACK_IMPORTED_MODULE_4__.parse)('traceData.hookType')(options) || 'internal', context = (0,_common_hof__WEBPACK_IMPORTED_MODULE_4__.parse)('traceData.context.state.name')(options) || (0,_common_hof__WEBPACK_IMPORTED_MODULE_4__.parse)('traceData.context')(options) || 'unknown', name = (0,_common_strings__WEBPACK_IMPORTED_MODULE_2__.fnToString)(registeredHook.callback); + return event + " context: " + context + ", " + (0,_common_strings__WEBPACK_IMPORTED_MODULE_2__.maxLength)(200, name); + }; + /** + * These GetResultHandler(s) are used by [[invokeHook]] below + * Each HookType chooses a GetResultHandler (See: [[TransitionService._defineCoreEvents]]) + */ + TransitionHook.HANDLE_RESULT = function (hook) { return function (result) { + return hook.handleHookResult(result); + }; }; + /** + * If the result is a promise rejection, log it. + * Otherwise, ignore the result. + */ + TransitionHook.LOG_REJECTED_RESULT = function (hook) { return function (result) { + (0,_common_predicates__WEBPACK_IMPORTED_MODULE_3__.isPromise)(result) && result.catch(function (err) { return hook.logError(_rejectFactory__WEBPACK_IMPORTED_MODULE_7__.Rejection.normalize(err)); }); + return undefined; + }; }; + /** + * These GetErrorHandler(s) are used by [[invokeHook]] below + * Each HookType chooses a GetErrorHandler (See: [[TransitionService._defineCoreEvents]]) + */ + TransitionHook.LOG_ERROR = function (hook) { return function (error) { return hook.logError(error); }; }; + TransitionHook.REJECT_ERROR = function (hook) { return function (error) { return (0,_common_common__WEBPACK_IMPORTED_MODULE_1__.silentRejection)(error); }; }; + TransitionHook.THROW_ERROR = function (hook) { return function (error) { + throw error; + }; }; + return TransitionHook; +}()); + +//# sourceMappingURL=transitionHook.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/transition/transitionService.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/transition/transitionService.js ***! + \*****************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "defaultTransOpts": () => (/* binding */ defaultTransOpts), +/* harmony export */ "TransitionService": () => (/* binding */ TransitionService) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/transition/interface.js"); +/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition */ "./node_modules/@uirouter/core/lib-esm/transition/transition.js"); +/* harmony import */ var _hookRegistry__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./hookRegistry */ "./node_modules/@uirouter/core/lib-esm/transition/hookRegistry.js"); +/* harmony import */ var _hooks_coreResolvables__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../hooks/coreResolvables */ "./node_modules/@uirouter/core/lib-esm/hooks/coreResolvables.js"); +/* harmony import */ var _hooks_redirectTo__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../hooks/redirectTo */ "./node_modules/@uirouter/core/lib-esm/hooks/redirectTo.js"); +/* harmony import */ var _hooks_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../hooks/onEnterExitRetain */ "./node_modules/@uirouter/core/lib-esm/hooks/onEnterExitRetain.js"); +/* harmony import */ var _hooks_resolve__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../hooks/resolve */ "./node_modules/@uirouter/core/lib-esm/hooks/resolve.js"); +/* harmony import */ var _hooks_views__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../hooks/views */ "./node_modules/@uirouter/core/lib-esm/hooks/views.js"); +/* harmony import */ var _hooks_updateGlobals__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../hooks/updateGlobals */ "./node_modules/@uirouter/core/lib-esm/hooks/updateGlobals.js"); +/* harmony import */ var _hooks_url__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../hooks/url */ "./node_modules/@uirouter/core/lib-esm/hooks/url.js"); +/* harmony import */ var _hooks_lazyLoad__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../hooks/lazyLoad */ "./node_modules/@uirouter/core/lib-esm/hooks/lazyLoad.js"); +/* harmony import */ var _transitionEventType__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./transitionEventType */ "./node_modules/@uirouter/core/lib-esm/transition/transitionEventType.js"); +/* harmony import */ var _transitionHook__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./transitionHook */ "./node_modules/@uirouter/core/lib-esm/transition/transitionHook.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _hooks_ignoredTransition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../hooks/ignoredTransition */ "./node_modules/@uirouter/core/lib-esm/hooks/ignoredTransition.js"); +/* harmony import */ var _hooks_invalidTransition__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../hooks/invalidTransition */ "./node_modules/@uirouter/core/lib-esm/hooks/invalidTransition.js"); + + + + + + + + + + + + + + + + + + +/** + * The default [[Transition]] options. + * + * Include this object when applying custom defaults: + * let reloadOpts = { reload: true, notify: true } + * let options = defaults(theirOpts, customDefaults, defaultOptions); + */ +var defaultTransOpts = { + location: true, + relative: null, + inherit: false, + notify: true, + reload: false, + supercede: true, + custom: {}, + current: function () { return null; }, + source: 'unknown', +}; +/** + * This class provides services related to Transitions. + * + * - Most importantly, it allows global Transition Hooks to be registered. + * - It allows the default transition error handler to be set. + * - It also has a factory function for creating new [[Transition]] objects, (used internally by the [[StateService]]). + * + * At bootstrap, [[UIRouter]] creates a single instance (singleton) of this class. + * + * This API is located at `router.transitionService` ([[UIRouter.transitionService]]) + */ +var TransitionService = /** @class */ (function () { + /** @internal */ + function TransitionService(_router) { + /** @internal */ + this._transitionCount = 0; + /** The transition hook types, such as `onEnter`, `onStart`, etc */ + this._eventTypes = []; + /** @internal The registered transition hooks */ + this._registeredHooks = {}; + /** The paths on a criteria object */ + this._criteriaPaths = {}; + this._router = _router; + this.$view = _router.viewService; + this._deregisterHookFns = {}; + this._pluginapi = ((0,_common_common__WEBPACK_IMPORTED_MODULE_14__.createProxyFunctions)((0,_common_hof__WEBPACK_IMPORTED_MODULE_15__.val)(this), {}, (0,_common_hof__WEBPACK_IMPORTED_MODULE_15__.val)(this), [ + '_definePathType', + '_defineEvent', + '_getPathTypes', + '_getEvents', + 'getHooks', + ])); + this._defineCorePaths(); + this._defineCoreEvents(); + this._registerCoreTransitionHooks(); + _router.globals.successfulTransitions.onEvict(_hooks_coreResolvables__WEBPACK_IMPORTED_MODULE_3__.treeChangesCleanup); + } + /** + * Registers a [[TransitionHookFn]], called *while a transition is being constructed*. + * + * Registers a transition lifecycle hook, which is invoked during transition construction. + * + * This low level hook should only be used by plugins. + * This can be a useful time for plugins to add resolves or mutate the transition as needed. + * The Sticky States plugin uses this hook to modify the treechanges. + * + * ### Lifecycle + * + * `onCreate` hooks are invoked *while a transition is being constructed*. + * + * ### Return value + * + * The hook's return value is ignored + * + * @internal + * @param criteria defines which Transitions the Hook should be invoked for. + * @param callback the hook function which will be invoked. + * @param options the registration options + * @returns a function which deregisters the hook. + */ + TransitionService.prototype.onCreate = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onBefore = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onStart = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onExit = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onRetain = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onEnter = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onFinish = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onSuccess = function (criteria, callback, options) { + return; + }; + /** @inheritdoc */ + TransitionService.prototype.onError = function (criteria, callback, options) { + return; + }; + /** + * dispose + * @internal + */ + TransitionService.prototype.dispose = function (router) { + (0,_common_common__WEBPACK_IMPORTED_MODULE_14__.values)(this._registeredHooks).forEach(function (hooksArray) { + return hooksArray.forEach(function (hook) { + hook._deregistered = true; + (0,_common_common__WEBPACK_IMPORTED_MODULE_14__.removeFrom)(hooksArray, hook); + }); + }); + }; + /** + * Creates a new [[Transition]] object + * + * This is a factory function for creating new Transition objects. + * It is used internally by the [[StateService]] and should generally not be called by application code. + * + * @internal + * @param fromPath the path to the current state (the from state) + * @param targetState the target state (destination) + * @returns a Transition + */ + TransitionService.prototype.create = function (fromPath, targetState) { + return new _transition__WEBPACK_IMPORTED_MODULE_1__.Transition(fromPath, targetState, this._router); + }; + /** @internal */ + TransitionService.prototype._defineCoreEvents = function () { + var Phase = _interface__WEBPACK_IMPORTED_MODULE_0__.TransitionHookPhase; + var TH = _transitionHook__WEBPACK_IMPORTED_MODULE_12__.TransitionHook; + var paths = this._criteriaPaths; + var NORMAL_SORT = false, REVERSE_SORT = true; + var SYNCHRONOUS = true; + this._defineEvent('onCreate', Phase.CREATE, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.THROW_ERROR, SYNCHRONOUS); + this._defineEvent('onBefore', Phase.BEFORE, 0, paths.to); + this._defineEvent('onStart', Phase.RUN, 0, paths.to); + this._defineEvent('onExit', Phase.RUN, 100, paths.exiting, REVERSE_SORT); + this._defineEvent('onRetain', Phase.RUN, 200, paths.retained); + this._defineEvent('onEnter', Phase.RUN, 300, paths.entering); + this._defineEvent('onFinish', Phase.RUN, 400, paths.to); + this._defineEvent('onSuccess', Phase.SUCCESS, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.LOG_ERROR, SYNCHRONOUS); + this._defineEvent('onError', Phase.ERROR, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.LOG_ERROR, SYNCHRONOUS); + }; + /** @internal */ + TransitionService.prototype._defineCorePaths = function () { + var STATE = _interface__WEBPACK_IMPORTED_MODULE_0__.TransitionHookScope.STATE, TRANSITION = _interface__WEBPACK_IMPORTED_MODULE_0__.TransitionHookScope.TRANSITION; + this._definePathType('to', TRANSITION); + this._definePathType('from', TRANSITION); + this._definePathType('exiting', STATE); + this._definePathType('retained', STATE); + this._definePathType('entering', STATE); + }; + /** @internal */ + TransitionService.prototype._defineEvent = function (name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous) { + if (reverseSort === void 0) { reverseSort = false; } + if (getResultHandler === void 0) { getResultHandler = _transitionHook__WEBPACK_IMPORTED_MODULE_12__.TransitionHook.HANDLE_RESULT; } + if (getErrorHandler === void 0) { getErrorHandler = _transitionHook__WEBPACK_IMPORTED_MODULE_12__.TransitionHook.REJECT_ERROR; } + if (synchronous === void 0) { synchronous = false; } + var eventType = new _transitionEventType__WEBPACK_IMPORTED_MODULE_11__.TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous); + this._eventTypes.push(eventType); + (0,_hookRegistry__WEBPACK_IMPORTED_MODULE_2__.makeEvent)(this, this, eventType); + }; + /** @internal */ + TransitionService.prototype._getEvents = function (phase) { + var transitionHookTypes = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_13__.isDefined)(phase) + ? this._eventTypes.filter(function (type) { return type.hookPhase === phase; }) + : this._eventTypes.slice(); + return transitionHookTypes.sort(function (l, r) { + var cmpByPhase = l.hookPhase - r.hookPhase; + return cmpByPhase === 0 ? l.hookOrder - r.hookOrder : cmpByPhase; + }); + }; + /** + * Adds a Path to be used as a criterion against a TreeChanges path + * + * For example: the `exiting` path in [[HookMatchCriteria]] is a STATE scoped path. + * It was defined by calling `defineTreeChangesCriterion('exiting', TransitionHookScope.STATE)` + * Each state in the exiting path is checked against the criteria and returned as part of the match. + * + * Another example: the `to` path in [[HookMatchCriteria]] is a TRANSITION scoped path. + * It was defined by calling `defineTreeChangesCriterion('to', TransitionHookScope.TRANSITION)` + * Only the tail of the `to` path is checked against the criteria and returned as part of the match. + * + * @internal + */ + TransitionService.prototype._definePathType = function (name, hookScope) { + this._criteriaPaths[name] = { name: name, scope: hookScope }; + }; + /** @internal */ + // tslint:disable-next-line + TransitionService.prototype._getPathTypes = function () { + return this._criteriaPaths; + }; + /** @internal */ + TransitionService.prototype.getHooks = function (hookName) { + return this._registeredHooks[hookName]; + }; + /** @internal */ + TransitionService.prototype._registerCoreTransitionHooks = function () { + var fns = this._deregisterHookFns; + fns.addCoreResolves = (0,_hooks_coreResolvables__WEBPACK_IMPORTED_MODULE_3__.registerAddCoreResolvables)(this); + fns.ignored = (0,_hooks_ignoredTransition__WEBPACK_IMPORTED_MODULE_16__.registerIgnoredTransitionHook)(this); + fns.invalid = (0,_hooks_invalidTransition__WEBPACK_IMPORTED_MODULE_17__.registerInvalidTransitionHook)(this); + // Wire up redirectTo hook + fns.redirectTo = (0,_hooks_redirectTo__WEBPACK_IMPORTED_MODULE_4__.registerRedirectToHook)(this); + // Wire up onExit/Retain/Enter state hooks + fns.onExit = (0,_hooks_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__.registerOnExitHook)(this); + fns.onRetain = (0,_hooks_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__.registerOnRetainHook)(this); + fns.onEnter = (0,_hooks_onEnterExitRetain__WEBPACK_IMPORTED_MODULE_5__.registerOnEnterHook)(this); + // Wire up Resolve hooks + fns.eagerResolve = (0,_hooks_resolve__WEBPACK_IMPORTED_MODULE_6__.registerEagerResolvePath)(this); + fns.lazyResolve = (0,_hooks_resolve__WEBPACK_IMPORTED_MODULE_6__.registerLazyResolveState)(this); + fns.resolveAll = (0,_hooks_resolve__WEBPACK_IMPORTED_MODULE_6__.registerResolveRemaining)(this); + // Wire up the View management hooks + fns.loadViews = (0,_hooks_views__WEBPACK_IMPORTED_MODULE_7__.registerLoadEnteringViews)(this); + fns.activateViews = (0,_hooks_views__WEBPACK_IMPORTED_MODULE_7__.registerActivateViews)(this); + // Updates global state after a transition + fns.updateGlobals = (0,_hooks_updateGlobals__WEBPACK_IMPORTED_MODULE_8__.registerUpdateGlobalState)(this); + // After globals.current is updated at priority: 10000 + fns.updateUrl = (0,_hooks_url__WEBPACK_IMPORTED_MODULE_9__.registerUpdateUrl)(this); + // Lazy load state trees + fns.lazyLoad = (0,_hooks_lazyLoad__WEBPACK_IMPORTED_MODULE_10__.registerLazyLoadHook)(this); + }; + return TransitionService; +}()); + +//# sourceMappingURL=transitionService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/index.js": +/*!**********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/index.js ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlMatcher": () => (/* reexport safe */ _urlMatcher__WEBPACK_IMPORTED_MODULE_1__.UrlMatcher), +/* harmony export */ "ParamFactory": () => (/* reexport safe */ _urlMatcherFactory__WEBPACK_IMPORTED_MODULE_2__.ParamFactory), +/* harmony export */ "UrlMatcherFactory": () => (/* reexport safe */ _urlMatcherFactory__WEBPACK_IMPORTED_MODULE_2__.UrlMatcherFactory), +/* harmony export */ "UrlRouter": () => (/* reexport safe */ _urlRouter__WEBPACK_IMPORTED_MODULE_3__.UrlRouter), +/* harmony export */ "BaseUrlRule": () => (/* reexport safe */ _urlRule__WEBPACK_IMPORTED_MODULE_4__.BaseUrlRule), +/* harmony export */ "UrlRuleFactory": () => (/* reexport safe */ _urlRule__WEBPACK_IMPORTED_MODULE_4__.UrlRuleFactory), +/* harmony export */ "UrlService": () => (/* reexport safe */ _urlService__WEBPACK_IMPORTED_MODULE_5__.UrlService), +/* harmony export */ "UrlRules": () => (/* reexport safe */ _urlRules__WEBPACK_IMPORTED_MODULE_6__.UrlRules), +/* harmony export */ "UrlConfig": () => (/* reexport safe */ _urlConfig__WEBPACK_IMPORTED_MODULE_7__.UrlConfig) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/url/interface.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_interface__WEBPACK_IMPORTED_MODULE_0__); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _interface__WEBPACK_IMPORTED_MODULE_0__) if(["default","UrlRules","UrlConfig"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _interface__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _urlMatcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./urlMatcher */ "./node_modules/@uirouter/core/lib-esm/url/urlMatcher.js"); +/* harmony import */ var _urlMatcherFactory__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./urlMatcherFactory */ "./node_modules/@uirouter/core/lib-esm/url/urlMatcherFactory.js"); +/* harmony import */ var _urlRouter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./urlRouter */ "./node_modules/@uirouter/core/lib-esm/url/urlRouter.js"); +/* harmony import */ var _urlRule__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./urlRule */ "./node_modules/@uirouter/core/lib-esm/url/urlRule.js"); +/* harmony import */ var _urlService__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./urlService */ "./node_modules/@uirouter/core/lib-esm/url/urlService.js"); +/* harmony import */ var _urlRules__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./urlRules */ "./node_modules/@uirouter/core/lib-esm/url/urlRules.js"); +/* harmony import */ var _urlConfig__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./urlConfig */ "./node_modules/@uirouter/core/lib-esm/url/urlConfig.js"); + + + + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/interface.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/interface.js ***! + \**************************************************************/ +/***/ (() => { + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlConfig.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlConfig.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlConfig": () => (/* binding */ UrlConfig) +/* harmony export */ }); +/* harmony import */ var _params__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../params */ "./node_modules/@uirouter/core/lib-esm/params/index.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + +/** + * An API to customize the URL behavior and retrieve URL configuration + * + * This API is used to customize the behavior of the URL. + * This includes optional trailing slashes ([[strictMode]]), case sensitivity ([[caseInsensitive]]), + * and custom parameter encoding (custom [[type]]). + * + * It also has information about the location (url) configuration such as [[port]] and [[baseHref]]. + * This information can be used to build absolute URLs, such as + * `https://example.com:443/basepath/state/substate?param1=a#hashvalue`; + * + * This API is found at `router.urlService.config` (see: [[UIRouter.urlService]], [[URLService.config]]) + */ +var UrlConfig = /** @class */ (function () { + /** @internal */ function UrlConfig(/** @internal */ router) { + var _this = this; + this.router = router; + /** @internal */ this.paramTypes = new _params__WEBPACK_IMPORTED_MODULE_0__.ParamTypes(); + /** @internal */ this._decodeParams = true; + /** @internal */ this._isCaseInsensitive = false; + /** @internal */ this._isStrictMode = true; + /** @internal */ this._defaultSquashPolicy = false; + /** @internal */ this.dispose = function () { return _this.paramTypes.dispose(); }; + // Delegate these calls to the current LocationConfig implementation + /** + * Gets the base Href, e.g., `http://localhost/approot/` + * + * @return the application's base href + */ + this.baseHref = function () { return _this.router.locationConfig.baseHref(); }; + /** + * Gets or sets the hashPrefix + * + * This only applies when not running in [[html5Mode]] (pushstate mode) + * + * If the current url is `http://localhost/app#!/uirouter/path/#anchor`, it returns `!` which is the prefix for the "hashbang" portion. + * + * @return the hash prefix + */ + this.hashPrefix = function (newprefix) { return _this.router.locationConfig.hashPrefix(newprefix); }; + /** + * Gets the host, e.g., `localhost` + * + * @return the protocol + */ + this.host = function () { return _this.router.locationConfig.host(); }; + /** + * Returns true when running in pushstate mode + * + * @return true when running in html5 mode (pushstate mode). + */ + this.html5Mode = function () { return _this.router.locationConfig.html5Mode(); }; + /** + * Gets the port, e.g., `80` + * + * @return the port number + */ + this.port = function () { return _this.router.locationConfig.port(); }; + /** + * Gets the protocol, e.g., `http` + * + * @return the protocol + */ + this.protocol = function () { return _this.router.locationConfig.protocol(); }; + } + /** + * Defines whether URL matching should be case sensitive (the default behavior), or not. + * + * #### Example: + * ```js + * // Allow case insensitive url matches + * urlService.config.caseInsensitive(true); + * ``` + * + * @param value `false` to match URL in a case sensitive manner; otherwise `true`; + * @returns the current value of caseInsensitive + */ + UrlConfig.prototype.caseInsensitive = function (value) { + return (this._isCaseInsensitive = (0,_common__WEBPACK_IMPORTED_MODULE_1__.isDefined)(value) ? value : this._isCaseInsensitive); + }; + /** + * Sets the default behavior when generating or matching URLs with default parameter values. + * + * #### Example: + * ```js + * // Remove default parameter values from the url + * urlService.config.defaultSquashPolicy(true); + * ``` + * + * @param 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. + * @returns the current value of defaultSquashPolicy + */ + UrlConfig.prototype.defaultSquashPolicy = function (value) { + if ((0,_common__WEBPACK_IMPORTED_MODULE_1__.isDefined)(value) && value !== true && value !== false && !(0,_common__WEBPACK_IMPORTED_MODULE_1__.isString)(value)) + throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string"); + return (this._defaultSquashPolicy = (0,_common__WEBPACK_IMPORTED_MODULE_1__.isDefined)(value) ? value : this._defaultSquashPolicy); + }; + /** + * Defines whether URLs should match trailing slashes, or not (the default behavior). + * + * #### Example: + * ```js + * // Allow optional trailing slashes + * urlService.config.strictMode(false); + * ``` + * + * @param value `false` to match trailing slashes in URLs, otherwise `true`. + * @returns the current value of strictMode + */ + UrlConfig.prototype.strictMode = function (value) { + return (this._isStrictMode = (0,_common__WEBPACK_IMPORTED_MODULE_1__.isDefined)(value) ? value : this._isStrictMode); + }; + /** + * Creates and registers a custom [[ParamType]] object + * + * A custom parameter type can be used to generate URLs with typed parameters or custom encoding/decoding. + * + * #### Note: Register custom types *before using them* in a state definition. + * + * #### Example: + * ```js + * // Encode object parameter as JSON string + * urlService.config.type('myjson', { + * encode: (obj) => JSON.stringify(obj), + * decode: (str) => JSON.parse(str), + * is: (val) => typeof(val) === 'object', + * pattern: /[^/]+/, + * equals: (a, b) => _.isEqual(a, b), + * }); + * ``` + * + * See [[ParamTypeDefinition]] for more examples + * + * @param name The type name. + * @param definition The type definition. See [[ParamTypeDefinition]] for information on the values accepted. + * @param definitionFn A function that is injected before the app runtime starts. + * The result of this function should be a [[ParamTypeDefinition]]. + * The result is merged into the existing `definition`. + * See [[ParamType]] for information on the values accepted. + * + * @returns if only the `name` parameter was specified: the currently registered [[ParamType]] object, or undefined + */ + UrlConfig.prototype.type = function (name, definition, definitionFn) { + var type = this.paramTypes.type(name, definition, definitionFn); + return !(0,_common__WEBPACK_IMPORTED_MODULE_1__.isDefined)(definition) ? type : this; + }; + return UrlConfig; +}()); + +//# sourceMappingURL=urlConfig.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlMatcher.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlMatcher.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlMatcher": () => (/* binding */ UrlMatcher) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _params_param__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../params/param */ "./node_modules/@uirouter/core/lib-esm/params/param.js"); +/* harmony import */ var _common_strings__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../common/strings */ "./node_modules/@uirouter/core/lib-esm/common/strings.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + + + + + + +function quoteRegExp(str, param) { + var surroundPattern = ['', ''], result = str.replace(/[\\\[\]\^$*+?.()|{}]/g, '\\$&'); + if (!param) + return result; + switch (param.squash) { + case false: + surroundPattern = ['(', ')' + (param.isOptional ? '?' : '')]; + break; + case true: + result = result.replace(/\/$/, ''); + surroundPattern = ['(?:/(', ')|/)?']; + break; + default: + surroundPattern = ["(" + param.squash + "|", ')?']; + break; + } + return result + surroundPattern[0] + param.type.pattern.source + surroundPattern[1]; +} +var memoizeTo = function (obj, _prop, fn) { return (obj[_prop] = obj[_prop] || fn()); }; +var splitOnSlash = (0,_common_strings__WEBPACK_IMPORTED_MODULE_4__.splitOnDelim)('/'); +var defaultConfig = { + state: { params: {} }, + strict: true, + caseInsensitive: true, + decodeParams: true, +}; +/** + * Matches URLs against patterns. + * + * 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 (query) + * 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 [[UrlMatcher.exec]]. + * + * - *Path parameters* are defined using curly brace placeholders (`/somepath/{param}`) + * or colon placeholders (`/somePath/:param`). + * + * - *A parameter RegExp* may be defined for a param after a colon + * (`/somePath/{param:[a-zA-Z0-9]+}`) in a curly brace placeholder. + * The regexp must match for the url to be matched. + * Should the regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash. + * + * Note: a RegExp parameter will encode its value using either [[ParamTypes.path]] or [[ParamTypes.query]]. + * + * - *Custom parameter types* may also be specified after a colon (`/somePath/{param:int}`) in curly brace parameters. + * See [[UrlMatcherFactory.type]] for more information. + * + * - *Catch-all parameters* are defined using an asterisk placeholder (`/somepath/*catchallparam`). + * A catch-all * parameter value will contain the remainder of the URL. + * + * --- + * + * 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). + * 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` ParamType matches `2014-11-12`) and provides a Date object in $stateParams.start + * + */ +var UrlMatcher = /** @class */ (function () { + /** + * @param pattern The pattern to compile into a matcher. + * @param paramTypes The [[ParamTypes]] registry + * @param paramFactory A [[ParamFactory]] object + * @param config A [[UrlMatcherCompileConfig]] configuration object + */ + function UrlMatcher(pattern, paramTypes, paramFactory, config) { + var _this = this; + /** @internal */ + this._cache = { path: [this] }; + /** @internal */ + this._children = []; + /** @internal */ + this._params = []; + /** @internal */ + this._segments = []; + /** @internal */ + this._compiled = []; + this.config = config = (0,_common__WEBPACK_IMPORTED_MODULE_5__.defaults)(config, defaultConfig); + this.pattern = pattern; + // 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; + var searchPlaceholder = /([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g; + var patterns = []; + var last = 0; + var matchArray; + var checkParamErrors = function (id) { + if (!UrlMatcher.nameValidator.test(id)) + throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); + if ((0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(_this._params, (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.propEq)('id', id))) + throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); + }; + // Split into static segments separated by path parameter placeholders. + // The number of segments is always 1 more than the number of parameters. + var matchDetails = function (m, isSearch) { + // IE[78] returns '' for unmatched groups instead of null + var id = m[2] || m[3]; + var regexp = isSearch ? m[4] : m[4] || (m[1] === '*' ? '[\\s\\S]*' : null); + var makeRegexpType = function (str) { + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inherit)(paramTypes.type(isSearch ? 'query' : 'path'), { + pattern: new RegExp(str, _this.config.caseInsensitive ? 'i' : undefined), + }); + }; + return { + id: id, + regexp: regexp, + segment: pattern.substring(last, m.index), + type: !regexp ? null : paramTypes.type(regexp) || makeRegexpType(regexp), + }; + }; + var details; + var segment; + // tslint:disable-next-line:no-conditional-assignment + while ((matchArray = placeholder.exec(pattern))) { + details = matchDetails(matchArray, false); + if (details.segment.indexOf('?') >= 0) + break; // we're into the search part + checkParamErrors(details.id); + this._params.push(paramFactory.fromPath(details.id, details.type, config.state)); + this._segments.push(details.segment); + patterns.push([details.segment, (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.tail)(this._params)]); + 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 = segment.substring(i); + segment = segment.substring(0, i); + if (search.length > 0) { + last = 0; + // tslint:disable-next-line:no-conditional-assignment + while ((matchArray = searchPlaceholder.exec(search))) { + details = matchDetails(matchArray, true); + checkParamErrors(details.id); + this._params.push(paramFactory.fromSearch(details.id, details.type, config.state)); + last = placeholder.lastIndex; + // check if ?& + } + } + } + this._segments.push(segment); + this._compiled = patterns.map(function (_pattern) { return quoteRegExp.apply(null, _pattern); }).concat(quoteRegExp(segment)); + } + /** @internal */ + UrlMatcher.encodeDashes = function (str) { + // Replace dashes with encoded "\-" + return encodeURIComponent(str).replace(/-/g, function (c) { return "%5C%" + c.charCodeAt(0).toString(16).toUpperCase(); }); + }; + /** @internal Given a matcher, return an array with the matcher's path segments and path params, in order */ + UrlMatcher.pathSegmentsAndParams = function (matcher) { + var staticSegments = matcher._segments; + var pathParams = matcher._params.filter(function (p) { return p.location === _params_param__WEBPACK_IMPORTED_MODULE_3__.DefType.PATH; }); + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.arrayTuples)(staticSegments, pathParams.concat(undefined)) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .filter(function (x) { return x !== '' && (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(x); }); + }; + /** @internal Given a matcher, return an array with the matcher's query params */ + UrlMatcher.queryParams = function (matcher) { + return matcher._params.filter(function (p) { return p.location === _params_param__WEBPACK_IMPORTED_MODULE_3__.DefType.SEARCH; }); + }; + /** + * Compare two UrlMatchers + * + * This comparison function converts a UrlMatcher into static and dynamic path segments. + * Each static path segment is a static string between a path separator (slash character). + * Each dynamic segment is a path parameter. + * + * The comparison function sorts static segments before dynamic ones. + */ + UrlMatcher.compare = function (a, b) { + /** + * Turn a UrlMatcher and all its parent matchers into an array + * of slash literals '/', string literals, and Param objects + * + * This example matcher matches strings like "/foo/:param/tail": + * var matcher = $umf.compile("/foo").append($umf.compile("/:param")).append($umf.compile("/")).append($umf.compile("tail")); + * var result = segments(matcher); // [ '/', 'foo', '/', Param, '/', 'tail' ] + * + * Caches the result as `matcher._cache.segments` + */ + var segments = function (matcher) { + return (matcher._cache.segments = + matcher._cache.segments || + matcher._cache.path + .map(UrlMatcher.pathSegmentsAndParams) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .reduce(_common_strings__WEBPACK_IMPORTED_MODULE_4__.joinNeighborsR, []) + .map(function (x) { return ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(x) ? splitOnSlash(x) : x); }) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, [])); + }; + /** + * Gets the sort weight for each segment of a UrlMatcher + * + * Caches the result as `matcher._cache.weights` + */ + var weights = function (matcher) { + return (matcher._cache.weights = + matcher._cache.weights || + segments(matcher).map(function (segment) { + // Sort slashes first, then static strings, the Params + if (segment === '/') + return 1; + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(segment)) + return 2; + if (segment instanceof _params_param__WEBPACK_IMPORTED_MODULE_3__.Param) + return 3; + })); + }; + /** + * Pads shorter array in-place (mutates) + */ + var padArrays = function (l, r, padVal) { + var len = Math.max(l.length, r.length); + while (l.length < len) + l.push(padVal); + while (r.length < len) + r.push(padVal); + }; + var weightsA = weights(a), weightsB = weights(b); + padArrays(weightsA, weightsB, 0); + var _pairs = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.arrayTuples)(weightsA, weightsB); + var cmp, i; + for (i = 0; i < _pairs.length; i++) { + cmp = _pairs[i][0] - _pairs[i][1]; + if (cmp !== 0) + return cmp; + } + return 0; + }; + /** + * Creates a new concatenated UrlMatcher + * + * Builds a new UrlMatcher by appending another UrlMatcher to this one. + * + * @param url A `UrlMatcher` instance to append as a child of the current `UrlMatcher`. + */ + UrlMatcher.prototype.append = function (url) { + this._children.push(url); + url._cache = { + path: this._cache.path.concat(url), + parent: this, + pattern: null, + }; + return url; + }; + /** @internal */ + UrlMatcher.prototype.isRoot = function () { + return this._cache.path[0] === this; + }; + /** Returns the input pattern string */ + UrlMatcher.prototype.toString = function () { + return this.pattern; + }; + UrlMatcher.prototype._getDecodedParamValue = function (value, param) { + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isDefined)(value)) { + if (this.config.decodeParams && !param.type.raw) { + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isArray)(value)) { + value = value.map(function (paramValue) { return decodeURIComponent(paramValue); }); + } + else { + value = decodeURIComponent(value); + } + } + value = param.type.decode(value); + } + return param.value(value); + }; + /** + * Tests the specified url/path against this matcher. + * + * Tests if the given url matches this matcher's pattern, and returns an object containing the captured + * parameter values. Returns 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 `search`. This means that search parameters are always treated + * as optional. + * + * #### Example: + * ```js + * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { + * x: '1', q: 'hello' + * }); + * // returns { id: 'bob', q: 'hello', r: null } + * ``` + * + * @param path The URL path to match, e.g. `$location.path()`. + * @param search URL search parameters, e.g. `$location.search()`. + * @param hash URL hash e.g. `$location.hash()`. + * @param options + * + * @returns The captured parameter values. + */ + UrlMatcher.prototype.exec = function (path, search, hash, options) { + var _this = this; + if (search === void 0) { search = {}; } + if (options === void 0) { options = {}; } + var match = memoizeTo(this._cache, 'pattern', function () { + return new RegExp([ + '^', + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.unnest)(_this._cache.path.map((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('_compiled'))).join(''), + _this.config.strict === false ? '/?' : '', + '$', + ].join(''), _this.config.caseInsensitive ? 'i' : undefined); + }).exec(path); + if (!match) + return null; + // options = defaults(options, { isolate: false }); + var allParams = this.parameters(), pathParams = allParams.filter(function (param) { return !param.isSearch(); }), searchParams = allParams.filter(function (param) { return param.isSearch(); }), nPathSegments = this._cache.path.map(function (urlm) { return urlm._segments.length - 1; }).reduce(function (a, x) { return a + x; }), values = {}; + if (nPathSegments !== match.length - 1) + throw new Error("Unbalanced capture group in route '" + this.pattern + "'"); + function decodePathArray(paramVal) { + var reverseString = function (str) { return str.split('').reverse().join(''); }; + var unquoteDashes = function (str) { return str.replace(/\\-/g, '-'); }; + var split = reverseString(paramVal).split(/-(?!\\)/); + var allReversed = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(split, reverseString); + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(allReversed, unquoteDashes).reverse(); + } + for (var i = 0; i < nPathSegments; i++) { + var param = pathParams[i]; + var value = match[i + 1]; + // if the param value matches a pre-replace pair, replace the value before decoding. + for (var j = 0; j < param.replace.length; j++) { + if (param.replace[j].from === value) + value = param.replace[j].to; + } + if (value && param.array === true) + value = decodePathArray(value); + values[param.id] = this._getDecodedParamValue(value, param); + } + searchParams.forEach(function (param) { + var value = search[param.id]; + for (var j = 0; j < param.replace.length; j++) { + if (param.replace[j].from === value) + value = param.replace[j].to; + } + values[param.id] = _this._getDecodedParamValue(value, param); + }); + if (hash) + values['#'] = hash; + return values; + }; + /** + * @internal + * Returns all the [[Param]] objects of all path and search parameters of this pattern in order of appearance. + * + * @returns {Array.} An array of [[Param]] objects. Must be treated as read-only. If the + * pattern has no parameters, an empty array is returned. + */ + UrlMatcher.prototype.parameters = function (opts) { + if (opts === void 0) { opts = {}; } + if (opts.inherit === false) + return this._params; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.unnest)(this._cache.path.map(function (matcher) { return matcher._params; })); + }; + /** + * @internal + * Returns a single parameter from this UrlMatcher by id + * + * @param id + * @param opts + * @returns {T|Param|any|boolean|UrlMatcher|null} + */ + UrlMatcher.prototype.parameter = function (id, opts) { + var _this = this; + if (opts === void 0) { opts = {}; } + var findParam = function () { + for (var _i = 0, _a = _this._params; _i < _a.length; _i++) { + var param = _a[_i]; + if (param.id === id) + return param; + } + }; + var parent = this._cache.parent; + return findParam() || (opts.inherit !== false && parent && parent.parameter(id, opts)) || null; + }; + /** + * Validates the input parameter values against this UrlMatcher + * + * Checks an object hash of parameters to validate their correctness according to the parameter + * types of this `UrlMatcher`. + * + * @param params The object hash of parameters to validate. + * @returns Returns `true` if `params` validates, otherwise `false`. + */ + UrlMatcher.prototype.validates = function (params) { + var validParamVal = function (param, val) { return !param || param.validates(val); }; + params = params || {}; + // I'm not sure why this checks only the param keys passed in, and not all the params known to the matcher + var paramSchema = this.parameters().filter(function (paramDef) { return params.hasOwnProperty(paramDef.id); }); + return paramSchema.map(function (paramDef) { return validParamVal(paramDef, params[paramDef.id]); }).reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.allTrueR, true); + }; + /** + * Given a set of parameter values, creates a URL from this UrlMatcher. + * + * Creates a URL that matches this pattern by substituting the specified values + * for the path and search parameters. + * + * #### Example: + * ```js + * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' }); + * // returns '/user/bob?q=yes' + * ``` + * + * @param values the values to substitute for the parameters in this pattern. + * @returns the formatted URL (path and optionally search part). + */ + UrlMatcher.prototype.format = function (values) { + if (values === void 0) { values = {}; } + // Build the full path of UrlMatchers (including all parent UrlMatchers) + var urlMatchers = this._cache.path; + // Extract all the static segments and Params (processed as ParamDetails) + // into an ordered array + var pathSegmentsAndParams = urlMatchers + .map(UrlMatcher.pathSegmentsAndParams) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .map(function (x) { return ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(x) ? x : getDetails(x)); }); + // Extract the query params into a separate array + var queryParams = urlMatchers + .map(UrlMatcher.queryParams) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .map(getDetails); + var isInvalid = function (param) { return param.isValid === false; }; + if (pathSegmentsAndParams.concat(queryParams).filter(isInvalid).length) { + return null; + } + /** + * Given a Param, applies the parameter value, then returns detailed information about it + */ + function getDetails(param) { + // Normalize to typed value + var value = param.value(values[param.id]); + var isValid = param.validates(value); + var isDefaultValue = param.isDefaultValue(value); + // Check if we're in squash mode for the parameter + var squash = isDefaultValue ? param.squash : false; + // Allow the Parameter's Type to encode the value + var encoded = param.type.encode(value); + return { param: param, value: value, isValid: isValid, isDefaultValue: isDefaultValue, squash: squash, encoded: encoded }; + } + // Build up the path-portion from the list of static segments and parameters + var pathString = pathSegmentsAndParams.reduce(function (acc, x) { + // The element is a static segment (a raw string); just append it + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(x)) + return acc + x; + // Otherwise, it's a ParamDetails. + var squash = x.squash, encoded = x.encoded, param = x.param; + // If squash is === true, try to remove a slash from the path + if (squash === true) + return acc.match(/\/$/) ? acc.slice(0, -1) : acc; + // If squash is a string, use the string for the param value + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(squash)) + return acc + squash; + if (squash !== false) + return acc; // ? + if (encoded == null) + return acc; + // If this parameter value is an array, encode the value using encodeDashes + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isArray)(encoded)) + return acc + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(encoded, UrlMatcher.encodeDashes).join('-'); + // If the parameter type is "raw", then do not encodeURIComponent + if (param.raw) + return acc + encoded; + // Encode the value + return acc + encodeURIComponent(encoded); + }, ''); + // Build the query string by applying parameter values (array or regular) + // then mapping to key=value, then flattening and joining using "&" + var queryString = queryParams + .map(function (paramDetails) { + var param = paramDetails.param, squash = paramDetails.squash, encoded = paramDetails.encoded, isDefaultValue = paramDetails.isDefaultValue; + if (encoded == null || (isDefaultValue && squash !== false)) + return; + if (!(0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isArray)(encoded)) + encoded = [encoded]; + if (encoded.length === 0) + return; + if (!param.raw) + encoded = (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.map)(encoded, encodeURIComponent); + return encoded.map(function (val) { return param.id + "=" + val; }); + }) + .filter(_common_common__WEBPACK_IMPORTED_MODULE_0__.identity) + .reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .join('&'); + // Concat the pathstring with the queryString (if exists) and the hashString (if exists) + return pathString + (queryString ? "?" + queryString : '') + (values['#'] ? '#' + values['#'] : ''); + }; + /** @internal */ + UrlMatcher.nameValidator = /^\w+([-.]+\w+)*(?:\[\])?$/; + return UrlMatcher; +}()); + +//# sourceMappingURL=urlMatcher.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlMatcherFactory.js": +/*!**********************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlMatcherFactory.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ParamFactory": () => (/* binding */ ParamFactory), +/* harmony export */ "UrlMatcherFactory": () => (/* binding */ UrlMatcherFactory) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _urlMatcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./urlMatcher */ "./node_modules/@uirouter/core/lib-esm/url/urlMatcher.js"); +/* harmony import */ var _params__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../params */ "./node_modules/@uirouter/core/lib-esm/params/index.js"); +var __assign = (undefined && undefined.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; + + + +var ParamFactory = /** @class */ (function () { + function ParamFactory(router) { + this.router = router; + } + ParamFactory.prototype.fromConfig = function (id, type, state) { + return new _params__WEBPACK_IMPORTED_MODULE_2__.Param(id, type, _params__WEBPACK_IMPORTED_MODULE_2__.DefType.CONFIG, this.router.urlService.config, state); + }; + ParamFactory.prototype.fromPath = function (id, type, state) { + return new _params__WEBPACK_IMPORTED_MODULE_2__.Param(id, type, _params__WEBPACK_IMPORTED_MODULE_2__.DefType.PATH, this.router.urlService.config, state); + }; + ParamFactory.prototype.fromSearch = function (id, type, state) { + return new _params__WEBPACK_IMPORTED_MODULE_2__.Param(id, type, _params__WEBPACK_IMPORTED_MODULE_2__.DefType.SEARCH, this.router.urlService.config, state); + }; + return ParamFactory; +}()); + +/** + * Factory for [[UrlMatcher]] instances. + * + * The factory is available to ng1 services as + * `$urlMatcherFactory` or ng1 providers as `$urlMatcherFactoryProvider`. + */ +var UrlMatcherFactory = /** @class */ (function () { + // TODO: move implementations to UrlConfig (urlService.config) + function UrlMatcherFactory(/** @internal */ router) { + var _this = this; + this.router = router; + /** Creates a new [[Param]] for a given location (DefType) */ + this.paramFactory = new ParamFactory(this.router); + // TODO: Check if removal of this will break anything, then remove these + this.UrlMatcher = _urlMatcher__WEBPACK_IMPORTED_MODULE_1__.UrlMatcher; + this.Param = _params__WEBPACK_IMPORTED_MODULE_2__.Param; + /** @deprecated use [[UrlConfig.caseInsensitive]] */ + this.caseInsensitive = function (value) { return _this.router.urlService.config.caseInsensitive(value); }; + /** @deprecated use [[UrlConfig.defaultSquashPolicy]] */ + this.defaultSquashPolicy = function (value) { return _this.router.urlService.config.defaultSquashPolicy(value); }; + /** @deprecated use [[UrlConfig.strictMode]] */ + this.strictMode = function (value) { return _this.router.urlService.config.strictMode(value); }; + /** @deprecated use [[UrlConfig.type]] */ + this.type = function (name, definition, definitionFn) { + return _this.router.urlService.config.type(name, definition, definitionFn) || _this; + }; + } + /** + * Creates a [[UrlMatcher]] for the specified pattern. + * + * @param pattern The URL pattern. + * @param config The config object hash. + * @returns The UrlMatcher. + */ + UrlMatcherFactory.prototype.compile = function (pattern, config) { + var urlConfig = this.router.urlService.config; + // backward-compatible support for config.params -> config.state.params + var params = config && !config.state && config.params; + config = params ? __assign({ state: { params: params } }, config) : config; + var globalConfig = { + strict: urlConfig._isStrictMode, + caseInsensitive: urlConfig._isCaseInsensitive, + decodeParams: urlConfig._decodeParams, + }; + return new _urlMatcher__WEBPACK_IMPORTED_MODULE_1__.UrlMatcher(pattern, urlConfig.paramTypes, this.paramFactory, (0,_common__WEBPACK_IMPORTED_MODULE_0__.extend)(globalConfig, config)); + }; + /** + * Returns true if the specified object is a [[UrlMatcher]], or false otherwise. + * + * @param object The object to perform the type check against. + * @returns `true` if the object matches the `UrlMatcher` interface, by + * implementing all the same methods. + */ + UrlMatcherFactory.prototype.isMatcher = function (object) { + // TODO: typeof? + if (!(0,_common__WEBPACK_IMPORTED_MODULE_0__.isObject)(object)) + return false; + var result = true; + (0,_common__WEBPACK_IMPORTED_MODULE_0__.forEach)(_urlMatcher__WEBPACK_IMPORTED_MODULE_1__.UrlMatcher.prototype, function (val, name) { + if ((0,_common__WEBPACK_IMPORTED_MODULE_0__.isFunction)(val)) + result = result && (0,_common__WEBPACK_IMPORTED_MODULE_0__.isDefined)(object[name]) && (0,_common__WEBPACK_IMPORTED_MODULE_0__.isFunction)(object[name]); + }); + return result; + }; + /** @internal */ + UrlMatcherFactory.prototype.$get = function () { + var urlConfig = this.router.urlService.config; + urlConfig.paramTypes.enqueue = false; + urlConfig.paramTypes._flushTypeQueue(); + return this; + }; + return UrlMatcherFactory; +}()); + +//# sourceMappingURL=urlMatcherFactory.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlRouter.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlRouter.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlRouter": () => (/* binding */ UrlRouter) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _urlRule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./urlRule */ "./node_modules/@uirouter/core/lib-esm/url/urlRule.js"); + + +function appendBasePath(url, isHtml5, absolute, baseHref) { + if (baseHref === '/') + return url; + if (isHtml5) + return (0,_common__WEBPACK_IMPORTED_MODULE_0__.stripLastPathElement)(baseHref) + url; + if (absolute) + return baseHref.slice(1) + url; + return url; +} +/** + * Updates URL and responds to URL changes + * + * ### Deprecation warning: + * This class is now considered to be an internal API + * Use the [[UrlService]] instead. + * For configuring URL rules, use the [[UrlRules]] which can be found as [[UrlService.rules]]. + */ +var UrlRouter = /** @class */ (function () { + /** @internal */ + function UrlRouter(/** @internal */ router) { + var _this = this; + this.router = router; + // Delegate these calls to [[UrlService]] + /** @deprecated use [[UrlService.sync]]*/ + this.sync = function (evt) { return _this.router.urlService.sync(evt); }; + /** @deprecated use [[UrlService.listen]]*/ + this.listen = function (enabled) { return _this.router.urlService.listen(enabled); }; + /** @deprecated use [[UrlService.deferIntercept]]*/ + this.deferIntercept = function (defer) { return _this.router.urlService.deferIntercept(defer); }; + /** @deprecated use [[UrlService.match]]*/ + this.match = function (urlParts) { return _this.router.urlService.match(urlParts); }; + // Delegate these calls to [[UrlRules]] + /** @deprecated use [[UrlRules.initial]]*/ + this.initial = function (handler) { + return _this.router.urlService.rules.initial(handler); + }; + /** @deprecated use [[UrlRules.otherwise]]*/ + this.otherwise = function (handler) { + return _this.router.urlService.rules.otherwise(handler); + }; + /** @deprecated use [[UrlRules.removeRule]]*/ + this.removeRule = function (rule) { return _this.router.urlService.rules.removeRule(rule); }; + /** @deprecated use [[UrlRules.rule]]*/ + this.rule = function (rule) { return _this.router.urlService.rules.rule(rule); }; + /** @deprecated use [[UrlRules.rules]]*/ + this.rules = function () { return _this.router.urlService.rules.rules(); }; + /** @deprecated use [[UrlRules.sort]]*/ + this.sort = function (compareFn) { return _this.router.urlService.rules.sort(compareFn); }; + /** @deprecated use [[UrlRules.when]]*/ + this.when = function (matcher, handler, options) { return _this.router.urlService.rules.when(matcher, handler, options); }; + this.urlRuleFactory = new _urlRule__WEBPACK_IMPORTED_MODULE_1__.UrlRuleFactory(router); + } + /** Internal API. */ + UrlRouter.prototype.update = function (read) { + var $url = this.router.locationService; + if (read) { + this.location = $url.url(); + return; + } + if ($url.url() === this.location) + return; + $url.url(this.location, true); + }; + /** + * Internal API. + * + * Pushes a new location to the browser history. + * + * @internal + * @param urlMatcher + * @param params + * @param options + */ + UrlRouter.prototype.push = function (urlMatcher, params, options) { + var replace = options && !!options.replace; + this.router.urlService.url(urlMatcher.format(params || {}), replace); + }; + /** + * Builds and returns a URL with interpolated parameters + * + * #### Example: + * ```js + * matcher = $umf.compile("/about/:person"); + * params = { person: "bob" }; + * $bob = $urlRouter.href(matcher, params); + * // $bob == "/about/bob"; + * ``` + * + * @param urlMatcher The [[UrlMatcher]] object which is used as the template of the URL to generate. + * @param params An object of parameter values to fill the matcher's required parameters. + * @param options Options object. The options are: + * + * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". + * + * @returns Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` + */ + UrlRouter.prototype.href = function (urlMatcher, params, options) { + var url = urlMatcher.format(params); + if (url == null) + return null; + options = options || { absolute: false }; + var cfg = this.router.urlService.config; + var isHtml5 = cfg.html5Mode(); + if (!isHtml5 && url !== null) { + url = '#' + cfg.hashPrefix() + url; + } + url = appendBasePath(url, isHtml5, options.absolute, cfg.baseHref()); + if (!options.absolute || !url) { + return url; + } + var slash = !isHtml5 && url ? '/' : ''; + var cfgPort = cfg.port(); + var port = (cfgPort === 80 || cfgPort === 443 ? '' : ':' + cfgPort); + return [cfg.protocol(), '://', cfg.host(), port, slash, url].join(''); + }; + Object.defineProperty(UrlRouter.prototype, "interceptDeferred", { + /** @deprecated use [[UrlService.interceptDeferred]]*/ + get: function () { + return this.router.urlService.interceptDeferred; + }, + enumerable: false, + configurable: true + }); + return UrlRouter; +}()); + +//# sourceMappingURL=urlRouter.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlRule.js": +/*!************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlRule.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlRuleFactory": () => (/* binding */ UrlRuleFactory), +/* harmony export */ "BaseUrlRule": () => (/* binding */ BaseUrlRule) +/* harmony export */ }); +/* harmony import */ var _urlMatcher__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./urlMatcher */ "./node_modules/@uirouter/core/lib-esm/url/urlMatcher.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _state_stateObject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../state/stateObject */ "./node_modules/@uirouter/core/lib-esm/state/stateObject.js"); + + + + + +/** + * Creates a [[UrlRule]] + * + * Creates a [[UrlRule]] from a: + * + * - `string` + * - [[UrlMatcher]] + * - `RegExp` + * - [[StateObject]] + */ +var UrlRuleFactory = /** @class */ (function () { + function UrlRuleFactory(router) { + this.router = router; + } + UrlRuleFactory.prototype.compile = function (str) { + return this.router.urlMatcherFactory.compile(str); + }; + UrlRuleFactory.prototype.create = function (what, handler) { + var _this = this; + var isState = _state_stateObject__WEBPACK_IMPORTED_MODULE_4__.StateObject.isState, isStateDeclaration = _state_stateObject__WEBPACK_IMPORTED_MODULE_4__.StateObject.isStateDeclaration; + var makeRule = (0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.pattern)([ + [_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString, function (_what) { return makeRule(_this.compile(_what)); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.is)(_urlMatcher__WEBPACK_IMPORTED_MODULE_0__.UrlMatcher), function (_what) { return _this.fromUrlMatcher(_what, handler); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.or)(isState, isStateDeclaration), function (_what) { return _this.fromState(_what, _this.router); }], + [(0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.is)(RegExp), function (_what) { return _this.fromRegExp(_what, handler); }], + [_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isFunction, function (_what) { return new BaseUrlRule(_what, handler); }], + ]); + var rule = makeRule(what); + if (!rule) + throw new Error("invalid 'what' in when()"); + return rule; + }; + /** + * A UrlRule which matches based on a UrlMatcher + * + * The `handler` may be either a `string`, a [[UrlRuleHandlerFn]] or another [[UrlMatcher]] + * + * ## Handler as a function + * + * If `handler` is a function, the function is invoked with: + * + * - matched parameter values ([[RawParams]] from [[UrlMatcher.exec]]) + * - url: the current Url ([[UrlParts]]) + * - router: the router object ([[UIRouter]]) + * + * #### Example: + * ```js + * var urlMatcher = $umf.compile("/foo/:fooId/:barId"); + * var rule = factory.fromUrlMatcher(urlMatcher, match => "/home/" + match.fooId + "/" + match.barId); + * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' } + * var result = rule.handler(match); // '/home/123/456' + * ``` + * + * ## Handler as UrlMatcher + * + * If `handler` is a UrlMatcher, the handler matcher is used to create the new url. + * The `handler` UrlMatcher is formatted using the matched param from the first matcher. + * The url is replaced with the result. + * + * #### Example: + * ```js + * var urlMatcher = $umf.compile("/foo/:fooId/:barId"); + * var handler = $umf.compile("/home/:fooId/:barId"); + * var rule = factory.fromUrlMatcher(urlMatcher, handler); + * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' } + * var result = rule.handler(match); // '/home/123/456' + * ``` + */ + UrlRuleFactory.prototype.fromUrlMatcher = function (urlMatcher, handler) { + var _handler = handler; + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(handler)) + handler = this.router.urlMatcherFactory.compile(handler); + if ((0,_common_hof__WEBPACK_IMPORTED_MODULE_3__.is)(_urlMatcher__WEBPACK_IMPORTED_MODULE_0__.UrlMatcher)(handler)) + _handler = function (match) { return handler.format(match); }; + function matchUrlParamters(url) { + var params = urlMatcher.exec(url.path, url.search, url.hash); + return urlMatcher.validates(params) && params; + } + // Prioritize URLs, lowest to highest: + // - Some optional URL parameters, but none matched + // - No optional parameters in URL + // - Some optional parameters, some matched + // - Some optional parameters, all matched + function matchPriority(params) { + var optional = urlMatcher.parameters().filter(function (param) { return param.isOptional; }); + if (!optional.length) + return 0.000001; + var matched = optional.filter(function (param) { return params[param.id]; }); + return matched.length / optional.length; + } + var details = { urlMatcher: urlMatcher, matchPriority: matchPriority, type: 'URLMATCHER' }; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_2__.extend)(new BaseUrlRule(matchUrlParamters, _handler), details); + }; + /** + * A UrlRule which matches a state by its url + * + * #### Example: + * ```js + * var rule = factory.fromState($state.get('foo'), router); + * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' } + * var result = rule.handler(match); + * // Starts a transition to 'foo' with params: { fooId: '123', barId: '456' } + * ``` + */ + UrlRuleFactory.prototype.fromState = function (stateOrDecl, router) { + var state = _state_stateObject__WEBPACK_IMPORTED_MODULE_4__.StateObject.isStateDeclaration(stateOrDecl) ? stateOrDecl.$$state() : stateOrDecl; + /** + * Handles match by transitioning to matched state + * + * First checks if the router should start a new transition. + * A new transition is not required if the current state's URL + * and the new URL are already identical + */ + var handler = function (match) { + var $state = router.stateService; + var globals = router.globals; + if ($state.href(state, match) !== $state.href(globals.current, globals.params)) { + $state.transitionTo(state, match, { inherit: true, source: 'url' }); + } + }; + var details = { state: state, type: 'STATE' }; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_2__.extend)(this.fromUrlMatcher(state.url, handler), details); + }; + /** + * A UrlRule which matches based on a regular expression + * + * The `handler` may be either a [[UrlRuleHandlerFn]] or a string. + * + * ## Handler as a function + * + * If `handler` is a function, the function is invoked with: + * + * - regexp match array (from `regexp`) + * - url: the current Url ([[UrlParts]]) + * - router: the router object ([[UIRouter]]) + * + * #### Example: + * ```js + * var rule = factory.fromRegExp(/^\/foo\/(bar|baz)$/, match => "/home/" + match[1]) + * var match = rule.match('/foo/bar'); // results in [ '/foo/bar', 'bar' ] + * var result = rule.handler(match); // '/home/bar' + * ``` + * + * ## Handler as string + * + * If `handler` is a string, the url is *replaced by the string* when the Rule is invoked. + * The string is first interpolated using `string.replace()` style pattern. + * + * #### Example: + * ```js + * var rule = factory.fromRegExp(/^\/foo\/(bar|baz)$/, "/home/$1") + * var match = rule.match('/foo/bar'); // results in [ '/foo/bar', 'bar' ] + * var result = rule.handler(match); // '/home/bar' + * ``` + */ + UrlRuleFactory.prototype.fromRegExp = function (regexp, handler) { + if (regexp.global || regexp.sticky) + throw new Error('Rule RegExp must not be global or sticky'); + /** + * If handler is a string, the url will be replaced by the string. + * If the string has any String.replace() style variables in it (like `$2`), + * they will be replaced by the captures from [[match]] + */ + var redirectUrlTo = function (match) { + // Interpolates matched values into $1 $2, etc using a String.replace()-style pattern + return handler.replace(/\$(\$|\d{1,2})/, function (m, what) { return match[what === '$' ? 0 : Number(what)]; }); + }; + var _handler = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isString)(handler) ? redirectUrlTo : handler; + var matchParamsFromRegexp = function (url) { return regexp.exec(url.path); }; + var details = { regexp: regexp, type: 'REGEXP' }; + return (0,_common_common__WEBPACK_IMPORTED_MODULE_2__.extend)(new BaseUrlRule(matchParamsFromRegexp, _handler), details); + }; + UrlRuleFactory.isUrlRule = function (obj) { return obj && ['type', 'match', 'handler'].every(function (key) { return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_1__.isDefined)(obj[key]); }); }; + return UrlRuleFactory; +}()); + +/** + * A base rule which calls `match` + * + * The value from the `match` function is passed through to the `handler`. + * @internal + */ +var BaseUrlRule = /** @class */ (function () { + function BaseUrlRule(match, handler) { + var _this = this; + this.match = match; + this.type = 'RAW'; + this.matchPriority = function (match) { return 0 - _this.$id; }; + this.handler = handler || _common_common__WEBPACK_IMPORTED_MODULE_2__.identity; + } + return BaseUrlRule; +}()); + +//# sourceMappingURL=urlRule.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlRules.js": +/*!*************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlRules.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlRules": () => (/* binding */ UrlRules) +/* harmony export */ }); +/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../state */ "./node_modules/@uirouter/core/lib-esm/state/index.js"); +/* harmony import */ var _urlMatcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./urlMatcher */ "./node_modules/@uirouter/core/lib-esm/url/urlMatcher.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _urlRule__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./urlRule */ "./node_modules/@uirouter/core/lib-esm/url/urlRule.js"); + + + + +var prioritySort = function (a, b) { return (b.priority || 0) - (a.priority || 0); }; +var typeSort = function (a, b) { + var weights = { STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1 }; + return (weights[a.type] || 0) - (weights[b.type] || 0); +}; +var urlMatcherSort = function (a, b) { + return !a.urlMatcher || !b.urlMatcher ? 0 : _urlMatcher__WEBPACK_IMPORTED_MODULE_1__.UrlMatcher.compare(a.urlMatcher, b.urlMatcher); +}; +var idSort = function (a, b) { + // Identically sorted STATE and URLMATCHER best rule will be chosen by `matchPriority` after each rule matches the URL + var useMatchPriority = { STATE: true, URLMATCHER: true }; + var equal = useMatchPriority[a.type] && useMatchPriority[b.type]; + return equal ? 0 : (a.$id || 0) - (b.$id || 0); +}; +/** + * Default rule priority sorting function. + * + * Sorts rules by: + * + * - Explicit priority (set rule priority using [[UrlRules.when]]) + * - Rule type (STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1) + * - `UrlMatcher` specificity ([[UrlMatcher.compare]]): works for STATE and URLMATCHER types to pick the most specific rule. + * - Rule registration order (for rule types other than STATE and URLMATCHER) + * - Equally sorted State and UrlMatcher rules will each match the URL. + * Then, the *best* match is chosen based on how many parameter values were matched. + */ +var defaultRuleSortFn; +defaultRuleSortFn = function (a, b) { + var cmp = prioritySort(a, b); + if (cmp !== 0) + return cmp; + cmp = typeSort(a, b); + if (cmp !== 0) + return cmp; + cmp = urlMatcherSort(a, b); + if (cmp !== 0) + return cmp; + return idSort(a, b); +}; +function getHandlerFn(handler) { + if (!(0,_common__WEBPACK_IMPORTED_MODULE_2__.isFunction)(handler) && !(0,_common__WEBPACK_IMPORTED_MODULE_2__.isString)(handler) && !(0,_common__WEBPACK_IMPORTED_MODULE_2__.is)(_state__WEBPACK_IMPORTED_MODULE_0__.TargetState)(handler) && !_state__WEBPACK_IMPORTED_MODULE_0__.TargetState.isDef(handler)) { + throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property"); + } + return (0,_common__WEBPACK_IMPORTED_MODULE_2__.isFunction)(handler) ? handler : (0,_common__WEBPACK_IMPORTED_MODULE_2__.val)(handler); +} +/** + * API for managing URL rules + * + * This API is used to create and manage URL rules. + * URL rules are a mechanism to respond to specific URL patterns. + * + * The most commonly used methods are [[otherwise]] and [[when]]. + * + * This API is found at `router.urlService.rules` (see: [[UIRouter.urlService]], [[URLService.rules]]) + */ +var UrlRules = /** @class */ (function () { + /** @internal */ + function UrlRules(/** @internal */ router) { + this.router = router; + /** @internal */ this._sortFn = defaultRuleSortFn; + /** @internal */ this._rules = []; + /** @internal */ this._id = 0; + this.urlRuleFactory = new _urlRule__WEBPACK_IMPORTED_MODULE_3__.UrlRuleFactory(router); + } + /** @internal */ + UrlRules.prototype.dispose = function (router) { + this._rules = []; + delete this._otherwiseFn; + }; + /** + * Defines the initial state, path, or behavior to use when the app starts. + * + * This rule defines the initial/starting state for the application. + * + * This rule is triggered the first time the URL is checked (when the app initially loads). + * The rule is triggered only when the url matches either `""` or `"/"`. + * + * Note: The rule is intended to be used when the root of the application is directly linked to. + * When the URL is *not* `""` or `"/"` and doesn't match other rules, the [[otherwise]] rule is triggered. + * This allows 404-like behavior when an unknown URL is deep-linked. + * + * #### Example: + * Start app at `home` state. + * ```js + * .initial({ state: 'home' }); + * ``` + * + * #### Example: + * Start app at `/home` (by url) + * ```js + * .initial('/home'); + * ``` + * + * #### Example: + * When no other url rule matches, go to `home` state + * ```js + * .initial((matchValue, url, router) => { + * console.log('initial state'); + * return { state: 'home' }; + * }) + * ``` + * + * @param handler The initial state or url path, or a function which returns the state or url path (or performs custom logic). + */ + UrlRules.prototype.initial = function (handler) { + var handlerFn = getHandlerFn(handler); + var matchFn = function (urlParts, router) { + return router.globals.transitionHistory.size() === 0 && !!/^\/?$/.exec(urlParts.path); + }; + this.rule(this.urlRuleFactory.create(matchFn, handlerFn)); + }; + /** + * Defines the state, url, or behavior to use when no other rule matches the URL. + * + * This rule is matched when *no other rule* matches. + * It is generally used to handle unknown URLs (similar to "404" behavior, but on the client side). + * + * - If `handler` a string, it is treated as a url redirect + * + * #### Example: + * When no other url rule matches, redirect to `/index` + * ```js + * .otherwise('/index'); + * ``` + * + * - If `handler` is an object with a `state` property, the state is activated. + * + * #### Example: + * When no other url rule matches, redirect to `home` and provide a `dashboard` parameter value. + * ```js + * .otherwise({ state: 'home', params: { dashboard: 'default' } }); + * ``` + * + * - If `handler` is a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object. + * The function can perform actions, and/or return a value. + * + * #### Example: + * When no other url rule matches, manually trigger a transition to the `home` state + * ```js + * .otherwise((matchValue, urlParts, router) => { + * router.stateService.go('home'); + * }); + * ``` + * + * #### Example: + * When no other url rule matches, go to `home` state + * ```js + * .otherwise((matchValue, urlParts, router) => { + * return { state: 'home' }; + * }); + * ``` + * + * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic). + */ + UrlRules.prototype.otherwise = function (handler) { + var handlerFn = getHandlerFn(handler); + this._otherwiseFn = this.urlRuleFactory.create((0,_common__WEBPACK_IMPORTED_MODULE_2__.val)(true), handlerFn); + this._sorted = false; + }; + /** + * Remove a rule previously registered + * + * @param rule the matcher rule that was previously registered using [[rule]] + */ + UrlRules.prototype.removeRule = function (rule) { + (0,_common__WEBPACK_IMPORTED_MODULE_2__.removeFrom)(this._rules, rule); + }; + /** + * Manually adds a URL Rule. + * + * Usually, a url rule is added using [[StateDeclaration.url]] or [[when]]. + * This api can be used directly for more control (to register a [[BaseUrlRule]], for example). + * Rules can be created using [[urlRuleFactory]], or created manually as simple objects. + * + * A rule should have a `match` function which returns truthy if the rule matched. + * It should also have a `handler` function which is invoked if the rule is the best match. + * + * @return a function that deregisters the rule + */ + UrlRules.prototype.rule = function (rule) { + var _this = this; + if (!_urlRule__WEBPACK_IMPORTED_MODULE_3__.UrlRuleFactory.isUrlRule(rule)) + throw new Error('invalid rule'); + rule.$id = this._id++; + rule.priority = rule.priority || 0; + this._rules.push(rule); + this._sorted = false; + return function () { return _this.removeRule(rule); }; + }; + /** + * Gets all registered rules + * + * @returns an array of all the registered rules + */ + UrlRules.prototype.rules = function () { + this.ensureSorted(); + return this._rules.concat(this._otherwiseFn ? [this._otherwiseFn] : []); + }; + /** + * Defines URL Rule priorities + * + * More than one rule ([[UrlRule]]) might match a given URL. + * This `compareFn` is used to sort the rules by priority. + * Higher priority rules should sort earlier. + * + * The [[defaultRuleSortFn]] is used by default. + * + * You only need to call this function once. + * The `compareFn` will be used to sort the rules as each is registered. + * + * If called without any parameter, it will re-sort the rules. + * + * --- + * + * Url rules may come from multiple sources: states's urls ([[StateDeclaration.url]]), [[when]], and [[rule]]. + * Each rule has a (user-provided) [[UrlRule.priority]], a [[UrlRule.type]], and a [[UrlRule.$id]] + * The `$id` is is the order in which the rule was registered. + * + * The sort function should use these data, or data found on a specific type + * of [[UrlRule]] (such as [[StateRule.state]]), to order the rules as desired. + * + * #### Example: + * This compare function prioritizes rules by the order in which the rules were registered. + * A rule registered earlier has higher priority. + * + * ```js + * function compareFn(a, b) { + * return a.$id - b.$id; + * } + * ``` + * + * @param compareFn a function that compares to [[UrlRule]] objects. + * The `compareFn` should abide by the `Array.sort` compare function rules. + * Given two rules, `a` and `b`, return a negative number if `a` should be higher priority. + * Return a positive number if `b` should be higher priority. + * Return `0` if the rules are identical. + * + * See the [mozilla reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description) + * for details. + */ + UrlRules.prototype.sort = function (compareFn) { + var sorted = this.stableSort(this._rules, (this._sortFn = compareFn || this._sortFn)); + // precompute _sortGroup values and apply to each rule + var group = 0; + for (var i = 0; i < sorted.length; i++) { + sorted[i]._group = group; + if (i < sorted.length - 1 && this._sortFn(sorted[i], sorted[i + 1]) !== 0) { + group++; + } + } + this._rules = sorted; + this._sorted = true; + }; + /** @internal */ + UrlRules.prototype.ensureSorted = function () { + this._sorted || this.sort(); + }; + /** @internal */ + UrlRules.prototype.stableSort = function (arr, compareFn) { + var arrOfWrapper = arr.map(function (elem, idx) { return ({ elem: elem, idx: idx }); }); + arrOfWrapper.sort(function (wrapperA, wrapperB) { + var cmpDiff = compareFn(wrapperA.elem, wrapperB.elem); + return cmpDiff === 0 ? wrapperA.idx - wrapperB.idx : cmpDiff; + }); + return arrOfWrapper.map(function (wrapper) { return wrapper.elem; }); + }; + /** + * Registers a `matcher` and `handler` for custom URLs handling. + * + * The `matcher` can be: + * + * - a [[UrlMatcher]]: See: [[UrlMatcherFactory.compile]] + * - a `string`: The string is compiled to a [[UrlMatcher]] + * - a `RegExp`: The regexp is used to match the url. + * + * The `handler` can be: + * + * - a string: The url is redirected to the value of the string. + * - a function: The url is redirected to the return value of the function. + * + * --- + * + * When the `handler` is a `string` and the `matcher` is a `UrlMatcher` (or string), the redirect + * string is interpolated with parameter values. + * + * #### Example: + * When the URL is `/foo/123` the rule will redirect to `/bar/123`. + * ```js + * .when("/foo/:param1", "/bar/:param1") + * ``` + * + * --- + * + * When the `handler` is a string and the `matcher` is a `RegExp`, the redirect string is + * interpolated with capture groups from the RegExp. + * + * #### Example: + * When the URL is `/foo/123` the rule will redirect to `/bar/123`. + * ```js + * .when(new RegExp("^/foo/(.*)$"), "/bar/$1"); + * ``` + * + * --- + * + * When the handler is a function, it receives the matched value, the current URL, and the `UIRouter` object (See [[UrlRuleHandlerFn]]). + * The "matched value" differs based on the `matcher`. + * For [[UrlMatcher]]s, it will be the matched state params. + * For `RegExp`, it will be the match array from `regexp.exec()`. + * + * If the handler returns a string, the URL is redirected to the string. + * + * #### Example: + * When the URL is `/foo/123` the rule will redirect to `/bar/123`. + * ```js + * .when(new RegExp("^/foo/(.*)$"), match => "/bar/" + match[1]); + * ``` + * + * Note: the `handler` may also invoke arbitrary code, such as `$state.go()` + * + * @param matcher A pattern `string` to match, compiled as a [[UrlMatcher]], or a `RegExp`. + * @param handler The path to redirect to, or a function that returns the path. + * @param options `{ priority: number }` + * + * @return the registered [[UrlRule]] + */ + UrlRules.prototype.when = function (matcher, handler, options) { + var rule = this.urlRuleFactory.create(matcher, handler); + if ((0,_common__WEBPACK_IMPORTED_MODULE_2__.isDefined)(options && options.priority)) + rule.priority = options.priority; + this.rule(rule); + return rule; + }; + return UrlRules; +}()); + +//# sourceMappingURL=urlRules.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/url/urlService.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/url/urlService.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "UrlService": () => (/* binding */ UrlService) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _urlRules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./urlRules */ "./node_modules/@uirouter/core/lib-esm/url/urlRules.js"); +/* harmony import */ var _urlConfig__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./urlConfig */ "./node_modules/@uirouter/core/lib-esm/url/urlConfig.js"); +/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../state */ "./node_modules/@uirouter/core/lib-esm/state/index.js"); + + + + +/** + * API for URL management + */ +var UrlService = /** @class */ (function () { + /** @internal */ + function UrlService(/** @internal */ router) { + var _this = this; + this.router = router; + /** @internal */ this.interceptDeferred = false; + /** + * The nested [[UrlRules]] API for managing URL rules and rewrites + * + * See: [[UrlRules]] for details + */ + this.rules = new _urlRules__WEBPACK_IMPORTED_MODULE_1__.UrlRules(this.router); + /** + * The nested [[UrlConfig]] API to configure the URL and retrieve URL information + * + * See: [[UrlConfig]] for details + */ + this.config = new _urlConfig__WEBPACK_IMPORTED_MODULE_2__.UrlConfig(this.router); + // Delegate these calls to the current LocationServices implementation + /** + * Gets the current url, or updates the url + * + * ### Getting the current URL + * + * When no arguments are passed, returns the current URL. + * The URL is normalized using the internal [[path]]/[[search]]/[[hash]] values. + * + * For example, the URL may be stored in the hash ([[HashLocationServices]]) or + * have a base HREF prepended ([[PushStateLocationServices]]). + * + * The raw URL in the browser might be: + * + * ``` + * http://mysite.com/somepath/index.html#/internal/path/123?param1=foo#anchor + * ``` + * + * or + * + * ``` + * http://mysite.com/basepath/internal/path/123?param1=foo#anchor + * ``` + * + * then this method returns: + * + * ``` + * /internal/path/123?param1=foo#anchor + * ``` + * + * + * #### Example: + * ```js + * locationServices.url(); // "/some/path?query=value#anchor" + * ``` + * + * ### Updating the URL + * + * When `newurl` arguments is provided, changes the URL to reflect `newurl` + * + * #### Example: + * ```js + * locationServices.url("/some/path?query=value#anchor", true); + * ``` + * + * @param newurl The new value for the URL. + * This url should reflect only the new internal [[path]], [[search]], and [[hash]] values. + * It should not include the protocol, site, port, or base path of an absolute HREF. + * @param replace When true, replaces the current history entry (instead of appending it) with this new url + * @param state The history's state object, i.e., pushState (if the LocationServices implementation supports it) + * + * @return the url (after potentially being processed) + */ + this.url = function (newurl, replace, state) { + return _this.router.locationService.url(newurl, replace, state); + }; + /** + * Gets the path part of the current url + * + * If the current URL is `/some/path?query=value#anchor`, this returns `/some/path` + * + * @return the path portion of the url + */ + this.path = function () { return _this.router.locationService.path(); }; + /** + * Gets the search part of the current url as an object + * + * If the current URL is `/some/path?query=value#anchor`, this returns `{ query: 'value' }` + * + * @return the search (query) portion of the url, as an object + */ + this.search = function () { return _this.router.locationService.search(); }; + /** + * Gets the hash part of the current url + * + * If the current URL is `/some/path?query=value#anchor`, this returns `anchor` + * + * @return the hash (anchor) portion of the url + */ + this.hash = function () { return _this.router.locationService.hash(); }; + /** + * @internal + * + * Registers a low level url change handler + * + * Note: Because this is a low level handler, it's not recommended for general use. + * + * #### Example: + * ```js + * let deregisterFn = locationServices.onChange((evt) => console.log("url change", evt)); + * ``` + * + * @param callback a function that will be called when the url is changing + * @return a function that de-registers the callback + */ + this.onChange = function (callback) { return _this.router.locationService.onChange(callback); }; + } + /** @internal */ + UrlService.prototype.dispose = function () { + this.listen(false); + this.rules.dispose(); + }; + /** + * Gets the current URL parts + * + * This method returns the different parts of the current URL (the [[path]], [[search]], and [[hash]]) as a [[UrlParts]] object. + */ + UrlService.prototype.parts = function () { + return { path: this.path(), search: this.search(), hash: this.hash() }; + }; + /** + * Activates the best rule for the current URL + * + * Checks the current URL for a matching [[UrlRule]], then invokes that rule's handler. + * This method is called internally any time the URL has changed. + * + * This effectively activates the state (or redirect, etc) which matches the current URL. + * + * #### Example: + * ```js + * urlService.deferIntercept(); + * + * fetch('/states.json').then(resp => resp.json()).then(data => { + * data.forEach(state => $stateRegistry.register(state)); + * urlService.listen(); + * // Find the matching URL and invoke the handler. + * urlService.sync(); + * }); + * ``` + */ + UrlService.prototype.sync = function (evt) { + if (evt && evt.defaultPrevented) + return; + var _a = this.router, urlService = _a.urlService, stateService = _a.stateService; + var url = { path: urlService.path(), search: urlService.search(), hash: urlService.hash() }; + var best = this.match(url); + var applyResult = (0,_common__WEBPACK_IMPORTED_MODULE_0__.pattern)([ + [_common__WEBPACK_IMPORTED_MODULE_0__.isString, function (newurl) { return urlService.url(newurl, true); }], + [_state__WEBPACK_IMPORTED_MODULE_3__.TargetState.isDef, function (def) { return stateService.go(def.state, def.params, def.options); }], + [(0,_common__WEBPACK_IMPORTED_MODULE_0__.is)(_state__WEBPACK_IMPORTED_MODULE_3__.TargetState), function (target) { return stateService.go(target.state(), target.params(), target.options()); }], + ]); + applyResult(best && best.rule.handler(best.match, url, this.router)); + }; + /** + * Starts or stops listening for URL changes + * + * Call this sometime after calling [[deferIntercept]] to start monitoring the url. + * This causes UI-Router to start listening for changes to the URL, if it wasn't already listening. + * + * If called with `false`, UI-Router will stop listening (call listen(true) to start listening again). + * + * #### Example: + * ```js + * urlService.deferIntercept(); + * + * fetch('/states.json').then(resp => resp.json()).then(data => { + * data.forEach(state => $stateRegistry.register(state)); + * // Start responding to URL changes + * urlService.listen(); + * urlService.sync(); + * }); + * ``` + * + * @param enabled `true` or `false` to start or stop listening to URL changes + */ + UrlService.prototype.listen = function (enabled) { + var _this = this; + if (enabled === false) { + this._stopListeningFn && this._stopListeningFn(); + delete this._stopListeningFn; + } + else { + return (this._stopListeningFn = + this._stopListeningFn || this.router.urlService.onChange(function (evt) { return _this.sync(evt); })); + } + }; + /** + * Disables monitoring of the URL. + * + * Call this method before UI-Router has bootstrapped. + * It will stop UI-Router from performing the initial url sync. + * + * This can be useful to perform some asynchronous initialization before the router starts. + * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL. + * + * #### Example: + * ```js + * // Prevent UI-Router from automatically intercepting URL changes when it starts; + * urlService.deferIntercept(); + * + * fetch('/states.json').then(resp => resp.json()).then(data => { + * data.forEach(state => $stateRegistry.register(state)); + * urlService.listen(); + * urlService.sync(); + * }); + * ``` + * + * @param defer Indicates whether to defer location change interception. + * Passing no parameter is equivalent to `true`. + */ + UrlService.prototype.deferIntercept = function (defer) { + if (defer === undefined) + defer = true; + this.interceptDeferred = defer; + }; + /** + * Matches a URL + * + * Given a URL (as a [[UrlParts]] object), check all rules and determine the best matching rule. + * Return the result as a [[MatchResult]]. + */ + UrlService.prototype.match = function (url) { + var _this = this; + url = (0,_common__WEBPACK_IMPORTED_MODULE_0__.extend)({ path: '', search: {}, hash: '' }, url); + var rules = this.rules.rules(); + // Checks a single rule. Returns { rule: rule, match: match, weight: weight } if it matched, or undefined + var checkRule = function (rule) { + var match = rule.match(url, _this.router); + return match && { match: match, rule: rule, weight: rule.matchPriority(match) }; + }; + // The rules are pre-sorted. + // - Find the first matching rule. + // - Find any other matching rule that sorted *exactly the same*, according to `.sort()`. + // - Choose the rule with the highest match weight. + var best; + for (var i = 0; i < rules.length; i++) { + // Stop when there is a 'best' rule and the next rule sorts differently than it. + if (best && best.rule._group !== rules[i]._group) + break; + var current = checkRule(rules[i]); + // Pick the best MatchResult + best = !best || (current && current.weight > best.weight) ? current : best; + } + return best; + }; + return UrlService; +}()); + +//# sourceMappingURL=urlService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla.js": +/*!********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _vanilla_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./vanilla/index */ "./node_modules/@uirouter/core/lib-esm/vanilla/index.js"); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _vanilla_index__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== "default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _vanilla_index__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); + +//# sourceMappingURL=vanilla.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/baseLocationService.js": +/*!****************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/baseLocationService.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "BaseLocationServices": () => (/* binding */ BaseLocationServices) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./node_modules/@uirouter/core/lib-esm/vanilla/utils.js"); + + +/** A base `LocationServices` */ +var BaseLocationServices = /** @class */ (function () { + function BaseLocationServices(router, fireAfterUpdate) { + var _this = this; + this.fireAfterUpdate = fireAfterUpdate; + this._listeners = []; + this._listener = function (evt) { return _this._listeners.forEach(function (cb) { return cb(evt); }); }; + this.hash = function () { return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.parseUrl)(_this._get()).hash; }; + this.path = function () { return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.parseUrl)(_this._get()).path; }; + this.search = function () { return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getParams)((0,_utils__WEBPACK_IMPORTED_MODULE_1__.parseUrl)(_this._get()).search); }; + this._location = _common__WEBPACK_IMPORTED_MODULE_0__.root.location; + this._history = _common__WEBPACK_IMPORTED_MODULE_0__.root.history; + } + BaseLocationServices.prototype.url = function (url, replace) { + if (replace === void 0) { replace = true; } + if ((0,_common__WEBPACK_IMPORTED_MODULE_0__.isDefined)(url) && url !== this._get()) { + this._set(null, null, url, replace); + if (this.fireAfterUpdate) { + this._listeners.forEach(function (cb) { return cb({ url: url }); }); + } + } + return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.buildUrl)(this); + }; + BaseLocationServices.prototype.onChange = function (cb) { + var _this = this; + this._listeners.push(cb); + return function () { return (0,_common__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(_this._listeners, cb); }; + }; + BaseLocationServices.prototype.dispose = function (router) { + (0,_common__WEBPACK_IMPORTED_MODULE_0__.deregAll)(this._listeners); + }; + return BaseLocationServices; +}()); + +//# sourceMappingURL=baseLocationService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/browserLocationConfig.js": +/*!******************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/browserLocationConfig.js ***! + \******************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "BrowserLocationConfig": () => (/* binding */ BrowserLocationConfig) +/* harmony export */ }); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); + +/** A `LocationConfig` that delegates to the browser's `location` object */ +var BrowserLocationConfig = /** @class */ (function () { + function BrowserLocationConfig(router, _isHtml5) { + if (_isHtml5 === void 0) { _isHtml5 = false; } + this._isHtml5 = _isHtml5; + this._baseHref = undefined; + this._hashPrefix = ''; + } + BrowserLocationConfig.prototype.port = function () { + if (location.port) { + return Number(location.port); + } + return this.protocol() === 'https' ? 443 : 80; + }; + BrowserLocationConfig.prototype.protocol = function () { + return location.protocol.replace(/:/g, ''); + }; + BrowserLocationConfig.prototype.host = function () { + return location.hostname; + }; + BrowserLocationConfig.prototype.html5Mode = function () { + return this._isHtml5; + }; + BrowserLocationConfig.prototype.hashPrefix = function (newprefix) { + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isDefined)(newprefix) ? (this._hashPrefix = newprefix) : this._hashPrefix; + }; + BrowserLocationConfig.prototype.baseHref = function (href) { + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isDefined)(href)) + this._baseHref = href; + if ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isUndefined)(this._baseHref)) + this._baseHref = this.getBaseHref(); + return this._baseHref; + }; + BrowserLocationConfig.prototype.getBaseHref = function () { + var baseTag = document.getElementsByTagName('base')[0]; + if (baseTag && baseTag.href) { + return baseTag.href.replace(/^([^/:]*:)?\/\/[^/]*/, ''); + } + return this._isHtml5 ? '/' : location.pathname || '/'; + }; + BrowserLocationConfig.prototype.dispose = function () { }; + return BrowserLocationConfig; +}()); + +//# sourceMappingURL=browserLocationConfig.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/hashLocationService.js": +/*!****************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/hashLocationService.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "HashLocationService": () => (/* binding */ HashLocationService) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +/* harmony import */ var _baseLocationService__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/baseLocationService.js"); +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); + + +/** A `LocationServices` that uses the browser hash "#" to get/set the current location */ +var HashLocationService = /** @class */ (function (_super) { + __extends(HashLocationService, _super); + function HashLocationService(router) { + var _this = _super.call(this, router, false) || this; + _common__WEBPACK_IMPORTED_MODULE_0__.root.addEventListener('hashchange', _this._listener, false); + return _this; + } + HashLocationService.prototype._get = function () { + return (0,_common__WEBPACK_IMPORTED_MODULE_0__.trimHashVal)(this._location.hash); + }; + HashLocationService.prototype._set = function (state, title, url, replace) { + this._location.hash = url; + }; + HashLocationService.prototype.dispose = function (router) { + _super.prototype.dispose.call(this, router); + _common__WEBPACK_IMPORTED_MODULE_0__.root.removeEventListener('hashchange', this._listener); + }; + return HashLocationService; +}(_baseLocationService__WEBPACK_IMPORTED_MODULE_1__.BaseLocationServices)); + +//# sourceMappingURL=hashLocationService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/index.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "$q": () => (/* reexport safe */ _q__WEBPACK_IMPORTED_MODULE_1__.$q), +/* harmony export */ "$injector": () => (/* reexport safe */ _injector__WEBPACK_IMPORTED_MODULE_2__.$injector), +/* harmony export */ "BaseLocationServices": () => (/* reexport safe */ _baseLocationService__WEBPACK_IMPORTED_MODULE_3__.BaseLocationServices), +/* harmony export */ "HashLocationService": () => (/* reexport safe */ _hashLocationService__WEBPACK_IMPORTED_MODULE_4__.HashLocationService), +/* harmony export */ "MemoryLocationService": () => (/* reexport safe */ _memoryLocationService__WEBPACK_IMPORTED_MODULE_5__.MemoryLocationService), +/* harmony export */ "PushStateLocationService": () => (/* reexport safe */ _pushStateLocationService__WEBPACK_IMPORTED_MODULE_6__.PushStateLocationService), +/* harmony export */ "MemoryLocationConfig": () => (/* reexport safe */ _memoryLocationConfig__WEBPACK_IMPORTED_MODULE_7__.MemoryLocationConfig), +/* harmony export */ "BrowserLocationConfig": () => (/* reexport safe */ _browserLocationConfig__WEBPACK_IMPORTED_MODULE_8__.BrowserLocationConfig), +/* harmony export */ "buildUrl": () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_9__.buildUrl), +/* harmony export */ "getParams": () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_9__.getParams), +/* harmony export */ "keyValsToObjectR": () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_9__.keyValsToObjectR), +/* harmony export */ "locationPluginFactory": () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_9__.locationPluginFactory), +/* harmony export */ "parseUrl": () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_9__.parseUrl), +/* harmony export */ "hashLocationPlugin": () => (/* reexport safe */ _plugins__WEBPACK_IMPORTED_MODULE_10__.hashLocationPlugin), +/* harmony export */ "memoryLocationPlugin": () => (/* reexport safe */ _plugins__WEBPACK_IMPORTED_MODULE_10__.memoryLocationPlugin), +/* harmony export */ "pushStateLocationPlugin": () => (/* reexport safe */ _plugins__WEBPACK_IMPORTED_MODULE_10__.pushStateLocationPlugin), +/* harmony export */ "servicesPlugin": () => (/* reexport safe */ _plugins__WEBPACK_IMPORTED_MODULE_10__.servicesPlugin) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/vanilla/interface.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_interface__WEBPACK_IMPORTED_MODULE_0__); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _interface__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== "default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _interface__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _q__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./q */ "./node_modules/@uirouter/core/lib-esm/vanilla/q.js"); +/* harmony import */ var _injector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./injector */ "./node_modules/@uirouter/core/lib-esm/vanilla/injector.js"); +/* harmony import */ var _baseLocationService__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./baseLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/baseLocationService.js"); +/* harmony import */ var _hashLocationService__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./hashLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/hashLocationService.js"); +/* harmony import */ var _memoryLocationService__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./memoryLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationService.js"); +/* harmony import */ var _pushStateLocationService__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./pushStateLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/pushStateLocationService.js"); +/* harmony import */ var _memoryLocationConfig__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./memoryLocationConfig */ "./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationConfig.js"); +/* harmony import */ var _browserLocationConfig__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./browserLocationConfig */ "./node_modules/@uirouter/core/lib-esm/vanilla/browserLocationConfig.js"); +/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./utils */ "./node_modules/@uirouter/core/lib-esm/vanilla/utils.js"); +/* harmony import */ var _plugins__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./plugins */ "./node_modules/@uirouter/core/lib-esm/vanilla/plugins.js"); +/** + * Naive, pure JS implementation of core ui-router services + * + * @packageDocumentation + */ + + + + + + + + + + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/injector.js": +/*!*****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/injector.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "$injector": () => (/* binding */ $injector) +/* harmony export */ }); +/* harmony import */ var _common_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/index */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + +// globally available injectables +var globals = {}; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; +var ARGUMENT_NAMES = /([^\s,]+)/g; +/** + * A basic angular1-like injector api + * + * This object implements four methods similar to the + * [angular 1 dependency injector](https://docs.angularjs.org/api/auto/service/$injector) + * + * UI-Router evolved from an angular 1 library to a framework agnostic library. + * However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection. + * + * This object provides a naive implementation of a globally scoped dependency injection system. + * It supports the following DI approaches: + * + * ### Function parameter names + * + * A function's `.toString()` is called, and the parameter names are parsed. + * This only works when the parameter names aren't "mangled" by a minifier such as UglifyJS. + * + * ```js + * function injectedFunction(FooService, BarService) { + * // FooService and BarService are injected + * } + * ``` + * + * ### Function annotation + * + * A function may be annotated with an array of dependency names as the `$inject` property. + * + * ```js + * injectedFunction.$inject = [ 'FooService', 'BarService' ]; + * function injectedFunction(fs, bs) { + * // FooService and BarService are injected as fs and bs parameters + * } + * ``` + * + * ### Array notation + * + * An array provides the names of the dependencies to inject (as strings). + * The function is the last element of the array. + * + * ```js + * [ 'FooService', 'BarService', function (fs, bs) { + * // FooService and BarService are injected as fs and bs parameters + * }] + * ``` + * + * @type {$InjectorLike} + */ +var $injector = { + /** Gets an object from DI based on a string token */ + get: function (name) { return globals[name]; }, + /** Returns true if an object named `name` exists in global DI */ + has: function (name) { return $injector.get(name) != null; }, + /** + * Injects a function + * + * @param fn the function to inject + * @param context the function's `this` binding + * @param locals An object with additional DI tokens and values, such as `{ someToken: { foo: 1 } }` + */ + invoke: function (fn, context, locals) { + var all = (0,_common_index__WEBPACK_IMPORTED_MODULE_0__.extend)({}, globals, locals || {}); + var params = $injector.annotate(fn); + var ensureExist = (0,_common_index__WEBPACK_IMPORTED_MODULE_0__.assertPredicate)(function (key) { return all.hasOwnProperty(key); }, function (key) { return "DI can't find injectable: '" + key + "'"; }); + var args = params.filter(ensureExist).map(function (x) { return all[x]; }); + if ((0,_common_index__WEBPACK_IMPORTED_MODULE_0__.isFunction)(fn)) + return fn.apply(context, args); + else + return fn.slice(-1)[0].apply(context, args); + }, + /** + * Returns a function's dependencies + * + * Analyzes a function (or array) and returns an array of DI tokens that the function requires. + * @return an array of `string`s + */ + annotate: function (fn) { + if (!(0,_common_index__WEBPACK_IMPORTED_MODULE_0__.isInjectable)(fn)) + throw new Error("Not an injectable function: " + fn); + if (fn && fn.$inject) + return fn.$inject; + if ((0,_common_index__WEBPACK_IMPORTED_MODULE_0__.isArray)(fn)) + return fn.slice(0, -1); + var fnStr = fn.toString().replace(STRIP_COMMENTS, ''); + var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); + return result || []; + }, +}; +//# sourceMappingURL=injector.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/interface.js": +/*!******************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/interface.js ***! + \******************************************************************/ +/***/ (() => { + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationConfig.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationConfig.js ***! + \*****************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MemoryLocationConfig": () => (/* binding */ MemoryLocationConfig) +/* harmony export */ }); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); + + +/** A `LocationConfig` mock that gets/sets all config from an in-memory object */ +var MemoryLocationConfig = /** @class */ (function () { + function MemoryLocationConfig() { + var _this = this; + this.dispose = _common_common__WEBPACK_IMPORTED_MODULE_1__.noop; + this._baseHref = ''; + this._port = 80; + this._protocol = 'http'; + this._host = 'localhost'; + this._hashPrefix = ''; + this.port = function () { return _this._port; }; + this.protocol = function () { return _this._protocol; }; + this.host = function () { return _this._host; }; + this.baseHref = function () { return _this._baseHref; }; + this.html5Mode = function () { return false; }; + this.hashPrefix = function (newval) { return ((0,_common_predicates__WEBPACK_IMPORTED_MODULE_0__.isDefined)(newval) ? (_this._hashPrefix = newval) : _this._hashPrefix); }; + } + return MemoryLocationConfig; +}()); + +//# sourceMappingURL=memoryLocationConfig.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationService.js": +/*!******************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationService.js ***! + \******************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MemoryLocationService": () => (/* binding */ MemoryLocationService) +/* harmony export */ }); +/* harmony import */ var _baseLocationService__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./baseLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/baseLocationService.js"); +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); + +/** A `LocationServices` that gets/sets the current location from an in-memory object */ +var MemoryLocationService = /** @class */ (function (_super) { + __extends(MemoryLocationService, _super); + function MemoryLocationService(router) { + return _super.call(this, router, true) || this; + } + MemoryLocationService.prototype._get = function () { + return this._url; + }; + MemoryLocationService.prototype._set = function (state, title, url, replace) { + this._url = url; + }; + return MemoryLocationService; +}(_baseLocationService__WEBPACK_IMPORTED_MODULE_0__.BaseLocationServices)); + +//# sourceMappingURL=memoryLocationService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/plugins.js": +/*!****************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/plugins.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "servicesPlugin": () => (/* binding */ servicesPlugin), +/* harmony export */ "hashLocationPlugin": () => (/* binding */ hashLocationPlugin), +/* harmony export */ "pushStateLocationPlugin": () => (/* binding */ pushStateLocationPlugin), +/* harmony export */ "memoryLocationPlugin": () => (/* binding */ memoryLocationPlugin) +/* harmony export */ }); +/* harmony import */ var _browserLocationConfig__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./browserLocationConfig */ "./node_modules/@uirouter/core/lib-esm/vanilla/browserLocationConfig.js"); +/* harmony import */ var _hashLocationService__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hashLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/hashLocationService.js"); +/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./node_modules/@uirouter/core/lib-esm/vanilla/utils.js"); +/* harmony import */ var _pushStateLocationService__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./pushStateLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/pushStateLocationService.js"); +/* harmony import */ var _memoryLocationService__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./memoryLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationService.js"); +/* harmony import */ var _memoryLocationConfig__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./memoryLocationConfig */ "./node_modules/@uirouter/core/lib-esm/vanilla/memoryLocationConfig.js"); +/* harmony import */ var _injector__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./injector */ "./node_modules/@uirouter/core/lib-esm/vanilla/injector.js"); +/* harmony import */ var _q__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./q */ "./node_modules/@uirouter/core/lib-esm/vanilla/q.js"); +/* harmony import */ var _common_coreservices__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../common/coreservices */ "./node_modules/@uirouter/core/lib-esm/common/coreservices.js"); + + + + + + + + + +function servicesPlugin(router) { + _common_coreservices__WEBPACK_IMPORTED_MODULE_8__.services.$injector = _injector__WEBPACK_IMPORTED_MODULE_6__.$injector; + _common_coreservices__WEBPACK_IMPORTED_MODULE_8__.services.$q = _q__WEBPACK_IMPORTED_MODULE_7__.$q; + return { name: 'vanilla.services', $q: _q__WEBPACK_IMPORTED_MODULE_7__.$q, $injector: _injector__WEBPACK_IMPORTED_MODULE_6__.$injector, dispose: function () { return null; } }; +} +/** A `UIRouterPlugin` uses the browser hash to get/set the current location */ +var hashLocationPlugin = (0,_utils__WEBPACK_IMPORTED_MODULE_2__.locationPluginFactory)('vanilla.hashBangLocation', false, _hashLocationService__WEBPACK_IMPORTED_MODULE_1__.HashLocationService, _browserLocationConfig__WEBPACK_IMPORTED_MODULE_0__.BrowserLocationConfig); +/** A `UIRouterPlugin` that gets/sets the current location using the browser's `location` and `history` apis */ +var pushStateLocationPlugin = (0,_utils__WEBPACK_IMPORTED_MODULE_2__.locationPluginFactory)('vanilla.pushStateLocation', true, _pushStateLocationService__WEBPACK_IMPORTED_MODULE_3__.PushStateLocationService, _browserLocationConfig__WEBPACK_IMPORTED_MODULE_0__.BrowserLocationConfig); +/** A `UIRouterPlugin` that gets/sets the current location from an in-memory object */ +var memoryLocationPlugin = (0,_utils__WEBPACK_IMPORTED_MODULE_2__.locationPluginFactory)('vanilla.memoryLocation', false, _memoryLocationService__WEBPACK_IMPORTED_MODULE_4__.MemoryLocationService, _memoryLocationConfig__WEBPACK_IMPORTED_MODULE_5__.MemoryLocationConfig); +//# sourceMappingURL=plugins.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/pushStateLocationService.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/pushStateLocationService.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "PushStateLocationService": () => (/* binding */ PushStateLocationService) +/* harmony export */ }); +/* harmony import */ var _baseLocationService__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./baseLocationService */ "./node_modules/@uirouter/core/lib-esm/vanilla/baseLocationService.js"); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); + + +/** + * A `LocationServices` that gets/sets the current location using the browser's `location` and `history` apis + * + * Uses `history.pushState` and `history.replaceState` + */ +var PushStateLocationService = /** @class */ (function (_super) { + __extends(PushStateLocationService, _super); + function PushStateLocationService(router) { + var _this = _super.call(this, router, true) || this; + _this._config = router.urlService.config; + _common__WEBPACK_IMPORTED_MODULE_1__.root.addEventListener('popstate', _this._listener, false); + return _this; + } + /** + * Gets the base prefix without: + * - trailing slash + * - trailing filename + * - protocol and hostname + * + * If , this returns '/base'. + * If , this returns '/foo/base'. + * If , this returns '/base'. + * If , this returns '/base'. + * If , this returns ''. + * If , this returns ''. + * If , this returns ''. + * + * See: https://html.spec.whatwg.org/dev/semantics.html#the-base-element + */ + PushStateLocationService.prototype._getBasePrefix = function () { + return (0,_common__WEBPACK_IMPORTED_MODULE_1__.stripLastPathElement)(this._config.baseHref()); + }; + PushStateLocationService.prototype._get = function () { + var _a = this._location, pathname = _a.pathname, hash = _a.hash, search = _a.search; + search = (0,_common__WEBPACK_IMPORTED_MODULE_1__.splitQuery)(search)[1]; // strip ? if found + hash = (0,_common__WEBPACK_IMPORTED_MODULE_1__.splitHash)(hash)[1]; // strip # if found + var basePrefix = this._getBasePrefix(); + var exactBaseHrefMatch = pathname === this._config.baseHref(); + var startsWithBase = pathname.substr(0, basePrefix.length) === basePrefix; + pathname = exactBaseHrefMatch ? '/' : startsWithBase ? pathname.substring(basePrefix.length) : pathname; + return pathname + (search ? '?' + search : '') + (hash ? '#' + hash : ''); + }; + PushStateLocationService.prototype._set = function (state, title, url, replace) { + var basePrefix = this._getBasePrefix(); + var slash = url && url[0] !== '/' ? '/' : ''; + var fullUrl = url === '' || url === '/' ? this._config.baseHref() : basePrefix + slash + url; + if (replace) { + this._history.replaceState(state, title, fullUrl); + } + else { + this._history.pushState(state, title, fullUrl); + } + }; + PushStateLocationService.prototype.dispose = function (router) { + _super.prototype.dispose.call(this, router); + _common__WEBPACK_IMPORTED_MODULE_1__.root.removeEventListener('popstate', this._listener); + }; + return PushStateLocationService; +}(_baseLocationService__WEBPACK_IMPORTED_MODULE_0__.BaseLocationServices)); + +//# sourceMappingURL=pushStateLocationService.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/q.js": +/*!**********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/q.js ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "$q": () => (/* binding */ $q) +/* harmony export */ }); +/* harmony import */ var _common_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/index */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + +/** + * An angular1-like promise api + * + * This object implements four methods similar to the + * [angular 1 promise api](https://docs.angularjs.org/api/ng/service/$q) + * + * UI-Router evolved from an angular 1 library to a framework agnostic library. + * However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection. + * + * This API provides native ES6 promise support wrapped as a $q-like API. + * Internally, UI-Router uses this $q object to perform promise operations. + * The `angular-ui-router` (ui-router for angular 1) uses the $q API provided by angular. + * + * $q-like promise api + */ +var $q = { + /** Normalizes a value as a promise */ + when: function (val) { return new Promise(function (resolve, reject) { return resolve(val); }); }, + /** Normalizes a value as a promise rejection */ + reject: function (val) { + return new Promise(function (resolve, reject) { + reject(val); + }); + }, + /** @returns a deferred object, which has `resolve` and `reject` functions */ + defer: function () { + var deferred = {}; + deferred.promise = new Promise(function (resolve, reject) { + deferred.resolve = resolve; + deferred.reject = reject; + }); + return deferred; + }, + /** Like Promise.all(), but also supports object key/promise notation like $q */ + all: function (promises) { + if ((0,_common_index__WEBPACK_IMPORTED_MODULE_0__.isArray)(promises)) { + return Promise.all(promises); + } + if ((0,_common_index__WEBPACK_IMPORTED_MODULE_0__.isObject)(promises)) { + // Convert promises map to promises array. + // When each promise resolves, map it to a tuple { key: key, val: val } + var chain = Object.keys(promises).map(function (key) { return promises[key].then(function (val) { return ({ key: key, val: val }); }); }); + // Then wait for all promises to resolve, and convert them back to an object + return $q.all(chain).then(function (values) { + return values.reduce(function (acc, tuple) { + acc[tuple.key] = tuple.val; + return acc; + }, {}); + }); + } + }, +}; +//# sourceMappingURL=q.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/vanilla/utils.js": +/*!**************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/vanilla/utils.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "keyValsToObjectR": () => (/* binding */ keyValsToObjectR), +/* harmony export */ "getParams": () => (/* binding */ getParams), +/* harmony export */ "parseUrl": () => (/* binding */ parseUrl), +/* harmony export */ "buildUrl": () => (/* binding */ buildUrl), +/* harmony export */ "locationPluginFactory": () => (/* binding */ locationPluginFactory) +/* harmony export */ }); +/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common */ "./node_modules/@uirouter/core/lib-esm/common/index.js"); + +var keyValsToObjectR = function (accum, _a) { + var key = _a[0], val = _a[1]; + if (!accum.hasOwnProperty(key)) { + accum[key] = val; + } + else if ((0,_common__WEBPACK_IMPORTED_MODULE_0__.isArray)(accum[key])) { + accum[key].push(val); + } + else { + accum[key] = [accum[key], val]; + } + return accum; +}; +var getParams = function (queryString) { + return queryString.split('&').filter(_common__WEBPACK_IMPORTED_MODULE_0__.identity).map(_common__WEBPACK_IMPORTED_MODULE_0__.splitEqual).reduce(keyValsToObjectR, {}); +}; +function parseUrl(url) { + var orEmptyString = function (x) { return x || ''; }; + var _a = (0,_common__WEBPACK_IMPORTED_MODULE_0__.splitHash)(url).map(orEmptyString), beforehash = _a[0], hash = _a[1]; + var _b = (0,_common__WEBPACK_IMPORTED_MODULE_0__.splitQuery)(beforehash).map(orEmptyString), path = _b[0], search = _b[1]; + return { path: path, search: search, hash: hash, url: url }; +} +var buildUrl = function (loc) { + var path = loc.path(); + var searchObject = loc.search(); + var hash = loc.hash(); + var search = Object.keys(searchObject) + .map(function (key) { + var param = searchObject[key]; + var vals = (0,_common__WEBPACK_IMPORTED_MODULE_0__.isArray)(param) ? param : [param]; + return vals.map(function (val) { return key + '=' + val; }); + }) + .reduce(_common__WEBPACK_IMPORTED_MODULE_0__.unnestR, []) + .join('&'); + return path + (search ? '?' + search : '') + (hash ? '#' + hash : ''); +}; +function locationPluginFactory(name, isHtml5, serviceClass, configurationClass) { + return function (uiRouter) { + var service = (uiRouter.locationService = new serviceClass(uiRouter)); + var configuration = (uiRouter.locationConfig = new configurationClass(uiRouter, isHtml5)); + function dispose(router) { + router.dispose(service); + router.dispose(configuration); + } + return { name: name, service: service, configuration: configuration, dispose: dispose }; + }; +} +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/view/index.js": +/*!***********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/view/index.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ViewService": () => (/* reexport safe */ _view__WEBPACK_IMPORTED_MODULE_1__.ViewService) +/* harmony export */ }); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interface */ "./node_modules/@uirouter/core/lib-esm/view/interface.js"); +/* harmony import */ var _interface__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_interface__WEBPACK_IMPORTED_MODULE_0__); +/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; +/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _interface__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== "default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _interface__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__] +/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); +/* harmony import */ var _view__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./view */ "./node_modules/@uirouter/core/lib-esm/view/view.js"); + + +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/view/interface.js": +/*!***************************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/view/interface.js ***! + \***************************************************************/ +/***/ (() => { + +//# sourceMappingURL=interface.js.map + +/***/ }), + +/***/ "./node_modules/@uirouter/core/lib-esm/view/view.js": +/*!**********************************************************!*\ + !*** ./node_modules/@uirouter/core/lib-esm/view/view.js ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ViewService": () => (/* binding */ ViewService) +/* harmony export */ }); +/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./node_modules/@uirouter/core/lib-esm/common/common.js"); +/* harmony import */ var _common_hof__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/hof */ "./node_modules/@uirouter/core/lib-esm/common/hof.js"); +/* harmony import */ var _common_predicates__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../common/predicates */ "./node_modules/@uirouter/core/lib-esm/common/predicates.js"); +/* harmony import */ var _common_trace__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/trace */ "./node_modules/@uirouter/core/lib-esm/common/trace.js"); + + + + +/** + * The View service + * + * This service pairs existing `ui-view` components (which live in the DOM) + * with view configs (from the state declaration objects: [[StateDeclaration.views]]). + * + * - After a successful Transition, the views from the newly entered states are activated via [[activateViewConfig]]. + * The views from exited states are deactivated via [[deactivateViewConfig]]. + * (See: the [[registerActivateViews]] Transition Hook) + * + * - As `ui-view` components pop in and out of existence, they register themselves using [[registerUIView]]. + * + * - When the [[sync]] function is called, the registered `ui-view`(s) ([[ActiveUIView]]) + * are configured with the matching [[ViewConfig]](s) + * + */ +var ViewService = /** @class */ (function () { + /** @internal */ + function ViewService(/** @internal */ router) { + var _this = this; + this.router = router; + /** @internal */ this._uiViews = []; + /** @internal */ this._viewConfigs = []; + /** @internal */ this._viewConfigFactories = {}; + /** @internal */ this._listeners = []; + /** @internal */ + this._pluginapi = { + _rootViewContext: this._rootViewContext.bind(this), + _viewConfigFactory: this._viewConfigFactory.bind(this), + _registeredUIView: function (id) { return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.find)(_this._uiViews, function (view) { return _this.router.$id + "." + view.id === id; }); }, + _registeredUIViews: function () { return _this._uiViews; }, + _activeViewConfigs: function () { return _this._viewConfigs; }, + _onSync: function (listener) { + _this._listeners.push(listener); + return function () { return (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(_this._listeners, listener); }; + }, + }; + } + /** + * Normalizes a view's name from a state.views configuration block. + * + * This should be used by a framework implementation to calculate the values for + * [[_ViewDeclaration.$uiViewName]] and [[_ViewDeclaration.$uiViewContextAnchor]]. + * + * @param context the context object (state declaration) that the view belongs to + * @param rawViewName the name of the view, as declared in the [[StateDeclaration.views]] + * + * @returns the normalized uiViewName and uiViewContextAnchor that the view targets + */ + ViewService.normalizeUIViewTarget = function (context, rawViewName) { + if (rawViewName === void 0) { rawViewName = ''; } + // TODO: Validate incoming view name with a regexp to allow: + // ex: "view.name@foo.bar" , "^.^.view.name" , "view.name@^.^" , "" , + // "@" , "$default@^" , "!$default.$default" , "!foo.bar" + var viewAtContext = rawViewName.split('@'); + var uiViewName = viewAtContext[0] || '$default'; // default to unnamed view + var uiViewContextAnchor = (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isString)(viewAtContext[1]) ? viewAtContext[1] : '^'; // default to parent context + // Handle relative view-name sugar syntax. + // Matches rawViewName "^.^.^.foo.bar" into array: ["^.^.^.foo.bar", "^.^.^", "foo.bar"], + var relativeViewNameSugar = /^(\^(?:\.\^)*)\.(.*$)/.exec(uiViewName); + if (relativeViewNameSugar) { + // Clobbers existing contextAnchor (rawViewName validation will fix this) + uiViewContextAnchor = relativeViewNameSugar[1]; // set anchor to "^.^.^" + uiViewName = relativeViewNameSugar[2]; // set view-name to "foo.bar" + } + if (uiViewName.charAt(0) === '!') { + uiViewName = uiViewName.substr(1); + uiViewContextAnchor = ''; // target absolutely from root + } + // handle parent relative targeting "^.^.^" + var relativeMatch = /^(\^(?:\.\^)*)$/; + if (relativeMatch.exec(uiViewContextAnchor)) { + var anchorState = uiViewContextAnchor.split('.').reduce(function (anchor, x) { return anchor.parent; }, context); + uiViewContextAnchor = anchorState.name; + } + else if (uiViewContextAnchor === '.') { + uiViewContextAnchor = context.name; + } + return { uiViewName: uiViewName, uiViewContextAnchor: uiViewContextAnchor }; + }; + /** @internal */ + ViewService.prototype._rootViewContext = function (context) { + return (this._rootContext = context || this._rootContext); + }; + /** @internal */ + ViewService.prototype._viewConfigFactory = function (viewType, factory) { + this._viewConfigFactories[viewType] = factory; + }; + ViewService.prototype.createViewConfig = function (path, decl) { + var cfgFactory = this._viewConfigFactories[decl.$type]; + if (!cfgFactory) + throw new Error('ViewService: No view config factory registered for type ' + decl.$type); + var cfgs = cfgFactory(path, decl); + return (0,_common_predicates__WEBPACK_IMPORTED_MODULE_2__.isArray)(cfgs) ? cfgs : [cfgs]; + }; + /** + * Deactivates a ViewConfig. + * + * This function deactivates a `ViewConfig`. + * After calling [[sync]], it will un-pair from any `ui-view` with which it is currently paired. + * + * @param viewConfig The ViewConfig view to deregister. + */ + ViewService.prototype.deactivateViewConfig = function (viewConfig) { + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewServiceEvent('<- Removing', viewConfig); + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(this._viewConfigs, viewConfig); + }; + ViewService.prototype.activateViewConfig = function (viewConfig) { + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewServiceEvent('-> Registering', viewConfig); + this._viewConfigs.push(viewConfig); + }; + ViewService.prototype.sync = function () { + var _this = this; + var uiViewsByFqn = this._uiViews.map(function (uiv) { return [uiv.fqn, uiv]; }).reduce(_common_common__WEBPACK_IMPORTED_MODULE_0__.applyPairs, {}); + // Return a weighted depth value for a uiView. + // The depth is the nesting depth of ui-views (based on FQN; times 10,000) + // plus the depth of the state that is populating the uiView + function uiViewDepth(uiView) { + var stateDepth = function (context) { return (context && context.parent ? stateDepth(context.parent) + 1 : 1); }; + return uiView.fqn.split('.').length * 10000 + stateDepth(uiView.creationContext); + } + // Return the ViewConfig's context's depth in the context tree. + function viewConfigDepth(config) { + var context = config.viewDecl.$context, count = 0; + while (++count && context.parent) + context = context.parent; + return count; + } + // Given a depth function, returns a compare function which can return either ascending or descending order + var depthCompare = (0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.curry)(function (depthFn, posNeg, left, right) { return posNeg * (depthFn(left) - depthFn(right)); }); + var matchingConfigPair = function (uiView) { + var matchingConfigs = _this._viewConfigs.filter(ViewService.matches(uiViewsByFqn, uiView)); + if (matchingConfigs.length > 1) { + // This is OK. Child states can target a ui-view that the parent state also targets (the child wins) + // Sort by depth and return the match from the deepest child + // console.log(`Multiple matching view configs for ${uiView.fqn}`, matchingConfigs); + matchingConfigs.sort(depthCompare(viewConfigDepth, -1)); // descending + } + return { uiView: uiView, viewConfig: matchingConfigs[0] }; + }; + var configureUIView = function (tuple) { + // If a parent ui-view is reconfigured, it could destroy child ui-views. + // Before configuring a child ui-view, make sure it's still in the active uiViews array. + if (_this._uiViews.indexOf(tuple.uiView) !== -1) + tuple.uiView.configUpdated(tuple.viewConfig); + }; + // Sort views by FQN and state depth. Process uiviews nearest the root first. + var uiViewTuples = this._uiViews.sort(depthCompare(uiViewDepth, 1)).map(matchingConfigPair); + var matchedViewConfigs = uiViewTuples.map(function (tuple) { return tuple.viewConfig; }); + var unmatchedConfigTuples = this._viewConfigs + .filter(function (config) { return !(0,_common_common__WEBPACK_IMPORTED_MODULE_0__.inArray)(matchedViewConfigs, config); }) + .map(function (viewConfig) { return ({ uiView: undefined, viewConfig: viewConfig }); }); + uiViewTuples.forEach(configureUIView); + var allTuples = uiViewTuples.concat(unmatchedConfigTuples); + this._listeners.forEach(function (cb) { return cb(allTuples); }); + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewSync(allTuples); + }; + /** + * Registers a `ui-view` component + * + * When a `ui-view` component is created, it uses this method to register itself. + * After registration the [[sync]] method is used to ensure all `ui-view` are configured with the proper [[ViewConfig]]. + * + * Note: the `ui-view` component uses the `ViewConfig` to determine what view should be loaded inside the `ui-view`, + * and what the view's state context is. + * + * Note: There is no corresponding `deregisterUIView`. + * A `ui-view` should hang on to the return value of `registerUIView` and invoke it to deregister itself. + * + * @param uiView The metadata for a UIView + * @return a de-registration function used when the view is destroyed. + */ + ViewService.prototype.registerUIView = function (uiView) { + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewServiceUIViewEvent('-> Registering', uiView); + var uiViews = this._uiViews; + var fqnAndTypeMatches = function (uiv) { return uiv.fqn === uiView.fqn && uiv.$type === uiView.$type; }; + if (uiViews.filter(fqnAndTypeMatches).length) + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewServiceUIViewEvent('!!!! duplicate uiView named:', uiView); + uiViews.push(uiView); + this.sync(); + return function () { + var idx = uiViews.indexOf(uiView); + if (idx === -1) { + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewServiceUIViewEvent('Tried removing non-registered uiView', uiView); + return; + } + _common_trace__WEBPACK_IMPORTED_MODULE_3__.trace.traceViewServiceUIViewEvent('<- Deregistering', uiView); + (0,_common_common__WEBPACK_IMPORTED_MODULE_0__.removeFrom)(uiViews)(uiView); + }; + }; + /** + * Returns the list of views currently available on the page, by fully-qualified name. + * + * @return {Array} Returns an array of fully-qualified view names. + */ + ViewService.prototype.available = function () { + return this._uiViews.map((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('fqn')); + }; + /** + * Returns the list of views on the page containing loaded content. + * + * @return {Array} Returns an array of fully-qualified view names. + */ + ViewService.prototype.active = function () { + return this._uiViews.filter((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('$config')).map((0,_common_hof__WEBPACK_IMPORTED_MODULE_1__.prop)('name')); + }; + /** + * Given a ui-view and a ViewConfig, determines if they "match". + * + * A ui-view has a fully qualified name (fqn) and a context object. The fqn is built from its overall location in + * the DOM, describing its nesting relationship to any parent ui-view tags it is nested inside of. + * + * A ViewConfig has a target ui-view name and a context anchor. The ui-view name can be a simple name, or + * can be a segmented ui-view path, describing a portion of a ui-view fqn. + * + * In order for a ui-view to match ViewConfig, ui-view's $type must match the ViewConfig's $type + * + * If the ViewConfig's target ui-view name is a simple name (no dots), then a ui-view matches if: + * - the ui-view's name matches the ViewConfig's target name + * - the ui-view's context matches the ViewConfig's anchor + * + * If the ViewConfig's target ui-view name is a segmented name (with dots), then a ui-view matches if: + * - There exists a parent ui-view where: + * - the parent ui-view's name matches the first segment (index 0) of the ViewConfig's target name + * - the parent ui-view's context matches the ViewConfig's anchor + * - And the remaining segments (index 1..n) of the ViewConfig's target name match the tail of the ui-view's fqn + * + * Example: + * + * DOM: + * + * + * + * + * + * + * + * + * + * uiViews: [ + * { fqn: "$default", creationContext: { name: "" } }, + * { fqn: "$default.foo", creationContext: { name: "A" } }, + * { fqn: "$default.foo.$default", creationContext: { name: "A.B" } } + * { fqn: "$default.foo.$default.bar", creationContext: { name: "A.B.C" } } + * ] + * + * These four view configs all match the ui-view with the fqn: "$default.foo.$default.bar": + * + * - ViewConfig1: { uiViewName: "bar", uiViewContextAnchor: "A.B.C" } + * - ViewConfig2: { uiViewName: "$default.bar", uiViewContextAnchor: "A.B" } + * - ViewConfig3: { uiViewName: "foo.$default.bar", uiViewContextAnchor: "A" } + * - ViewConfig4: { uiViewName: "$default.foo.$default.bar", uiViewContextAnchor: "" } + * + * Using ViewConfig3 as an example, it matches the ui-view with fqn "$default.foo.$default.bar" because: + * - The ViewConfig's segmented target name is: [ "foo", "$default", "bar" ] + * - There exists a parent ui-view (which has fqn: "$default.foo") where: + * - the parent ui-view's name "foo" matches the first segment "foo" of the ViewConfig's target name + * - the parent ui-view's context "A" matches the ViewConfig's anchor context "A" + * - And the remaining segments [ "$default", "bar" ].join("."_ of the ViewConfig's target name match + * the tail of the ui-view's fqn "default.bar" + * + * @internal + */ + ViewService.matches = function (uiViewsByFqn, uiView) { return function (viewConfig) { + // Don't supply an ng1 ui-view with an ng2 ViewConfig, etc + if (uiView.$type !== viewConfig.viewDecl.$type) + return false; + // Split names apart from both viewConfig and uiView into segments + var vc = viewConfig.viewDecl; + var vcSegments = vc.$uiViewName.split('.'); + var uivSegments = uiView.fqn.split('.'); + // Check if the tails of the segment arrays match. ex, these arrays' tails match: + // vc: ["foo", "bar"], uiv fqn: ["$default", "foo", "bar"] + if (!(0,_common_common__WEBPACK_IMPORTED_MODULE_0__.equals)(vcSegments, uivSegments.slice(0 - vcSegments.length))) + return false; + // Now check if the fqn ending at the first segment of the viewConfig matches the context: + // ["$default", "foo"].join(".") == "$default.foo", does the ui-view $default.foo context match? + var negOffset = 1 - vcSegments.length || undefined; + var fqnToFirstSegment = uivSegments.slice(0, negOffset).join('.'); + var uiViewContext = uiViewsByFqn[fqnToFirstSegment].creationContext; + return vc.$uiViewContextAnchor === (uiViewContext && uiViewContext.name); + }; }; + return ViewService; +}()); + +//# sourceMappingURL=view.js.map + +/***/ }), + +/***/ "./node_modules/angular-animate/angular-animate.js": +/*!*********************************************************!*\ + !*** ./node_modules/angular-animate/angular-animate.js ***! + \*********************************************************/ +/***/ (() => { + +/** + * @license AngularJS v1.8.2 + * (c) 2010-2020 Google LLC. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +var ELEMENT_NODE = 1; +var COMMENT_NODE = 8; + +var ADD_CLASS_SUFFIX = '-add'; +var REMOVE_CLASS_SUFFIX = '-remove'; +var EVENT_CLASS_PREFIX = 'ng-'; +var ACTIVE_CLASS_SUFFIX = '-active'; +var PREPARE_CLASS_SUFFIX = '-prepare'; + +var NG_ANIMATE_CLASSNAME = 'ng-animate'; +var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren'; + +// Detect proper transitionend/animationend event names. +var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; + +// If unprefixed events are not supported but webkit-prefixed are, use the latter. +// Otherwise, just use W3C names, browsers not supporting them at all will just ignore them. +// Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend` +// but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`. +// Register both events in case `window.onanimationend` is not supported because of that, +// do the same for `transitionend` as Safari is likely to exhibit similar behavior. +// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit +// therefore there is no reason to test anymore for other vendor prefixes: +// http://caniuse.com/#search=transition +if ((window.ontransitionend === undefined) && (window.onwebkittransitionend !== undefined)) { + CSS_PREFIX = '-webkit-'; + TRANSITION_PROP = 'WebkitTransition'; + TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; +} else { + TRANSITION_PROP = 'transition'; + TRANSITIONEND_EVENT = 'transitionend'; +} + +if ((window.onanimationend === undefined) && (window.onwebkitanimationend !== undefined)) { + CSS_PREFIX = '-webkit-'; + ANIMATION_PROP = 'WebkitAnimation'; + ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; +} else { + ANIMATION_PROP = 'animation'; + ANIMATIONEND_EVENT = 'animationend'; +} + +var DURATION_KEY = 'Duration'; +var PROPERTY_KEY = 'Property'; +var DELAY_KEY = 'Delay'; +var TIMING_KEY = 'TimingFunction'; +var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; +var ANIMATION_PLAYSTATE_KEY = 'PlayState'; +var SAFE_FAST_FORWARD_DURATION_VALUE = 9999; + +var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY; +var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY; +var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY; +var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY; + +var ngMinErr = angular.$$minErr('ng'); +function assertArg(arg, name, reason) { + if (!arg) { + throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required')); + } + return arg; +} + +function mergeClasses(a,b) { + if (!a && !b) return ''; + if (!a) return b; + if (!b) return a; + if (isArray(a)) a = a.join(' '); + if (isArray(b)) b = b.join(' '); + return a + ' ' + b; +} + +function packageStyles(options) { + var styles = {}; + if (options && (options.to || options.from)) { + styles.to = options.to; + styles.from = options.from; + } + return styles; +} + +function pendClasses(classes, fix, isPrefix) { + var className = ''; + classes = isArray(classes) + ? classes + : classes && isString(classes) && classes.length + ? classes.split(/\s+/) + : []; + forEach(classes, function(klass, i) { + if (klass && klass.length > 0) { + className += (i > 0) ? ' ' : ''; + className += isPrefix ? fix + klass + : klass + fix; + } + }); + return className; +} + +function removeFromArray(arr, val) { + var index = arr.indexOf(val); + if (val >= 0) { + arr.splice(index, 1); + } +} + +function stripCommentsFromElement(element) { + if (element instanceof jqLite) { + switch (element.length) { + case 0: + return element; + + case 1: + // there is no point of stripping anything if the element + // is the only element within the jqLite wrapper. + // (it's important that we retain the element instance.) + if (element[0].nodeType === ELEMENT_NODE) { + return element; + } + break; + + default: + return jqLite(extractElementNode(element)); + } + } + + if (element.nodeType === ELEMENT_NODE) { + return jqLite(element); + } +} + +function extractElementNode(element) { + if (!element[0]) return element; + for (var i = 0; i < element.length; i++) { + var elm = element[i]; + if (elm.nodeType === ELEMENT_NODE) { + return elm; + } + } +} + +function $$addClass($$jqLite, element, className) { + forEach(element, function(elm) { + $$jqLite.addClass(elm, className); + }); +} + +function $$removeClass($$jqLite, element, className) { + forEach(element, function(elm) { + $$jqLite.removeClass(elm, className); + }); +} + +function applyAnimationClassesFactory($$jqLite) { + return function(element, options) { + if (options.addClass) { + $$addClass($$jqLite, element, options.addClass); + options.addClass = null; + } + if (options.removeClass) { + $$removeClass($$jqLite, element, options.removeClass); + options.removeClass = null; + } + }; +} + +function prepareAnimationOptions(options) { + options = options || {}; + if (!options.$$prepared) { + var domOperation = options.domOperation || noop; + options.domOperation = function() { + options.$$domOperationFired = true; + domOperation(); + domOperation = noop; + }; + options.$$prepared = true; + } + return options; +} + +function applyAnimationStyles(element, options) { + applyAnimationFromStyles(element, options); + applyAnimationToStyles(element, options); +} + +function applyAnimationFromStyles(element, options) { + if (options.from) { + element.css(options.from); + options.from = null; + } +} + +function applyAnimationToStyles(element, options) { + if (options.to) { + element.css(options.to); + options.to = null; + } +} + +function mergeAnimationDetails(element, oldAnimation, newAnimation) { + var target = oldAnimation.options || {}; + var newOptions = newAnimation.options || {}; + + var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || ''); + var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || ''); + var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove); + + if (newOptions.preparationClasses) { + target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses); + delete newOptions.preparationClasses; + } + + // noop is basically when there is no callback; otherwise something has been set + var realDomOperation = target.domOperation !== noop ? target.domOperation : null; + + extend(target, newOptions); + + // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this. + if (realDomOperation) { + target.domOperation = realDomOperation; + } + + if (classes.addClass) { + target.addClass = classes.addClass; + } else { + target.addClass = null; + } + + if (classes.removeClass) { + target.removeClass = classes.removeClass; + } else { + target.removeClass = null; + } + + oldAnimation.addClass = target.addClass; + oldAnimation.removeClass = target.removeClass; + + return target; +} + +function resolveElementClasses(existing, toAdd, toRemove) { + var ADD_CLASS = 1; + var REMOVE_CLASS = -1; + + var flags = {}; + existing = splitClassesToLookup(existing); + + toAdd = splitClassesToLookup(toAdd); + forEach(toAdd, function(value, key) { + flags[key] = ADD_CLASS; + }); + + toRemove = splitClassesToLookup(toRemove); + forEach(toRemove, function(value, key) { + flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS; + }); + + var classes = { + addClass: '', + removeClass: '' + }; + + forEach(flags, function(val, klass) { + var prop, allow; + if (val === ADD_CLASS) { + prop = 'addClass'; + allow = !existing[klass] || existing[klass + REMOVE_CLASS_SUFFIX]; + } else if (val === REMOVE_CLASS) { + prop = 'removeClass'; + allow = existing[klass] || existing[klass + ADD_CLASS_SUFFIX]; + } + if (allow) { + if (classes[prop].length) { + classes[prop] += ' '; + } + classes[prop] += klass; + } + }); + + function splitClassesToLookup(classes) { + if (isString(classes)) { + classes = classes.split(' '); + } + + var obj = {}; + forEach(classes, function(klass) { + // sometimes the split leaves empty string values + // incase extra spaces were applied to the options + if (klass.length) { + obj[klass] = true; + } + }); + return obj; + } + + return classes; +} + +function getDomNode(element) { + return (element instanceof jqLite) ? element[0] : element; +} + +function applyGeneratedPreparationClasses($$jqLite, element, event, options) { + var classes = ''; + if (event) { + classes = pendClasses(event, EVENT_CLASS_PREFIX, true); + } + if (options.addClass) { + classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX)); + } + if (options.removeClass) { + classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX)); + } + if (classes.length) { + options.preparationClasses = classes; + element.addClass(classes); + } +} + +function clearGeneratedClasses(element, options) { + if (options.preparationClasses) { + element.removeClass(options.preparationClasses); + options.preparationClasses = null; + } + if (options.activeClasses) { + element.removeClass(options.activeClasses); + options.activeClasses = null; + } +} + +function blockKeyframeAnimations(node, applyBlock) { + var value = applyBlock ? 'paused' : ''; + var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY; + applyInlineStyle(node, [key, value]); + return [key, value]; +} + +function applyInlineStyle(node, styleTuple) { + var prop = styleTuple[0]; + var value = styleTuple[1]; + node.style[prop] = value; +} + +function concatWithSpace(a,b) { + if (!a) return b; + if (!b) return a; + return a + ' ' + b; +} + +var helpers = { + blockTransitions: function(node, duration) { + // we use a negative delay value since it performs blocking + // yet it doesn't kill any existing transitions running on the + // same element which makes this safe for class-based animations + var value = duration ? '-' + duration + 's' : ''; + applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]); + return [TRANSITION_DELAY_PROP, value]; + } +}; + +var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) { + var queue, cancelFn; + + function scheduler(tasks) { + // we make a copy since RAFScheduler mutates the state + // of the passed in array variable and this would be difficult + // to track down on the outside code + queue = queue.concat(tasks); + nextTick(); + } + + queue = scheduler.queue = []; + + /* waitUntilQuiet does two things: + * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through + * 2. It will delay the next wave of tasks from running until the quiet `fn` has run. + * + * The motivation here is that animation code can request more time from the scheduler + * before the next wave runs. This allows for certain DOM properties such as classes to + * be resolved in time for the next animation to run. + */ + scheduler.waitUntilQuiet = function(fn) { + if (cancelFn) cancelFn(); + + cancelFn = $$rAF(function() { + cancelFn = null; + fn(); + nextTick(); + }); + }; + + return scheduler; + + function nextTick() { + if (!queue.length) return; + + var items = queue.shift(); + for (var i = 0; i < items.length; i++) { + items[i](); + } + + if (!cancelFn) { + $$rAF(function() { + if (!cancelFn) nextTick(); + }); + } + } +}]; + +/** + * @ngdoc directive + * @name ngAnimateChildren + * @restrict AE + * @element ANY + * + * @description + * + * ngAnimateChildren allows you to specify that children of this element should animate even if any + * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move` + * (structural) animation, child elements that also have an active structural animation are not animated. + * + * Note that even if `ngAnimateChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation). + * + * + * @param {string} ngAnimateChildren If the value is empty, `true` or `on`, + * then child animations are allowed. If the value is `false`, child animations are not allowed. + * + * @example + * + +
    + + +
    +
    +
    + List of items: +
    Item {{item}}
    +
    +
    +
    +
    + + + .container.ng-enter, + .container.ng-leave { + transition: all ease 1.5s; + } + + .container.ng-enter, + .container.ng-leave-active { + opacity: 0; + } + + .container.ng-leave, + .container.ng-enter-active { + opacity: 1; + } + + .item { + background: firebrick; + color: #FFF; + margin-bottom: 10px; + } + + .item.ng-enter, + .item.ng-leave { + transition: transform 1.5s ease; + } + + .item.ng-enter { + transform: translateX(50px); + } + + .item.ng-enter-active { + transform: translateX(0); + } + + + angular.module('ngAnimateChildren', ['ngAnimate']) + .controller('MainController', function MainController() { + this.animateChildren = false; + this.enterElement = false; + }); + +
    + */ +var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) { + return { + link: function(scope, element, attrs) { + var val = attrs.ngAnimateChildren; + if (isString(val) && val.length === 0) { //empty attribute + element.data(NG_ANIMATE_CHILDREN_DATA, true); + } else { + // Interpolate and set the value, so that it is available to + // animations that run right after compilation + setData($interpolate(val)(scope)); + attrs.$observe('ngAnimateChildren', setData); + } + + function setData(value) { + value = value === 'on' || value === 'true'; + element.data(NG_ANIMATE_CHILDREN_DATA, value); + } + } + }; +}]; + +/* exported $AnimateCssProvider */ + +var ANIMATE_TIMER_KEY = '$$animateCss'; + +/** + * @ngdoc service + * @name $animateCss + * @kind object + * + * @description + * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes + * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT + * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or + * directives to create more complex animations that can be purely driven using CSS code. + * + * Note that only browsers that support CSS transitions and/or keyframe animations are capable of + * rendering animations triggered via `$animateCss` (bad news for IE9 and lower). + * + * ## General Use + * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that + * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however, + * any automatic control over cancelling animations and/or preventing animations from being run on + * child elements will not be handled by AngularJS. For this to work as expected, please use `$animate` to + * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger + * the CSS animation. + * + * The example below shows how we can create a folding animation on an element using `ng-if`: + * + * ```html + * + *
    + * This element will go BOOM + *
    + * + * ``` + * + * Now we create the **JavaScript animation** that will trigger the CSS transition: + * + * ```js + * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { + * return { + * enter: function(element, doneFn) { + * var height = element[0].offsetHeight; + * return $animateCss(element, { + * from: { height:'0px' }, + * to: { height:height + 'px' }, + * duration: 1 // one second + * }); + * } + * } + * }]); + * ``` + * + * ## More Advanced Uses + * + * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks + * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code. + * + * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation, + * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with + * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order + * to provide a working animation that will run in CSS. + * + * The example below showcases a more advanced version of the `.fold-animation` from the example above: + * + * ```js + * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { + * return { + * enter: function(element, doneFn) { + * var height = element[0].offsetHeight; + * return $animateCss(element, { + * addClass: 'red large-text pulse-twice', + * easing: 'ease-out', + * from: { height:'0px' }, + * to: { height:height + 'px' }, + * duration: 1 // one second + * }); + * } + * } + * }]); + * ``` + * + * Since we're adding/removing CSS classes then the CSS transition will also pick those up: + * + * ```css + * /* since a hardcoded duration value of 1 was provided in the JavaScript animation code, + * the CSS classes below will be transitioned despite them being defined as regular CSS classes */ + * .red { background:red; } + * .large-text { font-size:20px; } + * + * /* we can also use a keyframe animation and $animateCss will make it work alongside the transition */ + * .pulse-twice { + * animation: 0.5s pulse linear 2; + * -webkit-animation: 0.5s pulse linear 2; + * } + * + * @keyframes pulse { + * from { transform: scale(0.5); } + * to { transform: scale(1.5); } + * } + * + * @-webkit-keyframes pulse { + * from { -webkit-transform: scale(0.5); } + * to { -webkit-transform: scale(1.5); } + * } + * ``` + * + * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen. + * + * ## How the Options are handled + * + * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation + * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to also animate some inline + * styles using the `from` and `to` properties. + * + * ```js + * var animator = $animateCss(element, { + * from: { background:'red' }, + * to: { background:'blue' } + * }); + * animator.start(); + * ``` + * + * ```css + * .rotating-animation { + * animation:0.5s rotate linear; + * -webkit-animation:0.5s rotate linear; + * } + * + * @keyframes rotate { + * from { transform: rotate(0deg); } + * to { transform: rotate(360deg); } + * } + * + * @-webkit-keyframes rotate { + * from { -webkit-transform: rotate(0deg); } + * to { -webkit-transform: rotate(360deg); } + * } + * ``` + * + * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is + * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition + * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition + * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied + * and spread across the transition and keyframe animation. + * + * ## What is returned + * + * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually + * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are + * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties: + * + * ```js + * var animator = $animateCss(element, { ... }); + * ``` + * + * Now what do the contents of our `animator` variable look like: + * + * ```js + * { + * // starts the animation + * start: Function, + * + * // ends (aborts) the animation + * end: Function + * } + * ``` + * + * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends. + * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been + * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties + * and that changing them will not reconfigure the parameters of the animation. + * + * ### runner.done() vs runner.then() + * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the + * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**. + * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()` + * unless you really need a digest to kick off afterwards. + * + * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss + * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code). + * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works. + * + * @param {DOMElement} element the element that will be animated + * @param {object} options the animation-related options that will be applied during the animation + * + * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied + * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.) + * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and + * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted. + * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both). + * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`). + * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`). + * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation. + * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition. + * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation. + * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation. + * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0` + * is provided then the animation will be skipped entirely. + * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is + * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value + * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same + * CSS delay value. + * * `stagger` - A numeric time value representing the delay between successively animated elements + * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.}) + * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a + * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`) + * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occurring on the classes being added and removed.) + * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once + * the animation is closed. This is useful for when the styles are used purely for the sake of + * the animation and do not have a lasting visual effect on the element (e.g. a collapse and open animation). + * By default this value is set to `false`. + * + * @return {object} an object with start and end methods and details about the animation. + * + * * `start` - The method to start the animation. This will return a `Promise` when called. + * * `end` - This method will cancel the animation and remove all applied CSS classes and styles. + */ +var ONE_SECOND = 1000; + +var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; +var CLOSING_TIME_BUFFER = 1.5; + +var DETECT_CSS_PROPERTIES = { + transitionDuration: TRANSITION_DURATION_PROP, + transitionDelay: TRANSITION_DELAY_PROP, + transitionProperty: TRANSITION_PROP + PROPERTY_KEY, + animationDuration: ANIMATION_DURATION_PROP, + animationDelay: ANIMATION_DELAY_PROP, + animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY +}; + +var DETECT_STAGGER_CSS_PROPERTIES = { + transitionDuration: TRANSITION_DURATION_PROP, + transitionDelay: TRANSITION_DELAY_PROP, + animationDuration: ANIMATION_DURATION_PROP, + animationDelay: ANIMATION_DELAY_PROP +}; + +function getCssKeyframeDurationStyle(duration) { + return [ANIMATION_DURATION_PROP, duration + 's']; +} + +function getCssDelayStyle(delay, isKeyframeAnimation) { + var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP; + return [prop, delay + 's']; +} + +function computeCssStyles($window, element, properties) { + var styles = Object.create(null); + var detectedStyles = $window.getComputedStyle(element) || {}; + forEach(properties, function(formalStyleName, actualStyleName) { + var val = detectedStyles[formalStyleName]; + if (val) { + var c = val.charAt(0); + + // only numerical-based values have a negative sign or digit as the first value + if (c === '-' || c === '+' || c >= 0) { + val = parseMaxTime(val); + } + + // by setting this to null in the event that the delay is not set or is set directly as 0 + // then we can still allow for negative values to be used later on and not mistake this + // value for being greater than any other negative value. + if (val === 0) { + val = null; + } + styles[actualStyleName] = val; + } + }); + + return styles; +} + +function parseMaxTime(str) { + var maxValue = 0; + var values = str.split(/\s*,\s*/); + forEach(values, function(value) { + // it's always safe to consider only second values and omit `ms` values since + // getComputedStyle will always handle the conversion for us + if (value.charAt(value.length - 1) === 's') { + value = value.substring(0, value.length - 1); + } + value = parseFloat(value) || 0; + maxValue = maxValue ? Math.max(value, maxValue) : value; + }); + return maxValue; +} + +function truthyTimingValue(val) { + return val === 0 || val != null; +} + +function getCssTransitionDurationStyle(duration, applyOnlyDuration) { + var style = TRANSITION_PROP; + var value = duration + 's'; + if (applyOnlyDuration) { + style += DURATION_KEY; + } else { + value += ' linear all'; + } + return [style, value]; +} + +// we do not reassign an already present style value since +// if we detect the style property value again we may be +// detecting styles that were added via the `from` styles. +// We make use of `isDefined` here since an empty string +// or null value (which is what getPropertyValue will return +// for a non-existing style) will still be marked as a valid +// value for the style (a falsy value implies that the style +// is to be removed at the end of the animation). If we had a simple +// "OR" statement then it would not be enough to catch that. +function registerRestorableStyles(backup, node, properties) { + forEach(properties, function(prop) { + backup[prop] = isDefined(backup[prop]) + ? backup[prop] + : node.style.getPropertyValue(prop); + }); +} + +var $AnimateCssProvider = ['$animateProvider', /** @this */ function($animateProvider) { + + this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout', '$$animateCache', + '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue', + function($window, $$jqLite, $$AnimateRunner, $timeout, $$animateCache, + $$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) { + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function computeCachedCssStyles(node, className, cacheKey, allowNoDuration, properties) { + var timings = $$animateCache.get(cacheKey); + + if (!timings) { + timings = computeCssStyles($window, node, properties); + if (timings.animationIterationCount === 'infinite') { + timings.animationIterationCount = 1; + } + } + + // if a css animation has no duration we + // should mark that so that repeated addClass/removeClass calls are skipped + var hasDuration = allowNoDuration || (timings.transitionDuration > 0 || timings.animationDuration > 0); + + // we keep putting this in multiple times even though the value and the cacheKey are the same + // because we're keeping an internal tally of how many duplicate animations are detected. + $$animateCache.put(cacheKey, timings, hasDuration); + + return timings; + } + + function computeCachedCssStaggerStyles(node, className, cacheKey, properties) { + var stagger; + var staggerCacheKey = 'stagger-' + cacheKey; + + // if we have one or more existing matches of matching elements + // containing the same parent + CSS styles (which is how cacheKey works) + // then staggering is possible + if ($$animateCache.count(cacheKey) > 0) { + stagger = $$animateCache.get(staggerCacheKey); + + if (!stagger) { + var staggerClassName = pendClasses(className, '-stagger'); + + $$jqLite.addClass(node, staggerClassName); + + stagger = computeCssStyles($window, node, properties); + + // force the conversion of a null value to zero incase not set + stagger.animationDuration = Math.max(stagger.animationDuration, 0); + stagger.transitionDuration = Math.max(stagger.transitionDuration, 0); + + $$jqLite.removeClass(node, staggerClassName); + + $$animateCache.put(staggerCacheKey, stagger, true); + } + } + + return stagger || {}; + } + + var rafWaitQueue = []; + function waitUntilQuiet(callback) { + rafWaitQueue.push(callback); + $$rAFScheduler.waitUntilQuiet(function() { + $$animateCache.flush(); + + // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable. + // PLEASE EXAMINE THE `$$forceReflow` service to understand why. + var pageWidth = $$forceReflow(); + + // we use a for loop to ensure that if the queue is changed + // during this looping then it will consider new requests + for (var i = 0; i < rafWaitQueue.length; i++) { + rafWaitQueue[i](pageWidth); + } + rafWaitQueue.length = 0; + }); + } + + function computeTimings(node, className, cacheKey, allowNoDuration) { + var timings = computeCachedCssStyles(node, className, cacheKey, allowNoDuration, DETECT_CSS_PROPERTIES); + var aD = timings.animationDelay; + var tD = timings.transitionDelay; + timings.maxDelay = aD && tD + ? Math.max(aD, tD) + : (aD || tD); + timings.maxDuration = Math.max( + timings.animationDuration * timings.animationIterationCount, + timings.transitionDuration); + + return timings; + } + + return function init(element, initialOptions) { + // all of the animation functions should create + // a copy of the options data, however, if a + // parent service has already created a copy then + // we should stick to using that + var options = initialOptions || {}; + if (!options.$$prepared) { + options = prepareAnimationOptions(copy(options)); + } + + var restoreStyles = {}; + var node = getDomNode(element); + if (!node + || !node.parentNode + || !$$animateQueue.enabled()) { + return closeAndReturnNoopAnimator(); + } + + var temporaryStyles = []; + var classes = element.attr('class'); + var styles = packageStyles(options); + var animationClosed; + var animationPaused; + var animationCompleted; + var runner; + var runnerHost; + var maxDelay; + var maxDelayTime; + var maxDuration; + var maxDurationTime; + var startTime; + var events = []; + + if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) { + return closeAndReturnNoopAnimator(); + } + + var method = options.event && isArray(options.event) + ? options.event.join(' ') + : options.event; + + var isStructural = method && options.structural; + var structuralClassName = ''; + var addRemoveClassName = ''; + + if (isStructural) { + structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true); + } else if (method) { + structuralClassName = method; + } + + if (options.addClass) { + addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX); + } + + if (options.removeClass) { + if (addRemoveClassName.length) { + addRemoveClassName += ' '; + } + addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX); + } + + // there may be a situation where a structural animation is combined together + // with CSS classes that need to resolve before the animation is computed. + // However this means that there is no explicit CSS code to block the animation + // from happening (by setting 0s none in the class name). If this is the case + // we need to apply the classes before the first rAF so we know to continue if + // there actually is a detected transition or keyframe animation + if (options.applyClassesEarly && addRemoveClassName.length) { + applyAnimationClasses(element, options); + } + + var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim(); + var fullClassName = classes + ' ' + preparationClasses; + var hasToStyles = styles.to && Object.keys(styles.to).length > 0; + var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0; + + // there is no way we can trigger an animation if no styles and + // no classes are being applied which would then trigger a transition, + // unless there a is raw keyframe value that is applied to the element. + if (!containsKeyframeAnimation + && !hasToStyles + && !preparationClasses) { + return closeAndReturnNoopAnimator(); + } + + var stagger, cacheKey = $$animateCache.cacheKey(node, method, options.addClass, options.removeClass); + if ($$animateCache.containsCachedAnimationWithoutDuration(cacheKey)) { + preparationClasses = null; + return closeAndReturnNoopAnimator(); + } + + if (options.stagger > 0) { + var staggerVal = parseFloat(options.stagger); + stagger = { + transitionDelay: staggerVal, + animationDelay: staggerVal, + transitionDuration: 0, + animationDuration: 0 + }; + } else { + stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES); + } + + if (!options.$$skipPreparationClasses) { + $$jqLite.addClass(element, preparationClasses); + } + + var applyOnlyDuration; + + if (options.transitionStyle) { + var transitionStyle = [TRANSITION_PROP, options.transitionStyle]; + applyInlineStyle(node, transitionStyle); + temporaryStyles.push(transitionStyle); + } + + if (options.duration >= 0) { + applyOnlyDuration = node.style[TRANSITION_PROP].length > 0; + var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration); + + // we set the duration so that it will be picked up by getComputedStyle later + applyInlineStyle(node, durationStyle); + temporaryStyles.push(durationStyle); + } + + if (options.keyframeStyle) { + var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle]; + applyInlineStyle(node, keyframeStyle); + temporaryStyles.push(keyframeStyle); + } + + var itemIndex = stagger + ? options.staggerIndex >= 0 + ? options.staggerIndex + : $$animateCache.count(cacheKey) + : 0; + + var isFirst = itemIndex === 0; + + // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY + // without causing any combination of transitions to kick in. By adding a negative delay value + // it forces the setup class' transition to end immediately. We later then remove the negative + // transition delay to allow for the transition to naturally do it's thing. The beauty here is + // that if there is no transition defined then nothing will happen and this will also allow + // other transitions to be stacked on top of each other without any chopping them out. + if (isFirst && !options.skipBlocking) { + helpers.blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE); + } + + var timings = computeTimings(node, fullClassName, cacheKey, !isStructural); + var relativeDelay = timings.maxDelay; + maxDelay = Math.max(relativeDelay, 0); + maxDuration = timings.maxDuration; + + var flags = {}; + flags.hasTransitions = timings.transitionDuration > 0; + flags.hasAnimations = timings.animationDuration > 0; + flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty === 'all'; + flags.applyTransitionDuration = hasToStyles && ( + (flags.hasTransitions && !flags.hasTransitionAll) + || (flags.hasAnimations && !flags.hasTransitions)); + flags.applyAnimationDuration = options.duration && flags.hasAnimations; + flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions); + flags.applyAnimationDelay = truthyTimingValue(options.delay) && flags.hasAnimations; + flags.recalculateTimingStyles = addRemoveClassName.length > 0; + + if (flags.applyTransitionDuration || flags.applyAnimationDuration) { + maxDuration = options.duration ? parseFloat(options.duration) : maxDuration; + + if (flags.applyTransitionDuration) { + flags.hasTransitions = true; + timings.transitionDuration = maxDuration; + applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0; + temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration)); + } + + if (flags.applyAnimationDuration) { + flags.hasAnimations = true; + timings.animationDuration = maxDuration; + temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration)); + } + } + + if (maxDuration === 0 && !flags.recalculateTimingStyles) { + return closeAndReturnNoopAnimator(); + } + + var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX); + + if (options.delay != null) { + var delayStyle; + if (typeof options.delay !== 'boolean') { + delayStyle = parseFloat(options.delay); + // number in options.delay means we have to recalculate the delay for the closing timeout + maxDelay = Math.max(delayStyle, 0); + } + + if (flags.applyTransitionDelay) { + temporaryStyles.push(getCssDelayStyle(delayStyle)); + } + + if (flags.applyAnimationDelay) { + temporaryStyles.push(getCssDelayStyle(delayStyle, true)); + } + } + + // we need to recalculate the delay value since we used a pre-emptive negative + // delay value and the delay value is required for the final event checking. This + // property will ensure that this will happen after the RAF phase has passed. + if (options.duration == null && timings.transitionDuration > 0) { + flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst; + } + + maxDelayTime = maxDelay * ONE_SECOND; + maxDurationTime = maxDuration * ONE_SECOND; + if (!options.skipBlocking) { + flags.blockTransition = timings.transitionDuration > 0; + flags.blockKeyframeAnimation = timings.animationDuration > 0 && + stagger.animationDelay > 0 && + stagger.animationDuration === 0; + } + + if (options.from) { + if (options.cleanupStyles) { + registerRestorableStyles(restoreStyles, node, Object.keys(options.from)); + } + applyAnimationFromStyles(element, options); + } + + if (flags.blockTransition || flags.blockKeyframeAnimation) { + applyBlocking(maxDuration); + } else if (!options.skipBlocking) { + helpers.blockTransitions(node, false); + } + + // TODO(matsko): for 1.5 change this code to have an animator object for better debugging + return { + $$willAnimate: true, + end: endFn, + start: function() { + if (animationClosed) return; + + runnerHost = { + end: endFn, + cancel: cancelFn, + resume: null, //this will be set during the start() phase + pause: null + }; + + runner = new $$AnimateRunner(runnerHost); + + waitUntilQuiet(start); + + // we don't have access to pause/resume the animation + // since it hasn't run yet. AnimateRunner will therefore + // set noop functions for resume and pause and they will + // later be overridden once the animation is triggered + return runner; + } + }; + + function endFn() { + close(); + } + + function cancelFn() { + close(true); + } + + function close(rejected) { + // if the promise has been called already then we shouldn't close + // the animation again + if (animationClosed || (animationCompleted && animationPaused)) return; + animationClosed = true; + animationPaused = false; + + if (preparationClasses && !options.$$skipPreparationClasses) { + $$jqLite.removeClass(element, preparationClasses); + } + + if (activeClasses) { + $$jqLite.removeClass(element, activeClasses); + } + + blockKeyframeAnimations(node, false); + helpers.blockTransitions(node, false); + + forEach(temporaryStyles, function(entry) { + // There is only one way to remove inline style properties entirely from elements. + // By using `removeProperty` this works, but we need to convert camel-cased CSS + // styles down to hyphenated values. + node.style[entry[0]] = ''; + }); + + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + + if (Object.keys(restoreStyles).length) { + forEach(restoreStyles, function(value, prop) { + if (value) { + node.style.setProperty(prop, value); + } else { + node.style.removeProperty(prop); + } + }); + } + + // the reason why we have this option is to allow a synchronous closing callback + // that is fired as SOON as the animation ends (when the CSS is removed) or if + // the animation never takes off at all. A good example is a leave animation since + // the element must be removed just after the animation is over or else the element + // will appear on screen for one animation frame causing an overbearing flicker. + if (options.onDone) { + options.onDone(); + } + + if (events && events.length) { + // Remove the transitionend / animationend listener(s) + element.off(events.join(' '), onAnimationProgress); + } + + //Cancel the fallback closing timeout and remove the timer data + var animationTimerData = element.data(ANIMATE_TIMER_KEY); + if (animationTimerData) { + $timeout.cancel(animationTimerData[0].timer); + element.removeData(ANIMATE_TIMER_KEY); + } + + // if the preparation function fails then the promise is not setup + if (runner) { + runner.complete(!rejected); + } + } + + function applyBlocking(duration) { + if (flags.blockTransition) { + helpers.blockTransitions(node, duration); + } + + if (flags.blockKeyframeAnimation) { + blockKeyframeAnimations(node, !!duration); + } + } + + function closeAndReturnNoopAnimator() { + runner = new $$AnimateRunner({ + end: endFn, + cancel: cancelFn + }); + + // should flush the cache animation + waitUntilQuiet(noop); + close(); + + return { + $$willAnimate: false, + start: function() { + return runner; + }, + end: endFn + }; + } + + function onAnimationProgress(event) { + event.stopPropagation(); + var ev = event.originalEvent || event; + + if (ev.target !== node) { + // Since TransitionEvent / AnimationEvent bubble up, + // we have to ignore events by finished child animations + return; + } + + // we now always use `Date.now()` due to the recent changes with + // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info) + var timeStamp = ev.$manualTimeStamp || Date.now(); + + /* Firefox (or possibly just Gecko) likes to not round values up + * when a ms measurement is used for the animation */ + var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); + + /* $manualTimeStamp is a mocked timeStamp value which is set + * within browserTrigger(). This is only here so that tests can + * mock animations properly. Real events fallback to event.timeStamp, + * or, if they don't, then a timeStamp is automatically created for them. + * We're checking to see if the timeStamp surpasses the expected delay, + * but we're using elapsedTime instead of the timeStamp on the 2nd + * pre-condition since animationPauseds sometimes close off early */ + if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { + // we set this flag to ensure that if the transition is paused then, when resumed, + // the animation will automatically close itself since transitions cannot be paused. + animationCompleted = true; + close(); + } + } + + function start() { + if (animationClosed) return; + if (!node.parentNode) { + close(); + return; + } + + // even though we only pause keyframe animations here the pause flag + // will still happen when transitions are used. Only the transition will + // not be paused since that is not possible. If the animation ends when + // paused then it will not complete until unpaused or cancelled. + var playPause = function(playAnimation) { + if (!animationCompleted) { + animationPaused = !playAnimation; + if (timings.animationDuration) { + var value = blockKeyframeAnimations(node, animationPaused); + if (animationPaused) { + temporaryStyles.push(value); + } else { + removeFromArray(temporaryStyles, value); + } + } + } else if (animationPaused && playAnimation) { + animationPaused = false; + close(); + } + }; + + // checking the stagger duration prevents an accidentally cascade of the CSS delay style + // being inherited from the parent. If the transition duration is zero then we can safely + // rely that the delay value is an intentional stagger delay style. + var maxStagger = itemIndex > 0 + && ((timings.transitionDuration && stagger.transitionDuration === 0) || + (timings.animationDuration && stagger.animationDuration === 0)) + && Math.max(stagger.animationDelay, stagger.transitionDelay); + if (maxStagger) { + $timeout(triggerAnimationStart, + Math.floor(maxStagger * itemIndex * ONE_SECOND), + false); + } else { + triggerAnimationStart(); + } + + // this will decorate the existing promise runner with pause/resume methods + runnerHost.resume = function() { + playPause(true); + }; + + runnerHost.pause = function() { + playPause(false); + }; + + function triggerAnimationStart() { + // just incase a stagger animation kicks in when the animation + // itself was cancelled entirely + if (animationClosed) return; + + applyBlocking(false); + + forEach(temporaryStyles, function(entry) { + var key = entry[0]; + var value = entry[1]; + node.style[key] = value; + }); + + applyAnimationClasses(element, options); + $$jqLite.addClass(element, activeClasses); + + if (flags.recalculateTimingStyles) { + fullClassName = node.getAttribute('class') + ' ' + preparationClasses; + cacheKey = $$animateCache.cacheKey(node, method, options.addClass, options.removeClass); + + timings = computeTimings(node, fullClassName, cacheKey, false); + relativeDelay = timings.maxDelay; + maxDelay = Math.max(relativeDelay, 0); + maxDuration = timings.maxDuration; + + if (maxDuration === 0) { + close(); + return; + } + + flags.hasTransitions = timings.transitionDuration > 0; + flags.hasAnimations = timings.animationDuration > 0; + } + + if (flags.applyAnimationDelay) { + relativeDelay = typeof options.delay !== 'boolean' && truthyTimingValue(options.delay) + ? parseFloat(options.delay) + : relativeDelay; + + maxDelay = Math.max(relativeDelay, 0); + timings.animationDelay = relativeDelay; + delayStyle = getCssDelayStyle(relativeDelay, true); + temporaryStyles.push(delayStyle); + node.style[delayStyle[0]] = delayStyle[1]; + } + + maxDelayTime = maxDelay * ONE_SECOND; + maxDurationTime = maxDuration * ONE_SECOND; + + if (options.easing) { + var easeProp, easeVal = options.easing; + if (flags.hasTransitions) { + easeProp = TRANSITION_PROP + TIMING_KEY; + temporaryStyles.push([easeProp, easeVal]); + node.style[easeProp] = easeVal; + } + if (flags.hasAnimations) { + easeProp = ANIMATION_PROP + TIMING_KEY; + temporaryStyles.push([easeProp, easeVal]); + node.style[easeProp] = easeVal; + } + } + + if (timings.transitionDuration) { + events.push(TRANSITIONEND_EVENT); + } + + if (timings.animationDuration) { + events.push(ANIMATIONEND_EVENT); + } + + startTime = Date.now(); + var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime; + var endTime = startTime + timerTime; + + var animationsData = element.data(ANIMATE_TIMER_KEY) || []; + var setupFallbackTimer = true; + if (animationsData.length) { + var currentTimerData = animationsData[0]; + setupFallbackTimer = endTime > currentTimerData.expectedEndTime; + if (setupFallbackTimer) { + $timeout.cancel(currentTimerData.timer); + } else { + animationsData.push(close); + } + } + + if (setupFallbackTimer) { + var timer = $timeout(onAnimationExpired, timerTime, false); + animationsData[0] = { + timer: timer, + expectedEndTime: endTime + }; + animationsData.push(close); + element.data(ANIMATE_TIMER_KEY, animationsData); + } + + if (events.length) { + element.on(events.join(' '), onAnimationProgress); + } + + if (options.to) { + if (options.cleanupStyles) { + registerRestorableStyles(restoreStyles, node, Object.keys(options.to)); + } + applyAnimationToStyles(element, options); + } + } + + function onAnimationExpired() { + var animationsData = element.data(ANIMATE_TIMER_KEY); + + // this will be false in the event that the element was + // removed from the DOM (via a leave animation or something + // similar) + if (animationsData) { + for (var i = 1; i < animationsData.length; i++) { + animationsData[i](); + } + element.removeData(ANIMATE_TIMER_KEY); + } + } + } + }; + }]; +}]; + +var $$AnimateCssDriverProvider = ['$$animationProvider', /** @this */ function($$animationProvider) { + $$animationProvider.drivers.push('$$animateCssDriver'); + + var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim'; + var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-anchor'; + + var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out'; + var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in'; + + function isDocumentFragment(node) { + return node.parentNode && node.parentNode.nodeType === 11; + } + + this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document', + function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) { + + // only browsers that support these properties can render animations + if (!$sniffer.animations && !$sniffer.transitions) return noop; + + var bodyNode = $document[0].body; + var rootNode = getDomNode($rootElement); + + var rootBodyElement = jqLite( + // this is to avoid using something that exists outside of the body + // we also special case the doc fragment case because our unit test code + // appends the $rootElement to the body after the app has been bootstrapped + isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode + ); + + return function initDriverFn(animationDetails) { + return animationDetails.from && animationDetails.to + ? prepareFromToAnchorAnimation(animationDetails.from, + animationDetails.to, + animationDetails.classes, + animationDetails.anchors) + : prepareRegularAnimation(animationDetails); + }; + + function filterCssClasses(classes) { + //remove all the `ng-` stuff + return classes.replace(/\bng-\S+\b/g, ''); + } + + function getUniqueValues(a, b) { + if (isString(a)) a = a.split(' '); + if (isString(b)) b = b.split(' '); + return a.filter(function(val) { + return b.indexOf(val) === -1; + }).join(' '); + } + + function prepareAnchoredAnimation(classes, outAnchor, inAnchor) { + var clone = jqLite(getDomNode(outAnchor).cloneNode(true)); + var startingClasses = filterCssClasses(getClassVal(clone)); + + outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME); + inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME); + + clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME); + + rootBodyElement.append(clone); + + var animatorIn, animatorOut = prepareOutAnimation(); + + // the user may not end up using the `out` animation and + // only making use of the `in` animation or vice-versa. + // In either case we should allow this and not assume the + // animation is over unless both animations are not used. + if (!animatorOut) { + animatorIn = prepareInAnimation(); + if (!animatorIn) { + return end(); + } + } + + var startingAnimator = animatorOut || animatorIn; + + return { + start: function() { + var runner; + + var currentAnimation = startingAnimator.start(); + currentAnimation.done(function() { + currentAnimation = null; + if (!animatorIn) { + animatorIn = prepareInAnimation(); + if (animatorIn) { + currentAnimation = animatorIn.start(); + currentAnimation.done(function() { + currentAnimation = null; + end(); + runner.complete(); + }); + return currentAnimation; + } + } + // in the event that there is no `in` animation + end(); + runner.complete(); + }); + + runner = new $$AnimateRunner({ + end: endFn, + cancel: endFn + }); + + return runner; + + function endFn() { + if (currentAnimation) { + currentAnimation.end(); + } + } + } + }; + + function calculateAnchorStyles(anchor) { + var styles = {}; + + var coords = getDomNode(anchor).getBoundingClientRect(); + + // we iterate directly since safari messes up and doesn't return + // all the keys for the coords object when iterated + forEach(['width','height','top','left'], function(key) { + var value = coords[key]; + switch (key) { + case 'top': + value += bodyNode.scrollTop; + break; + case 'left': + value += bodyNode.scrollLeft; + break; + } + styles[key] = Math.floor(value) + 'px'; + }); + return styles; + } + + function prepareOutAnimation() { + var animator = $animateCss(clone, { + addClass: NG_OUT_ANCHOR_CLASS_NAME, + delay: true, + from: calculateAnchorStyles(outAnchor) + }); + + // read the comment within `prepareRegularAnimation` to understand + // why this check is necessary + return animator.$$willAnimate ? animator : null; + } + + function getClassVal(element) { + return element.attr('class') || ''; + } + + function prepareInAnimation() { + var endingClasses = filterCssClasses(getClassVal(inAnchor)); + var toAdd = getUniqueValues(endingClasses, startingClasses); + var toRemove = getUniqueValues(startingClasses, endingClasses); + + var animator = $animateCss(clone, { + to: calculateAnchorStyles(inAnchor), + addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd, + removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove, + delay: true + }); + + // read the comment within `prepareRegularAnimation` to understand + // why this check is necessary + return animator.$$willAnimate ? animator : null; + } + + function end() { + clone.remove(); + outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME); + inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME); + } + } + + function prepareFromToAnchorAnimation(from, to, classes, anchors) { + var fromAnimation = prepareRegularAnimation(from, noop); + var toAnimation = prepareRegularAnimation(to, noop); + + var anchorAnimations = []; + forEach(anchors, function(anchor) { + var outElement = anchor['out']; + var inElement = anchor['in']; + var animator = prepareAnchoredAnimation(classes, outElement, inElement); + if (animator) { + anchorAnimations.push(animator); + } + }); + + // no point in doing anything when there are no elements to animate + if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return; + + return { + start: function() { + var animationRunners = []; + + if (fromAnimation) { + animationRunners.push(fromAnimation.start()); + } + + if (toAnimation) { + animationRunners.push(toAnimation.start()); + } + + forEach(anchorAnimations, function(animation) { + animationRunners.push(animation.start()); + }); + + var runner = new $$AnimateRunner({ + end: endFn, + cancel: endFn // CSS-driven animations cannot be cancelled, only ended + }); + + $$AnimateRunner.all(animationRunners, function(status) { + runner.complete(status); + }); + + return runner; + + function endFn() { + forEach(animationRunners, function(runner) { + runner.end(); + }); + } + } + }; + } + + function prepareRegularAnimation(animationDetails) { + var element = animationDetails.element; + var options = animationDetails.options || {}; + + if (animationDetails.structural) { + options.event = animationDetails.event; + options.structural = true; + options.applyClassesEarly = true; + + // we special case the leave animation since we want to ensure that + // the element is removed as soon as the animation is over. Otherwise + // a flicker might appear or the element may not be removed at all + if (animationDetails.event === 'leave') { + options.onDone = options.domOperation; + } + } + + // We assign the preparationClasses as the actual animation event since + // the internals of $animateCss will just suffix the event token values + // with `-active` to trigger the animation. + if (options.preparationClasses) { + options.event = concatWithSpace(options.event, options.preparationClasses); + } + + var animator = $animateCss(element, options); + + // the driver lookup code inside of $$animation attempts to spawn a + // driver one by one until a driver returns a.$$willAnimate animator object. + // $animateCss will always return an object, however, it will pass in + // a flag as a hint as to whether an animation was detected or not + return animator.$$willAnimate ? animator : null; + } + }]; +}]; + +// TODO(matsko): use caching here to speed things up for detection +// TODO(matsko): add documentation +// by the time... + +var $$AnimateJsProvider = ['$animateProvider', /** @this */ function($animateProvider) { + this.$get = ['$injector', '$$AnimateRunner', '$$jqLite', + function($injector, $$AnimateRunner, $$jqLite) { + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + // $animateJs(element, 'enter'); + return function(element, event, classes, options) { + var animationClosed = false; + + // the `classes` argument is optional and if it is not used + // then the classes will be resolved from the element's className + // property as well as options.addClass/options.removeClass. + if (arguments.length === 3 && isObject(classes)) { + options = classes; + classes = null; + } + + options = prepareAnimationOptions(options); + if (!classes) { + classes = element.attr('class') || ''; + if (options.addClass) { + classes += ' ' + options.addClass; + } + if (options.removeClass) { + classes += ' ' + options.removeClass; + } + } + + var classesToAdd = options.addClass; + var classesToRemove = options.removeClass; + + // the lookupAnimations function returns a series of animation objects that are + // matched up with one or more of the CSS classes. These animation objects are + // defined via the module.animation factory function. If nothing is detected then + // we don't return anything which then makes $animation query the next driver. + var animations = lookupAnimations(classes); + var before, after; + if (animations.length) { + var afterFn, beforeFn; + if (event === 'leave') { + beforeFn = 'leave'; + afterFn = 'afterLeave'; // TODO(matsko): get rid of this + } else { + beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1); + afterFn = event; + } + + if (event !== 'enter' && event !== 'move') { + before = packageAnimations(element, event, options, animations, beforeFn); + } + after = packageAnimations(element, event, options, animations, afterFn); + } + + // no matching animations + if (!before && !after) return; + + function applyOptions() { + options.domOperation(); + applyAnimationClasses(element, options); + } + + function close() { + animationClosed = true; + applyOptions(); + applyAnimationStyles(element, options); + } + + var runner; + + return { + $$willAnimate: true, + end: function() { + if (runner) { + runner.end(); + } else { + close(); + runner = new $$AnimateRunner(); + runner.complete(true); + } + return runner; + }, + start: function() { + if (runner) { + return runner; + } + + runner = new $$AnimateRunner(); + var closeActiveAnimations; + var chain = []; + + if (before) { + chain.push(function(fn) { + closeActiveAnimations = before(fn); + }); + } + + if (chain.length) { + chain.push(function(fn) { + applyOptions(); + fn(true); + }); + } else { + applyOptions(); + } + + if (after) { + chain.push(function(fn) { + closeActiveAnimations = after(fn); + }); + } + + runner.setHost({ + end: function() { + endAnimations(); + }, + cancel: function() { + endAnimations(true); + } + }); + + $$AnimateRunner.chain(chain, onComplete); + return runner; + + function onComplete(success) { + close(success); + runner.complete(success); + } + + function endAnimations(cancelled) { + if (!animationClosed) { + (closeActiveAnimations || noop)(cancelled); + onComplete(cancelled); + } + } + } + }; + + function executeAnimationFn(fn, element, event, options, onDone) { + var args; + switch (event) { + case 'animate': + args = [element, options.from, options.to, onDone]; + break; + + case 'setClass': + args = [element, classesToAdd, classesToRemove, onDone]; + break; + + case 'addClass': + args = [element, classesToAdd, onDone]; + break; + + case 'removeClass': + args = [element, classesToRemove, onDone]; + break; + + default: + args = [element, onDone]; + break; + } + + args.push(options); + + var value = fn.apply(fn, args); + if (value) { + if (isFunction(value.start)) { + value = value.start(); + } + + if (value instanceof $$AnimateRunner) { + value.done(onDone); + } else if (isFunction(value)) { + // optional onEnd / onCancel callback + return value; + } + } + + return noop; + } + + function groupEventedAnimations(element, event, options, animations, fnName) { + var operations = []; + forEach(animations, function(ani) { + var animation = ani[fnName]; + if (!animation) return; + + // note that all of these animations will run in parallel + operations.push(function() { + var runner; + var endProgressCb; + + var resolved = false; + var onAnimationComplete = function(rejected) { + if (!resolved) { + resolved = true; + (endProgressCb || noop)(rejected); + runner.complete(!rejected); + } + }; + + runner = new $$AnimateRunner({ + end: function() { + onAnimationComplete(); + }, + cancel: function() { + onAnimationComplete(true); + } + }); + + endProgressCb = executeAnimationFn(animation, element, event, options, function(result) { + var cancelled = result === false; + onAnimationComplete(cancelled); + }); + + return runner; + }); + }); + + return operations; + } + + function packageAnimations(element, event, options, animations, fnName) { + var operations = groupEventedAnimations(element, event, options, animations, fnName); + if (operations.length === 0) { + var a, b; + if (fnName === 'beforeSetClass') { + a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass'); + b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass'); + } else if (fnName === 'setClass') { + a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass'); + b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass'); + } + + if (a) { + operations = operations.concat(a); + } + if (b) { + operations = operations.concat(b); + } + } + + if (operations.length === 0) return; + + // TODO(matsko): add documentation + return function startAnimation(callback) { + var runners = []; + if (operations.length) { + forEach(operations, function(animateFn) { + runners.push(animateFn()); + }); + } + + if (runners.length) { + $$AnimateRunner.all(runners, callback); + } else { + callback(); + } + + return function endFn(reject) { + forEach(runners, function(runner) { + if (reject) { + runner.cancel(); + } else { + runner.end(); + } + }); + }; + }; + } + }; + + function lookupAnimations(classes) { + classes = isArray(classes) ? classes : classes.split(' '); + var matches = [], flagMap = {}; + for (var i = 0; i < classes.length; i++) { + var klass = classes[i], + animationFactory = $animateProvider.$$registeredAnimations[klass]; + if (animationFactory && !flagMap[klass]) { + matches.push($injector.get(animationFactory)); + flagMap[klass] = true; + } + } + return matches; + } + }]; +}]; + +var $$AnimateJsDriverProvider = ['$$animationProvider', /** @this */ function($$animationProvider) { + $$animationProvider.drivers.push('$$animateJsDriver'); + this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) { + return function initDriverFn(animationDetails) { + if (animationDetails.from && animationDetails.to) { + var fromAnimation = prepareAnimation(animationDetails.from); + var toAnimation = prepareAnimation(animationDetails.to); + if (!fromAnimation && !toAnimation) return; + + return { + start: function() { + var animationRunners = []; + + if (fromAnimation) { + animationRunners.push(fromAnimation.start()); + } + + if (toAnimation) { + animationRunners.push(toAnimation.start()); + } + + $$AnimateRunner.all(animationRunners, done); + + var runner = new $$AnimateRunner({ + end: endFnFactory(), + cancel: endFnFactory() + }); + + return runner; + + function endFnFactory() { + return function() { + forEach(animationRunners, function(runner) { + // at this point we cannot cancel animations for groups just yet. 1.5+ + runner.end(); + }); + }; + } + + function done(status) { + runner.complete(status); + } + } + }; + } else { + return prepareAnimation(animationDetails); + } + }; + + function prepareAnimation(animationDetails) { + // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations + var element = animationDetails.element; + var event = animationDetails.event; + var options = animationDetails.options; + var classes = animationDetails.classes; + return $$animateJs(element, event, classes, options); + } + }]; +}]; + +var NG_ANIMATE_ATTR_NAME = 'data-ng-animate'; +var NG_ANIMATE_PIN_DATA = '$ngAnimatePin'; +var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animateProvider) { + var PRE_DIGEST_STATE = 1; + var RUNNING_STATE = 2; + var ONE_SPACE = ' '; + + var rules = this.rules = { + skip: [], + cancel: [], + join: [] + }; + + function getEventData(options) { + return { + addClass: options.addClass, + removeClass: options.removeClass, + from: options.from, + to: options.to + }; + } + + function makeTruthyCssClassMap(classString) { + if (!classString) { + return null; + } + + var keys = classString.split(ONE_SPACE); + var map = Object.create(null); + + forEach(keys, function(key) { + map[key] = true; + }); + return map; + } + + function hasMatchingClasses(newClassString, currentClassString) { + if (newClassString && currentClassString) { + var currentClassMap = makeTruthyCssClassMap(currentClassString); + return newClassString.split(ONE_SPACE).some(function(className) { + return currentClassMap[className]; + }); + } + } + + function isAllowed(ruleType, currentAnimation, previousAnimation) { + return rules[ruleType].some(function(fn) { + return fn(currentAnimation, previousAnimation); + }); + } + + function hasAnimationClasses(animation, and) { + var a = (animation.addClass || '').length > 0; + var b = (animation.removeClass || '').length > 0; + return and ? a && b : a || b; + } + + rules.join.push(function(newAnimation, currentAnimation) { + // if the new animation is class-based then we can just tack that on + return !newAnimation.structural && hasAnimationClasses(newAnimation); + }); + + rules.skip.push(function(newAnimation, currentAnimation) { + // there is no need to animate anything if no classes are being added and + // there is no structural animation that will be triggered + return !newAnimation.structural && !hasAnimationClasses(newAnimation); + }); + + rules.skip.push(function(newAnimation, currentAnimation) { + // why should we trigger a new structural animation if the element will + // be removed from the DOM anyway? + return currentAnimation.event === 'leave' && newAnimation.structural; + }); + + rules.skip.push(function(newAnimation, currentAnimation) { + // if there is an ongoing current animation then don't even bother running the class-based animation + return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural; + }); + + rules.cancel.push(function(newAnimation, currentAnimation) { + // there can never be two structural animations running at the same time + return currentAnimation.structural && newAnimation.structural; + }); + + rules.cancel.push(function(newAnimation, currentAnimation) { + // if the previous animation is already running, but the new animation will + // be triggered, but the new animation is structural + return currentAnimation.state === RUNNING_STATE && newAnimation.structural; + }); + + rules.cancel.push(function(newAnimation, currentAnimation) { + // cancel the animation if classes added / removed in both animation cancel each other out, + // but only if the current animation isn't structural + + if (currentAnimation.structural) return false; + + var nA = newAnimation.addClass; + var nR = newAnimation.removeClass; + var cA = currentAnimation.addClass; + var cR = currentAnimation.removeClass; + + // early detection to save the global CPU shortage :) + if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) { + return false; + } + + return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA); + }); + + this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$Map', + '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow', + '$$isDocumentHidden', + function($$rAF, $rootScope, $rootElement, $document, $$Map, + $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow, + $$isDocumentHidden) { + + var activeAnimationsLookup = new $$Map(); + var disabledElementsLookup = new $$Map(); + var animationsEnabled = null; + + function removeFromDisabledElementsLookup(evt) { + disabledElementsLookup.delete(evt.target); + } + + function postDigestTaskFactory() { + var postDigestCalled = false; + return function(fn) { + // we only issue a call to postDigest before + // it has first passed. This prevents any callbacks + // from not firing once the animation has completed + // since it will be out of the digest cycle. + if (postDigestCalled) { + fn(); + } else { + $rootScope.$$postDigest(function() { + postDigestCalled = true; + fn(); + }); + } + }; + } + + // Wait until all directive and route-related templates are downloaded and + // compiled. The $templateRequest.totalPendingRequests variable keeps track of + // all of the remote templates being currently downloaded. If there are no + // templates currently downloading then the watcher will still fire anyway. + var deregisterWatch = $rootScope.$watch( + function() { return $templateRequest.totalPendingRequests === 0; }, + function(isEmpty) { + if (!isEmpty) return; + deregisterWatch(); + + // Now that all templates have been downloaded, $animate will wait until + // the post digest queue is empty before enabling animations. By having two + // calls to $postDigest calls we can ensure that the flag is enabled at the + // very end of the post digest queue. Since all of the animations in $animate + // use $postDigest, it's important that the code below executes at the end. + // This basically means that the page is fully downloaded and compiled before + // any animations are triggered. + $rootScope.$$postDigest(function() { + $rootScope.$$postDigest(function() { + // we check for null directly in the event that the application already called + // .enabled() with whatever arguments that it provided it with + if (animationsEnabled === null) { + animationsEnabled = true; + } + }); + }); + } + ); + + var callbackRegistry = Object.create(null); + + // remember that the `customFilter`/`classNameFilter` are set during the + // provider/config stage therefore we can optimize here and setup helper functions + var customFilter = $animateProvider.customFilter(); + var classNameFilter = $animateProvider.classNameFilter(); + var returnTrue = function() { return true; }; + + var isAnimatableByFilter = customFilter || returnTrue; + var isAnimatableClassName = !classNameFilter ? returnTrue : function(node, options) { + var className = [node.getAttribute('class'), options.addClass, options.removeClass].join(' '); + return classNameFilter.test(className); + }; + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function normalizeAnimationDetails(element, animation) { + return mergeAnimationDetails(element, animation, {}); + } + + // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. + var contains = window.Node.prototype.contains || /** @this */ function(arg) { + // eslint-disable-next-line no-bitwise + return this === arg || !!(this.compareDocumentPosition(arg) & 16); + }; + + function findCallbacks(targetParentNode, targetNode, event) { + var matches = []; + var entries = callbackRegistry[event]; + if (entries) { + forEach(entries, function(entry) { + if (contains.call(entry.node, targetNode)) { + matches.push(entry.callback); + } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) { + matches.push(entry.callback); + } + }); + } + + return matches; + } + + function filterFromRegistry(list, matchContainer, matchCallback) { + var containerNode = extractElementNode(matchContainer); + return list.filter(function(entry) { + var isMatch = entry.node === containerNode && + (!matchCallback || entry.callback === matchCallback); + return !isMatch; + }); + } + + function cleanupEventListeners(phase, node) { + if (phase === 'close' && !node.parentNode) { + // If the element is not attached to a parentNode, it has been removed by + // the domOperation, and we can safely remove the event callbacks + $animate.off(node); + } + } + + var $animate = { + on: function(event, container, callback) { + var node = extractElementNode(container); + callbackRegistry[event] = callbackRegistry[event] || []; + callbackRegistry[event].push({ + node: node, + callback: callback + }); + + // Remove the callback when the element is removed from the DOM + jqLite(container).on('$destroy', function() { + var animationDetails = activeAnimationsLookup.get(node); + + if (!animationDetails) { + // If there's an animation ongoing, the callback calling code will remove + // the event listeners. If we'd remove here, the callbacks would be removed + // before the animation ends + $animate.off(event, container, callback); + } + }); + }, + + off: function(event, container, callback) { + if (arguments.length === 1 && !isString(arguments[0])) { + container = arguments[0]; + for (var eventType in callbackRegistry) { + callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container); + } + + return; + } + + var entries = callbackRegistry[event]; + if (!entries) return; + + callbackRegistry[event] = arguments.length === 1 + ? null + : filterFromRegistry(entries, container, callback); + }, + + pin: function(element, parentElement) { + assertArg(isElement(element), 'element', 'not an element'); + assertArg(isElement(parentElement), 'parentElement', 'not an element'); + element.data(NG_ANIMATE_PIN_DATA, parentElement); + }, + + push: function(element, event, options, domOperation) { + options = options || {}; + options.domOperation = domOperation; + return queueAnimation(element, event, options); + }, + + // this method has four signatures: + // () - global getter + // (bool) - global setter + // (element) - element getter + // (element, bool) - element setter + enabled: function(element, bool) { + var argCount = arguments.length; + + if (argCount === 0) { + // () - Global getter + bool = !!animationsEnabled; + } else { + var hasElement = isElement(element); + + if (!hasElement) { + // (bool) - Global setter + bool = animationsEnabled = !!element; + } else { + var node = getDomNode(element); + + if (argCount === 1) { + // (element) - Element getter + bool = !disabledElementsLookup.get(node); + } else { + // (element, bool) - Element setter + if (!disabledElementsLookup.has(node)) { + // The element is added to the map for the first time. + // Create a listener to remove it on `$destroy` (to avoid memory leak). + jqLite(element).on('$destroy', removeFromDisabledElementsLookup); + } + disabledElementsLookup.set(node, !bool); + } + } + } + + return bool; + } + }; + + return $animate; + + function queueAnimation(originalElement, event, initialOptions) { + // we always make a copy of the options since + // there should never be any side effects on + // the input data when running `$animateCss`. + var options = copy(initialOptions); + + var element = stripCommentsFromElement(originalElement); + var node = getDomNode(element); + var parentNode = node && node.parentNode; + + options = prepareAnimationOptions(options); + + // we create a fake runner with a working promise. + // These methods will become available after the digest has passed + var runner = new $$AnimateRunner(); + + // this is used to trigger callbacks in postDigest mode + var runInNextPostDigestOrNow = postDigestTaskFactory(); + + if (isArray(options.addClass)) { + options.addClass = options.addClass.join(' '); + } + + if (options.addClass && !isString(options.addClass)) { + options.addClass = null; + } + + if (isArray(options.removeClass)) { + options.removeClass = options.removeClass.join(' '); + } + + if (options.removeClass && !isString(options.removeClass)) { + options.removeClass = null; + } + + if (options.from && !isObject(options.from)) { + options.from = null; + } + + if (options.to && !isObject(options.to)) { + options.to = null; + } + + // If animations are hard-disabled for the whole application there is no need to continue. + // There are also situations where a directive issues an animation for a jqLite wrapper that + // contains only comment nodes. In this case, there is no way we can perform an animation. + if (!animationsEnabled || + !node || + !isAnimatableByFilter(node, event, initialOptions) || + !isAnimatableClassName(node, options)) { + close(); + return runner; + } + + var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + + var documentHidden = $$isDocumentHidden(); + + // This is a hard disable of all animations the element itself, therefore there is no need to + // continue further past this point if not enabled + // Animations are also disabled if the document is currently hidden (page is not visible + // to the user), because browsers slow down or do not flush calls to requestAnimationFrame + var skipAnimations = documentHidden || disabledElementsLookup.get(node); + var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {}; + var hasExistingAnimation = !!existingAnimation.state; + + // there is no point in traversing the same collection of parent ancestors if a followup + // animation will be run on the same element that already did all that checking work + if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state !== PRE_DIGEST_STATE)) { + skipAnimations = !areAnimationsAllowed(node, parentNode, event); + } + + if (skipAnimations) { + // Callbacks should fire even if the document is hidden (regression fix for issue #14120) + if (documentHidden) notifyProgress(runner, event, 'start', getEventData(options)); + close(); + if (documentHidden) notifyProgress(runner, event, 'close', getEventData(options)); + return runner; + } + + if (isStructural) { + closeChildAnimations(node); + } + + var newAnimation = { + structural: isStructural, + element: element, + event: event, + addClass: options.addClass, + removeClass: options.removeClass, + close: close, + options: options, + runner: runner + }; + + if (hasExistingAnimation) { + var skipAnimationFlag = isAllowed('skip', newAnimation, existingAnimation); + if (skipAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + close(); + return runner; + } else { + mergeAnimationDetails(element, existingAnimation, newAnimation); + return existingAnimation.runner; + } + } + var cancelAnimationFlag = isAllowed('cancel', newAnimation, existingAnimation); + if (cancelAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + // this will end the animation right away and it is safe + // to do so since the animation is already running and the + // runner callback code will run in async + existingAnimation.runner.end(); + } else if (existingAnimation.structural) { + // this means that the animation is queued into a digest, but + // hasn't started yet. Therefore it is safe to run the close + // method which will call the runner methods in async. + existingAnimation.close(); + } else { + // this will merge the new animation options into existing animation options + mergeAnimationDetails(element, existingAnimation, newAnimation); + + return existingAnimation.runner; + } + } else { + // a joined animation means that this animation will take over the existing one + // so an example would involve a leave animation taking over an enter. Then when + // the postDigest kicks in the enter will be ignored. + var joinAnimationFlag = isAllowed('join', newAnimation, existingAnimation); + if (joinAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + normalizeAnimationDetails(element, newAnimation); + } else { + applyGeneratedPreparationClasses($$jqLite, element, isStructural ? event : null, options); + + event = newAnimation.event = existingAnimation.event; + options = mergeAnimationDetails(element, existingAnimation, newAnimation); + + //we return the same runner since only the option values of this animation will + //be fed into the `existingAnimation`. + return existingAnimation.runner; + } + } + } + } else { + // normalization in this case means that it removes redundant CSS classes that + // already exist (addClass) or do not exist (removeClass) on the element + normalizeAnimationDetails(element, newAnimation); + } + + // when the options are merged and cleaned up we may end up not having to do + // an animation at all, therefore we should check this before issuing a post + // digest callback. Structural animations will always run no matter what. + var isValidAnimation = newAnimation.structural; + if (!isValidAnimation) { + // animate (from/to) can be quickly checked first, otherwise we check if any classes are present + isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0) + || hasAnimationClasses(newAnimation); + } + + if (!isValidAnimation) { + close(); + clearElementAnimationState(node); + return runner; + } + + // the counter keeps track of cancelled animations + var counter = (existingAnimation.counter || 0) + 1; + newAnimation.counter = counter; + + markElementAnimationState(node, PRE_DIGEST_STATE, newAnimation); + + $rootScope.$$postDigest(function() { + // It is possible that the DOM nodes inside `originalElement` have been replaced. This can + // happen if the animated element is a transcluded clone and also has a `templateUrl` + // directive on it. Therefore, we must recreate `element` in order to interact with the + // actual DOM nodes. + // Note: We still need to use the old `node` for certain things, such as looking up in + // HashMaps where it was used as the key. + + element = stripCommentsFromElement(originalElement); + + var animationDetails = activeAnimationsLookup.get(node); + var animationCancelled = !animationDetails; + animationDetails = animationDetails || {}; + + // if addClass/removeClass is called before something like enter then the + // registered parent element may not be present. The code below will ensure + // that a final value for parent element is obtained + var parentElement = element.parent() || []; + + // animate/structural/class-based animations all have requirements. Otherwise there + // is no point in performing an animation. The parent node must also be set. + var isValidAnimation = parentElement.length > 0 + && (animationDetails.event === 'animate' + || animationDetails.structural + || hasAnimationClasses(animationDetails)); + + // this means that the previous animation was cancelled + // even if the follow-up animation is the same event + if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) { + // if another animation did not take over then we need + // to make sure that the domOperation and options are + // handled accordingly + if (animationCancelled) { + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + } + + // if the event changed from something like enter to leave then we do + // it, otherwise if it's the same then the end result will be the same too + if (animationCancelled || (isStructural && animationDetails.event !== event)) { + options.domOperation(); + runner.end(); + } + + // in the event that the element animation was not cancelled or a follow-up animation + // isn't allowed to animate from here then we need to clear the state of the element + // so that any future animations won't read the expired animation data. + if (!isValidAnimation) { + clearElementAnimationState(node); + } + + return; + } + + // this combined multiple class to addClass / removeClass into a setClass event + // so long as a structural event did not take over the animation + event = !animationDetails.structural && hasAnimationClasses(animationDetails, true) + ? 'setClass' + : animationDetails.event; + + markElementAnimationState(node, RUNNING_STATE); + var realRunner = $$animation(element, event, animationDetails.options); + + // this will update the runner's flow-control events based on + // the `realRunner` object. + runner.setHost(realRunner); + notifyProgress(runner, event, 'start', getEventData(options)); + + realRunner.done(function(status) { + close(!status); + var animationDetails = activeAnimationsLookup.get(node); + if (animationDetails && animationDetails.counter === counter) { + clearElementAnimationState(node); + } + notifyProgress(runner, event, 'close', getEventData(options)); + }); + }); + + return runner; + + function notifyProgress(runner, event, phase, data) { + runInNextPostDigestOrNow(function() { + var callbacks = findCallbacks(parentNode, node, event); + if (callbacks.length) { + // do not optimize this call here to RAF because + // we don't know how heavy the callback code here will + // be and if this code is buffered then this can + // lead to a performance regression. + $$rAF(function() { + forEach(callbacks, function(callback) { + callback(element, phase, data); + }); + cleanupEventListeners(phase, node); + }); + } else { + cleanupEventListeners(phase, node); + } + }); + runner.progress(event, phase, data); + } + + function close(reject) { + clearGeneratedClasses(element, options); + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + options.domOperation(); + runner.complete(!reject); + } + } + + function closeChildAnimations(node) { + var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']'); + forEach(children, function(child) { + var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10); + var animationDetails = activeAnimationsLookup.get(child); + if (animationDetails) { + switch (state) { + case RUNNING_STATE: + animationDetails.runner.end(); + /* falls through */ + case PRE_DIGEST_STATE: + activeAnimationsLookup.delete(child); + break; + } + } + }); + } + + function clearElementAnimationState(node) { + node.removeAttribute(NG_ANIMATE_ATTR_NAME); + activeAnimationsLookup.delete(node); + } + + /** + * This fn returns false if any of the following is true: + * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed + * b) a parent element has an ongoing structural animation, and animateChildren is false + * c) the element is not a child of the body + * d) the element is not a child of the $rootElement + */ + function areAnimationsAllowed(node, parentNode, event) { + var bodyNode = $document[0].body; + var rootNode = getDomNode($rootElement); + + var bodyNodeDetected = (node === bodyNode) || node.nodeName === 'HTML'; + var rootNodeDetected = (node === rootNode); + var parentAnimationDetected = false; + var elementDisabled = disabledElementsLookup.get(node); + var animateChildren; + + var parentHost = jqLite.data(node, NG_ANIMATE_PIN_DATA); + if (parentHost) { + parentNode = getDomNode(parentHost); + } + + while (parentNode) { + if (!rootNodeDetected) { + // AngularJS doesn't want to attempt to animate elements outside of the application + // therefore we need to ensure that the rootElement is an ancestor of the current element + rootNodeDetected = (parentNode === rootNode); + } + + if (parentNode.nodeType !== ELEMENT_NODE) { + // no point in inspecting the #document element + break; + } + + var details = activeAnimationsLookup.get(parentNode) || {}; + // either an enter, leave or move animation will commence + // therefore we can't allow any animations to take place + // but if a parent animation is class-based then that's ok + if (!parentAnimationDetected) { + var parentNodeDisabled = disabledElementsLookup.get(parentNode); + + if (parentNodeDisabled === true && elementDisabled !== false) { + // disable animations if the user hasn't explicitly enabled animations on the + // current element + elementDisabled = true; + // element is disabled via parent element, no need to check anything else + break; + } else if (parentNodeDisabled === false) { + elementDisabled = false; + } + parentAnimationDetected = details.structural; + } + + if (isUndefined(animateChildren) || animateChildren === true) { + var value = jqLite.data(parentNode, NG_ANIMATE_CHILDREN_DATA); + if (isDefined(value)) { + animateChildren = value; + } + } + + // there is no need to continue traversing at this point + if (parentAnimationDetected && animateChildren === false) break; + + if (!bodyNodeDetected) { + // we also need to ensure that the element is or will be a part of the body element + // otherwise it is pointless to even issue an animation to be rendered + bodyNodeDetected = (parentNode === bodyNode); + } + + if (bodyNodeDetected && rootNodeDetected) { + // If both body and root have been found, any other checks are pointless, + // as no animation data should live outside the application + break; + } + + if (!rootNodeDetected) { + // If `rootNode` is not detected, check if `parentNode` is pinned to another element + parentHost = jqLite.data(parentNode, NG_ANIMATE_PIN_DATA); + if (parentHost) { + // The pin target element becomes the next parent element + parentNode = getDomNode(parentHost); + continue; + } + } + + parentNode = parentNode.parentNode; + } + + var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true; + return allowAnimation && rootNodeDetected && bodyNodeDetected; + } + + function markElementAnimationState(node, state, details) { + details = details || {}; + details.state = state; + + node.setAttribute(NG_ANIMATE_ATTR_NAME, state); + + var oldValue = activeAnimationsLookup.get(node); + var newValue = oldValue + ? extend(oldValue, details) + : details; + activeAnimationsLookup.set(node, newValue); + } + }]; +}]; + +/** @this */ +var $$AnimateCacheProvider = function() { + + var KEY = '$$ngAnimateParentKey'; + var parentCounter = 0; + var cache = Object.create(null); + + this.$get = [function() { + return { + cacheKey: function(node, method, addClass, removeClass) { + var parentNode = node.parentNode; + var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter); + var parts = [parentID, method, node.getAttribute('class')]; + if (addClass) { + parts.push(addClass); + } + if (removeClass) { + parts.push(removeClass); + } + return parts.join(' '); + }, + + containsCachedAnimationWithoutDuration: function(key) { + var entry = cache[key]; + + // nothing cached, so go ahead and animate + // otherwise it should be a valid animation + return (entry && !entry.isValid) || false; + }, + + flush: function() { + cache = Object.create(null); + }, + + count: function(key) { + var entry = cache[key]; + return entry ? entry.total : 0; + }, + + get: function(key) { + var entry = cache[key]; + return entry && entry.value; + }, + + put: function(key, value, isValid) { + if (!cache[key]) { + cache[key] = { total: 1, value: value, isValid: isValid }; + } else { + cache[key].total++; + cache[key].value = value; + } + } + }; + }]; +}; + +/* exported $$AnimationProvider */ + +var $$AnimationProvider = ['$animateProvider', /** @this */ function($animateProvider) { + var NG_ANIMATE_REF_ATTR = 'ng-animate-ref'; + + var drivers = this.drivers = []; + + var RUNNER_STORAGE_KEY = '$$animationRunner'; + var PREPARE_CLASSES_KEY = '$$animatePrepareClasses'; + + function setRunner(element, runner) { + element.data(RUNNER_STORAGE_KEY, runner); + } + + function removeRunner(element) { + element.removeData(RUNNER_STORAGE_KEY); + } + + function getRunner(element) { + return element.data(RUNNER_STORAGE_KEY); + } + + this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$Map', '$$rAFScheduler', '$$animateCache', + function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$Map, $$rAFScheduler, $$animateCache) { + + var animationQueue = []; + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function sortAnimations(animations) { + var tree = { children: [] }; + var i, lookup = new $$Map(); + + // this is done first beforehand so that the map + // is filled with a list of the elements that will be animated + for (i = 0; i < animations.length; i++) { + var animation = animations[i]; + lookup.set(animation.domNode, animations[i] = { + domNode: animation.domNode, + element: animation.element, + fn: animation.fn, + children: [] + }); + } + + for (i = 0; i < animations.length; i++) { + processNode(animations[i]); + } + + return flatten(tree); + + function processNode(entry) { + if (entry.processed) return entry; + entry.processed = true; + + var elementNode = entry.domNode; + var parentNode = elementNode.parentNode; + lookup.set(elementNode, entry); + + var parentEntry; + while (parentNode) { + parentEntry = lookup.get(parentNode); + if (parentEntry) { + if (!parentEntry.processed) { + parentEntry = processNode(parentEntry); + } + break; + } + parentNode = parentNode.parentNode; + } + + (parentEntry || tree).children.push(entry); + return entry; + } + + function flatten(tree) { + var result = []; + var queue = []; + var i; + + for (i = 0; i < tree.children.length; i++) { + queue.push(tree.children[i]); + } + + var remainingLevelEntries = queue.length; + var nextLevelEntries = 0; + var row = []; + + for (i = 0; i < queue.length; i++) { + var entry = queue[i]; + if (remainingLevelEntries <= 0) { + remainingLevelEntries = nextLevelEntries; + nextLevelEntries = 0; + result.push(row); + row = []; + } + row.push(entry); + entry.children.forEach(function(childEntry) { + nextLevelEntries++; + queue.push(childEntry); + }); + remainingLevelEntries--; + } + + if (row.length) { + result.push(row); + } + + return result; + } + } + + // TODO(matsko): document the signature in a better way + return function(element, event, options) { + options = prepareAnimationOptions(options); + var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + + // there is no animation at the current moment, however + // these runner methods will get later updated with the + // methods leading into the driver's end/cancel methods + // for now they just stop the animation from starting + var runner = new $$AnimateRunner({ + end: function() { close(); }, + cancel: function() { close(true); } + }); + + if (!drivers.length) { + close(); + return runner; + } + + var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass)); + var tempClasses = options.tempClasses; + if (tempClasses) { + classes += ' ' + tempClasses; + options.tempClasses = null; + } + + if (isStructural) { + element.data(PREPARE_CLASSES_KEY, 'ng-' + event + PREPARE_CLASS_SUFFIX); + } + + setRunner(element, runner); + + animationQueue.push({ + // this data is used by the postDigest code and passed into + // the driver step function + element: element, + classes: classes, + event: event, + structural: isStructural, + options: options, + beforeStart: beforeStart, + close: close + }); + + element.on('$destroy', handleDestroyedElement); + + // we only want there to be one function called within the post digest + // block. This way we can group animations for all the animations that + // were apart of the same postDigest flush call. + if (animationQueue.length > 1) return runner; + + $rootScope.$$postDigest(function() { + var animations = []; + forEach(animationQueue, function(entry) { + // the element was destroyed early on which removed the runner + // form its storage. This means we can't animate this element + // at all and it already has been closed due to destruction. + if (getRunner(entry.element)) { + animations.push(entry); + } else { + entry.close(); + } + }); + + // now any future animations will be in another postDigest + animationQueue.length = 0; + + var groupedAnimations = groupAnimations(animations); + var toBeSortedAnimations = []; + + forEach(groupedAnimations, function(animationEntry) { + var element = animationEntry.from ? animationEntry.from.element : animationEntry.element; + var extraClasses = options.addClass; + + extraClasses = (extraClasses ? (extraClasses + ' ') : '') + NG_ANIMATE_CLASSNAME; + var cacheKey = $$animateCache.cacheKey(element[0], animationEntry.event, extraClasses, options.removeClass); + + toBeSortedAnimations.push({ + element: element, + domNode: getDomNode(element), + fn: function triggerAnimationStart() { + var startAnimationFn, closeFn = animationEntry.close; + + // in the event that we've cached the animation status for this element + // and it's in fact an invalid animation (something that has duration = 0) + // then we should skip all the heavy work from here on + if ($$animateCache.containsCachedAnimationWithoutDuration(cacheKey)) { + closeFn(); + return; + } + + // it's important that we apply the `ng-animate` CSS class and the + // temporary classes before we do any driver invoking since these + // CSS classes may be required for proper CSS detection. + animationEntry.beforeStart(); + + // in the event that the element was removed before the digest runs or + // during the RAF sequencing then we should not trigger the animation. + var targetElement = animationEntry.anchors + ? (animationEntry.from.element || animationEntry.to.element) + : animationEntry.element; + + if (getRunner(targetElement)) { + var operation = invokeFirstDriver(animationEntry); + if (operation) { + startAnimationFn = operation.start; + } + } + + if (!startAnimationFn) { + closeFn(); + } else { + var animationRunner = startAnimationFn(); + animationRunner.done(function(status) { + closeFn(!status); + }); + updateAnimationRunners(animationEntry, animationRunner); + } + } + }); + }); + + // we need to sort each of the animations in order of parent to child + // relationships. This ensures that the child classes are applied at the + // right time. + var finalAnimations = sortAnimations(toBeSortedAnimations); + for (var i = 0; i < finalAnimations.length; i++) { + var innerArray = finalAnimations[i]; + for (var j = 0; j < innerArray.length; j++) { + var entry = innerArray[j]; + var element = entry.element; + + // the RAFScheduler code only uses functions + finalAnimations[i][j] = entry.fn; + + // the first row of elements shouldn't have a prepare-class added to them + // since the elements are at the top of the animation hierarchy and they + // will be applied without a RAF having to pass... + if (i === 0) { + element.removeData(PREPARE_CLASSES_KEY); + continue; + } + + var prepareClassName = element.data(PREPARE_CLASSES_KEY); + if (prepareClassName) { + $$jqLite.addClass(element, prepareClassName); + } + } + } + + $$rAFScheduler(finalAnimations); + }); + + return runner; + + // TODO(matsko): change to reference nodes + function getAnchorNodes(node) { + var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']'; + var items = node.hasAttribute(NG_ANIMATE_REF_ATTR) + ? [node] + : node.querySelectorAll(SELECTOR); + var anchors = []; + forEach(items, function(node) { + var attr = node.getAttribute(NG_ANIMATE_REF_ATTR); + if (attr && attr.length) { + anchors.push(node); + } + }); + return anchors; + } + + function groupAnimations(animations) { + var preparedAnimations = []; + var refLookup = {}; + forEach(animations, function(animation, index) { + var element = animation.element; + var node = getDomNode(element); + var event = animation.event; + var enterOrMove = ['enter', 'move'].indexOf(event) >= 0; + var anchorNodes = animation.structural ? getAnchorNodes(node) : []; + + if (anchorNodes.length) { + var direction = enterOrMove ? 'to' : 'from'; + + forEach(anchorNodes, function(anchor) { + var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR); + refLookup[key] = refLookup[key] || {}; + refLookup[key][direction] = { + animationID: index, + element: jqLite(anchor) + }; + }); + } else { + preparedAnimations.push(animation); + } + }); + + var usedIndicesLookup = {}; + var anchorGroups = {}; + forEach(refLookup, function(operations, key) { + var from = operations.from; + var to = operations.to; + + if (!from || !to) { + // only one of these is set therefore we can't have an + // anchor animation since all three pieces are required + var index = from ? from.animationID : to.animationID; + var indexKey = index.toString(); + if (!usedIndicesLookup[indexKey]) { + usedIndicesLookup[indexKey] = true; + preparedAnimations.push(animations[index]); + } + return; + } + + var fromAnimation = animations[from.animationID]; + var toAnimation = animations[to.animationID]; + var lookupKey = from.animationID.toString(); + if (!anchorGroups[lookupKey]) { + var group = anchorGroups[lookupKey] = { + structural: true, + beforeStart: function() { + fromAnimation.beforeStart(); + toAnimation.beforeStart(); + }, + close: function() { + fromAnimation.close(); + toAnimation.close(); + }, + classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes), + from: fromAnimation, + to: toAnimation, + anchors: [] // TODO(matsko): change to reference nodes + }; + + // the anchor animations require that the from and to elements both have at least + // one shared CSS class which effectively marries the two elements together to use + // the same animation driver and to properly sequence the anchor animation. + if (group.classes.length) { + preparedAnimations.push(group); + } else { + preparedAnimations.push(fromAnimation); + preparedAnimations.push(toAnimation); + } + } + + anchorGroups[lookupKey].anchors.push({ + 'out': from.element, 'in': to.element + }); + }); + + return preparedAnimations; + } + + function cssClassesIntersection(a,b) { + a = a.split(' '); + b = b.split(' '); + var matches = []; + + for (var i = 0; i < a.length; i++) { + var aa = a[i]; + if (aa.substring(0,3) === 'ng-') continue; + + for (var j = 0; j < b.length; j++) { + if (aa === b[j]) { + matches.push(aa); + break; + } + } + } + + return matches.join(' '); + } + + function invokeFirstDriver(animationDetails) { + // we loop in reverse order since the more general drivers (like CSS and JS) + // may attempt more elements, but custom drivers are more particular + for (var i = drivers.length - 1; i >= 0; i--) { + var driverName = drivers[i]; + var factory = $injector.get(driverName); + var driver = factory(animationDetails); + if (driver) { + return driver; + } + } + } + + function beforeStart() { + tempClasses = (tempClasses ? (tempClasses + ' ') : '') + NG_ANIMATE_CLASSNAME; + $$jqLite.addClass(element, tempClasses); + + var prepareClassName = element.data(PREPARE_CLASSES_KEY); + if (prepareClassName) { + $$jqLite.removeClass(element, prepareClassName); + prepareClassName = null; + } + } + + function updateAnimationRunners(animation, newRunner) { + if (animation.from && animation.to) { + update(animation.from.element); + update(animation.to.element); + } else { + update(animation.element); + } + + function update(element) { + var runner = getRunner(element); + if (runner) runner.setHost(newRunner); + } + } + + function handleDestroyedElement() { + var runner = getRunner(element); + if (runner && (event !== 'leave' || !options.$$domOperationFired)) { + runner.end(); + } + } + + function close(rejected) { + element.off('$destroy', handleDestroyedElement); + removeRunner(element); + + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + options.domOperation(); + + if (tempClasses) { + $$jqLite.removeClass(element, tempClasses); + } + + runner.complete(!rejected); + } + }; + }]; +}]; + +/** + * @ngdoc directive + * @name ngAnimateSwap + * @restrict A + * @scope + * + * @description + * + * ngAnimateSwap is a animation-oriented directive that allows for the container to + * be removed and entered in whenever the associated expression changes. A + * common usecase for this directive is a rotating banner or slider component which + * contains one image being present at a time. When the active image changes + * then the old image will perform a `leave` animation and the new element + * will be inserted via an `enter` animation. + * + * @animations + * | Animation | Occurs | + * |----------------------------------|--------------------------------------| + * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM | + * | {@link ng.$animate#leave leave} | when the old element is removed from the DOM | + * + * @example + * + * + *
    + *
    + * {{ number }} + *
    + *
    + *
    + * + * angular.module('ngAnimateSwapExample', ['ngAnimate']) + * .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) { + * $scope.number = 0; + * $interval(function() { + * $scope.number++; + * }, 1000); + * + * var colors = ['red','blue','green','yellow','orange']; + * $scope.colorClass = function(number) { + * return colors[number % colors.length]; + * }; + * }]); + * + * + * .container { + * height:250px; + * width:250px; + * position:relative; + * overflow:hidden; + * border:2px solid black; + * } + * .container .cell { + * font-size:150px; + * text-align:center; + * line-height:250px; + * position:absolute; + * top:0; + * left:0; + * right:0; + * border-bottom:2px solid black; + * } + * .swap-animation.ng-enter, .swap-animation.ng-leave { + * transition:0.5s linear all; + * } + * .swap-animation.ng-enter { + * top:-250px; + * } + * .swap-animation.ng-enter-active { + * top:0px; + * } + * .swap-animation.ng-leave { + * top:0px; + * } + * .swap-animation.ng-leave-active { + * top:250px; + * } + * .red { background:red; } + * .green { background:green; } + * .blue { background:blue; } + * .yellow { background:yellow; } + * .orange { background:orange; } + * + *
    + */ +var ngAnimateSwapDirective = ['$animate', function($animate) { + return { + restrict: 'A', + transclude: 'element', + terminal: true, + priority: 550, // We use 550 here to ensure that the directive is caught before others, + // but after `ngIf` (at priority 600). + link: function(scope, $element, attrs, ctrl, $transclude) { + var previousElement, previousScope; + scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) { + if (previousElement) { + $animate.leave(previousElement); + } + if (previousScope) { + previousScope.$destroy(); + previousScope = null; + } + if (value || value === 0) { + $transclude(function(clone, childScope) { + previousElement = clone; + previousScope = childScope; + $animate.enter(clone, null, $element); + }); + } + }); + } + }; +}]; + +/** + * @ngdoc module + * @name ngAnimate + * @description + * + * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via + * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an AngularJS app. + * + * ## Usage + * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based + * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For + * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within + * the HTML element that the animation will be triggered on. + * + * ## Directive Support + * The following directives are "animation aware": + * + * | Directive | Supported Animations | + * |-------------------------------------------------------------------------------|---------------------------------------------------------------------------| + * | {@link ng.directive:form#animations form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) | + * | {@link ngAnimate.directive:ngAnimateSwap#animations ngAnimateSwap} | enter and leave | + * | {@link ng.directive:ngClass#animations ngClass / {{class}​}} | add and remove | + * | {@link ng.directive:ngClassEven#animations ngClassEven} | add and remove | + * | {@link ng.directive:ngClassOdd#animations ngClassOdd} | add and remove | + * | {@link ng.directive:ngHide#animations ngHide} | add and remove (the `ng-hide` class) | + * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | + * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | + * | {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave | + * | {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) | + * | {@link ng.directive:ngModel#animations ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) | + * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move | + * | {@link ng.directive:ngShow#animations ngShow} | add and remove (the `ng-hide` class) | + * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | + * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave | + * + * (More information can be found by visiting the documentation associated with each directive.) + * + * For a full breakdown of the steps involved during each animation event, refer to the + * {@link ng.$animate `$animate` API docs}. + * + * ## CSS-based Animations + * + * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML + * and CSS code we can create an animation that will be picked up by AngularJS when an underlying directive performs an operation. + * + * The example below shows how an `enter` animation can be made possible on an element using `ng-if`: + * + * ```html + *
    + * Fade me in out + *
    + * + * + * ``` + * + * Notice the CSS class **fade**? We can now create the CSS transition code that references this class: + * + * ```css + * /* The starting CSS styles for the enter animation */ + * .fade.ng-enter { + * transition:0.5s linear all; + * opacity:0; + * } + * + * /* The finishing CSS styles for the enter animation */ + * .fade.ng-enter.ng-enter-active { + * opacity:1; + * } + * ``` + * + * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two + * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition + * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards. + * + * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions: + * + * ```css + * /* now the element will fade out before it is removed from the DOM */ + * .fade.ng-leave { + * transition:0.5s linear all; + * opacity:1; + * } + * .fade.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class: + * + * ```css + * /* there is no need to define anything inside of the destination + * CSS class since the keyframe will take charge of the animation */ + * .fade.ng-leave { + * animation: my_fade_animation 0.5s linear; + * -webkit-animation: my_fade_animation 0.5s linear; + * } + * + * @keyframes my_fade_animation { + * from { opacity:1; } + * to { opacity:0; } + * } + * + * @-webkit-keyframes my_fade_animation { + * from { opacity:1; } + * to { opacity:0; } + * } + * ``` + * + * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element. + * + * ### CSS Class-based Animations + * + * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different + * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added + * and removed. + * + * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class: + * + * ```html + *
    + * Show and hide me + *
    + * + * + * + * ``` + * + * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since + * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest. + * + * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation + * with CSS styles. + * + * ```html + *
    + * Highlight this box + *
    + * + * + * + * ``` + * + * We can also make use of CSS keyframes by placing them within the CSS classes. + * + * + * ### CSS Staggering Animations + * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a + * curtain-like effect. The ngAnimate module (versions >=1.2) supports staggering animations and the stagger effect can be + * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for + * the animation. The style property expected within the stagger class can either be a **transition-delay** or an + * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). + * + * ```css + * .my-animation.ng-enter { + * /* standard transition code */ + * transition: 1s linear all; + * opacity:0; + * } + * .my-animation.ng-enter-stagger { + * /* this will have a 100ms delay between each successive leave animation */ + * transition-delay: 0.1s; + * + * /* As of 1.4.4, this must always be set: it signals ngAnimate + * to not accidentally inherit a delay property from another CSS class */ + * transition-duration: 0s; + * + * /* if you are using animations instead of transitions you should configure as follows: + * animation-delay: 0.1s; + * animation-duration: 0s; */ + * } + * .my-animation.ng-enter.ng-enter-active { + * /* standard transition styles */ + * opacity:1; + * } + * ``` + * + * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations + * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this + * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation + * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired. + * + * The following code will issue the **ng-leave-stagger** event on the element provided: + * + * ```js + * var kids = parent.children(); + * + * $animate.leave(kids[0]); //stagger index=0 + * $animate.leave(kids[1]); //stagger index=1 + * $animate.leave(kids[2]); //stagger index=2 + * $animate.leave(kids[3]); //stagger index=3 + * $animate.leave(kids[4]); //stagger index=4 + * + * window.requestAnimationFrame(function() { + * //stagger has reset itself + * $animate.leave(kids[5]); //stagger index=0 + * $animate.leave(kids[6]); //stagger index=1 + * + * $scope.$digest(); + * }); + * ``` + * + * Stagger animations are currently only supported within CSS-defined animations. + * + * ### The `ng-animate` CSS class + * + * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation. + * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations). + * + * Therefore, animations can be applied to an element using this temporary class directly via CSS. + * + * ```css + * .zipper.ng-animate { + * transition:0.5s linear all; + * } + * .zipper.ng-enter { + * opacity:0; + * } + * .zipper.ng-enter.ng-enter-active { + * opacity:1; + * } + * .zipper.ng-leave { + * opacity:1; + * } + * .zipper.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove + * the CSS class once an animation has completed.) + * + * + * ### The `ng-[event]-prepare` class + * + * This is a special class that can be used to prevent unwanted flickering / flash of content before + * the actual animation starts. The class is added as soon as an animation is initialized, but removed + * before the actual animation starts (after waiting for a $digest). + * It is also only added for *structural* animations (`enter`, `move`, and `leave`). + * + * In practice, flickering can appear when nesting elements with structural animations such as `ngIf` + * into elements that have class-based animations such as `ngClass`. + * + * ```html + *
    + *
    + *
    + *
    + *
    + * ``` + * + * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating. + * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts: + * + * ```css + * .message.ng-enter-prepare { + * opacity: 0; + * } + * ``` + * + * ### Animating between value changes + * + * Sometimes you need to animate between different expression states, whose values + * don't necessary need to be known or referenced in CSS styles. + * Unless possible with another {@link ngAnimate#directive-support "animation aware" directive}, + * that specific use case can always be covered with {@link ngAnimate.directive:ngAnimateSwap} as + * can be seen in {@link ngAnimate.directive:ngAnimateSwap#examples this example}. + * + * Note that {@link ngAnimate.directive:ngAnimateSwap} is a *structural directive*, which means it + * creates a new instance of the element (including any other/child directives it may have) and + * links it to a new scope every time *swap* happens. In some cases this might not be desirable + * (e.g. for performance reasons, or when you wish to retain internal state on the original + * element instance). + * + * ## JavaScript-based Animations + * + * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared + * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the + * `module.animation()` module function we can register the animation. + * + * Let's see an example of a enter/leave animation using `ngRepeat`: + * + * ```html + *
    + * {{ item }} + *
    + * ``` + * + * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`: + * + * ```js + * myModule.animation('.slide', [function() { + * return { + * // make note that other events (like addClass/removeClass) + * // have different function input parameters + * enter: function(element, doneFn) { + * jQuery(element).fadeIn(1000, doneFn); + * + * // remember to call doneFn so that AngularJS + * // knows that the animation has concluded + * }, + * + * move: function(element, doneFn) { + * jQuery(element).fadeIn(1000, doneFn); + * }, + * + * leave: function(element, doneFn) { + * jQuery(element).fadeOut(1000, doneFn); + * } + * } + * }]); + * ``` + * + * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as + * greensock.js and velocity.js. + * + * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define + * our animations inside of the same registered animation, however, the function input arguments are a bit different: + * + * ```html + *
    + * this box is moody + *
    + * + * + * + * ``` + * + * ```js + * myModule.animation('.colorful', [function() { + * return { + * addClass: function(element, className, doneFn) { + * // do some cool animation and call the doneFn + * }, + * removeClass: function(element, className, doneFn) { + * // do some cool animation and call the doneFn + * }, + * setClass: function(element, addedClass, removedClass, doneFn) { + * // do some cool animation and call the doneFn + * } + * } + * }]); + * ``` + * + * ## CSS + JS Animations Together + * + * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of AngularJS, + * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking + * charge of the animation**: + * + * ```html + *
    + * Slide in and out + *
    + * ``` + * + * ```js + * myModule.animation('.slide', [function() { + * return { + * enter: function(element, doneFn) { + * jQuery(element).slideIn(1000, doneFn); + * } + * } + * }]); + * ``` + * + * ```css + * .slide.ng-enter { + * transition:0.5s linear all; + * transform:translateY(-100px); + * } + * .slide.ng-enter.ng-enter-active { + * transform:translateY(0); + * } + * ``` + * + * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the + * lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from + * our own JS-based animation code: + * + * ```js + * myModule.animation('.slide', ['$animateCss', function($animateCss) { + * return { + * enter: function(element) { +* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`. + * return $animateCss(element, { + * event: 'enter', + * structural: true + * }); + * } + * } + * }]); + * ``` + * + * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework. + * + * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or + * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that + * data into `$animateCss` directly: + * + * ```js + * myModule.animation('.slide', ['$animateCss', function($animateCss) { + * return { + * enter: function(element) { + * return $animateCss(element, { + * event: 'enter', + * structural: true, + * addClass: 'maroon-setting', + * from: { height:0 }, + * to: { height: 200 } + * }); + * } + * } + * }]); + * ``` + * + * Now we can fill in the rest via our transition CSS code: + * + * ```css + * /* the transition tells ngAnimate to make the animation happen */ + * .slide.ng-enter { transition:0.5s linear all; } + * + * /* this extra CSS class will be absorbed into the transition + * since the $animateCss code is adding the class */ + * .maroon-setting { background:red; } + * ``` + * + * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over. + * + * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}. + * + * ## Animation Anchoring (via `ng-animate-ref`) + * + * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between + * structural areas of an application (like views) by pairing up elements using an attribute + * called `ng-animate-ref`. + * + * Let's say for example we have two views that are managed by `ng-view` and we want to show + * that there is a relationship between two components situated in within these views. By using the + * `ng-animate-ref` attribute we can identify that the two components are paired together and we + * can then attach an animation, which is triggered when the view changes. + * + * Say for example we have the following template code: + * + * ```html + * + *
    + *
    + * + * + *
    + * + * + * + * + * + * ``` + * + * Now, when the view changes (once the link is clicked), ngAnimate will examine the + * HTML contents to see if there is a match reference between any components in the view + * that is leaving and the view that is entering. It will scan both the view which is being + * removed (leave) and inserted (enter) to see if there are any paired DOM elements that + * contain a matching ref value. + * + * The two images match since they share the same ref value. ngAnimate will now create a + * transport element (which is a clone of the first image element) and it will then attempt + * to animate to the position of the second image element in the next view. For the animation to + * work a special CSS class called `ng-anchor` will be added to the transported element. + * + * We can now attach a transition onto the `.banner.ng-anchor` CSS class and then + * ngAnimate will handle the entire transition for us as well as the addition and removal of + * any changes of CSS classes between the elements: + * + * ```css + * .banner.ng-anchor { + * /* this animation will last for 1 second since there are + * two phases to the animation (an `in` and an `out` phase) */ + * transition:0.5s linear all; + * } + * ``` + * + * We also **must** include animations for the views that are being entered and removed + * (otherwise anchoring wouldn't be possible since the new view would be inserted right away). + * + * ```css + * .view-animation.ng-enter, .view-animation.ng-leave { + * transition:0.5s linear all; + * position:fixed; + * left:0; + * top:0; + * width:100%; + * } + * .view-animation.ng-enter { + * transform:translateX(100%); + * } + * .view-animation.ng-leave, + * .view-animation.ng-enter.ng-enter-active { + * transform:translateX(0%); + * } + * .view-animation.ng-leave.ng-leave-active { + * transform:translateX(-100%); + * } + * ``` + * + * Now we can jump back to the anchor animation. When the animation happens, there are two stages that occur: + * an `out` and an `in` stage. The `out` stage happens first and that is when the element is animated away + * from its origin. Once that animation is over then the `in` stage occurs which animates the + * element to its destination. The reason why there are two animations is to give enough time + * for the enter animation on the new element to be ready. + * + * The example above sets up a transition for both the in and out phases, but we can also target the out or + * in phases directly via `ng-anchor-out` and `ng-anchor-in`. + * + * ```css + * .banner.ng-anchor-out { + * transition: 0.5s linear all; + * + * /* the scale will be applied during the out animation, + * but will be animated away when the in animation runs */ + * transform: scale(1.2); + * } + * + * .banner.ng-anchor-in { + * transition: 1s linear all; + * } + * ``` + * + * + * + * + * ### Anchoring Demo + * + + + Home +
    +
    +
    +
    +
    + + angular.module('anchoringExample', ['ngAnimate', 'ngRoute']) + .config(['$routeProvider', function($routeProvider) { + $routeProvider.when('/', { + templateUrl: 'home.html', + controller: 'HomeController as home' + }); + $routeProvider.when('/profile/:id', { + templateUrl: 'profile.html', + controller: 'ProfileController as profile' + }); + }]) + .run(['$rootScope', function($rootScope) { + $rootScope.records = [ + { id: 1, title: 'Miss Beulah Roob' }, + { id: 2, title: 'Trent Morissette' }, + { id: 3, title: 'Miss Ava Pouros' }, + { id: 4, title: 'Rod Pouros' }, + { id: 5, title: 'Abdul Rice' }, + { id: 6, title: 'Laurie Rutherford Sr.' }, + { id: 7, title: 'Nakia McLaughlin' }, + { id: 8, title: 'Jordon Blanda DVM' }, + { id: 9, title: 'Rhoda Hand' }, + { id: 10, title: 'Alexandrea Sauer' } + ]; + }]) + .controller('HomeController', [function() { + //empty + }]) + .controller('ProfileController', ['$rootScope', '$routeParams', + function ProfileController($rootScope, $routeParams) { + var index = parseInt($routeParams.id, 10); + var record = $rootScope.records[index - 1]; + + this.title = record.title; + this.id = record.id; + }]); + + +

    Welcome to the home page

    +

    Please click on an element

    + + {{ record.title }} + +
    + +
    + {{ profile.title }} +
    +
    + + .record { + display:block; + font-size:20px; + } + .profile { + background:black; + color:white; + font-size:100px; + } + .view-container { + position:relative; + } + .view-container > .view.ng-animate { + position:absolute; + top:0; + left:0; + width:100%; + min-height:500px; + } + .view.ng-enter, .view.ng-leave, + .record.ng-anchor { + transition:0.5s linear all; + } + .view.ng-enter { + transform:translateX(100%); + } + .view.ng-enter.ng-enter-active, .view.ng-leave { + transform:translateX(0%); + } + .view.ng-leave.ng-leave-active { + transform:translateX(-100%); + } + .record.ng-anchor-out { + background:red; + } + +
    + * + * ### How is the element transported? + * + * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting + * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element + * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The + * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match + * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied + * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class + * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element + * will become visible since the shim class will be removed. + * + * ### How is the morphing handled? + * + * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out + * what CSS classes differ between the starting element and the destination element. These different CSS classes + * will be added/removed on the anchor element and a transition will be applied (the transition that is provided + * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will + * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that + * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since + * the cloned element is placed inside of root element which is likely close to the body element). + * + * Note that if the root element is on the `` element then the cloned node will be placed inside of body. + * + * + * ## Using $animate in your directive code + * + * So far we've explored how to feed in animations into an AngularJS application, but how do we trigger animations within our own directives in our application? + * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be consumed by animations. Let's + * imagine we have a greeting box that shows and hides itself when the data changes + * + * ```html + * Hi there + * ``` + * + * ```js + * ngModule.directive('greetingBox', ['$animate', function($animate) { + * return function(scope, element, attrs) { + * attrs.$observe('active', function(value) { + * value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on'); + * }); + * }); + * }]); + * ``` + * + * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element + * in our HTML code then we can trigger a CSS or JS animation to happen. + * + * ```css + * /* normally we would create a CSS class to reference on the element */ + * greeting-box.on { transition:0.5s linear all; background:green; color:white; } + * ``` + * + * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's + * possible be sure to visit the {@link ng.$animate $animate service API page}. + * + * + * ## Callbacks and Promises + * + * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger + * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has + * ended by chaining onto the returned promise that animation method returns. + * + * ```js + * // somewhere within the depths of the directive + * $animate.enter(element, parent).then(function() { + * //the animation has completed + * }); + * ``` + * + * (Note that earlier versions of AngularJS prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case + * anymore.) + * + * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering + * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our view + * routing controller to hook into that: + * + * ```js + * ngModule.controller('HomePageController', ['$animate', function($animate) { + * $animate.on('enter', ngViewElement, function(element) { + * // the animation for this route has completed + * }]); + * }]) + * ``` + * + * (Note that you will need to trigger a digest within the callback to get AngularJS to notice any scope-related changes.) + */ + +var copy; +var extend; +var forEach; +var isArray; +var isDefined; +var isElement; +var isFunction; +var isObject; +var isString; +var isUndefined; +var jqLite; +var noop; + +/** + * @ngdoc service + * @name $animate + * @kind object + * + * @description + * The ngAnimate `$animate` service documentation is the same for the core `$animate` service. + * + * Click here {@link ng.$animate to learn more about animations with `$animate`}. + */ +angular.module('ngAnimate', [], function initAngularHelpers() { + // Access helpers from AngularJS core. + // Do it inside a `config` block to ensure `window.angular` is available. + noop = angular.noop; + copy = angular.copy; + extend = angular.extend; + jqLite = angular.element; + forEach = angular.forEach; + isArray = angular.isArray; + isString = angular.isString; + isObject = angular.isObject; + isUndefined = angular.isUndefined; + isDefined = angular.isDefined; + isFunction = angular.isFunction; + isElement = angular.isElement; +}) + .info({ angularVersion: '1.8.2' }) + .directive('ngAnimateSwap', ngAnimateSwapDirective) + + .directive('ngAnimateChildren', $$AnimateChildrenDirective) + .factory('$$rAFScheduler', $$rAFSchedulerFactory) + + .provider('$$animateQueue', $$AnimateQueueProvider) + .provider('$$animateCache', $$AnimateCacheProvider) + .provider('$$animation', $$AnimationProvider) + + .provider('$animateCss', $AnimateCssProvider) + .provider('$$animateCssDriver', $$AnimateCssDriverProvider) + + .provider('$$animateJs', $$AnimateJsProvider) + .provider('$$animateJsDriver', $$AnimateJsDriverProvider); + + +})(window, window.angular); + + +/***/ }), + +/***/ "./node_modules/angular-animate/index.js": +/*!***********************************************!*\ + !*** ./node_modules/angular-animate/index.js ***! + \***********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +__webpack_require__(/*! ./angular-animate */ "./node_modules/angular-animate/angular-animate.js"); +module.exports = 'ngAnimate'; + + +/***/ }), + +/***/ "./node_modules/angular-aria/angular-aria.js": +/*!***************************************************!*\ + !*** ./node_modules/angular-aria/angular-aria.js ***! + \***************************************************/ +/***/ (() => { + +/** + * @license AngularJS v1.8.2 + * (c) 2010-2020 Google LLC. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +/** + * @ngdoc module + * @name ngAria + * @description + * + * The `ngAria` module provides support for common + * [ARIA](http://www.w3.org/TR/wai-aria/) + * attributes that convey state or semantic information about the application for users + * of assistive technologies, such as screen readers. + * + * ## Usage + * + * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following + * directives are supported: + * `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, + * `ngClick`, `ngDblClick`, and `ngMessages`. + * + * Below is a more detailed breakdown of the attributes handled by ngAria: + * + * | Directive | Supported Attributes | + * |---------------------------------------------|-----------------------------------------------------------------------------------------------------| + * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles | + * | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled | + * | {@link ng.directive:ngRequired ngRequired} | aria-required | + * | {@link ng.directive:ngChecked ngChecked} | aria-checked | + * | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly | + * | {@link ng.directive:ngValue ngValue} | aria-checked | + * | {@link ng.directive:ngShow ngShow} | aria-hidden | + * | {@link ng.directive:ngHide ngHide} | aria-hidden | + * | {@link ng.directive:ngDblclick ngDblclick} | tabindex | + * | {@link module:ngMessages ngMessages} | aria-live | + * | {@link ng.directive:ngClick ngClick} | tabindex, keydown event, button role | + * + * Find out more information about each directive by reading the + * {@link guide/accessibility ngAria Developer Guide}. + * + * ## Example + * Using ngDisabled with ngAria: + * ```html + * + * ``` + * Becomes: + * ```html + * + * ``` + * + * ## Disabling Specific Attributes + * It is possible to disable individual attributes added by ngAria with the + * {@link ngAria.$ariaProvider#config config} method. For more details, see the + * {@link guide/accessibility Developer Guide}. + * + * ## Disabling `ngAria` on Specific Elements + * It is possible to make `ngAria` ignore a specific element, by adding the `ng-aria-disable` + * attribute on it. Note that only the element itself (and not its child elements) will be ignored. + */ +var ARIA_DISABLE_ATTR = 'ngAriaDisable'; + +var ngAriaModule = angular.module('ngAria', ['ng']). + info({ angularVersion: '1.8.2' }). + provider('$aria', $AriaProvider); + +/** +* Internal Utilities +*/ +var nativeAriaNodeNames = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY']; + +var isNodeOneOf = function(elem, nodeTypeArray) { + if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) { + return true; + } +}; +/** + * @ngdoc provider + * @name $ariaProvider + * @this + * + * @description + * + * Used for configuring the ARIA attributes injected and managed by ngAria. + * + * ```js + * angular.module('myApp', ['ngAria'], function config($ariaProvider) { + * $ariaProvider.config({ + * ariaValue: true, + * tabindex: false + * }); + * }); + *``` + * + * ## Dependencies + * Requires the {@link ngAria} module to be installed. + * + */ +function $AriaProvider() { + var config = { + ariaHidden: true, + ariaChecked: true, + ariaReadonly: true, + ariaDisabled: true, + ariaRequired: true, + ariaInvalid: true, + ariaValue: true, + tabindex: true, + bindKeydown: true, + bindRoleForClick: true + }; + + /** + * @ngdoc method + * @name $ariaProvider#config + * + * @param {object} config object to enable/disable specific ARIA attributes + * + * - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags + * - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags + * - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags + * - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags + * - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags + * - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags + * - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and + * aria-valuenow tags + * - **tabindex** – `{boolean}` – Enables/disables tabindex tags + * - **bindKeydown** – `{boolean}` – Enables/disables keyboard event binding on non-interactive + * elements (such as `div` or `li`) using ng-click, making them more accessible to users of + * assistive technologies + * - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements (such as + * `div` or `li`) using ng-click, making them more accessible to users of assistive + * technologies + * + * @description + * Enables/disables various ARIA attributes + */ + this.config = function(newConfig) { + config = angular.extend(config, newConfig); + }; + + function watchExpr(attrName, ariaAttr, nativeAriaNodeNames, negate) { + return function(scope, elem, attr) { + if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; + + var ariaCamelName = attr.$normalize(ariaAttr); + if (config[ariaCamelName] && !isNodeOneOf(elem, nativeAriaNodeNames) && !attr[ariaCamelName]) { + scope.$watch(attr[attrName], function(boolVal) { + // ensure boolean value + boolVal = negate ? !boolVal : !!boolVal; + elem.attr(ariaAttr, boolVal); + }); + } + }; + } + /** + * @ngdoc service + * @name $aria + * + * @description + * + * The $aria service contains helper methods for applying common + * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives. + * + * ngAria injects common accessibility attributes that tell assistive technologies when HTML + * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria, + * let's review a code snippet from ngAria itself: + * + *```js + * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) { + * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nativeAriaNodeNames, false); + * }]) + *``` + * Shown above, the ngAria module creates a directive with the same signature as the + * traditional `ng-disabled` directive. But this ngAria version is dedicated to + * solely managing accessibility attributes on custom elements. The internal `$aria` service is + * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the + * developer, `aria-disabled` is injected as an attribute with its value synchronized to the + * value in `ngDisabled`. + * + * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do + * anything to enable this feature. The `aria-disabled` attribute is automatically managed + * simply as a silent side-effect of using `ng-disabled` with the ngAria module. + * + * The full list of directives that interface with ngAria: + * * **ngModel** + * * **ngChecked** + * * **ngReadonly** + * * **ngRequired** + * * **ngDisabled** + * * **ngValue** + * * **ngShow** + * * **ngHide** + * * **ngClick** + * * **ngDblclick** + * * **ngMessages** + * + * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each + * directive. + * + * + * ## Dependencies + * Requires the {@link ngAria} module to be installed. + */ + this.$get = function() { + return { + config: function(key) { + return config[key]; + }, + $$watchExpr: watchExpr + }; + }; +} + + +ngAriaModule.directive('ngShow', ['$aria', function($aria) { + return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true); +}]) +.directive('ngHide', ['$aria', function($aria) { + return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false); +}]) +.directive('ngValue', ['$aria', function($aria) { + return $aria.$$watchExpr('ngValue', 'aria-checked', nativeAriaNodeNames, false); +}]) +.directive('ngChecked', ['$aria', function($aria) { + return $aria.$$watchExpr('ngChecked', 'aria-checked', nativeAriaNodeNames, false); +}]) +.directive('ngReadonly', ['$aria', function($aria) { + return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nativeAriaNodeNames, false); +}]) +.directive('ngRequired', ['$aria', function($aria) { + return $aria.$$watchExpr('ngRequired', 'aria-required', nativeAriaNodeNames, false); +}]) +.directive('ngModel', ['$aria', function($aria) { + + function shouldAttachAttr(attr, normalizedAttr, elem, allowNonAriaNodes) { + return $aria.config(normalizedAttr) && + !elem.attr(attr) && + (allowNonAriaNodes || !isNodeOneOf(elem, nativeAriaNodeNames)) && + (elem.attr('type') !== 'hidden' || elem[0].nodeName !== 'INPUT'); + } + + function shouldAttachRole(role, elem) { + // if element does not have role attribute + // AND element type is equal to role (if custom element has a type equaling shape) <-- remove? + // AND element is not in nativeAriaNodeNames + return !elem.attr('role') && (elem.attr('type') === role) && !isNodeOneOf(elem, nativeAriaNodeNames); + } + + function getShape(attr, elem) { + var type = attr.type, + role = attr.role; + + return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' : + ((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' : + (type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : ''; + } + + return { + restrict: 'A', + require: 'ngModel', + priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value + compile: function(elem, attr) { + if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; + + var shape = getShape(attr, elem); + + return { + post: function(scope, elem, attr, ngModel) { + var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false); + + function ngAriaWatchModelValue() { + return ngModel.$modelValue; + } + + function getRadioReaction(newVal) { + // Strict comparison would cause a BC + // eslint-disable-next-line eqeqeq + var boolVal = (attr.value == ngModel.$viewValue); + elem.attr('aria-checked', boolVal); + } + + function getCheckboxReaction() { + elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue)); + } + + switch (shape) { + case 'radio': + case 'checkbox': + if (shouldAttachRole(shape, elem)) { + elem.attr('role', shape); + } + if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) { + scope.$watch(ngAriaWatchModelValue, shape === 'radio' ? + getRadioReaction : getCheckboxReaction); + } + if (needsTabIndex) { + elem.attr('tabindex', 0); + } + break; + case 'range': + if (shouldAttachRole(shape, elem)) { + elem.attr('role', 'slider'); + } + if ($aria.config('ariaValue')) { + var needsAriaValuemin = !elem.attr('aria-valuemin') && + (attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin')); + var needsAriaValuemax = !elem.attr('aria-valuemax') && + (attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax')); + var needsAriaValuenow = !elem.attr('aria-valuenow'); + + if (needsAriaValuemin) { + attr.$observe('min', function ngAriaValueMinReaction(newVal) { + elem.attr('aria-valuemin', newVal); + }); + } + if (needsAriaValuemax) { + attr.$observe('max', function ngAriaValueMinReaction(newVal) { + elem.attr('aria-valuemax', newVal); + }); + } + if (needsAriaValuenow) { + scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) { + elem.attr('aria-valuenow', newVal); + }); + } + } + if (needsTabIndex) { + elem.attr('tabindex', 0); + } + break; + } + + if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required + && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) { + // ngModel.$error.required is undefined on custom controls + attr.$observe('required', function() { + elem.attr('aria-required', !!attr['required']); + }); + } + + if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) { + scope.$watch(function ngAriaInvalidWatch() { + return ngModel.$invalid; + }, function ngAriaInvalidReaction(newVal) { + elem.attr('aria-invalid', !!newVal); + }); + } + } + }; + } + }; +}]) +.directive('ngDisabled', ['$aria', function($aria) { + return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nativeAriaNodeNames, false); +}]) +.directive('ngMessages', function() { + return { + restrict: 'A', + require: '?ngMessages', + link: function(scope, elem, attr, ngMessages) { + if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; + + if (!elem.attr('aria-live')) { + elem.attr('aria-live', 'assertive'); + } + } + }; +}) +.directive('ngClick',['$aria', '$parse', function($aria, $parse) { + return { + restrict: 'A', + compile: function(elem, attr) { + if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; + + var fn = $parse(attr.ngClick); + return function(scope, elem, attr) { + + if (!isNodeOneOf(elem, nativeAriaNodeNames)) { + + if ($aria.config('bindRoleForClick') && !elem.attr('role')) { + elem.attr('role', 'button'); + } + + if ($aria.config('tabindex') && !elem.attr('tabindex')) { + elem.attr('tabindex', 0); + } + + if ($aria.config('bindKeydown') && !attr.ngKeydown && !attr.ngKeypress && !attr.ngKeyup) { + elem.on('keydown', function(event) { + var keyCode = event.which || event.keyCode; + + if (keyCode === 13 || keyCode === 32) { + // If the event is triggered on a non-interactive element ... + if (nativeAriaNodeNames.indexOf(event.target.nodeName) === -1 && !event.target.isContentEditable) { + // ... prevent the default browser behavior (e.g. scrolling when pressing spacebar) + // See https://github.com/angular/angular.js/issues/16664 + event.preventDefault(); + } + scope.$apply(callback); + } + + function callback() { + fn(scope, { $event: event }); + } + }); + } + } + }; + } + }; +}]) +.directive('ngDblclick', ['$aria', function($aria) { + return function(scope, elem, attr) { + if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; + + if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nativeAriaNodeNames)) { + elem.attr('tabindex', 0); + } + }; +}]); + + +})(window, window.angular); + + +/***/ }), + +/***/ "./node_modules/angular-aria/index.js": +/*!********************************************!*\ + !*** ./node_modules/angular-aria/index.js ***! + \********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +__webpack_require__(/*! ./angular-aria */ "./node_modules/angular-aria/angular-aria.js"); +module.exports = 'ngAria'; + + +/***/ }), + +/***/ "./node_modules/angular-local-storage/dist/angular-local-storage.js": +/*!**************************************************************************!*\ + !*** ./node_modules/angular-local-storage/dist/angular-local-storage.js ***! + \**************************************************************************/ +/***/ (() => { + +/** + * An Angular module that gives you access to the browsers local storage + * @version v0.7.1 - 2017-06-21 + * @link https://github.com/grevory/angular-local-storage + * @author grevory + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +(function (window, angular) { +var isDefined = angular.isDefined, + isUndefined = angular.isUndefined, + isNumber = angular.isNumber, + isObject = angular.isObject, + isArray = angular.isArray, + isString = angular.isString, + extend = angular.extend, + toJson = angular.toJson; + +angular + .module('LocalStorageModule', []) + .provider('localStorageService', function() { + // You should set a prefix to avoid overwriting any local storage variables from the rest of your app + // e.g. localStorageServiceProvider.setPrefix('yourAppName'); + // With provider you can use config as this: + // myApp.config(function (localStorageServiceProvider) { + // localStorageServiceProvider.prefix = 'yourAppName'; + // }); + this.prefix = 'ls'; + + // You could change web storage type localstorage or sessionStorage + this.storageType = 'localStorage'; + + // Cookie options (usually in case of fallback) + // expiry = Number of days before cookies expire // 0 = Does not expire + // path = The web path the cookie represents + // secure = Wether the cookies should be secure (i.e only sent on HTTPS requests) + this.cookie = { + expiry: 30, + path: '/', + secure: false + }; + + // Decides wether we should default to cookies if localstorage is not supported. + this.defaultToCookie = true; + + // Send signals for each of the following actions? + this.notify = { + setItem: true, + removeItem: false + }; + + // Setter for the prefix + this.setPrefix = function(prefix) { + this.prefix = prefix; + return this; + }; + + // Setter for the storageType + this.setStorageType = function(storageType) { + this.storageType = storageType; + return this; + }; + // Setter for defaultToCookie value, default is true. + this.setDefaultToCookie = function (shouldDefault) { + this.defaultToCookie = !!shouldDefault; // Double-not to make sure it's a bool value. + return this; + }; + // Setter for cookie config + this.setStorageCookie = function(exp, path, secure) { + this.cookie.expiry = exp; + this.cookie.path = path; + this.cookie.secure = secure; + return this; + }; + + // Setter for cookie domain + this.setStorageCookieDomain = function(domain) { + this.cookie.domain = domain; + return this; + }; + + // Setter for notification config + // itemSet & itemRemove should be booleans + this.setNotify = function(itemSet, itemRemove) { + this.notify = { + setItem: itemSet, + removeItem: itemRemove + }; + return this; + }; + + this.$get = ['$rootScope', '$window', '$document', '$parse','$timeout', function($rootScope, $window, $document, $parse, $timeout) { + var self = this; + var prefix = self.prefix; + var cookie = self.cookie; + var notify = self.notify; + var storageType = self.storageType; + var webStorage; + + // When Angular's $document is not available + if (!$document) { + $document = document; + } else if ($document[0]) { + $document = $document[0]; + } + + // If there is a prefix set in the config lets use that with an appended period for readability + if (prefix.substr(-1) !== '.') { + prefix = !!prefix ? prefix + '.' : ''; + } + var deriveQualifiedKey = function(key) { + return prefix + key; + }; + + // Removes prefix from the key. + var underiveQualifiedKey = function (key) { + return key.replace(new RegExp('^' + prefix, 'g'), ''); + }; + + // Check if the key is within our prefix namespace. + var isKeyPrefixOurs = function (key) { + return key.indexOf(prefix) === 0; + }; + + // Checks the browser to see if local storage is supported + var checkSupport = function () { + try { + var supported = (storageType in $window && $window[storageType] !== null); + + // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage + // is available, but trying to call .setItem throws an exception. + // + // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage + // that exceeded the quota." + var key = deriveQualifiedKey('__' + Math.round(Math.random() * 1e7)); + if (supported) { + webStorage = $window[storageType]; + webStorage.setItem(key, ''); + webStorage.removeItem(key); + } + + return supported; + } catch (e) { + // Only change storageType to cookies if defaulting is enabled. + if (self.defaultToCookie) + storageType = 'cookie'; + $rootScope.$broadcast('LocalStorageModule.notification.error', e.message); + return false; + } + }; + var browserSupportsLocalStorage = checkSupport(); + + // Directly adds a value to local storage + // If local storage is not available in the browser use cookies + // Example use: localStorageService.add('library','angular'); + var addToLocalStorage = function (key, value, type) { + var previousType = getStorageType(); + + try { + setStorageType(type); + + // Let's convert undefined values to null to get the value consistent + if (isUndefined(value)) { + value = null; + } else { + value = toJson(value); + } + + // If this browser does not support local storage use cookies + if (!browserSupportsLocalStorage && self.defaultToCookie || self.storageType === 'cookie') { + if (!browserSupportsLocalStorage) { + $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED'); + } + + if (notify.setItem) { + $rootScope.$broadcast('LocalStorageModule.notification.setitem', {key: key, newvalue: value, storageType: 'cookie'}); + } + return addToCookies(key, value); + } + + try { + if (webStorage) { + webStorage.setItem(deriveQualifiedKey(key), value); + } + if (notify.setItem) { + $rootScope.$broadcast('LocalStorageModule.notification.setitem', {key: key, newvalue: value, storageType: self.storageType}); + } + } catch (e) { + $rootScope.$broadcast('LocalStorageModule.notification.error', e.message); + return addToCookies(key, value); + } + return true; + } finally { + setStorageType(previousType); + } + }; + + // Directly get a value from local storage + // Example use: localStorageService.get('library'); // returns 'angular' + var getFromLocalStorage = function (key, type) { + var previousType = getStorageType(); + + try { + setStorageType(type); + + if (!browserSupportsLocalStorage && self.defaultToCookie || self.storageType === 'cookie') { + if (!browserSupportsLocalStorage) { + $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED'); + } + + return getFromCookies(key); + } + + var item = webStorage ? webStorage.getItem(deriveQualifiedKey(key)) : null; + // angular.toJson will convert null to 'null', so a proper conversion is needed + // FIXME not a perfect solution, since a valid 'null' string can't be stored + if (!item || item === 'null') { + return null; + } + + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } finally { + setStorageType(previousType); + } + }; + + // Remove an item from local storage + // Example use: localStorageService.remove('library'); // removes the key/value pair of library='angular' + // + // This is var-arg removal, check the last argument to see if it is a storageType + // and set type accordingly before removing. + // + var removeFromLocalStorage = function () { + var previousType = getStorageType(); + + try { + // can't pop on arguments, so we do this + var consumed = 0; + if (arguments.length >= 1 && + (arguments[arguments.length - 1] === 'localStorage' || + arguments[arguments.length - 1] === 'sessionStorage')) { + consumed = 1; + setStorageType(arguments[arguments.length - 1]); + } + + var i, key; + for (i = 0; i < arguments.length - consumed; i++) { + key = arguments[i]; + if (!browserSupportsLocalStorage && self.defaultToCookie || self.storageType === 'cookie') { + if (!browserSupportsLocalStorage) { + $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED'); + } + + if (notify.removeItem) { + $rootScope.$broadcast('LocalStorageModule.notification.removeitem', {key: key, storageType: 'cookie'}); + } + removeFromCookies(key); + } + else { + try { + webStorage.removeItem(deriveQualifiedKey(key)); + if (notify.removeItem) { + $rootScope.$broadcast('LocalStorageModule.notification.removeitem', { + key: key, + storageType: self.storageType + }); + } + } catch (e) { + $rootScope.$broadcast('LocalStorageModule.notification.error', e.message); + removeFromCookies(key); + } + } + } + } finally { + setStorageType(previousType); + } + }; + + // Return array of keys for local storage + // Example use: var keys = localStorageService.keys() + var getKeysForLocalStorage = function (type) { + var previousType = getStorageType(); + + try { + setStorageType(type); + + if (!browserSupportsLocalStorage) { + $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED'); + return []; + } + + var prefixLength = prefix.length; + var keys = []; + for (var key in webStorage) { + // Only return keys that are for this app + if (key.substr(0, prefixLength) === prefix) { + try { + keys.push(key.substr(prefixLength)); + } catch (e) { + $rootScope.$broadcast('LocalStorageModule.notification.error', e.Description); + return []; + } + } + } + + return keys; + } finally { + setStorageType(previousType); + } + }; + + // Remove all data for this app from local storage + // Also optionally takes a regular expression string and removes the matching key-value pairs + // Example use: localStorageService.clearAll(); + // Should be used mostly for development purposes + var clearAllFromLocalStorage = function (regularExpression, type) { + var previousType = getStorageType(); + + try { + setStorageType(type); + + // Setting both regular expressions independently + // Empty strings result in catchall RegExp + var prefixRegex = !!prefix ? new RegExp('^' + prefix) : new RegExp(); + var testRegex = !!regularExpression ? new RegExp(regularExpression) : new RegExp(); + + if (!browserSupportsLocalStorage && self.defaultToCookie || self.storageType === 'cookie') { + if (!browserSupportsLocalStorage) { + $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED'); + } + return clearAllFromCookies(); + } + if (!browserSupportsLocalStorage && !self.defaultToCookie) + return false; + var prefixLength = prefix.length; + + for (var key in webStorage) { + // Only remove items that are for this app and match the regular expression + if (prefixRegex.test(key) && testRegex.test(key.substr(prefixLength))) { + try { + removeFromLocalStorage(key.substr(prefixLength)); + } catch (e) { + $rootScope.$broadcast('LocalStorageModule.notification.error', e.message); + return clearAllFromCookies(); + } + } + } + + return true; + } finally { + setStorageType(previousType); + } + }; + + // Checks the browser to see if cookies are supported + var browserSupportsCookies = (function() { + try { + return $window.navigator.cookieEnabled || + ("cookie" in $document && ($document.cookie.length > 0 || + ($document.cookie = "test").indexOf.call($document.cookie, "test") > -1)); + } catch (e) { + $rootScope.$broadcast('LocalStorageModule.notification.error', e.message); + return false; + } + }()); + + // Directly adds a value to cookies + // Typically used as a fallback if local storage is not available in the browser + // Example use: localStorageService.cookie.add('library','angular'); + var addToCookies = function (key, value, daysToExpiry, secure) { + + if (isUndefined(value)) { + return false; + } else if(isArray(value) || isObject(value)) { + value = toJson(value); + } + + if (!browserSupportsCookies) { + $rootScope.$broadcast('LocalStorageModule.notification.error', 'COOKIES_NOT_SUPPORTED'); + return false; + } + + try { + var expiry = '', + expiryDate = new Date(), + cookieDomain = ''; + + if (value === null) { + // Mark that the cookie has expired one day ago + expiryDate.setTime(expiryDate.getTime() + (-1 * 24 * 60 * 60 * 1000)); + expiry = "; expires=" + expiryDate.toGMTString(); + value = ''; + } else if (isNumber(daysToExpiry) && daysToExpiry !== 0) { + expiryDate.setTime(expiryDate.getTime() + (daysToExpiry * 24 * 60 * 60 * 1000)); + expiry = "; expires=" + expiryDate.toGMTString(); + } else if (cookie.expiry !== 0) { + expiryDate.setTime(expiryDate.getTime() + (cookie.expiry * 24 * 60 * 60 * 1000)); + expiry = "; expires=" + expiryDate.toGMTString(); + } + if (!!key) { + var cookiePath = "; path=" + cookie.path; + if (cookie.domain) { + cookieDomain = "; domain=" + cookie.domain; + } + /* Providing the secure parameter always takes precedence over config + * (allows developer to mix and match secure + non-secure) */ + if (typeof secure === 'boolean') { + if (secure === true) { + /* We've explicitly specified secure, + * add the secure attribute to the cookie (after domain) */ + cookieDomain += "; secure"; + } + // else - secure has been supplied but isn't true - so don't set secure flag, regardless of what config says + } + else if (cookie.secure === true) { + // secure parameter wasn't specified, get default from config + cookieDomain += "; secure"; + } + $document.cookie = deriveQualifiedKey(key) + "=" + encodeURIComponent(value) + expiry + cookiePath + cookieDomain; + } + } catch (e) { + $rootScope.$broadcast('LocalStorageModule.notification.error', e.message); + return false; + } + return true; + }; + + // Directly get a value from a cookie + // Example use: localStorageService.cookie.get('library'); // returns 'angular' + var getFromCookies = function (key) { + if (!browserSupportsCookies) { + $rootScope.$broadcast('LocalStorageModule.notification.error', 'COOKIES_NOT_SUPPORTED'); + return false; + } + + var cookies = $document.cookie && $document.cookie.split(';') || []; + for(var i=0; i < cookies.length; i++) { + var thisCookie = cookies[i]; + while (thisCookie.charAt(0) === ' ') { + thisCookie = thisCookie.substring(1,thisCookie.length); + } + if (thisCookie.indexOf(deriveQualifiedKey(key) + '=') === 0) { + var storedValues = decodeURIComponent(thisCookie.substring(prefix.length + key.length + 1, thisCookie.length)); + try { + var parsedValue = JSON.parse(storedValues); + return typeof(parsedValue) === 'number' ? storedValues : parsedValue; + } catch(e) { + return storedValues; + } + } + } + return null; + }; + + var removeFromCookies = function (key) { + addToCookies(key,null); + }; + + var clearAllFromCookies = function () { + var thisCookie = null; + var prefixLength = prefix.length; + var cookies = $document.cookie.split(';'); + for(var i = 0; i < cookies.length; i++) { + thisCookie = cookies[i]; + + while (thisCookie.charAt(0) === ' ') { + thisCookie = thisCookie.substring(1, thisCookie.length); + } + + var key = thisCookie.substring(prefixLength, thisCookie.indexOf('=')); + removeFromCookies(key); + } + }; + + var getStorageType = function() { + return storageType; + }; + + var setStorageType = function(type) { + if (type && storageType !== type) { + storageType = type; + browserSupportsLocalStorage = checkSupport(); + } + return browserSupportsLocalStorage; + }; + + // Add a listener on scope variable to save its changes to local storage + // Return a function which when called cancels binding + var bindToScope = function(scope, key, def, lsKey, type) { + lsKey = lsKey || key; + var value = getFromLocalStorage(lsKey, type); + + if (value === null && isDefined(def)) { + value = def; + } else if (isObject(value) && isObject(def)) { + value = extend(value, def); + } + + $parse(key).assign(scope, value); + + return scope.$watch(key, function(newVal) { + addToLocalStorage(lsKey, newVal, type); + }, isObject(scope[key])); + }; + + // Add listener to local storage, for update callbacks. + if (browserSupportsLocalStorage) { + if ($window.addEventListener) { + $window.addEventListener("storage", handleStorageChangeCallback, false); + $rootScope.$on('$destroy', function() { + $window.removeEventListener("storage", handleStorageChangeCallback); + }); + } else if($window.attachEvent){ + // attachEvent and detachEvent are proprietary to IE v6-10 + $window.attachEvent("onstorage", handleStorageChangeCallback); + $rootScope.$on('$destroy', function() { + $window.detachEvent("onstorage", handleStorageChangeCallback); + }); + } + } + + // Callback handler for storage changed. + function handleStorageChangeCallback(e) { + if (!e) { e = $window.event; } + if (notify.setItem) { + if (isString(e.key) && isKeyPrefixOurs(e.key)) { + var key = underiveQualifiedKey(e.key); + // Use timeout, to avoid using $rootScope.$apply. + $timeout(function () { + $rootScope.$broadcast('LocalStorageModule.notification.changed', { key: key, newvalue: e.newValue, storageType: self.storageType }); + }); + } + } + } + + // Return localStorageService.length + // ignore keys that not owned + var lengthOfLocalStorage = function(type) { + var previousType = getStorageType(); + + try { + setStorageType(type); + + var count = 0; + var storage = $window[storageType]; + for(var i = 0; i < storage.length; i++) { + if(storage.key(i).indexOf(prefix) === 0 ) { + count++; + } + } + + return count; + } finally { + setStorageType(previousType); + } + }; + + var changePrefix = function(localStoragePrefix) { + prefix = localStoragePrefix; + }; + + return { + isSupported: browserSupportsLocalStorage, + getStorageType: getStorageType, + setStorageType: setStorageType, + setPrefix: changePrefix, + set: addToLocalStorage, + add: addToLocalStorage, //DEPRECATED + get: getFromLocalStorage, + keys: getKeysForLocalStorage, + remove: removeFromLocalStorage, + clearAll: clearAllFromLocalStorage, + bind: bindToScope, + deriveKey: deriveQualifiedKey, + underiveKey: underiveQualifiedKey, + length: lengthOfLocalStorage, + defaultToCookie: this.defaultToCookie, + cookie: { + isSupported: browserSupportsCookies, + set: addToCookies, + add: addToCookies, //DEPRECATED + get: getFromCookies, + remove: removeFromCookies, + clearAll: clearAllFromCookies + } + }; + }]; + }); +})(window, window.angular); + +/***/ }), + +/***/ "./node_modules/angular-local-storage/index.js": +/*!*****************************************************!*\ + !*** ./node_modules/angular-local-storage/index.js ***! + \*****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +__webpack_require__(/*! ./dist/angular-local-storage.js */ "./node_modules/angular-local-storage/dist/angular-local-storage.js"); +module.exports = 'LocalStorageModule'; + + +/***/ }), + +/***/ "./node_modules/angular-material/angular-material.js": +/*!***********************************************************!*\ + !*** ./node_modules/angular-material/angular-material.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +/* provided dependency */ var __webpack_provided_window_dot_jQuery = __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js"); +/*! + * AngularJS Material Design + * https://github.com/angular/material + * @license MIT + * v1.2.3 + */ +(function( window, angular, undefined ){ +"use strict"; + +(function(){ +"use strict"; + +angular.module('ngMaterial', ["ng","ngAnimate","ngAria","material.core","material.core.animate","material.core.gestures","material.core.interaction","material.core.layout","material.core.meta","material.core.theming.palette","material.core.theming","material.components.autocomplete","material.components.backdrop","material.components.bottomSheet","material.components.button","material.components.card","material.components.checkbox","material.components.chips","material.components.colors","material.components.content","material.components.datepicker","material.components.dialog","material.components.divider","material.components.fabActions","material.components.fabShared","material.components.fabSpeedDial","material.components.fabToolbar","material.components.gridList","material.components.icon","material.components.input","material.components.list","material.components.menu","material.components.menuBar","material.components.navBar","material.components.panel","material.components.progressCircular","material.components.progressLinear","material.components.radioButton","material.components.select","material.components.showHide","material.components.sidenav","material.components.slider","material.components.sticky","material.components.subheader","material.components.swipe","material.components.switch","material.components.tabs","material.components.toast","material.components.toolbar","material.components.tooltip","material.components.truncate","material.components.virtualRepeat","material.components.whiteframe"]); +})(); +(function(){ +"use strict"; + +/** + * Initialization function that validates environment + * requirements. + */ +DetectNgTouch.$inject = ["$log", "$injector"]; +MdCoreConfigure.$inject = ["$provide", "$mdThemingProvider"]; +rAFDecorator.$inject = ["$delegate"]; +qDecorator.$inject = ["$delegate"]; +angular + .module('material.core', [ + 'ngAnimate', + 'material.core.animate', + 'material.core.layout', + 'material.core.interaction', + 'material.core.gestures', + 'material.core.theming' + ]) + .config(MdCoreConfigure) + .run(DetectNgTouch); + + +/** + * Detect if the ng-Touch module is also being used. + * Warn if detected. + * @ngInject + */ +function DetectNgTouch($log, $injector) { + if ($injector.has('$swipe')) { + var msg = "" + + "You are using the ngTouch module. \n" + + "AngularJS Material already has mobile click, tap, and swipe support... \n" + + "ngTouch is not supported with AngularJS Material!"; + $log.warn(msg); + } +} + +/** + * @ngInject + */ +function MdCoreConfigure($provide, $mdThemingProvider) { + + $provide.decorator('$$rAF', ['$delegate', rAFDecorator]); + $provide.decorator('$q', ['$delegate', qDecorator]); + + $mdThemingProvider.theme('default') + .primaryPalette('indigo') + .accentPalette('pink') + .warnPalette('deep-orange') + .backgroundPalette('grey'); +} + +/** + * @ngInject + */ +function rAFDecorator($delegate) { + /** + * Use this to throttle events that come in often. + * The throttled function will always use the *last* invocation before the + * coming frame. + * + * For example, window resize events that fire many times a second: + * If we set to use an raf-throttled callback on window resize, then + * our callback will only be fired once per frame, with the last resize + * event that happened before that frame. + * + * @param {function} cb function to debounce + */ + $delegate.throttle = function(cb) { + var queuedArgs, alreadyQueued, queueCb, context; + return function debounced() { + queuedArgs = arguments; + context = this; + queueCb = cb; + if (!alreadyQueued) { + alreadyQueued = true; + $delegate(function() { + queueCb.apply(context, Array.prototype.slice.call(queuedArgs)); + alreadyQueued = false; + }); + } + }; + }; + return $delegate; +} + +/** + * @ngInject + */ +function qDecorator($delegate) { + /** + * Adds a shim for $q.resolve for AngularJS version that don't have it, + * so we don't have to think about it. + * + * via https://github.com/angular/angular.js/pull/11987 + */ + + // TODO(crisbeto): this won't be necessary once we drop AngularJS 1.3 + if (!$delegate.resolve) { + $delegate.resolve = $delegate.when; + } + return $delegate; +} + +})(); +(function(){ +"use strict"; + + +MdAutofocusDirective.$inject = ["$parse"];angular.module('material.core') + .directive('mdAutofocus', MdAutofocusDirective); + +/** + * @ngdoc directive + * @name mdAutofocus + * @module material.core.util + * + * @description + * + * `[md-autofocus]` provides an optional way to identify the focused element when a `$mdDialog`, + * `$mdBottomSheet`, `$mdMenu` or `$mdSidenav` opens or upon page load for input-like elements. + * + * When one of these opens, it will find the first nested element with the `[md-autofocus]` + * attribute directive and optional expression. An expression may be specified as the directive + * value to enable conditional activation of the autofocus. + * + * @usage + * + * ### Dialog + * + * + *
    + * + * + * + * + *
    + *
    + *
    + * + * ### Bottomsheet + * + * + * Comment Actions + * + * + * + * + * + * {{ item.name }} + * + * + * + * + * + * + * + * ### Autocomplete + * + * + * {{item.display}} + * + * + * + * ### Sidenav + * + *
    + * + * Left Nav! + * + * + * + * Center Content + * + * Open Left Menu + * + * + * + * + *
    + * + * + * + * + *
    + *
    + *
    + *
    + **/ +function MdAutofocusDirective($parse) { + return { + restrict: 'A', + link: { + pre: preLink + } + }; + + function preLink(scope, element, attr) { + var attrExp = attr.mdAutoFocus || attr.mdAutofocus || attr.mdSidenavFocus; + + // Initially update the expression by manually parsing the expression as per $watch source. + updateExpression($parse(attrExp)(scope)); + + // Only watch the expression if it is not empty. + if (attrExp) { + scope.$watch(attrExp, updateExpression); + } + + /** + * Updates the autofocus class which is used to determine whether the attribute + * expression evaluates to true or false. + * @param {string|boolean} value Attribute Value + */ + function updateExpression(value) { + + // Rather than passing undefined to the jqLite toggle class function we explicitly set the + // value to true. Otherwise the class will be just toggled instead of being forced. + if (angular.isUndefined(value)) { + value = true; + } + + element.toggleClass('md-autofocus', !!value); + } + } + +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.core.colorUtil + * @description + * Color Util + */ +angular + .module('material.core') + .factory('$mdColorUtil', ColorUtilFactory); + +function ColorUtilFactory() { + /** + * Converts hex value to RGBA string + * @param color {string} + * @returns {string} + */ + function hexToRgba (color) { + var hex = color[ 0 ] === '#' ? color.substr(1) : color, + dig = hex.length / 3, + red = hex.substr(0, dig), + green = hex.substr(dig, dig), + blue = hex.substr(dig * 2); + if (dig === 1) { + red += red; + green += green; + blue += blue; + } + return 'rgba(' + parseInt(red, 16) + ',' + parseInt(green, 16) + ',' + parseInt(blue, 16) + ',0.1)'; + } + + /** + * Converts rgba value to hex string + * @param {string} color + * @returns {string} + */ + function rgbaToHex(color) { + color = color.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + + var hex = (color && color.length === 4) ? "#" + + ("0" + parseInt(color[1],10).toString(16)).slice(-2) + + ("0" + parseInt(color[2],10).toString(16)).slice(-2) + + ("0" + parseInt(color[3],10).toString(16)).slice(-2) : ''; + + return hex.toUpperCase(); + } + + /** + * Converts an RGB color to RGBA + * @param {string} color + * @returns {string} + */ + function rgbToRgba (color) { + return color.replace(')', ', 0.1)').replace('(', 'a('); + } + + /** + * Converts an RGBA color to RGB + * @param {string} color + * @returns {string} + */ + function rgbaToRgb (color) { + return color + ? color.replace('rgba', 'rgb').replace(/,[^),]+\)/, ')') + : 'rgb(0,0,0)'; + } + + return { + rgbaToHex: rgbaToHex, + hexToRgba: hexToRgba, + rgbToRgba: rgbToRgba, + rgbaToRgb: rgbaToRgb + }; +} + +})(); +(function(){ +"use strict"; + +angular.module('material.core') +.factory('$mdConstant', MdConstantFactory); + +/** + * Factory function that creates the grab-bag $mdConstant service. + * @ngInject + */ +function MdConstantFactory() { + + var prefixTestEl = document.createElement('div'); + var vendorPrefix = getVendorPrefix(prefixTestEl); + var isWebkit = /webkit/i.test(vendorPrefix); + var SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g; + + /** + * @param {string} name CSS property name + * @return {string} the property name supported by the browser + */ + function vendorProperty(name) { + // Add a dash between the prefix and name, to be able to transform the string into camelcase. + var prefixedName = vendorPrefix + '-' + name; + var ucPrefix = camelCase(prefixedName); + var lcPrefix = ucPrefix.charAt(0).toLowerCase() + ucPrefix.substring(1); + + return hasStyleProperty(prefixTestEl, name) ? name : // The current browser supports the un-prefixed property + hasStyleProperty(prefixTestEl, ucPrefix) ? ucPrefix : // The current browser only supports the prefixed property. + hasStyleProperty(prefixTestEl, lcPrefix) ? lcPrefix : name; // Some browsers are only supporting the prefix in lowercase. + } + + function hasStyleProperty(testElement, property) { + return angular.isDefined(testElement.style[property]); + } + + /** + * @param {!string} input value to convert to camelCase + * @return {string} camelCased version of the input string + */ + function camelCase(input) { + return input.replace(SPECIAL_CHARS_REGEXP, function(matches, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); + } + + function getVendorPrefix(testElement) { + var prop, match; + var vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/; + + for (prop in testElement.style) { + if (match = vendorRegex.exec(prop)) { + return match[0]; + } + } + } + + var self = { + isInputKey : function(e) { return (e.keyCode >= 31 && e.keyCode <= 90); }, + isNumPadKey : function(e) { return (3 === e.location && e.keyCode >= 97 && e.keyCode <= 105); }, + isMetaKey: function(e) { return (e.keyCode >= 91 && e.keyCode <= 93); }, + isFnLockKey: function(e) { return (e.keyCode >= 112 && e.keyCode <= 145); }, + isNavigationKey : function(e) { + var kc = self.KEY_CODE, NAVIGATION_KEYS = [kc.SPACE, kc.ENTER, kc.UP_ARROW, kc.DOWN_ARROW]; + return (NAVIGATION_KEYS.indexOf(e.keyCode) != -1); + }, + hasModifierKey: function(e) { + return e.ctrlKey || e.metaKey || e.altKey; + }, + + /** + * Maximum size, in pixels, that can be explicitly set to an element. The actual value varies + * between browsers, but IE11 has the very lowest size at a mere 1,533,917px. Ideally we could + * compute this value, but Firefox always reports an element to have a size of zero if it + * goes over the max, meaning that we'd have to binary search for the value. + */ + ELEMENT_MAX_PIXELS: 1533917, + + /** + * Priority for a directive that should run before the directives from ngAria. + */ + BEFORE_NG_ARIA: 210, + + /** + * Common Keyboard actions and their associated keycode. + */ + KEY_CODE: { + COMMA: 188, + SEMICOLON : 186, + ENTER: 13, + ESCAPE: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT_ARROW : 37, + UP_ARROW : 38, + RIGHT_ARROW : 39, + DOWN_ARROW : 40, + TAB : 9, + BACKSPACE: 8, + DELETE: 46 + }, + + /** + * Vendor prefixed CSS properties to be used to support the given functionality in older browsers + * as well. + */ + CSS: { + /* Constants */ + TRANSITIONEND: 'transitionend' + (isWebkit ? ' webkitTransitionEnd' : ''), + ANIMATIONEND: 'animationend' + (isWebkit ? ' webkitAnimationEnd' : ''), + + TRANSFORM: vendorProperty('transform'), + TRANSFORM_ORIGIN: vendorProperty('transformOrigin'), + TRANSITION: vendorProperty('transition'), + TRANSITION_DURATION: vendorProperty('transitionDuration'), + ANIMATION_PLAY_STATE: vendorProperty('animationPlayState'), + ANIMATION_DURATION: vendorProperty('animationDuration'), + ANIMATION_NAME: vendorProperty('animationName'), + ANIMATION_TIMING: vendorProperty('animationTimingFunction'), + ANIMATION_DIRECTION: vendorProperty('animationDirection') + }, + + /** + * As defined in core/style/_variables.scss + * + * $layout-breakpoint-xs: 600px !default; + * $layout-breakpoint-sm: 960px !default; + * $layout-breakpoint-md: 1280px !default; + * $layout-breakpoint-lg: 1920px !default; + * + */ + MEDIA: { + 'xs' : '(max-width: 599px)' , + 'gt-xs' : '(min-width: 600px)' , + 'sm' : '(min-width: 600px) and (max-width: 959px)' , + 'gt-sm' : '(min-width: 960px)' , + 'md' : '(min-width: 960px) and (max-width: 1279px)' , + 'gt-md' : '(min-width: 1280px)' , + 'lg' : '(min-width: 1280px) and (max-width: 1919px)', + 'gt-lg' : '(min-width: 1920px)' , + 'xl' : '(min-width: 1920px)' , + 'landscape' : '(orientation: landscape)' , + 'portrait' : '(orientation: portrait)' , + 'print' : 'print' + }, + + MEDIA_PRIORITY: [ + 'xl', + 'gt-lg', + 'lg', + 'gt-md', + 'md', + 'gt-sm', + 'sm', + 'gt-xs', + 'xs', + 'landscape', + 'portrait', + 'print' + ] + }; + + return self; +} + +})(); +(function(){ +"use strict"; + + angular + .module('material.core') + .config(["$provide", function($provide){ + $provide.decorator('$mdUtil', ['$delegate', function ($delegate){ + /** + * Inject the iterator facade to easily support iteration and accessors + * @see iterator below + */ + $delegate.iterator = MdIterator; + + return $delegate; + } + ]); + }]); + + /** + * iterator is a list facade to easily support iteration and accessors/ + * + * @param {any[]} items Array list which this iterator will enumerate + * @param {boolean=} reloop enables iterator to consider the list as an endless loop + * @return {{add: add, next: (function()), last: (function(): any|null), previous: (function()), count: (function(): number), hasNext: (function(*=): Array.length|*|number|boolean), inRange: (function(*): boolean), remove: remove, contains: (function(*=): *|boolean), itemAt: (function(*=): any|null), findBy: (function(*, *): *[]), hasPrevious: (function(*=): Array.length|*|number|boolean), items: (function(): *[]), indexOf: (function(*=): number), first: (function(): any|null)}} + * @constructor + */ + function MdIterator(items, reloop) { + var trueFn = function() { return true; }; + + if (items && !angular.isArray(items)) { + items = Array.prototype.slice.call(items); + } + + reloop = !!reloop; + var _items = items || []; + + // Published API + return { + items: getItems, + count: count, + + inRange: inRange, + contains: contains, + indexOf: indexOf, + itemAt: itemAt, + + findBy: findBy, + + add: add, + remove: remove, + + first: first, + last: last, + next: angular.bind(null, findSubsequentItem, false), + previous: angular.bind(null, findSubsequentItem, true), + + hasPrevious: hasPrevious, + hasNext: hasNext + }; + + /** + * Publish copy of the enumerable set + * @returns {Array|*} + */ + function getItems() { + return [].concat(_items); + } + + /** + * Determine length of the list + * @returns {Array.length|*|number} + */ + function count() { + return _items.length; + } + + /** + * Is the index specified valid + * @param index + * @returns {Array.length|*|number|boolean} + */ + function inRange(index) { + return _items.length && (index > -1) && (index < _items.length); + } + + /** + * Can the iterator proceed to the next item in the list; relative to + * the specified item. + * + * @param item + * @returns {Array.length|*|number|boolean} + */ + function hasNext(item) { + return item ? inRange(indexOf(item) + 1) : false; + } + + /** + * Can the iterator proceed to the previous item in the list; relative to + * the specified item. + * + * @param item + * @returns {Array.length|*|number|boolean} + */ + function hasPrevious(item) { + return item ? inRange(indexOf(item) - 1) : false; + } + + /** + * Get item at specified index/position + * @param index + * @returns {*} + */ + function itemAt(index) { + return inRange(index) ? _items[index] : null; + } + + /** + * Find all elements matching the key/value pair + * otherwise return null + * + * @param val + * @param key + * + * @return array + */ + function findBy(key, val) { + return _items.filter(function(item) { + return item[key] === val; + }); + } + + /** + * Add item to list + * @param item + * @param index + * @returns {*} + */ + function add(item, index) { + if (!item) return -1; + + if (!angular.isNumber(index)) { + index = _items.length; + } + + _items.splice(index, 0, item); + + return indexOf(item); + } + + /** + * Remove item from list... + * @param item + */ + function remove(item) { + if (contains(item)){ + _items.splice(indexOf(item), 1); + } + } + + /** + * Get the zero-based index of the target item + * @param item + * @returns {*} + */ + function indexOf(item) { + return _items.indexOf(item); + } + + /** + * Boolean existence check + * @param item + * @returns {boolean} + */ + function contains(item) { + return item && (indexOf(item) > -1); + } + + /** + * Return first item in the list + * @returns {*} + */ + function first() { + return _items.length ? _items[0] : null; + } + + /** + * Return last item in the list... + * @returns {*} + */ + function last() { + return _items.length ? _items[_items.length - 1] : null; + } + + /** + * Find the next item. If reloop is true and at the end of the list, it will go back to the + * first item. If given, the `validate` callback will be used to determine whether the next item + * is valid. If not valid, it will try to find the next item again. + * + * @param {boolean} backwards Specifies the direction of searching (forwards/backwards) + * @param {*} item The item whose subsequent item we are looking for + * @param {Function=} validate The `validate` function + * @param {integer=} limit The recursion limit + * + * @returns {*} The subsequent item or null + */ + function findSubsequentItem(backwards, item, validate, limit) { + validate = validate || trueFn; + + var curIndex = indexOf(item); + while (true) { + if (!inRange(curIndex)) return null; + + var nextIndex = curIndex + (backwards ? -1 : 1); + var foundItem = null; + if (inRange(nextIndex)) { + foundItem = _items[nextIndex]; + } else if (reloop) { + foundItem = backwards ? last() : first(); + nextIndex = indexOf(foundItem); + } + + if ((foundItem === null) || (nextIndex === limit)) return null; + if (validate(foundItem)) return foundItem; + + if (angular.isUndefined(limit)) limit = nextIndex; + + curIndex = nextIndex; + } + } + } + + +})(); +(function(){ +"use strict"; + + +mdMediaFactory.$inject = ["$mdConstant", "$rootScope", "$window"];angular.module('material.core') +.factory('$mdMedia', mdMediaFactory); + +/** + * @ngdoc service + * @name $mdMedia + * @module material.core + * + * @description + * `$mdMedia` is used to evaluate whether a given media query is true or false given the + * current device's screen / window size. The media query will be re-evaluated on resize, allowing + * you to register a watch. + * + * `$mdMedia` also has pre-programmed support for media queries that match the layout breakpoints: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    BreakpointmediaQuery
    xs(max-width: 599px)
    gt-xs(min-width: 600px)
    sm(min-width: 600px) and (max-width: 959px)
    gt-sm(min-width: 960px)
    md(min-width: 960px) and (max-width: 1279px)
    gt-md(min-width: 1280px)
    lg(min-width: 1280px) and (max-width: 1919px)
    gt-lg(min-width: 1920px)
    xl(min-width: 1920px)
    landscapelandscape
    portraitportrait
    printprint
    + * + * See Material Design's Layout - Adaptive UI for more details. + * + * + * + * + * + * @returns {boolean} a boolean representing whether or not the given media query is true or false. + * + * @usage + * + * app.controller('MyController', function($mdMedia, $scope) { + * $scope.$watch(function() { return $mdMedia('lg'); }, function(big) { + * $scope.bigScreen = big; + * }); + * + * $scope.screenIsSmall = $mdMedia('sm'); + * $scope.customQuery = $mdMedia('(min-width: 1234px)'); + * $scope.anotherCustom = $mdMedia('max-width: 300px'); + * }); + * + */ + +/* @ngInject */ +function mdMediaFactory($mdConstant, $rootScope, $window) { + var queries = {}; + var mqls = {}; + var results = {}; + var normalizeCache = {}; + + $mdMedia.getResponsiveAttribute = getResponsiveAttribute; + $mdMedia.getQuery = getQuery; + $mdMedia.watchResponsiveAttributes = watchResponsiveAttributes; + + return $mdMedia; + + function $mdMedia(query) { + var validated = queries[query]; + if (angular.isUndefined(validated)) { + validated = queries[query] = validate(query); + } + + var result = results[validated]; + if (angular.isUndefined(result)) { + result = add(validated); + } + + return result; + } + + function validate(query) { + return $mdConstant.MEDIA[query] || + ((query.charAt(0) !== '(') ? ('(' + query + ')') : query); + } + + function add(query) { + var result = mqls[query]; + if (!result) { + result = mqls[query] = $window.matchMedia(query); + } + + result.addListener(onQueryChange); + return (results[result.media] = !!result.matches); + } + + function onQueryChange(query) { + $rootScope.$evalAsync(function() { + results[query.media] = !!query.matches; + }); + } + + function getQuery(name) { + return mqls[name]; + } + + function getResponsiveAttribute(attrs, attrName) { + for (var i = 0; i < $mdConstant.MEDIA_PRIORITY.length; i++) { + var mediaName = $mdConstant.MEDIA_PRIORITY[i]; + if (!mqls[queries[mediaName]].matches) { + continue; + } + + var normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName); + if (attrs[normalizedName]) { + return attrs[normalizedName]; + } + } + + // fallback on unprefixed + return attrs[getNormalizedName(attrs, attrName)]; + } + + function watchResponsiveAttributes(attrNames, attrs, watchFn) { + var unwatchFns = []; + attrNames.forEach(function(attrName) { + var normalizedName = getNormalizedName(attrs, attrName); + if (angular.isDefined(attrs[normalizedName])) { + unwatchFns.push( + attrs.$observe(normalizedName, angular.bind(void 0, watchFn, null))); + } + + for (var mediaName in $mdConstant.MEDIA) { + normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName); + if (angular.isDefined(attrs[normalizedName])) { + unwatchFns.push( + attrs.$observe(normalizedName, angular.bind(void 0, watchFn, mediaName))); + } + } + }); + + return function unwatch() { + unwatchFns.forEach(function(fn) { fn(); }); + }; + } + + // Improves performance dramatically + function getNormalizedName(attrs, attrName) { + return normalizeCache[attrName] || + (normalizeCache[attrName] = attrs.$normalize(attrName)); + } +} + +})(); +(function(){ +"use strict"; + +angular + .module('material.core') + .config(["$provide", function($provide) { + $provide.decorator('$mdUtil', ['$delegate', function ($delegate) { + + // Inject the prefixer into our original $mdUtil service. + $delegate.prefixer = MdPrefixer; + + return $delegate; + }]); + }]); + +/** + * @param {string|string[]} initialAttributes + * @param {boolean} buildSelector + * @return {string|string[]|{buildSelector: (function(string|string[]): string), + * buildList: (function(string|string[]): string[]), + * hasAttribute: (function(JQLite|Element, string): HTMLElement), + * removeAttribute: (function(JQLite|Element, string): void)}} + * @constructor + */ +function MdPrefixer(initialAttributes, buildSelector) { + var PREFIXES = ['data', 'x']; + + if (initialAttributes) { + // The prefixer also accepts attributes as a parameter, and immediately builds a list or selector for + // the specified attributes. + return buildSelector ? _buildSelector(initialAttributes) : _buildList(initialAttributes); + } + + return { + buildList: _buildList, + buildSelector: _buildSelector, + hasAttribute: _hasAttribute, + removeAttribute: _removeAttribute + }; + + function _buildList(attributes) { + attributes = angular.isArray(attributes) ? attributes : [attributes]; + + attributes.forEach(function(item) { + PREFIXES.forEach(function(prefix) { + attributes.push(prefix + '-' + item); + }); + }); + + return attributes; + } + + function _buildSelector(attributes) { + attributes = angular.isArray(attributes) ? attributes : [attributes]; + + return _buildList(attributes) + .map(function(item) { + return '[' + item + ']'; + }) + .join(','); + } + + function _hasAttribute(element, attribute) { + element = _getNativeElement(element); + + if (!element) { + return false; + } + + var prefixedAttrs = _buildList(attribute); + + for (var i = 0; i < prefixedAttrs.length; i++) { + if (element.hasAttribute(prefixedAttrs[i])) { + return true; + } + } + + return false; + } + + function _removeAttribute(element, attribute) { + element = _getNativeElement(element); + + if (!element) { + return; + } + + _buildList(attribute).forEach(function(prefixedAttribute) { + element.removeAttribute(prefixedAttribute); + }); + } + + /** + * Transforms a jqLite or DOM element into a HTML element. + * This is useful when supporting jqLite elements and DOM elements at + * same time. + * @param element {JQLite|Element} Element to be parsed + * @returns {HTMLElement} Parsed HTMLElement + */ + function _getNativeElement(element) { + element = element[0] || element; + + if (element.nodeType) { + return element; + } + } + +} + +})(); +(function(){ +"use strict"; + +/* + * This var has to be outside the angular factory, otherwise when + * there are multiple material apps on the same page, each app + * will create its own instance of this array and the app's IDs + * will not be unique. + */ +UtilFactory.$inject = ["$document", "$timeout", "$compile", "$rootScope", "$$mdAnimate", "$interpolate", "$log", "$rootElement", "$window", "$$rAF"]; +var nextUniqueId = 0, isIos, isAndroid, isFirefox; + +// Support material-tools builds. +if (window.navigator) { + var userAgent = window.navigator.userAgent || window.navigator.vendor || window.opera; + isIos = userAgent.match(/ipad|iphone|ipod/i); + isAndroid = userAgent.match(/android/i); + isFirefox = userAgent.match(/(firefox|minefield)/i); +} + +/** + * @ngdoc module + * @name material.core.util + * @description + * Util + */ +angular +.module('material.core') +.factory('$mdUtil', UtilFactory); + +/** + * @ngInject + */ +function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $interpolate, $log, + $rootElement, $window, $$rAF) { + // Setup some core variables for the processTemplate method + var startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}')); + + // Polyfill document.contains for IE11. + document.contains || (document.contains = function (node) { + return document.body.contains(node); + }); + + /** + * Checks if the target element has the requested style by key + * @param {DOMElement|JQLite} target Target element + * @param {string} key Style key + * @param {string=} expectedVal Optional expected value + * @returns {boolean} Whether the target element has the style or not + */ + var hasComputedStyle = function (target, key, expectedVal) { + var hasValue = false; + + if (target && target.length) { + var computedStyles = $window.getComputedStyle(target[0]); + hasValue = angular.isDefined(computedStyles[key]) && + (expectedVal ? computedStyles[key] == expectedVal : true); + } + + return hasValue; + }; + + function validateCssValue(value) { + return !value ? '0' : + hasPx(value) || hasPercent(value) ? value : value + 'px'; + } + + function hasPx(value) { + return String(value).indexOf('px') > -1; + } + + function hasPercent(value) { + return String(value).indexOf('%') > -1; + } + + var $mdUtil = { + dom: {}, + isIos: isIos, + isAndroid: isAndroid, + now: window.performance && window.performance.now ? + angular.bind(window.performance, window.performance.now) : Date.now || function() { + return new Date().getTime(); + }, + + /** + * Cross-version compatibility method to retrieve an option of a ngModel controller, + * which supports the breaking changes in the AngularJS snapshot (SHA 87a2ff76af5d0a9268d8eb84db5755077d27c84c). + * @param {!ngModel.NgModelController} ngModelCtrl + * @param {!string} optionName + * @returns {string|number|boolean|Object|undefined} + */ + getModelOption: function (ngModelCtrl, optionName) { + if (!ngModelCtrl.$options) { + return; + } + + var $options = ngModelCtrl.$options; + + // The newer versions of AngularJS introduced a getOption function and made the option values + // no longer visible on the $options object. + return $options.getOption ? $options.getOption(optionName) : $options[optionName]; + }, + + /** + * Determines the current 'dir'ectional value based on the value of 'dir' + * attribute of the element. If that is not defined, it will try to use + * a 'dir' attribute of the body or html tag. + * + * @param {Object=} attrs a hash object with key-value pairs of normalized + * attribute names and their corresponding attribute values. + * @returns {boolean} true if the element's passed in attributes, + * the document, or the body indicates RTL mode, false otherwise. + */ + isRtl: function(attrs) { + var dir = angular.isDefined(attrs) && attrs.hasOwnProperty('dir') && attrs.dir; + + switch (dir) { + case 'ltr': + return false; + + case 'rtl': + return true; + } + + return ($document[0].dir === 'rtl' || $document[0].body.dir === 'rtl'); + }, + + /** + * Bi-directional accessor/mutator used to easily update an element's + * property based on the current 'dir'ectional value. + */ + bidi: function(element, property, lValue, rValue) { + var ltr = !this.isRtl(); + + // If accessor + if (arguments.length == 0) return ltr ? 'ltr' : 'rtl'; + + // If mutator + var elem = angular.element(element); + + if (ltr && angular.isDefined(lValue)) { + elem.css(property, validateCssValue(lValue)); + } + else if (!ltr && angular.isDefined(rValue)) { + elem.css(property, validateCssValue(rValue)); + } + }, + + bidiProperty: function (element, lProperty, rProperty, value) { + var ltr = !this.isRtl(); + + var elem = angular.element(element); + + if (ltr && angular.isDefined(lProperty)) { + elem.css(lProperty, validateCssValue(value)); + elem.css(rProperty, ''); + } + else if (!ltr && angular.isDefined(rProperty)) { + elem.css(rProperty, validateCssValue(value)); + elem.css(lProperty, ''); + } + }, + + clientRect: function(element, offsetParent, isOffsetRect) { + var node = getNode(element); + offsetParent = getNode(offsetParent || node.offsetParent || document.body); + var nodeRect = node.getBoundingClientRect(); + + // The user can ask for an offsetRect: a rect relative to the offsetParent, + // or a clientRect: a rect relative to the page + var offsetRect = isOffsetRect ? + offsetParent.getBoundingClientRect() : + {left: 0, top: 0, width: 0, height: 0}; + return { + left: nodeRect.left - offsetRect.left, + top: nodeRect.top - offsetRect.top, + width: nodeRect.width, + height: nodeRect.height + }; + }, + offsetRect: function(element, offsetParent) { + return $mdUtil.clientRect(element, offsetParent, true); + }, + + /** + * Annoying method to copy nodes to an array, thanks to IE. + * @param nodes + * @return {Array} + */ + nodesToArray: function(nodes) { + var results = [], i; + nodes = nodes || []; + + for (i = 0; i < nodes.length; ++i) { + results.push(nodes.item(i)); + } + return results; + }, + + /** + * Determines the absolute position of the viewport. + * Useful when making client rectangles absolute. + * @returns {number} + */ + getViewportTop: function() { + // If body scrolling is disabled, then use the cached viewport top value, otherwise get it + // fresh from the $window. + if ($mdUtil.disableScrollAround._count && $mdUtil.disableScrollAround._viewPortTop) { + return $mdUtil.disableScrollAround._viewPortTop; + } else { + return $window.scrollY || $window.pageYOffset || 0; + } + }, + + /** + * Finds the proper focus target by searching the DOM. + * + * @param {!JQLite} containerEl + * @param {string=} attributeVal + * @returns {JQLite|undefined} + */ + findFocusTarget: function(containerEl, attributeVal) { + var AUTO_FOCUS = this.prefixer('md-autofocus', true); + var elToFocus; + + elToFocus = scanForFocusable(containerEl, attributeVal || AUTO_FOCUS); + + // Scan for fallback to 'universal' API + if (!elToFocus) { + elToFocus = scanForFocusable(containerEl, AUTO_FOCUS); + } + + return elToFocus; + + /** + * Can target and nested children for specified Selector (attribute) + * whose value may be an expression that evaluates to True/False. + * @param {!JQLite} target + * @param {!string} selector + * @return {JQLite|undefined} + */ + function scanForFocusable(target, selector) { + var elFound, items = target[0].querySelectorAll(selector); + + // Find the last child element with the focus attribute + if (items && items.length) { + items.length && angular.forEach(items, function(it) { + it = angular.element(it); + + // Check the element for the md-autofocus class to ensure any associated expression + // evaluated to true. + var isFocusable = it.hasClass('md-autofocus'); + if (isFocusable) elFound = it; + }); + } + return elFound; + } + }, + + /** + * Disables scroll around the passed parent element. + * @param {Element|JQLite=} element Origin Element (not used) + * @param {Element|JQLite=} parent Element to disable scrolling within. + * Defaults to body if none supplied. + * @param {Object=} options Object of options to modify functionality + * - disableScrollMask Boolean of whether or not to create a scroll mask element or + * use the passed parent element. + */ + disableScrollAround: function(element, parent, options) { + options = options || {}; + + $mdUtil.disableScrollAround._count = Math.max(0, $mdUtil.disableScrollAround._count || 0); + $mdUtil.disableScrollAround._count++; + + if ($mdUtil.disableScrollAround._restoreScroll) { + return $mdUtil.disableScrollAround._restoreScroll; + } + + var body = $document[0].body; + var restoreBody = disableBodyScroll(); + var restoreElement = disableElementScroll(parent, options); + + return $mdUtil.disableScrollAround._restoreScroll = function() { + if (--$mdUtil.disableScrollAround._count <= 0) { + delete $mdUtil.disableScrollAround._viewPortTop; + restoreBody(); + restoreElement(); + delete $mdUtil.disableScrollAround._restoreScroll; + } + }; + + /** + * Creates a virtual scrolling mask to prevent touchmove, keyboard, scrollbar clicking, + * and wheel events. + * @param {!Element|!JQLite} elementToDisable + * @param {Object=} scrollMaskOptions Object of options to modify functionality + * - disableScrollMask Boolean of whether or not to create a scroll mask element or + * use the passed parent element. + * @returns {Function} + */ + function disableElementScroll(elementToDisable, scrollMaskOptions) { + var scrollMask; + var wrappedElementToDisable = angular.element(elementToDisable || body); + + if (scrollMaskOptions.disableScrollMask) { + scrollMask = wrappedElementToDisable; + } else { + scrollMask = angular.element( + '
    ' + + '
    ' + + '
    '); + wrappedElementToDisable.append(scrollMask); + } + + /** + * @param {Event} $event + */ + function preventDefault($event) { + $event.preventDefault(); + } + + scrollMask.on('wheel touchmove', preventDefault); + + return function restoreElementScroll() { + scrollMask.off('wheel touchmove', preventDefault); + + if (!scrollMaskOptions.disableScrollMask && scrollMask[0].parentNode) { + scrollMask[0].parentNode.removeChild(scrollMask[0]); + } + }; + } + + // Converts the body to a position fixed block and translate it to the proper scroll position + function disableBodyScroll() { + var documentElement = $document[0].documentElement; + + var prevDocumentStyle = documentElement.style.cssText || ''; + var prevBodyStyle = body.style.cssText || ''; + + var viewportTop = $mdUtil.getViewportTop(); + $mdUtil.disableScrollAround._viewPortTop = viewportTop; + var clientWidth = body.clientWidth; + var hasVerticalScrollbar = body.scrollHeight > body.clientHeight + 1; + + // Scroll may be set on element (for example by overflow-y: scroll) + // but Chrome is reporting the scrollTop position always on . + // scrollElement will allow to restore the scrollTop position to proper target. + var scrollElement = documentElement.scrollTop > 0 ? documentElement : body; + + if (hasVerticalScrollbar) { + angular.element(body).css({ + position: 'fixed', + width: '100%', + top: -viewportTop + 'px' + }); + } + + if (body.clientWidth < clientWidth) { + body.style.overflow = 'hidden'; + } + + return function restoreScroll() { + // Reset the inline style CSS to the previous. + body.style.cssText = prevBodyStyle; + documentElement.style.cssText = prevDocumentStyle; + + // The scroll position while being fixed + scrollElement.scrollTop = viewportTop; + }; + } + + }, + + enableScrolling: function() { + var restoreFn = this.disableScrollAround._restoreScroll; + restoreFn && restoreFn(); + }, + + floatingScrollbars: function() { + if (this.floatingScrollbars.cached === undefined) { + var tempNode = angular.element('
    ').css({ + width: '100%', + 'z-index': -1, + position: 'absolute', + height: '35px', + 'overflow-y': 'scroll' + }); + tempNode.children().css('height', '60px'); + + $document[0].body.appendChild(tempNode[0]); + this.floatingScrollbars.cached = + (tempNode[0].offsetWidth === tempNode[0].childNodes[0].offsetWidth); + tempNode.remove(); + } + return this.floatingScrollbars.cached; + }, + + /** + * Mobile safari only allows you to set focus in click event listeners. + * @param {Element|JQLite} element to focus + */ + forceFocus: function(element) { + var node = element[0] || element; + + document.addEventListener('click', function focusOnClick(ev) { + if (ev.target === node && ev.$focus) { + node.focus(); + ev.stopImmediatePropagation(); + ev.preventDefault(); + node.removeEventListener('click', focusOnClick); + } + }, true); + + var newEvent = document.createEvent('MouseEvents'); + newEvent.initMouseEvent('click', false, true, window, {}, 0, 0, 0, 0, + false, false, false, false, 0, null); + newEvent.$material = true; + newEvent.$focus = true; + node.dispatchEvent(newEvent); + }, + + /** + * facade to build md-backdrop element with desired styles + * NOTE: Use $compile to trigger backdrop postLink function + */ + createBackdrop: function(scope, addClass) { + return $compile($mdUtil.supplant('', [addClass]))(scope); + }, + + /** + * supplant() method from Crockford's `Remedial Javascript` + * Equivalent to use of $interpolate; without dependency on + * interpolation symbols and scope. Note: the '{}' can + * be property names, property chains, or array indices. + */ + supplant: function(template, values, pattern) { + pattern = pattern || /\{([^{}]*)\}/g; + return template.replace(pattern, function(a, b) { + var p = b.split('.'), + r = values; + try { + for (var s in p) { + if (p.hasOwnProperty(s)) { + r = r[p[s]]; + } + } + } catch (e) { + r = a; + } + return (typeof r === 'string' || typeof r === 'number') ? r : a; + }); + }, + + fakeNgModel: function() { + return { + $fake: true, + $setTouched: angular.noop, + $setViewValue: function(value) { + this.$viewValue = value; + this.$render(value); + this.$viewChangeListeners.forEach(function(cb) { + cb(); + }); + }, + $isEmpty: function(value) { + return ('' + value).length === 0; + }, + $parsers: [], + $formatters: [], + $viewChangeListeners: [], + $render: angular.noop + }; + }, + + /** + * @param {Function} func original function to be debounced + * @param {number} wait number of milliseconds to delay (since last debounce reset). + * Default value 10 msecs. + * @param {Object} scope in which to apply the function after debouncing ends + * @param {boolean} invokeApply should the $timeout trigger $digest() dirty checking + * @return {Function} A function, that, as long as it continues to be invoked, will not be + * triggered. The function will be called after it stops being called for N milliseconds. + */ + debounce: function(func, wait, scope, invokeApply) { + var timer; + + return function debounced() { + var context = scope, + args = Array.prototype.slice.call(arguments); + + $timeout.cancel(timer); + timer = $timeout(function() { + + timer = undefined; + func.apply(context, args); + + }, wait || 10, invokeApply); + }; + }, + + /** + * The function will not be called unless it has been more than `delay` milliseconds since the + * last call. + * @param {Function} func original function to throttle + * @param {number} delay number of milliseconds to delay + * @return {Function} a function that can only be triggered every `delay` milliseconds. + */ + throttle: function throttle(func, delay) { + var recent; + return function throttled() { + var context = this; + var args = arguments; + var now = $mdUtil.now(); + + if (!recent || (now - recent > delay)) { + func.apply(context, args); + recent = now; + } + }; + }, + + /** + * Measures the number of milliseconds taken to run the provided callback + * function. Uses a high-precision timer if available. + */ + time: function time(cb) { + var start = $mdUtil.now(); + cb(); + return $mdUtil.now() - start; + }, + + /** + * Create an implicit getter that caches its `getter()` + * lookup value + */ + valueOnUse : function (scope, key, getter) { + var value = null, args = Array.prototype.slice.call(arguments); + var params = (args.length > 3) ? args.slice(3) : []; + + Object.defineProperty(scope, key, { + get: function () { + if (value === null) value = getter.apply(scope, params); + return value; + } + }); + }, + + /** + * Get a unique ID. + * + * @returns {string} an unique numeric string + */ + nextUid: function() { + return '' + nextUniqueId++; + }, + + /** + * Stop watchers and events from firing on a scope without destroying it, + * by disconnecting it from its parent and its siblings' linked lists. + * @param {Object} scope to disconnect + */ + disconnectScope: function disconnectScope(scope) { + if (!scope) return; + + // we can't destroy the root scope or a scope that has been already destroyed + if (scope.$root === scope) return; + if (scope.$$destroyed) return; + + var parent = scope.$parent; + scope.$$disconnected = true; + + // See Scope.$destroy + if (parent.$$childHead === scope) parent.$$childHead = scope.$$nextSibling; + if (parent.$$childTail === scope) parent.$$childTail = scope.$$prevSibling; + if (scope.$$prevSibling) scope.$$prevSibling.$$nextSibling = scope.$$nextSibling; + if (scope.$$nextSibling) scope.$$nextSibling.$$prevSibling = scope.$$prevSibling; + + scope.$$nextSibling = scope.$$prevSibling = null; + + }, + + /** + * Undo the effects of disconnectScope(). + * @param {Object} scope to reconnect + */ + reconnectScope: function reconnectScope(scope) { + if (!scope) return; + + // we can't disconnect the root node or scope already disconnected + if (scope.$root === scope) return; + if (!scope.$$disconnected) return; + + var child = scope; + + var parent = child.$parent; + child.$$disconnected = false; + // See Scope.$new for this logic... + child.$$prevSibling = parent.$$childTail; + if (parent.$$childHead) { + parent.$$childTail.$$nextSibling = child; + parent.$$childTail = child; + } else { + parent.$$childHead = parent.$$childTail = child; + } + }, + + /** + * Get an element's siblings matching a given tag name. + * + * @param {JQLite|angular.element|HTMLElement} element Element to start walking the DOM from + * @param {string} tagName HTML tag name to match against + * @returns {Object[]} JQLite + */ + getSiblings: function getSiblings(element, tagName) { + var upperCasedTagName = tagName.toUpperCase(); + if (element instanceof angular.element) { + element = element[0]; + } + var siblings = Array.prototype.filter.call(element.parentNode.children, function(node) { + return element !== node && node.tagName.toUpperCase() === upperCasedTagName; + }); + return siblings.map(function (sibling) { + return angular.element(sibling); + }); + }, + + /** + * getClosest replicates jQuery.closest() to walk up the DOM tree until it finds a matching + * nodeName. + * + * @param {Node} el Element to start walking the DOM from + * @param {string|function} validateWith If a string is passed, it will be evaluated against + * each of the parent nodes' tag name. If a function is passed, the loop will call it with each + * of the parents and will use the return value to determine whether the node is a match. + * @param {boolean=} onlyParent Only start checking from the parent element, not `el`. + * @returns {Node|null} closest matching parent Node or null if not found + */ + getClosest: function getClosest(el, validateWith, onlyParent) { + if (angular.isString(validateWith)) { + var tagName = validateWith.toUpperCase(); + validateWith = function(el) { + return el.nodeName.toUpperCase() === tagName; + }; + } + + if (el instanceof angular.element) el = el[0]; + if (onlyParent) el = el.parentNode; + if (!el) return null; + + do { + if (validateWith(el)) { + return el; + } + } while (el = el.parentNode); + + return null; + }, + + /** + * Build polyfill for the Node.contains feature (if needed) + * @param {Node} node + * @param {Node} child + * @returns {Node} + */ + elementContains: function(node, child) { + var hasContains = (window.Node && window.Node.prototype && Node.prototype.contains); + var findFn = hasContains ? angular.bind(node, node.contains) : angular.bind(node, function(arg) { + // compares the positions of two nodes and returns a bitmask + return (node === child) || !!(this.compareDocumentPosition(arg) & 16); + }); + + return findFn(child); + }, + + /** + * Functional equivalent for $element.filter(‘md-bottom-sheet’) + * useful with interimElements where the element and its container are important... + * + * @param {JQLite} element to scan + * @param {string} nodeName of node to find (e.g. 'md-dialog') + * @param {boolean=} scanDeep optional flag to allow deep scans; defaults to 'false'. + * @param {boolean=} warnNotFound optional flag to enable log warnings; defaults to false + */ + extractElementByName: function(element, nodeName, scanDeep, warnNotFound) { + var found = scanTree(element); + if (!found && !!warnNotFound) { + $log.warn($mdUtil.supplant("Unable to find node '{0}' in element '{1}'.",[nodeName, element[0].outerHTML])); + } + + return angular.element(found || element); + + /** + * Breadth-First tree scan for element with matching `nodeName` + */ + function scanTree(element) { + return scanLevel(element) || (scanDeep ? scanChildren(element) : null); + } + + /** + * Case-insensitive scan of current elements only (do not descend). + */ + function scanLevel(element) { + if (element) { + for (var i = 0, len = element.length; i < len; i++) { + if (element[i].nodeName.toLowerCase() === nodeName) { + return element[i]; + } + } + } + return null; + } + + /** + * Scan children of specified node + */ + function scanChildren(element) { + var found; + if (element) { + for (var i = 0, len = element.length; i < len; i++) { + var target = element[i]; + if (!found) { + for (var j = 0, numChild = target.childNodes.length; j < numChild; j++) { + found = found || scanTree([target.childNodes[j]]); + } + } + } + } + return found; + } + + }, + + /** + * Give optional properties with no value a boolean true if attr provided or false otherwise + */ + initOptionalProperties: function(scope, attr, defaults) { + defaults = defaults || {}; + angular.forEach(scope.$$isolateBindings, function(binding, key) { + if (binding.optional && angular.isUndefined(scope[key])) { + var attrIsDefined = angular.isDefined(attr[binding.attrName]); + scope[key] = angular.isDefined(defaults[key]) ? defaults[key] : attrIsDefined; + } + }); + }, + + /** + * Alternative to $timeout calls with 0 delay. + * nextTick() coalesces all calls within a single frame + * to minimize $digest thrashing + * + * @param {Function} callback function to be called after the tick + * @param {boolean=} digest true to call $rootScope.$digest() after callback + * @param {Object=} scope associated with callback. If the scope is destroyed, the callback will + * be skipped. + * @returns {*} + */ + nextTick: function(callback, digest, scope) { + // grab function reference for storing state details + var nextTick = $mdUtil.nextTick; + var timeout = nextTick.timeout; + var queue = nextTick.queue || []; + + // add callback to the queue + queue.push({scope: scope, callback: callback}); + + // set default value for digest + if (digest == null) digest = true; + + // store updated digest/queue values + nextTick.digest = nextTick.digest || digest; + nextTick.queue = queue; + + // either return existing timeout or create a new one + return timeout || (nextTick.timeout = $timeout(processQueue, 0, false)); + + /** + * Grab a copy of the current queue + * Clear the queue for future use + * Process the existing queue + * Trigger digest if necessary + */ + function processQueue() { + var queue = nextTick.queue; + var digest = nextTick.digest; + + nextTick.queue = []; + nextTick.timeout = null; + nextTick.digest = false; + + queue.forEach(function(queueItem) { + var skip = queueItem.scope && queueItem.scope.$$destroyed; + if (!skip) { + queueItem.callback(); + } + }); + + if (digest) $rootScope.$digest(); + } + }, + + /** + * Processes a template and replaces the start/end symbols if the application has + * overridden them. + * + * @param template The template to process whose start/end tags may be replaced. + * @returns {*} + */ + processTemplate: function(template) { + if (usesStandardSymbols) { + return template; + } else { + if (!template || !angular.isString(template)) return template; + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + } + }, + + /** + * Scan up dom hierarchy for enabled parent; + */ + getParentWithPointerEvents: function (element) { + var parent = element.parent(); + + // jqLite might return a non-null, but still empty, parent; so check for parent and length + while (hasComputedStyle(parent, 'pointer-events', 'none')) { + parent = parent.parent(); + } + + return parent; + }, + + getNearestContentElement: function (element) { + var current = element.parent()[0]; + // Look for the nearest parent md-content, stopping at the rootElement. + while (current && current !== $rootElement[0] && current !== document.body && current.nodeName.toUpperCase() !== 'MD-CONTENT') { + current = current.parentNode; + } + return current; + }, + + /** + * Checks if the current browser is natively supporting the `sticky` position. + * @returns {string} supported sticky property name + */ + checkStickySupport: function() { + var stickyProp; + var testEl = angular.element('
    '); + $document[0].body.appendChild(testEl[0]); + + var stickyProps = ['sticky', '-webkit-sticky']; + for (var i = 0; i < stickyProps.length; ++i) { + testEl.css({ + position: stickyProps[i], + top: 0, + 'z-index': 2 + }); + + if (testEl.css('position') == stickyProps[i]) { + stickyProp = stickyProps[i]; + break; + } + } + + testEl.remove(); + + return stickyProp; + }, + + /** + * Parses an attribute value, mostly a string. + * By default checks for negated values and returns `false´ if present. + * Negated values are: (native falsy) and negative strings like: + * `false` or `0`. + * @param value Attribute value which should be parsed. + * @param negatedCheck When set to false, won't check for negated values. + * @returns {boolean} + */ + parseAttributeBoolean: function(value, negatedCheck) { + return value === '' || !!value && (negatedCheck === false || value !== 'false' && value !== '0'); + }, + + hasComputedStyle: hasComputedStyle, + + /** + * Returns true if the parent form of the element has been submitted. + * @param element An AngularJS or HTML5 element. + * @returns {boolean} + */ + isParentFormSubmitted: function(element) { + var parent = $mdUtil.getClosest(element, 'form'); + var form = parent ? angular.element(parent).controller('form') : null; + + return form ? form.$submitted : false; + }, + + /** + * Animate the requested element's scrollTop to the requested scrollPosition with basic easing. + * @param {!Element} element The element to scroll. + * @param {number} scrollEnd The new/final scroll position. + * @param {number=} duration Duration of the scroll. Default is 1000ms. + */ + animateScrollTo: function(element, scrollEnd, duration) { + var scrollStart = element.scrollTop; + var scrollChange = scrollEnd - scrollStart; + var scrollingDown = scrollStart < scrollEnd; + var startTime = $mdUtil.now(); + + $$rAF(scrollChunk); + + function scrollChunk() { + var newPosition = calculateNewPosition(); + + element.scrollTop = newPosition; + + if (scrollingDown ? newPosition < scrollEnd : newPosition > scrollEnd) { + $$rAF(scrollChunk); + } + } + + function calculateNewPosition() { + var easeDuration = duration || 1000; + var currentTime = $mdUtil.now() - startTime; + + return ease(currentTime, scrollStart, scrollChange, easeDuration); + } + + function ease(currentTime, start, change, duration) { + // If the duration has passed (which can occur if our app loses focus due to $$rAF), jump + // straight to the proper position + if (currentTime > duration) { + return start + change; + } + + var ts = (currentTime /= duration) * currentTime; + var tc = ts * currentTime; + + return start + change * (-2 * tc + 3 * ts); + } + }, + + /** + * Provides an easy mechanism for removing duplicates from an array. + * + * var myArray = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]; + * + * $mdUtil.uniq(myArray) => [1, 2, 3, 4] + * + * @param {Array} array The array whose unique values should be returned. + * @returns {Array|void} A copy of the array containing only unique values. + */ + uniq: function(array) { + if (!array) { return; } + + return array.filter(function(value, index, self) { + return self.indexOf(value) === index; + }); + }, + + /** + * Gets the inner HTML content of the given HTMLElement. + * Only intended for use with SVG or Symbol elements in IE11. + * @param {Element} element + * @returns {string} the inner HTML of the element passed in + */ + getInnerHTML: function(element) { + // For SVG or Symbol elements, innerHTML returns `undefined` in IE. + // Reference: https://stackoverflow.com/q/28129956/633107 + // The XMLSerializer API is supported on IE11 and is the recommended workaround. + var serializer = new XMLSerializer(); + + return Array.prototype.map.call(element.childNodes, function (child) { + return serializer.serializeToString(child); + }).join(''); + }, + + /** + * Gets the outer HTML content of the given HTMLElement. + * Only intended for use with SVG or Symbol elements in IE11. + * @param {Element} element + * @returns {string} the outer HTML of the element passed in + */ + getOuterHTML: function(element) { + // For SVG or Symbol elements, outerHTML returns `undefined` in IE. + // Reference: https://stackoverflow.com/q/29888050/633107 + // The XMLSerializer API is supported on IE11 and is the recommended workaround. + var serializer = new XMLSerializer(); + return serializer.serializeToString(element); + }, + + /** + * Support: IE 9-11 only + * documentMode is an IE-only property + * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx + */ + msie: window.document.documentMode, + + getTouchAction: function() { + var testEl = document.createElement('div'); + var vendorPrefixes = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; + + for (var i = 0; i < vendorPrefixes.length; i++) { + var prefix = vendorPrefixes[i]; + var property = prefix ? prefix + 'TouchAction' : 'touchAction'; + if (angular.isDefined(testEl.style[property])) { + return property; + } + } + }, + + /** + * @param {Event} event the event to calculate the bubble path for + * @return {EventTarget[]} the set of nodes that this event could bubble up to + */ + getEventPath: function(event) { + var path = []; + var currentTarget = event.target; + while (currentTarget) { + path.push(currentTarget); + currentTarget = currentTarget.parentElement; + } + if (path.indexOf(window) === -1 && path.indexOf(document) === -1) + path.push(document); + if (path.indexOf(window) === -1) + path.push(window); + return path; + }, + + /** + * Gets the string the user has entered and removes Regex identifiers + * @param {string} term + * @returns {string} sanitized string + */ + sanitize: function(term) { + if (!term) return term; + return term.replace(/[\\^$*+?.()|{}[]/g, '\\$&'); + }, + + /********************************************************************************************** + * The following functions were sourced from + * https://github.com/angular/components/blob/3c37e4b1c1cb74a3d0a90d173240fc730d21d9d4/src/cdk/a11y/interactivity-checker/interactivity-checker.ts + **********************************************************************************************/ + + /** + * Gets whether an element is disabled. + * @param {HTMLElement} element Element to be checked. + * @returns {boolean} Whether the element is disabled. + */ + isDisabled: function(element) { + // This does not capture some cases, such as a non-form control with a disabled attribute or + // a form control inside of a disabled form, but should capture the most common cases. + return element.hasAttribute('disabled'); + }, + + /** + * Gets whether an element is visible for the purposes of interactivity. + * + * This will capture states like `display: none` and `visibility: hidden`, but not things like + * being clipped by an `overflow: hidden` parent or being outside the viewport. + * + * @param {HTMLElement} element + * @returns {boolean} Whether the element is visible. + */ + isVisible: function(element) { + return $mdUtil.hasGeometry(element) && getComputedStyle(element).visibility === 'visible'; + }, + + /** + * Gets whether an element can be reached via Tab key. + * Assumes that the element has already been checked with isFocusable. + * @param {HTMLElement} element Element to be checked. + * @returns {boolean} Whether the element is tabbable. + */ + isTabbable: function(element) { + var frameElement = $mdUtil.getFrameElement($mdUtil.getWindow(element)); + + if (frameElement) { + // Frame elements inherit their tabindex onto all child elements. + if ($mdUtil.getTabIndexValue(frameElement) === -1) { + return false; + } + + // Browsers disable tabbing to an element inside of an invisible frame. + if (!$mdUtil.isVisible(frameElement)) { + return false; + } + } + + var nodeName = element.nodeName.toLowerCase(); + var tabIndexValue = $mdUtil.getTabIndexValue(element); + + if (element.hasAttribute('contenteditable')) { + return tabIndexValue !== -1; + } + + if (nodeName === 'iframe' || nodeName === 'object') { + // The frame or object's content may be tabbable depending on the content, but it's + // not possibly to reliably detect the content of the frames. We always consider such + // elements as non-tabbable. + return false; + } + + // In iOS, the browser only considers some specific elements as tabbable. + if (isIos && !$mdUtil.isPotentiallyTabbableIOS(element)) { + return false; + } + + if (nodeName === 'audio') { + // Audio elements without controls enabled are never tabbable, regardless + // of the tabindex attribute explicitly being set. + if (!element.hasAttribute('controls')) { + return false; + } + // Audio elements with controls are by default tabbable unless the + // tabindex attribute is set to `-1` explicitly. + return tabIndexValue !== -1; + } + + if (nodeName === 'video') { + // For all video elements, if the tabindex attribute is set to `-1`, the video + // is not tabbable. Note: We cannot rely on the default `HTMLElement.tabIndex` + // property as that one is set to `-1` in Chrome, Edge and Safari v13.1. The + // tabindex attribute is the source of truth here. + if (tabIndexValue === -1) { + return false; + } + // If the tabindex is explicitly set, and not `-1` (as per check before), the + // video element is always tabbable (regardless of whether it has controls or not). + if (tabIndexValue !== null) { + return true; + } + // Otherwise (when no explicit tabindex is set), a video is only tabbable if it + // has controls enabled. Firefox is special as videos are always tabbable regardless + // of whether there are controls or not. + return isFirefox || element.hasAttribute('controls'); + } + + return element.tabIndex >= 0; + }, + + /** + * Gets whether an element can be focused by the user. + * @param {HTMLElement} element Element to be checked. + * @returns {boolean} Whether the element is focusable. + */ + isFocusable: function(element) { + // Perform checks in order of left to most expensive. + // Again, naive approach that does not capture many edge cases and browser quirks. + return $mdUtil.isPotentiallyFocusable(element) && !$mdUtil.isDisabled(element) && + $mdUtil.isVisible(element); + }, + + /** + * Gets whether an element is potentially focusable without taking current visible/disabled + * state into account. + * @param {HTMLElement} element + * @returns {boolean} + */ + isPotentiallyFocusable: function(element) { + // Inputs are potentially focusable *unless* they're type="hidden". + if ($mdUtil.isHiddenInput(element)) { + return false; + } + + return $mdUtil.isNativeFormElement(element) || + $mdUtil.isAnchorWithHref(element) || + element.hasAttribute('contenteditable') || + $mdUtil.hasValidTabIndex(element); + }, + + /** + * Checks whether the specified element is potentially tabbable on iOS. + * @param {HTMLElement} element + * @returns {boolean} + */ + isPotentiallyTabbableIOS: function(element) { + var nodeName = element.nodeName.toLowerCase(); + var inputType = nodeName === 'input' && element.type; + + return inputType === 'text' + || inputType === 'password' + || nodeName === 'select' + || nodeName === 'textarea'; + }, + + /** + * Returns the parsed tabindex from the element attributes instead of returning the + * evaluated tabindex from the browsers defaults. + * @param {HTMLElement} element + * @returns {null|number} + */ + getTabIndexValue: function(element) { + if (!$mdUtil.hasValidTabIndex(element)) { + return null; + } + + // See browser issue in Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 + var tabIndex = parseInt(element.getAttribute('tabindex') || '', 10); + + return isNaN(tabIndex) ? -1 : tabIndex; + }, + + /** + * Gets whether an element has a valid tabindex. + * @param {HTMLElement} element + * @returns {boolean} + */ + hasValidTabIndex: function(element) { + if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) { + return false; + } + + var tabIndex = element.getAttribute('tabindex'); + + // IE11 parses tabindex="" as the value "-32768" + if (tabIndex == '-32768') { + return false; + } + + return !!(tabIndex && !isNaN(parseInt(tabIndex, 10))); + }, + + /** + * Checks whether the specified element has any geometry / rectangles. + * @param {HTMLElement} element + * @returns {boolean} + */ + hasGeometry: function(element) { + // Use logic from jQuery to check for an invisible element. + // See https://github.com/jquery/jquery/blob/8969732518470a7f8e654d5bc5be0b0076cb0b87/src/css/hiddenVisibleSelectors.js#L9 + return !!(element.offsetWidth || element.offsetHeight || + (typeof element.getClientRects === 'function' && element.getClientRects().length)); + }, + + /** + * Returns the frame element from a window object. Since browsers like MS Edge throw errors if + * the frameElement property is being accessed from a different host address, this property + * should be accessed carefully. + * @param {Window} window + * @returns {null|HTMLElement} + */ + getFrameElement: function(window) { + try { + return window.frameElement; + } catch (error) { + return null; + } + }, + + /** + * Gets the parent window of a DOM node with regards of being inside of an iframe. + * @param {HTMLElement} node + * @returns {Window} + */ + getWindow: function(node) { + // ownerDocument is null if `node` itself *is* a document. + return node.ownerDocument && node.ownerDocument.defaultView || window; + }, + + /** + * Gets whether an element's + * @param {Node} element + * @returns {boolean} + */ + isNativeFormElement: function(element) { + var nodeName = element.nodeName.toLowerCase(); + return nodeName === 'input' || + nodeName === 'select' || + nodeName === 'button' || + nodeName === 'textarea'; + }, + + /** + * Gets whether an element is an ``. + * @param {HTMLElement} element + * @returns {boolean} + */ + isHiddenInput: function(element) { + return $mdUtil.isInputElement(element) && element.type == 'hidden'; + }, + + /** + * Gets whether an element is an anchor that has an href attribute. + * @param {HTMLElement} element + * @returns {boolean} + */ + isAnchorWithHref: function(element) { + return $mdUtil.isAnchorElement(element) && element.hasAttribute('href'); + }, + + /** + * Gets whether an element is an input element. + * @param {HTMLElement} element + * @returns {boolean} + */ + isInputElement: function(element) { + return element.nodeName.toLowerCase() == 'input'; + }, + + /** + * Gets whether an element is an anchor element. + * @param {HTMLElement} element + * @returns {boolean} + */ + isAnchorElement: function(element) { + return element.nodeName.toLowerCase() == 'a'; + }, + + /********************************************************************************************** + * The following two functions were sourced from + * https://github.com/angular/components/blob/3c37e4b1c1cb74a3d0a90d173240fc730d21d9d4/src/cdk/a11y/focus-trap/focus-trap.ts#L268-L311 + **********************************************************************************************/ + + /** + * Get the first tabbable element from a DOM subtree (inclusive). + * @param {HTMLElement} root + * @returns {HTMLElement|null} + */ + getFirstTabbableElement: function(root) { + if ($mdUtil.isFocusable(root) && $mdUtil.isTabbable(root)) { + return root; + } + + // Iterate in DOM order. Note that IE doesn't have `children` for SVG so we fall + // back to `childNodes` which includes text nodes, comments etc. + var children = root.children || root.childNodes; + + for (var i = 0; i < children.length; i++) { + var tabbableChild = children[i].nodeType === $document[0].ELEMENT_NODE ? + $mdUtil.getFirstTabbableElement(children[i]) : null; + + if (tabbableChild) { + return tabbableChild; + } + } + + return null; + }, + + /** + * Get the last tabbable element from a DOM subtree (inclusive). + * @param {HTMLElement} root + * @returns {HTMLElement|null} + */ + getLastTabbableElement: function(root) { + if ($mdUtil.isFocusable(root) && $mdUtil.isTabbable(root)) { + return root; + } + + // Iterate in reverse DOM order. + var children = root.children || root.childNodes; + + for (var i = children.length - 1; i >= 0; i--) { + var tabbableChild = children[i].nodeType === $document[0].ELEMENT_NODE ? + $mdUtil.getLastTabbableElement(children[i]) : null; + + if (tabbableChild) { + return tabbableChild; + } + } + + return null; + } + }; + + // Instantiate other namespace utility methods + + $mdUtil.dom.animator = $$mdAnimate($mdUtil); + + return $mdUtil; + + function getNode(el) { + return el[0] || el; + } +} + +/** + * Since removing jQuery from the demos, some code that uses `element.focus()` is broken. + * We need to add `element.focus()`, because it's testable unlike `element[0].focus`. + */ +angular.element.prototype.focus = angular.element.prototype.focus || function() { + if (this.length) { + this[0].focus(); + } + return this; +}; + +angular.element.prototype.blur = angular.element.prototype.blur || function() { + if (this.length) { + this[0].blur(); + } + return this; +}; + +})(); +(function(){ +"use strict"; + +// Polyfill angular < 1.4 (provide $animateCss) +angular + .module('material.core') + .factory('$$mdAnimate', ["$q", "$timeout", "$mdConstant", "$animateCss", function($q, $timeout, $mdConstant, $animateCss) { + // Since $$mdAnimate is injected into $mdUtil... use a wrapper function + // to subsequently inject $mdUtil as an argument to the AnimateDomUtils + return function($mdUtil) { + return AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss); + }; + }]); + +/** + * Factory function that requires special injections + */ +function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) { + var self; + return self = { + translate3d : function(target, from, to, options) { + return $animateCss(target, { + from: from, + to: to, + addClass: options.transitionInClass, + removeClass: options.transitionOutClass, + duration: options.duration + }) + .start() + .then(function() { + // Resolve with reverser function... + return reverseTranslate; + }); + + /** + * Specific reversal of the request translate animation above... + */ + function reverseTranslate (newFrom) { + return $animateCss(target, { + to: newFrom || from, + addClass: options.transitionOutClass, + removeClass: options.transitionInClass, + duration: options.duration + }).start(); + } + }, + + /** + * Listen for transitionEnd event (with optional timeout) + * Announce completion or failure via promise handlers + */ + waitTransitionEnd: function (element, opts) { + var TIMEOUT = 3000; // fallback is 3 secs + + return $q(function(resolve, reject){ + opts = opts || { }; + + // If there is no transition is found, resolve immediately + // + // NOTE: using $mdUtil.nextTick() causes delays/issues + if (noTransitionFound(opts.cachedTransitionStyles)) { + TIMEOUT = 0; + } + + var timer = $timeout(finished, opts.timeout || TIMEOUT); + element.on($mdConstant.CSS.TRANSITIONEND, finished); + + /** + * Upon timeout or transitionEnd, reject or resolve (respectively) this promise. + * NOTE: Make sure this transitionEnd didn't bubble up from a child + */ + function finished(ev) { + if (ev && ev.target !== element[0]) return; + + if (ev) $timeout.cancel(timer); + element.off($mdConstant.CSS.TRANSITIONEND, finished); + + // Never reject since ngAnimate may cause timeouts due missed transitionEnd events + resolve(); + } + + /** + * Checks whether or not there is a transition. + * + * @param styles The cached styles to use for the calculation. If null, getComputedStyle() + * will be used. + * + * @returns {boolean} True if there is no transition/duration; false otherwise. + */ + function noTransitionFound(styles) { + styles = styles || window.getComputedStyle(element[0]); + + return styles.transitionDuration === '0s' || + (!styles.transition && !styles.transitionProperty); + } + }); + }, + + calculateTransformValues: function (element, originator) { + var origin = originator.element; + var bounds = originator.bounds; + + if (origin || bounds) { + var originBnds = origin ? self.clientRect(origin) || currentBounds() : self.copyRect(bounds); + var dialogRect = self.copyRect(element[0].getBoundingClientRect()); + var dialogCenterPt = self.centerPointFor(dialogRect); + var originCenterPt = self.centerPointFor(originBnds); + + return { + centerX: originCenterPt.x - dialogCenterPt.x, + centerY: originCenterPt.y - dialogCenterPt.y, + scaleX: Math.round(100 * Math.min(0.5, originBnds.width / dialogRect.width)) / 100, + scaleY: Math.round(100 * Math.min(0.5, originBnds.height / dialogRect.height)) / 100 + }; + } + return {centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5}; + + /** + * This is a fallback if the origin information is no longer valid, then the + * origin bounds simply becomes the current bounds for the dialogContainer's parent. + * @returns {null|DOMRect} + */ + function currentBounds() { + var container = element ? element.parent() : null; + var parent = container ? container.parent() : null; + + return parent ? self.clientRect(parent) : null; + } + }, + + /** + * Calculate the zoom transform from dialog to origin. + * + * We use this to set the dialog position immediately; + * then the md-transition-in actually translates back to + * `translate3d(0,0,0) scale(1.0)`... + * + * NOTE: all values are rounded to the nearest integer + */ + calculateZoomToOrigin: function (element, originator) { + var zoomTemplate = "translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )"; + var buildZoom = angular.bind(null, $mdUtil.supplant, zoomTemplate); + + return buildZoom(self.calculateTransformValues(element, originator)); + }, + + /** + * Calculate the slide transform from panel to origin. + * NOTE: all values are rounded to the nearest integer + */ + calculateSlideToOrigin: function (element, originator) { + var slideTemplate = "translate3d( {centerX}px, {centerY}px, 0 )"; + var buildSlide = angular.bind(null, $mdUtil.supplant, slideTemplate); + + return buildSlide(self.calculateTransformValues(element, originator)); + }, + + /** + * Enhance raw values to represent valid css stylings... + */ + toCss : function(raw) { + var css = { }; + var lookups = 'left top right bottom width height x y min-width min-height max-width max-height'; + + angular.forEach(raw, function(value,key) { + if (angular.isUndefined(value)) return; + + if (lookups.indexOf(key) >= 0) { + css[key] = value + 'px'; + } else { + switch (key) { + case 'transition': + convertToVendor(key, $mdConstant.CSS.TRANSITION, value); + break; + case 'transform': + convertToVendor(key, $mdConstant.CSS.TRANSFORM, value); + break; + case 'transformOrigin': + convertToVendor(key, $mdConstant.CSS.TRANSFORM_ORIGIN, value); + break; + case 'font-size': + css['font-size'] = value; // font sizes aren't always in px + break; + } + } + }); + + return css; + + function convertToVendor(key, vendor, value) { + angular.forEach(vendor.split(' '), function (key) { + css[key] = value; + }); + } + }, + + /** + * Convert the translate CSS value to key/value pair(s). + * @param {string} transform + * @param {boolean=} addTransition + * @param {string=} transition + * @return {Object} object containing CSS translate key/value pair(s) + */ + toTransformCss: function (transform, addTransition, transition) { + var css = {}; + angular.forEach($mdConstant.CSS.TRANSFORM.split(' '), function (key) { + css[key] = transform; + }); + + if (addTransition) { + transition = transition || "all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important"; + css.transition = transition; + } + + return css; + }, + + /** + * Clone the Rect and calculate the height/width if needed. + * @param {DOMRect} source + * @param {DOMRect=} destination + * @returns {null|DOMRect} + */ + copyRect: function (source, destination) { + if (!source) return null; + + destination = destination || {}; + + angular.forEach('left top right bottom width height'.split(' '), function (key) { + destination[key] = Math.round(source[key]); + }); + + destination.width = destination.width || (destination.right - destination.left); + destination.height = destination.height || (destination.bottom - destination.top); + + return destination; + }, + + /** + * Calculate ClientRect of element; return null if hidden or zero size. + * @param {Element|string} element + * @returns {null|DOMRect} + */ + clientRect: function (element) { + var bounds = angular.element(element)[0].getBoundingClientRect(); + var isPositiveSizeClientRect = function (rect) { + return rect && (rect.width > 0) && (rect.height > 0); + }; + + // If the event origin element has zero size, it has probably been hidden. + return isPositiveSizeClientRect(bounds) ? self.copyRect(bounds) : null; + }, + + /** + * Calculate 'rounded' center point of Rect + * @param {DOMRect} targetRect + * @returns {{x: number, y: number}} + */ + centerPointFor: function (targetRect) { + return targetRect ? { + x: Math.round(targetRect.left + (targetRect.width / 2)), + y: Math.round(targetRect.top + (targetRect.height / 2)) + } : { x : 0, y : 0 }; + } + }; +} + + +})(); +(function(){ +"use strict"; + +if (angular.version.minor >= 4) { + angular.module('material.core.animate', []); +} else { +(function() { + "use strict"; + + var forEach = angular.forEach; + + var WEBKIT = angular.isDefined(document.documentElement.style.WebkitAppearance); + var TRANSITION_PROP = WEBKIT ? 'WebkitTransition' : 'transition'; + var ANIMATION_PROP = WEBKIT ? 'WebkitAnimation' : 'animation'; + var PREFIX = WEBKIT ? '-webkit-' : ''; + + var TRANSITION_EVENTS = (WEBKIT ? 'webkitTransitionEnd ' : '') + 'transitionend'; + var ANIMATION_EVENTS = (WEBKIT ? 'webkitAnimationEnd ' : '') + 'animationend'; + + var $$ForceReflowFactory = ['$document', function($document) { + return function() { + return $document[0].body.clientWidth + 1; + }; + }]; + + var $$rAFMutexFactory = ['$$rAF', function($$rAF) { + return function() { + var passed = false; + $$rAF(function() { + passed = true; + }); + return function(fn) { + passed ? fn() : $$rAF(fn); + }; + }; + }]; + + var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) { + var INITIAL_STATE = 0; + var DONE_PENDING_STATE = 1; + var DONE_COMPLETE_STATE = 2; + + function AnimateRunner(host) { + this.setHost(host); + + this._doneCallbacks = []; + this._runInAnimationFrame = $$rAFMutex(); + this._state = 0; + } + + AnimateRunner.prototype = { + setHost: function(host) { + this.host = host || {}; + }, + + done: function(fn) { + if (this._state === DONE_COMPLETE_STATE) { + fn(); + } else { + this._doneCallbacks.push(fn); + } + }, + + progress: angular.noop, + + getPromise: function() { + if (!this.promise) { + var self = this; + this.promise = $q(function(resolve, reject) { + self.done(function(status) { + status === false ? reject() : resolve(); + }); + }); + } + return this.promise; + }, + + then: function(resolveHandler, rejectHandler) { + return this.getPromise().then(resolveHandler, rejectHandler); + }, + + 'catch': function(handler) { + return this.getPromise()['catch'](handler); + }, + + 'finally': function(handler) { + return this.getPromise()['finally'](handler); + }, + + pause: function() { + if (this.host.pause) { + this.host.pause(); + } + }, + + resume: function() { + if (this.host.resume) { + this.host.resume(); + } + }, + + end: function() { + if (this.host.end) { + this.host.end(); + } + this._resolve(true); + }, + + cancel: function() { + if (this.host.cancel) { + this.host.cancel(); + } + this._resolve(false); + }, + + complete: function(response) { + var self = this; + if (self._state === INITIAL_STATE) { + self._state = DONE_PENDING_STATE; + self._runInAnimationFrame(function() { + self._resolve(response); + }); + } + }, + + _resolve: function(response) { + if (this._state !== DONE_COMPLETE_STATE) { + forEach(this._doneCallbacks, function(fn) { + fn(response); + }); + this._doneCallbacks.length = 0; + this._state = DONE_COMPLETE_STATE; + } + } + }; + + // Polyfill AnimateRunner.all which is used by input animations + AnimateRunner.all = function(runners, callback) { + var count = 0; + var status = true; + forEach(runners, function(runner) { + runner.done(onProgress); + }); + + function onProgress(response) { + status = status && response; + if (++count === runners.length) { + callback(status); + } + } + }; + + return AnimateRunner; + }]; + + angular + .module('material.core.animate', []) + .factory('$$forceReflow', $$ForceReflowFactory) + .factory('$$AnimateRunner', $$AnimateRunnerFactory) + .factory('$$rAFMutex', $$rAFMutexFactory) + .factory('$animateCss', ['$window', '$$rAF', '$$AnimateRunner', '$$forceReflow', '$$jqLite', '$timeout', '$animate', + function($window, $$rAF, $$AnimateRunner, $$forceReflow, $$jqLite, $timeout, $animate) { + + function init(element, options) { + + var temporaryStyles = []; + var node = getDomNode(element); + var areAnimationsAllowed = node && $animate.enabled(); + + var hasCompleteStyles = false; + var hasCompleteClasses = false; + + if (areAnimationsAllowed) { + if (options.transitionStyle) { + temporaryStyles.push([PREFIX + 'transition', options.transitionStyle]); + } + + if (options.keyframeStyle) { + temporaryStyles.push([PREFIX + 'animation', options.keyframeStyle]); + } + + if (options.delay) { + temporaryStyles.push([PREFIX + 'transition-delay', options.delay + 's']); + } + + if (options.duration) { + temporaryStyles.push([PREFIX + 'transition-duration', options.duration + 's']); + } + + hasCompleteStyles = options.keyframeStyle || + (options.to && (options.duration > 0 || options.transitionStyle)); + hasCompleteClasses = !!options.addClass || !!options.removeClass; + + blockTransition(element, true); + } + + var hasCompleteAnimation = areAnimationsAllowed && (hasCompleteStyles || hasCompleteClasses); + + applyAnimationFromStyles(element, options); + + var animationClosed = false; + var events, eventFn; + + return { + close: $window.close, + start: function() { + var runner = new $$AnimateRunner(); + waitUntilQuiet(function() { + blockTransition(element, false); + if (!hasCompleteAnimation) { + return close(); + } + + forEach(temporaryStyles, function(entry) { + var key = entry[0]; + var value = entry[1]; + node.style[camelCase(key)] = value; + }); + + applyClasses(element, options); + + var timings = computeTimings(element); + if (timings.duration === 0) { + return close(); + } + + var moreStyles = []; + + if (options.easing) { + if (timings.transitionDuration) { + moreStyles.push([PREFIX + 'transition-timing-function', options.easing]); + } + if (timings.animationDuration) { + moreStyles.push([PREFIX + 'animation-timing-function', options.easing]); + } + } + + if (options.delay && timings.animationDelay) { + moreStyles.push([PREFIX + 'animation-delay', options.delay + 's']); + } + + if (options.duration && timings.animationDuration) { + moreStyles.push([PREFIX + 'animation-duration', options.duration + 's']); + } + + forEach(moreStyles, function(entry) { + var key = entry[0]; + var value = entry[1]; + node.style[camelCase(key)] = value; + temporaryStyles.push(entry); + }); + + var maxDelay = timings.delay; + var maxDelayTime = maxDelay * 1000; + var maxDuration = timings.duration; + var maxDurationTime = maxDuration * 1000; + var startTime = Date.now(); + + events = []; + if (timings.transitionDuration) { + events.push(TRANSITION_EVENTS); + } + if (timings.animationDuration) { + events.push(ANIMATION_EVENTS); + } + events = events.join(' '); + eventFn = function(event) { + event.stopPropagation(); + var ev = event.originalEvent || event; + var timeStamp = ev.timeStamp || Date.now(); + var elapsedTime = parseFloat(ev.elapsedTime.toFixed(3)); + if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { + close(); + } + }; + element.on(events, eventFn); + + applyAnimationToStyles(element, options); + + $timeout(close, maxDelayTime + maxDurationTime * 1.5, false); + }); + + return runner; + + function close() { + if (animationClosed) return; + animationClosed = true; + + if (events && eventFn) { + element.off(events, eventFn); + } + applyClasses(element, options); + applyAnimationStyles(element, options); + forEach(temporaryStyles, function(entry) { + node.style[camelCase(entry[0])] = ''; + }); + runner.complete(true); + return runner; + } + } + }; + } + + function applyClasses(element, options) { + if (options.addClass) { + $$jqLite.addClass(element, options.addClass); + options.addClass = null; + } + if (options.removeClass) { + $$jqLite.removeClass(element, options.removeClass); + options.removeClass = null; + } + } + + function computeTimings(element) { + var node = getDomNode(element); + var cs = $window.getComputedStyle(node); + var tdr = parseMaxTime(cs[prop('transitionDuration')]); + var adr = parseMaxTime(cs[prop('animationDuration')]); + var tdy = parseMaxTime(cs[prop('transitionDelay')]); + var ady = parseMaxTime(cs[prop('animationDelay')]); + + adr *= (parseInt(cs[prop('animationIterationCount')], 10) || 1); + var duration = Math.max(adr, tdr); + var delay = Math.max(ady, tdy); + + return { + duration: duration, + delay: delay, + animationDuration: adr, + transitionDuration: tdr, + animationDelay: ady, + transitionDelay: tdy + }; + + function prop(key) { + return WEBKIT ? 'Webkit' + key.charAt(0).toUpperCase() + key.substr(1) + : key; + } + } + + function parseMaxTime(str) { + var maxValue = 0; + var values = (str || "").split(/\s*,\s*/); + forEach(values, function(value) { + // it's always safe to consider only second values and omit `ms` values since + // getComputedStyle will always handle the conversion for us + if (value.charAt(value.length - 1) == 's') { + value = value.substring(0, value.length - 1); + } + value = parseFloat(value) || 0; + maxValue = maxValue ? Math.max(value, maxValue) : value; + }); + return maxValue; + } + + var cancelLastRAFRequest; + var rafWaitQueue = []; + function waitUntilQuiet(callback) { + if (cancelLastRAFRequest) { + cancelLastRAFRequest(); // cancels the request + } + rafWaitQueue.push(callback); + cancelLastRAFRequest = $$rAF(function() { + cancelLastRAFRequest = null; + + // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable. + // PLEASE EXAMINE THE `$$forceReflow` service to understand why. + var pageWidth = $$forceReflow(); + + // we use a for loop to ensure that if the queue is changed + // during this looping then it will consider new requests + for (var i = 0; i < rafWaitQueue.length; i++) { + rafWaitQueue[i](pageWidth); + } + rafWaitQueue.length = 0; + }); + } + + function applyAnimationStyles(element, options) { + applyAnimationFromStyles(element, options); + applyAnimationToStyles(element, options); + } + + function applyAnimationFromStyles(element, options) { + if (options.from) { + element.css(options.from); + options.from = null; + } + } + + function applyAnimationToStyles(element, options) { + if (options.to) { + element.css(options.to); + options.to = null; + } + } + + function getDomNode(element) { + for (var i = 0; i < element.length; i++) { + if (element[i].nodeType === 1) return element[i]; + } + } + + function blockTransition(element, bool) { + var node = getDomNode(element); + var key = camelCase(PREFIX + 'transition-delay'); + node.style[key] = bool ? '-9999s' : ''; + } + + return init; + }]); + + /** + * Older browsers [FF31] expect camelCase + * property keys. + * e.g. + * animation-duration --> animationDuration + */ + function camelCase(str) { + return str.replace(/-[a-z]/g, function(str) { + return str.charAt(1).toUpperCase(); + }); + } + +})(); + +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.core.aria + * @description + * Aria Expectations for AngularJS Material components. + */ +MdAriaService.$inject = ["$$rAF", "$log", "$window", "$interpolate"]; +angular + .module('material.core') + .provider('$mdAria', MdAriaProvider); + +/** + * @ngdoc service + * @name $mdAriaProvider + * @module material.core.aria + * + * @description + * + * Modify options of the `$mdAria` service, which will be used by most of the AngularJS Material + * components. + * + * You are able to disable `$mdAria` warnings, by using the following markup. + * + * + * app.config(function($mdAriaProvider) { + * // Globally disables all ARIA warnings. + * $mdAriaProvider.disableWarnings(); + * }); + * + * + */ +function MdAriaProvider() { + + var config = { + /** Whether we should show ARIA warnings in the console if labels are missing on the element */ + showWarnings: true + }; + + return { + disableWarnings: disableWarnings, + $get: ["$$rAF", "$log", "$window", "$interpolate", function($$rAF, $log, $window, $interpolate) { + return MdAriaService.apply(config, arguments); + }] + }; + + /** + * @ngdoc method + * @name $mdAriaProvider#disableWarnings + * @description Disables all ARIA warnings generated by AngularJS Material. + */ + function disableWarnings() { + config.showWarnings = false; + } +} + +/* + * @ngInject + */ +function MdAriaService($$rAF, $log, $window, $interpolate) { + + // Load the showWarnings option from the current context and store it inside of a scope variable, + // because the context will be probably lost in some function calls. + var showWarnings = this.showWarnings; + + return { + expect: expect, + expectAsync: expectAsync, + expectWithText: expectWithText, + expectWithoutText: expectWithoutText, + getText: getText, + hasAriaLabel: hasAriaLabel, + parentHasAriaLabel: parentHasAriaLabel + }; + + /** + * Check if expected attribute has been specified on the target element or child + * @param {string|JQLite} element + * @param {string} attrName + * @param {string=} defaultValue What to set the attr to if no value is found + */ + function expect(element, attrName, defaultValue) { + + var node = angular.element(element)[0] || element; + + // if node exists and neither it nor its children have the attribute + if (node && + ((!node.hasAttribute(attrName) || + node.getAttribute(attrName).length === 0) && + !childHasAttribute(node, attrName))) { + + defaultValue = angular.isString(defaultValue) ? defaultValue.trim() : ''; + if (defaultValue.length) { + element.attr(attrName, defaultValue); + } else if (showWarnings) { + $log.warn('ARIA: Attribute "', attrName, '", required for accessibility, is missing on node:', node); + } + + } + } + + function expectAsync(element, attrName, defaultValueGetter) { + // Problem: when retrieving the element's contents synchronously to find the label, + // the text may not be defined yet in the case of a binding. + // There is a higher chance that a binding will be defined if we wait one frame. + $$rAF(function() { + expect(element, attrName, defaultValueGetter()); + }); + } + + function expectWithText(element, attrName) { + var content = getText(element) || ""; + var hasBinding = content.indexOf($interpolate.startSymbol()) > -1; + + if (hasBinding) { + expectAsync(element, attrName, function() { + return getText(element); + }); + } else { + expect(element, attrName, content); + } + } + + function expectWithoutText(element, attrName) { + var content = getText(element); + var hasBinding = content.indexOf($interpolate.startSymbol()) > -1; + + if (!hasBinding && !content) { + expect(element, attrName, content); + } + } + + /** + * @param {Element|JQLite} element + * @returns {string} + */ + function getText(element) { + element = element[0] || element; + var walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false); + var text = ''; + + var node; + while (node = walker.nextNode()) { + if (!isAriaHiddenNode(node)) { + text += node.textContent; + } + } + + return text.trim() || ''; + + /** + * @param {Node} node + * @returns {boolean} + */ + function isAriaHiddenNode(node) { + while (node.parentNode && (node = node.parentNode) !== element) { + if (node.getAttribute && node.getAttribute('aria-hidden') === 'true') { + return true; + } + } + } + } + + function childHasAttribute(node, attrName) { + var hasChildren = node.hasChildNodes(), + hasAttr = false; + + function isHidden(el) { + var style = el.currentStyle ? el.currentStyle : $window.getComputedStyle(el); + return (style.display === 'none'); + } + + if (hasChildren) { + var children = node.childNodes; + for (var i=0; i < children.length; i++) { + var child = children[i]; + if (child.nodeType === 1 && child.hasAttribute(attrName)) { + if (!isHidden(child)) { + hasAttr = true; + } + } + } + } + return hasAttr; + } + + /** + * Check if expected element has aria label attribute + * @param element + */ + function hasAriaLabel(element) { + var node = angular.element(element)[0] || element; + + /* Check if compatible node type (ie: not HTML Document node) */ + if (!node.hasAttribute) { + return false; + } + + /* Check label or description attributes */ + return node.hasAttribute('aria-label') || node.hasAttribute('aria-labelledby') || node.hasAttribute('aria-describedby'); + } + + /** + * Check if expected element's parent has aria label attribute and has valid role and tagName + * @param {string|JQLite|Node & ParentNode} element + * @param {number=} level Number of levels deep search should be performed + */ + function parentHasAriaLabel(element, level) { + level = level || 1; + var node = angular.element(element)[0] || element; + if (!node.parentNode) { + return false; + } + if (performCheck(node.parentNode)) { + return true; + } + level--; + if (level) { + return parentHasAriaLabel(node.parentNode, level); + } + return false; + + function performCheck(parentNode) { + if (!hasAriaLabel(parentNode)) { + return false; + } + /* Perform role block-list check */ + if (parentNode.hasAttribute('role')) { + switch (parentNode.getAttribute('role').toLowerCase()) { + case 'command': + case 'definition': + case 'directory': + case 'grid': + case 'list': + case 'listitem': + case 'log': + case 'marquee': + case 'menu': + case 'menubar': + case 'note': + case 'presentation': + case 'separator': + case 'scrollbar': + case 'status': + case 'tablist': + return false; + } + } + /* Perform tagName block-list check */ + switch (parentNode.tagName.toLowerCase()) { + case 'abbr': + case 'acronym': + case 'address': + case 'applet': + case 'audio': + case 'b': + case 'bdi': + case 'bdo': + case 'big': + case 'blockquote': + case 'br': + case 'canvas': + case 'caption': + case 'center': + case 'cite': + case 'code': + case 'col': + case 'data': + case 'dd': + case 'del': + case 'dfn': + case 'dir': + case 'div': + case 'dl': + case 'em': + case 'embed': + case 'fieldset': + case 'figcaption': + case 'font': + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + case 'hgroup': + case 'html': + case 'i': + case 'ins': + case 'isindex': + case 'kbd': + case 'keygen': + case 'label': + case 'legend': + case 'li': + case 'map': + case 'mark': + case 'menu': + case 'object': + case 'ol': + case 'output': + case 'pre': + case 'presentation': + case 'q': + case 'rt': + case 'ruby': + case 'samp': + case 'small': + case 'source': + case 'span': + case 'status': + case 'strike': + case 'strong': + case 'sub': + case 'sup': + case 'svg': + case 'tbody': + case 'td': + case 'th': + case 'thead': + case 'time': + case 'tr': + case 'track': + case 'tt': + case 'ul': + case 'var': + return false; + } + return true; + } + } +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.core.compiler + * @description + * AngularJS Material template and element compiler. + */ +angular + .module('material.core') + .provider('$mdCompiler', MdCompilerProvider); + +MdCompilerProvider.$inject = ['$compileProvider']; +function MdCompilerProvider() { + + this.$get = ["$q", "$templateRequest", "$injector", "$compile", "$controller", + function($q, $templateRequest, $injector, $compile, $controller) { + return new MdCompilerService($q, $templateRequest, $injector, $compile, $controller); + }]; + + /** + * @ngdoc service + * @name $mdCompiler + * @module material.core.compiler + * @description + * The $mdCompiler service is an abstraction of AngularJS's compiler, that allows developers + * to compile an element with options like in a Directive Definition Object. + * + * > The compiler powers a lot of components inside of AngularJS Material. + * > Like the `$mdPanel` or `$mdDialog` services. + * + * @usage + * + * Basic Usage with a template + * + * + * $mdCompiler.compile({ + * templateUrl: 'modal.html', + * controller: 'ModalCtrl', + * locals: { + * modal: myModalInstance; + * } + * }).then(function (compileData) { + * compileData.element; // Compiled DOM element + * compileData.link(myScope); // Instantiate controller and link element to scope. + * }); + * + * + * Example with a content element + * + * + * + * // Create a virtual element and link it manually. + * // The compiler doesn't need to recompile the element each time. + * var myElement = $compile('Test')(myScope); + * + * $mdCompiler.compile({ + * contentElement: myElement + * }).then(function (compileData) { + * compileData.element // Content Element (same as above) + * compileData.link // This does nothing when using a contentElement. + * }); + * + * + * > Content Element is a significant performance improvement when the developer already knows + * > that the compiled element will be always the same and the scope will not change either. + * + * The `contentElement` option also supports DOM elements which will be temporary removed and + * restored at its old position. + * + * + * var domElement = document.querySelector('#myElement'); + * + * $mdCompiler.compile({ + * contentElement: myElement + * }).then(function (compileData) { + * compileData.element // Content Element (same as above) + * compileData.link // This does nothing when using a contentElement. + * }); + * + * + * The `$mdCompiler` can also query for the element in the DOM itself. + * + * + * $mdCompiler.compile({ + * contentElement: '#myElement' + * }).then(function (compileData) { + * compileData.element // Content Element (same as above) + * compileData.link // This does nothing when using a contentElement. + * }); + * + * + */ + function MdCompilerService($q, $templateRequest, $injector, $compile, $controller) { + + /** + * @private @const + * @type {!IQService} + */ + this.$q = $q; + + /** + * @private @const + * @type {!ITemplateRequestService} + */ + this.$templateRequest = $templateRequest; + + /** + * @private @const + * @type {!IInjectorService} + */ + this.$injector = $injector; + + /** + * @private @const + * @type{!ICompileService} + */ + this.$compile = $compile; + + /** + * @private @const + * @type {!IControllerService} + */ + this.$controller = $controller; + } + + /** + * @ngdoc method + * @name $mdCompiler#compile + * @description + * + * A method to compile a HTML template with the AngularJS compiler. + * The `$mdCompiler` is wrapper around the AngularJS compiler and provides extra functionality + * like controller instantiation or async resolves. + * + * @param {!Object} options An options object, with the following properties: + * + * - `controller` - `{string|function}` Controller fn that should be associated with + * newly created scope or the name of a registered controller if passed as a string. + * - `controllerAs` - `{string=}` A controller alias name. If present the controller will be + * published to scope under the `controllerAs` name. + * - `contentElement` - `{string|Element}`: Instead of using a template, which will be + * compiled each time, you can also use a DOM element.
    + * - `template` - `{string=}` An html template as a string. + * - `templateUrl` - `{string=}` A path to an html template. + * - `transformTemplate` - `{function(template)=}` A function which transforms the template after + * it is loaded. It will be given the template string as a parameter, and should + * return a a new string representing the transformed template. + * - `resolve` - `{Object.=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, the compiler + * will wait for them all to be resolved, or if one is rejected before the controller is + * instantiated `compile()` will fail.. + * * `key` - `{string}`: a name of a dependency to be injected into the controller. + * * `factory` - `{string|function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is injected and the return value is treated as the + * dependency. If the result is a promise, it is resolved before its value is + * injected into the controller. + * + * @returns {Q.Promise<{element: JQLite, link: Function, locals: Object, cleanup: any, + * controller: Object=}>} promise A promise, which will be resolved with a `compileData` object. + * `compileData` has the following properties: + * + * - `element` - `{JQLite}`: an uncompiled element matching the provided template. + * - `link` - `{function(scope)}`: A link function, which, when called, will compile + * the element and instantiate the provided controller (if given). + * - `locals` - `{Object}`: The locals which will be passed into the controller once `link` is + * called. If `bindToController` is true, they will be copied to the ctrl instead + */ + MdCompilerService.prototype.compile = function(options) { + if (options.contentElement) { + return this._prepareContentElement(options); + } else { + return this._compileTemplate(options); + } + }; + + /** + * Instead of compiling any template, the compiler just fetches an existing HTML element from the + * DOM and provides a restore function to put the element back it old DOM position. + * @param {!Object} options Options to be used for the compiler. + * @returns {Q.Promise<{element: JQLite, link: Function, locals: Object, cleanup: any}>} + */ + MdCompilerService.prototype._prepareContentElement = function(options) { + + var contentElement = this._fetchContentElement(options); + + return this.$q.resolve({ + element: contentElement.element, + cleanup: contentElement.restore, + locals: {}, + link: function() { + return contentElement.element; + } + }); + + }; + + /** + * Compiles a template by considering all options and waiting for all resolves to be ready. + * @param {!Object} options Compile options + * @returns {!Q.Promise<{element: JQLite, link: Function, locals: Object, cleanup: any}>} Compile + * data with link function. + */ + MdCompilerService.prototype._compileTemplate = function(options) { + + var self = this; + var templateUrl = options.templateUrl; + var template = options.template || ''; + var resolve = angular.extend({}, options.resolve); + var locals = angular.extend({}, options.locals); + var transformTemplate = options.transformTemplate || angular.identity; + + // Take resolve values and invoke them. + // Resolves can either be a string (value: 'MyRegisteredAngularConst'), + // or an invokable 'factory' of sorts: (value: function ValueGetter($dependency) {}) + angular.forEach(resolve, function(value, key) { + if (angular.isString(value)) { + resolve[key] = self.$injector.get(value); + } else { + resolve[key] = self.$injector.invoke(value); + } + }); + + // Add the locals, which are just straight values to inject + // eg locals: { three: 3 }, will inject three into the controller + angular.extend(resolve, locals); + + if (templateUrl) { + resolve.$$ngTemplate = this.$templateRequest(templateUrl); + } else { + resolve.$$ngTemplate = this.$q.when(template); + } + + + // Wait for all the resolves to finish if they are promises + return this.$q.all(resolve).then(function(locals) { + + var template = transformTemplate(locals.$$ngTemplate, options); + var element = options.element || angular.element('
    ').html(template.trim()).contents(); + + return self._compileElement(locals, element, options); + }); + + }; + + /** + * Method to compile an element with the given options. + * @param {!Object} locals Locals to be injected to the controller if present + * @param {!JQLite} element Element to be compiled and linked + * @param {!Object} options Options to be used for linking. + * @returns {!{element: JQLite, link: Function, locals: Object, cleanup: any, controller: Object}} Compile data with link function. + */ + MdCompilerService.prototype._compileElement = function(locals, element, options) { + var self = this; + var ngLinkFn = this.$compile(element); + + var compileData = { + element: element, + cleanup: element.remove.bind(element), + locals: locals, + link: linkFn + }; + + function linkFn(scope) { + locals.$scope = scope; + + // Instantiate controller if the developer provided one. + if (options.controller) { + + var injectLocals = angular.extend({}, locals, { + $element: element + }); + + // Create the specified controller instance. + var ctrl = self._createController(options, injectLocals, locals); + + // Registering extra $destroy listeners should be avoided. + // Only register the listener if the controller implements a $onDestroy hook. + if (angular.isFunction(ctrl.$onDestroy)) { + scope.$on('$destroy', function() { + // Call the $onDestroy hook if it's present on the controller. + angular.isFunction(ctrl.$onDestroy) && ctrl.$onDestroy(); + }); + } + + // Unique identifier for AngularJS Route ngView controllers. + element.data('$ngControllerController', ctrl); + element.children().data('$ngControllerController', ctrl); + + // Expose the instantiated controller to the compile data + compileData.controller = ctrl; + } + + // Invoke the AngularJS $compile link function. + return ngLinkFn(scope); + } + + return compileData; + + }; + + /** + * Creates and instantiates a new controller with the specified options. + * @param {!Object} options Options that include the controller function or string. + * @param {!Object} injectLocals Locals to to be provided in the controller DI. + * @param {!Object} locals Locals to be injected to the controller. + * @returns {!Object} Created controller instance. + */ + MdCompilerService.prototype._createController = function(options, injectLocals, locals) { + var ctrl = this.$controller(options.controller, injectLocals); + + if (options.bindToController) { + angular.extend(ctrl, locals); + } + + if (options.controllerAs) { + injectLocals.$scope[options.controllerAs] = ctrl; + } + + // Call the $onInit hook if it's present on the controller. + angular.isFunction(ctrl.$onInit) && ctrl.$onInit(); + + return ctrl; + }; + + /** + * Fetches an element removing it from the DOM and using it temporary for the compiler. + * Elements which were fetched will be restored after use. + * @param {!Object} options Options to be used for the compilation. + * @returns {{element: !JQLite, restore: !function}} + */ + MdCompilerService.prototype._fetchContentElement = function(options) { + var contentEl = options.contentElement; + var restoreFn; + + if (angular.isString(contentEl)) { + contentEl = document.querySelector(contentEl); + restoreFn = createRestoreFn(contentEl); + } else { + contentEl = contentEl[0] || contentEl; + + // When the element is visible in the DOM, then we restore it at close of the dialog. + // Otherwise it will be removed from the DOM after close. + if (document.contains(contentEl)) { + restoreFn = createRestoreFn(contentEl); + } else { + restoreFn = function() { + if (contentEl.parentNode) { + contentEl.parentNode.removeChild(contentEl); + } + }; + } + } + + return { + element: angular.element(contentEl), + restore: restoreFn + }; + + function createRestoreFn(element) { + var parent = element.parentNode; + var nextSibling = element.nextElementSibling; + + return function() { + if (!nextSibling) { + // When the element didn't had any sibling, then it can be simply appended to the + // parent, because it plays no role, which index it had before. + parent.appendChild(element); + } else { + // When the element had a sibling, which marks the previous position of the element + // in the DOM, we insert it correctly before the sibling, to have the same index as + // before. + parent.insertBefore(element, nextSibling); + } + }; + } + }; +} + + +})(); +(function(){ +"use strict"; + + +MdGesture.$inject = ["$$MdGestureHandler", "$$rAF", "$timeout", "$mdUtil"]; +attachToDocument.$inject = ["$mdGesture", "$$MdGestureHandler", "$mdUtil"];var HANDLERS = {}; + +/** + * The state of the current 'pointer'. The pointer represents the state of the current touch. + * It contains normalized x and y coordinates from DOM events, + * as well as other information abstracted from the DOM. + */ +var pointer, lastPointer, maxClickDistance = 6; +var forceSkipClickHijack = false, disableAllGestures = false; + +/** + * The position of the most recent click if that click was on a label element. + * @type {{x: number, y: number}|null} + */ +var lastLabelClickPos = null; + +/** + * Used to attach event listeners once when multiple ng-apps are running. + * @type {boolean} + */ +var isInitialized = false; + +/** + * @ngdoc module + * @name material.core.gestures + * @description + * AngularJS Material Gesture handling for touch devices. + * This module replaced the usage of the HammerJS library. + */ +angular + .module('material.core.gestures', []) + .provider('$mdGesture', MdGestureProvider) + .factory('$$MdGestureHandler', MdGestureHandler) + .run(attachToDocument); + +/** + * @ngdoc service + * @name $mdGestureProvider + * @module material.core.gestures + * + * @description + * In some scenarios on mobile devices (without jQuery), the click events should NOT be hijacked. + * `$mdGestureProvider` is used to configure the Gesture module to ignore or skip click hijacking + * on mobile devices. + * + * You can also change the max click distance, `6px` by default, if you have issues on some touch + * screens. + * + * + * app.config(function($mdGestureProvider) { + * + * // For mobile devices without jQuery loaded, do not + * // intercept click events during the capture phase. + * $mdGestureProvider.skipClickHijack(); + * + * // If hijacking clicks, you may want to change the default click distance + * $mdGestureProvider.setMaxClickDistance(12); + * }); + * + * + */ +function MdGestureProvider() { } + +MdGestureProvider.prototype = { + + /** + * @ngdoc method + * @name $mdGestureProvider#disableAll + * + * @description + * Disable all gesture detection. This can be beneficial to application performance + * and memory usage. + */ + disableAll: function () { + disableAllGestures = true; + }, + + // Publish access to setter to configure a variable BEFORE the + // $mdGesture service is instantiated... + /** + * @ngdoc method + * @name $mdGestureProvider#skipClickHijack + * + * @description + * Tell the AngularJS Material Gesture module to skip (or ignore) click hijacking on mobile devices. + */ + skipClickHijack: function() { + return forceSkipClickHijack = true; + }, + + /** + * @ngdoc method + * @name $mdGestureProvider#setMaxClickDistance + * @param clickDistance {string} Distance in pixels. I.e. `12px`. + * @description + * Set the max distance from the origin of the touch event to trigger touch handlers. + */ + setMaxClickDistance: function(clickDistance) { + maxClickDistance = parseInt(clickDistance); + }, + + /** + * $get is used to build an instance of $mdGesture + * @ngInject + */ + $get : ["$$MdGestureHandler", "$$rAF", "$timeout", "$mdUtil", function($$MdGestureHandler, $$rAF, $timeout, $mdUtil) { + return new MdGesture($$MdGestureHandler, $$rAF, $timeout, $mdUtil); + }] +}; + + + +/** + * MdGesture factory construction function + * @ngInject + */ +function MdGesture($$MdGestureHandler, $$rAF, $timeout, $mdUtil) { + var touchActionProperty = $mdUtil.getTouchAction(); + var hasJQuery = (typeof __webpack_provided_window_dot_jQuery !== 'undefined') && (angular.element === __webpack_provided_window_dot_jQuery); + + var self = { + handler: addHandler, + register: register, + isAndroid: $mdUtil.isAndroid, + isIos: $mdUtil.isIos, + // On mobile w/out jQuery, we normally intercept clicks. Should we skip that? + isHijackingClicks: ($mdUtil.isIos || $mdUtil.isAndroid) && !hasJQuery && !forceSkipClickHijack + }; + + if (self.isHijackingClicks) { + self.handler('click', { + options: { + maxDistance: maxClickDistance + }, + onEnd: checkDistanceAndEmit('click') + }); + + self.handler('focus', { + options: { + maxDistance: maxClickDistance + }, + onEnd: function(ev, pointer) { + if (pointer.distance < this.state.options.maxDistance && canFocus(ev.target)) { + this.dispatchEvent(ev, 'focus', pointer); + ev.target.focus(); + } + } + }); + + self.handler('mouseup', { + options: { + maxDistance: maxClickDistance + }, + onEnd: checkDistanceAndEmit('mouseup') + }); + + self.handler('mousedown', { + onStart: function(ev) { + this.dispatchEvent(ev, 'mousedown'); + } + }); + } + + function checkDistanceAndEmit(eventName) { + return function(ev, pointer) { + if (pointer.distance < this.state.options.maxDistance) { + this.dispatchEvent(ev, eventName, pointer); + } + }; + } + + /** + * Register an element to listen for a handler. + * This allows an element to override the default options for a handler. + * Additionally, some handlers like drag and hold only dispatch events if + * the domEvent happens inside an element that's registered to listen for these events. + * + * @see GestureHandler for how overriding of default options works. + * @example $mdGesture.register(myElement, 'drag', { minDistance: 20, horizontal: false }) + */ + function register(element, handlerName, options) { + var handler = HANDLERS[handlerName.replace(/^\$md./, '')]; + if (!handler) { + throw new Error('Failed to register element with handler ' + handlerName + '. ' + + 'Available handlers: ' + Object.keys(HANDLERS).join(', ')); + } + return handler.registerElement(element, options); + } + + /* + * add a handler to $mdGesture. see below. + */ + function addHandler(name, definition) { + var handler = new $$MdGestureHandler(name); + angular.extend(handler, definition); + HANDLERS[name] = handler; + + return self; + } + + /** + * Register handlers. These listen to touch/start/move events, interpret them, + * and dispatch gesture events depending on options & conditions. These are all + * instances of GestureHandler. + * @see GestureHandler + */ + return self + /* + * The press handler dispatches an event on touchdown/touchend. + * It's a simple abstraction of touch/mouse/pointer start and end. + */ + .handler('press', { + onStart: function (ev, pointer) { + this.dispatchEvent(ev, '$md.pressdown'); + }, + onEnd: function (ev, pointer) { + this.dispatchEvent(ev, '$md.pressup'); + } + }) + + /* + * The hold handler dispatches an event if the user keeps their finger within + * the same area for ms. + * The hold handler will only run if a parent of the touch target is registered + * to listen for hold events through $mdGesture.register() + */ + .handler('hold', { + options: { + maxDistance: 6, + delay: 500 + }, + onCancel: function () { + $timeout.cancel(this.state.timeout); + }, + onStart: function (ev, pointer) { + // For hold, require a parent to be registered with $mdGesture.register() + // Because we prevent scroll events, this is necessary. + if (!this.state.registeredParent) return this.cancel(); + + this.state.pos = {x: pointer.x, y: pointer.y}; + this.state.timeout = $timeout(angular.bind(this, function holdDelayFn() { + this.dispatchEvent(ev, '$md.hold'); + this.cancel(); // we're done! + }), this.state.options.delay, false); + }, + onMove: function (ev, pointer) { + // Don't scroll while waiting for hold. + // If we don't preventDefault touchmove events here, Android will assume we don't + // want to listen to anymore touch events. It will start scrolling and stop sending + // touchmove events. + if (!touchActionProperty && ev.type === 'touchmove') ev.preventDefault(); + + // If the user moves greater than pixels, stop the hold timer + // set in onStart + var dx = this.state.pos.x - pointer.x; + var dy = this.state.pos.y - pointer.y; + if (Math.sqrt(dx * dx + dy * dy) > this.options.maxDistance) { + this.cancel(); + } + }, + onEnd: function () { + this.onCancel(); + } + }) + + /* + * The drag handler dispatches a drag event if the user holds and moves his finger greater than + * px in the x or y direction, depending on options.horizontal. + * The drag will be cancelled if the user moves his finger greater than * in + * the perpendicular direction. Eg if the drag is horizontal and the user moves his finger * + * pixels vertically, this handler won't consider the move part of a drag. + */ + .handler('drag', { + options: { + minDistance: 6, + horizontal: true, + cancelMultiplier: 1.5 + }, + /** + * @param {angular.JQLite} element where touch action styles need to be adjusted + * @param {{horizontal: boolean}=} options object whose horizontal property can specify to + * apply 'pan-y' or 'pan-x' touch actions. + */ + onSetup: function(element, options) { + if (touchActionProperty) { + // We check for horizontal to be false, because otherwise we would overwrite the default opts. + this.oldTouchAction = element[0].style[touchActionProperty]; + element[0].style[touchActionProperty] = options.horizontal ? 'pan-y' : 'pan-x'; + } + }, + /** + * @param {angular.JQLite} element where styles need to be cleaned up + */ + onCleanup: function(element) { + if (this.oldTouchAction) { + element[0].style[touchActionProperty] = this.oldTouchAction; + } else { + element[0].style[touchActionProperty] = null; + } + }, + onStart: function (ev) { + // For drag, require a parent to be registered with $mdGesture.register() + if (!this.state.registeredParent) this.cancel(); + }, + onMove: function (ev, pointer) { + var shouldStartDrag, shouldCancel; + // Don't scroll while deciding if this touchmove qualifies as a drag event. + // If we don't preventDefault touchmove events here, Android will assume we don't + // want to listen to anymore touch events. It will start scrolling and stop sending + // touchmove events. + if (!touchActionProperty && ev.type === 'touchmove') ev.preventDefault(); + + if (!this.state.dragPointer) { + if (this.state.options.horizontal) { + shouldStartDrag = Math.abs(pointer.distanceX) > this.state.options.minDistance; + shouldCancel = Math.abs(pointer.distanceY) > this.state.options.minDistance * this.state.options.cancelMultiplier; + } else { + shouldStartDrag = Math.abs(pointer.distanceY) > this.state.options.minDistance; + shouldCancel = Math.abs(pointer.distanceX) > this.state.options.minDistance * this.state.options.cancelMultiplier; + } + + if (shouldStartDrag) { + // Create a new pointer representing this drag, starting at this point where the drag started. + this.state.dragPointer = makeStartPointer(ev); + updatePointerState(ev, this.state.dragPointer); + this.dispatchEvent(ev, '$md.dragstart', this.state.dragPointer); + + } else if (shouldCancel) { + this.cancel(); + } + } else { + this.dispatchDragMove(ev); + } + }, + // Only dispatch dragmove events every frame; any more is unnecessary + dispatchDragMove: $$rAF.throttle(function (ev) { + // Make sure the drag didn't stop while waiting for the next frame + if (this.state.isRunning) { + updatePointerState(ev, this.state.dragPointer); + this.dispatchEvent(ev, '$md.drag', this.state.dragPointer); + } + }), + onEnd: function (ev, pointer) { + if (this.state.dragPointer) { + updatePointerState(ev, this.state.dragPointer); + this.dispatchEvent(ev, '$md.dragend', this.state.dragPointer); + } + } + }) + + /* + * The swipe handler will dispatch a swipe event if, on the end of a touch, + * the velocity and distance were high enough. + */ + .handler('swipe', { + options: { + minVelocity: 0.65, + minDistance: 10 + }, + onEnd: function (ev, pointer) { + var eventType; + + if (Math.abs(pointer.velocityX) > this.state.options.minVelocity && + Math.abs(pointer.distanceX) > this.state.options.minDistance) { + eventType = pointer.directionX == 'left' ? '$md.swipeleft' : '$md.swiperight'; + this.dispatchEvent(ev, eventType); + } + else if (Math.abs(pointer.velocityY) > this.state.options.minVelocity && + Math.abs(pointer.distanceY) > this.state.options.minDistance) { + eventType = pointer.directionY == 'up' ? '$md.swipeup' : '$md.swipedown'; + this.dispatchEvent(ev, eventType); + } + } + }); +} + +/** + * MdGestureHandler + * A GestureHandler is an object which is able to dispatch custom dom events + * based on native dom {touch,pointer,mouse}{start,move,end} events. + * + * A gesture will manage its lifecycle through the start,move,end, and cancel + * functions, which are called by native dom events. + * + * A gesture has the concept of 'options' (eg. a swipe's required velocity), which can be + * overridden by elements registering through $mdGesture.register(). + */ +function GestureHandler (name) { + this.name = name; + this.state = {}; +} + +function MdGestureHandler() { + var hasJQuery = (typeof __webpack_provided_window_dot_jQuery !== 'undefined') && (angular.element === __webpack_provided_window_dot_jQuery); + + GestureHandler.prototype = { + options: {}, + // jQuery listeners don't work with custom DOMEvents, so we have to dispatch events + // differently when jQuery is loaded + dispatchEvent: hasJQuery ? jQueryDispatchEvent : nativeDispatchEvent, + + // These are overridden by the registered handler + onSetup: angular.noop, + onCleanup: angular.noop, + onStart: angular.noop, + onMove: angular.noop, + onEnd: angular.noop, + onCancel: angular.noop, + + // onStart sets up a new state for the handler, which includes options from the + // nearest registered parent element of ev.target. + start: function (ev, pointer) { + if (this.state.isRunning) return; + var parentTarget = this.getNearestParent(ev.target); + // Get the options from the nearest registered parent + var parentTargetOptions = parentTarget && parentTarget.$mdGesture[this.name] || {}; + + this.state = { + isRunning: true, + // Override the default options with the nearest registered parent's options + options: angular.extend({}, this.options, parentTargetOptions), + // Pass in the registered parent node to the state so the onStart listener can use + registeredParent: parentTarget + }; + this.onStart(ev, pointer); + }, + move: function (ev, pointer) { + if (!this.state.isRunning) return; + this.onMove(ev, pointer); + }, + end: function (ev, pointer) { + if (!this.state.isRunning) return; + this.state.isRunning = false; + this.onEnd(ev, pointer); + }, + cancel: function (ev, pointer) { + this.onCancel(ev, pointer); + this.state = {}; + }, + + // Find and return the nearest parent element that has been registered to + // listen for this handler via $mdGesture.register(element, 'handlerName'). + getNearestParent: function (node) { + var current = node; + while (current) { + if ((current.$mdGesture || {})[this.name]) { + return current; + } + current = current.parentNode; + } + return null; + }, + + // Called from $mdGesture.register when an element registers itself with a handler. + // Store the options the user gave on the DOMElement itself. These options will + // be retrieved with getNearestParent when the handler starts. + registerElement: function (element, options) { + var self = this; + element[0].$mdGesture = element[0].$mdGesture || {}; + element[0].$mdGesture[this.name] = options || {}; + element.on('$destroy', onDestroy); + + self.onSetup(element, options || {}); + + return onDestroy; + + function onDestroy() { + delete element[0].$mdGesture[self.name]; + element.off('$destroy', onDestroy); + + self.onCleanup(element, options || {}); + } + } + }; + + return GestureHandler; + + /** + * Dispatch an event with jQuery + * TODO: Make sure this sends bubbling events + * + * @param srcEvent the original DOM touch event that started this. + * @param eventType the name of the custom event to send (eg 'click' or '$md.drag') + * @param eventPointer the pointer object that matches this event. + */ + function jQueryDispatchEvent(srcEvent, eventType, eventPointer) { + eventPointer = eventPointer || pointer; + var eventObj = new angular.element.Event(eventType); + + eventObj.$material = true; + eventObj.pointer = eventPointer; + eventObj.srcEvent = srcEvent; + + angular.extend(eventObj, { + clientX: eventPointer.x, + clientY: eventPointer.y, + screenX: eventPointer.x, + screenY: eventPointer.y, + pageX: eventPointer.x, + pageY: eventPointer.y, + ctrlKey: srcEvent.ctrlKey, + altKey: srcEvent.altKey, + shiftKey: srcEvent.shiftKey, + metaKey: srcEvent.metaKey + }); + angular.element(eventPointer.target).trigger(eventObj); + } + + /** + * NOTE: nativeDispatchEvent is very performance sensitive. + * @param srcEvent the original DOM touch event that started this. + * @param eventType the name of the custom event to send (eg 'click' or '$md.drag') + * @param eventPointer the pointer object that matches this event. + */ + function nativeDispatchEvent(srcEvent, eventType, eventPointer) { + eventPointer = eventPointer || pointer; + var eventObj; + + if (eventType === 'click' || eventType === 'mouseup' || eventType === 'mousedown') { + if (typeof window.MouseEvent === "function") { + eventObj = new MouseEvent(eventType, { + bubbles: true, + cancelable: true, + screenX: Number(srcEvent.screenX), + screenY: Number(srcEvent.screenY), + clientX: Number(eventPointer.x), + clientY: Number(eventPointer.y), + ctrlKey: srcEvent.ctrlKey, + altKey: srcEvent.altKey, + shiftKey: srcEvent.shiftKey, + metaKey: srcEvent.metaKey, + button: srcEvent.button, + buttons: srcEvent.buttons, + relatedTarget: srcEvent.relatedTarget || null + }); + } else { + eventObj = document.createEvent('MouseEvents'); + // This has been deprecated + // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent + eventObj.initMouseEvent( + eventType, true, true, window, srcEvent.detail, + eventPointer.x, eventPointer.y, eventPointer.x, eventPointer.y, + srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey, + srcEvent.button, srcEvent.relatedTarget || null + ); + } + } else { + if (typeof window.CustomEvent === "function") { + eventObj = new CustomEvent(eventType, { + bubbles: true, + cancelable: true, + detail: {} + }); + } else { + // This has been deprecated + // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent + eventObj = document.createEvent('CustomEvent'); + eventObj.initCustomEvent(eventType, true, true, {}); + } + } + eventObj.$material = true; + eventObj.pointer = eventPointer; + eventObj.srcEvent = srcEvent; + eventPointer.target.dispatchEvent(eventObj); + } +} + +/** + * Attach Gestures: hook document and check shouldHijack clicks + * @ngInject + */ +function attachToDocument($mdGesture, $$MdGestureHandler, $mdUtil) { + if (disableAllGestures) { + return; + } + + if (!isInitialized && $mdGesture.isHijackingClicks) { + /* + * If hijack clicks is true, we preventDefault any click that wasn't + * sent by AngularJS Material. This is because on older Android & iOS, a false, or 'ghost', + * click event will be sent ~400ms after a touchend event happens. + * The only way to know if this click is real is to prevent any normal + * click events, and add a flag to events sent by material so we know not to prevent those. + * + * Two exceptions to click events that should be prevented are: + * - click events sent by the keyboard (eg form submit) + * - events that originate from an Ionic app + */ + document.addEventListener('click' , clickHijacker , true); + document.addEventListener('mouseup' , mouseInputHijacker, true); + document.addEventListener('mousedown', mouseInputHijacker, true); + document.addEventListener('focus' , mouseInputHijacker, true); + + isInitialized = true; + } + + function mouseInputHijacker(ev) { + var isKeyClick = !ev.clientX && !ev.clientY; + + if ( + !isKeyClick && + !ev.$material && + !ev.isIonicTap && + !isInputEventFromLabelClick(ev) && + (ev.type !== 'mousedown' || (!canFocus(ev.target) && !canFocus(document.activeElement))) + ) { + ev.preventDefault(); + ev.stopPropagation(); + } + } + + /** + * Ignore click events that don't come from AngularJS Material, Ionic, Input Label clicks, + * or key presses that generate click events. This helps to ignore the ghost tap events on + * older mobile browsers that get sent after a 300-400ms delay. + * @param ev MouseEvent or modified MouseEvent with $material, pointer, and other fields + */ + function clickHijacker(ev) { + var isKeyClick; + if ($mdUtil.isIos) { + isKeyClick = angular.isDefined(ev.webkitForce) && ev.webkitForce === 0; + } else { + isKeyClick = ev.clientX === 0 && ev.clientY === 0; + } + if (!isKeyClick && !ev.$material && !ev.isIonicTap && !isInputEventFromLabelClick(ev)) { + ev.preventDefault(); + ev.stopPropagation(); + lastLabelClickPos = null; + } else { + lastLabelClickPos = null; + if (ev.target.tagName.toLowerCase() === 'label') { + lastLabelClickPos = {x: ev.x, y: ev.y}; + } + } + } + + + // Listen to all events to cover all platforms. + var START_EVENTS = 'mousedown touchstart pointerdown'; + var MOVE_EVENTS = 'mousemove touchmove pointermove'; + var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel'; + + angular.element(document) + .on(START_EVENTS, gestureStart) + .on(MOVE_EVENTS, gestureMove) + .on(END_EVENTS, gestureEnd) + // For testing + .on('$$mdGestureReset', function gestureClearCache () { + lastPointer = pointer = null; + }); + + /** + * When a DOM event happens, run all registered gesture handlers' lifecycle + * methods which match the DOM event. + * Eg. when a 'touchstart' event happens, runHandlers('start') will call and + * run `handler.cancel()` and `handler.start()` on all registered handlers. + */ + function runHandlers(handlerEvent, event) { + var handler; + for (var name in HANDLERS) { + handler = HANDLERS[name]; + if (handler instanceof $$MdGestureHandler) { + + if (handlerEvent === 'start') { + // Run cancel to reset any handlers' state + handler.cancel(); + } + handler[handlerEvent](event, pointer); + } + } + } + + /* + * gestureStart vets if a start event is legitimate (and not part of a 'ghost click' from iOS/Android) + * If it is legitimate, we initiate the pointer state and mark the current pointer's type + * For example, for a touchstart event, mark the current pointer as a 'touch' pointer, so mouse events + * won't effect it. + */ + function gestureStart(ev) { + // If we're already touched down, abort + if (pointer) return; + + var now = +Date.now(); + + // iOS & old android bug: after a touch event, a click event is sent 350 ms later. + // If <400ms have passed, don't allow an event of a different type than the previous event + if (lastPointer && !typesMatch(ev, lastPointer) && (now - lastPointer.endTime < 1500)) { + return; + } + + pointer = makeStartPointer(ev); + + runHandlers('start', ev); + } + + /** + * If a move event happens of the right type, update the pointer and run all the move handlers. + * "of the right type": if a mousemove happens but our pointer started with a touch event, do + * nothing. + */ + function gestureMove(ev) { + if (!pointer || !typesMatch(ev, pointer)) return; + + updatePointerState(ev, pointer); + runHandlers('move', ev); + } + + /** + * If an end event happens of the right type, update the pointer, run endHandlers, and save the + * pointer as 'lastPointer'. + */ + function gestureEnd(ev) { + if (!pointer || !typesMatch(ev, pointer)) return; + + updatePointerState(ev, pointer); + pointer.endTime = +Date.now(); + + if (ev.type !== 'pointercancel') { + runHandlers('end', ev); + } + + lastPointer = pointer; + pointer = null; + } + +} + +// ******************** +// Module Functions +// ******************** + +/* + * Initiate the pointer. x, y, and the pointer's type. + */ +function makeStartPointer(ev) { + var point = getEventPoint(ev); + var startPointer = { + startTime: +Date.now(), + target: ev.target, + // 'p' for pointer events, 'm' for mouse, 't' for touch + type: ev.type.charAt(0) + }; + startPointer.startX = startPointer.x = point.pageX; + startPointer.startY = startPointer.y = point.pageY; + return startPointer; +} + +/* + * return whether the pointer's type matches the event's type. + * Eg if a touch event happens but the pointer has a mouse type, return false. + */ +function typesMatch(ev, pointer) { + return ev && pointer && ev.type.charAt(0) === pointer.type; +} + +/** + * Gets whether the given event is an input event that was caused by clicking on an + * associated label element. + * + * This is necessary because the browser will, upon clicking on a label element, fire an + * *extra* click event on its associated input (if any). mdGesture is able to flag the label + * click as with `$material` correctly, but not the second input click. + * + * In order to determine whether an input event is from a label click, we compare the (x, y) for + * the event to the (x, y) for the most recent label click (which is cleared whenever a non-label + * click occurs). Unfortunately, there are no event properties that tie the input and the label + * together (such as relatedTarget). + * + * @param {MouseEvent} event + * @returns {boolean} + */ +function isInputEventFromLabelClick(event) { + return lastLabelClickPos + && lastLabelClickPos.x === event.x + && lastLabelClickPos.y === event.y; +} + +/* + * Update the given pointer based upon the given DOMEvent. + * Distance, velocity, direction, duration, etc + */ +function updatePointerState(ev, pointer) { + var point = getEventPoint(ev); + var x = pointer.x = point.pageX; + var y = pointer.y = point.pageY; + + pointer.distanceX = x - pointer.startX; + pointer.distanceY = y - pointer.startY; + pointer.distance = Math.sqrt( + pointer.distanceX * pointer.distanceX + pointer.distanceY * pointer.distanceY + ); + + pointer.directionX = pointer.distanceX > 0 ? 'right' : pointer.distanceX < 0 ? 'left' : ''; + pointer.directionY = pointer.distanceY > 0 ? 'down' : pointer.distanceY < 0 ? 'up' : ''; + + pointer.duration = +Date.now() - pointer.startTime; + pointer.velocityX = pointer.distanceX / pointer.duration; + pointer.velocityY = pointer.distanceY / pointer.duration; +} + +/** + * Normalize the point where the DOM event happened whether it's touch or mouse. + * @returns point event obj with pageX and pageY on it. + */ +function getEventPoint(ev) { + ev = ev.originalEvent || ev; // support jQuery events + return (ev.touches && ev.touches[0]) || + (ev.changedTouches && ev.changedTouches[0]) || + ev; +} + +/** Checks whether an element can be focused. */ +function canFocus(element) { + return ( + !!element && + element.getAttribute('tabindex') !== '-1' && + !element.hasAttribute('disabled') && + ( + element.hasAttribute('tabindex') || + element.hasAttribute('href') || + element.isContentEditable || + ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA', 'VIDEO', 'AUDIO'].indexOf(element.nodeName) !== -1 + ) + ); +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.core.interaction + * @description + * User interaction detection to provide proper accessibility. + */ +MdInteractionService.$inject = ["$timeout", "$mdUtil", "$rootScope"]; +angular + .module('material.core.interaction', []) + .service('$mdInteraction', MdInteractionService); + + +/** + * @ngdoc service + * @name $mdInteraction + * @module material.core.interaction + * + * @description + * + * Service which keeps track of the last interaction type and validates them for several browsers. + * The service hooks into the document's body and listens for touch, mouse and keyboard events. + * + * The most recent interaction type can be retrieved by calling the `getLastInteractionType` method. + * + * Here is an example markup for using the interaction service. + * + * + * var lastType = $mdInteraction.getLastInteractionType(); + * + * if (lastType === 'keyboard') { + * // We only restore the focus for keyboard users. + * restoreFocus(); + * } + * + * + */ +function MdInteractionService($timeout, $mdUtil, $rootScope) { + this.$timeout = $timeout; + this.$mdUtil = $mdUtil; + this.$rootScope = $rootScope; + + // IE browsers can also trigger pointer events, which also leads to an interaction. + this.pointerEvent = 'MSPointerEvent' in window ? 'MSPointerDown' : 'PointerEvent' in window ? 'pointerdown' : null; + this.bodyElement = angular.element(document.body); + this.isBuffering = false; + this.bufferTimeout = null; + this.lastInteractionType = null; + this.lastInteractionTime = null; + this.inputHandler = this.onInputEvent.bind(this); + this.bufferedInputHandler = this.onBufferInputEvent.bind(this); + + // Type Mappings for the different events + // There will be three three interaction types + // `keyboard`, `mouse` and `touch` + // type `pointer` will be evaluated in `pointerMap` for IE Browser events + this.inputEventMap = { + 'keydown': 'keyboard', + 'mousedown': 'mouse', + 'mouseenter': 'mouse', + 'touchstart': 'touch', + 'pointerdown': 'pointer', + 'MSPointerDown': 'pointer' + }; + + // IE PointerDown events will be validated in `touch` or `mouse` + // Index numbers referenced here: https://msdn.microsoft.com/library/windows/apps/hh466130.aspx + this.iePointerMap = { + 2: 'touch', + 3: 'touch', + 4: 'mouse' + }; + + this.initializeEvents(); + this.$rootScope.$on('$destroy', this.deregister.bind(this)); +} + +/** + * Removes all event listeners created by $mdInteration on the + * body element. + */ +MdInteractionService.prototype.deregister = function() { + + this.bodyElement.off('keydown mousedown', this.inputHandler); + + if ('ontouchstart' in document.documentElement) { + this.bodyElement.off('touchstart', this.bufferedInputHandler); + } + + if (this.pointerEvent) { + this.bodyElement.off(this.pointerEvent, this.inputHandler); + } + +}; + +/** + * Initializes the interaction service, by registering all interaction events to the + * body element. + */ +MdInteractionService.prototype.initializeEvents = function() { + + this.bodyElement.on('keydown mousedown', this.inputHandler); + + if ('ontouchstart' in document.documentElement) { + this.bodyElement.on('touchstart', this.bufferedInputHandler); + } + + if (this.pointerEvent) { + this.bodyElement.on(this.pointerEvent, this.inputHandler); + } + +}; + +/** + * Event listener for normal interaction events, which should be tracked. + * @param event {MouseEvent|KeyboardEvent|PointerEvent|TouchEvent} + */ +MdInteractionService.prototype.onInputEvent = function(event) { + if (this.isBuffering) { + return; + } + + var type = this.inputEventMap[event.type]; + + if (type === 'pointer') { + type = this.iePointerMap[event.pointerType] || event.pointerType; + } + + this.lastInteractionType = type; + this.lastInteractionTime = this.$mdUtil.now(); +}; + +/** + * Event listener for interaction events which should be buffered (touch events). + * @param event {TouchEvent} + */ +MdInteractionService.prototype.onBufferInputEvent = function(event) { + this.$timeout.cancel(this.bufferTimeout); + + this.onInputEvent(event); + this.isBuffering = true; + + // The timeout of 650ms is needed to delay the touchstart, because otherwise the touch will call + // the `onInput` function multiple times. + this.bufferTimeout = this.$timeout(function() { + this.isBuffering = false; + }.bind(this), 650, false); + +}; + +/** + * @ngdoc method + * @name $mdInteraction#getLastInteractionType + * @description Retrieves the last interaction type triggered in body. + * @returns {string|null} Last interaction type. + */ +MdInteractionService.prototype.getLastInteractionType = function() { + return this.lastInteractionType; +}; + +/** + * @ngdoc method + * @name $mdInteraction#isUserInvoked + * @description Method to detect whether any interaction happened recently or not. + * @param {number=} checkDelay Time to check for any interaction to have been triggered. + * @returns {boolean} Whether there was any interaction or not. + */ +MdInteractionService.prototype.isUserInvoked = function(checkDelay) { + var delay = angular.isNumber(checkDelay) ? checkDelay : 15; + + // Check for any interaction to be within the specified check time. + return this.lastInteractionTime >= this.$mdUtil.now() - delay; +}; + +})(); +(function(){ +"use strict"; + +angular.module('material.core') + .provider('$$interimElement', InterimElementProvider); + +/** + * @ngdoc service + * @name $$interimElementProvider + * @module material.core.interimElement + * + * @description + * + * Factory that constructs `$$interimElement.$service` services. + * Used internally in material design for elements that appear on screen temporarily. + * The service provides a promise-like API for interacting with the temporary + * elements. + * + * + * app.service('$mdToast', function($$interimElement) { + * var $mdToast = $$interimElement(toastDefaultOptions); + * return $mdToast; + * }); + * + * + * @param {object=} defaultOptions Options used by default for the `show` method on the service. + * + * @returns {$$interimElement.$service} + */ + +function InterimElementProvider() { + InterimElementFactory.$inject = ["$document", "$q", "$rootScope", "$timeout", "$rootElement", "$animate", "$mdUtil", "$mdCompiler", "$mdTheming", "$injector", "$exceptionHandler"]; + createInterimElementProvider.$get = InterimElementFactory; + return createInterimElementProvider; + + /** + * Returns a new provider which allows configuration of a new interimElement + * service. Allows configuration of default options & methods for options, + * as well as configuration of 'preset' methods (eg dialog.basic(): basic is a preset method) + */ + function createInterimElementProvider(interimFactoryName) { + factory.$inject = ["$$interimElement", "$injector"]; + var EXPOSED_METHODS = ['onHide', 'onShow', 'onRemove']; + + var customMethods = {}; + var providerConfig = { + presets: {} + }; + + var provider = { + setDefaults: setDefaults, + addPreset: addPreset, + addMethod: addMethod, + $get: factory + }; + + /** + * all interim elements will come with the 'build' preset + */ + provider.addPreset('build', { + methods: ['controller', 'controllerAs', 'resolve', 'multiple', + 'template', 'templateUrl', 'themable', 'transformTemplate', 'parent', 'contentElement'] + }); + + return provider; + + /** + * Save the configured defaults to be used when the factory is instantiated + */ + function setDefaults(definition) { + providerConfig.optionsFactory = definition.options; + providerConfig.methods = (definition.methods || []).concat(EXPOSED_METHODS); + return provider; + } + + /** + * Add a method to the factory that isn't specific to any interim element operations + */ + function addMethod(name, fn) { + customMethods[name] = fn; + return provider; + } + + /** + * Save the configured preset to be used when the factory is instantiated + */ + function addPreset(name, definition) { + definition = definition || {}; + definition.methods = definition.methods || []; + definition.options = definition.options || function() { return {}; }; + + if (/^cancel|hide|show$/.test(name)) { + throw new Error("Preset '" + name + "' in " + interimFactoryName + " is reserved!"); + } + if (definition.methods.indexOf('_options') > -1) { + throw new Error("Method '_options' in " + interimFactoryName + " is reserved!"); + } + providerConfig.presets[name] = { + methods: definition.methods.concat(EXPOSED_METHODS), + optionsFactory: definition.options, + argOption: definition.argOption + }; + return provider; + } + + function addPresetMethod(presetName, methodName, method) { + providerConfig.presets[presetName][methodName] = method; + } + + /** + * Create a factory that has the given methods & defaults implementing interimElement + */ + /* @ngInject */ + function factory($$interimElement, $injector) { + var defaultMethods; + var defaultOptions; + var interimElementService = $$interimElement(); + + /* + * publicService is what the developer will be using. + * It has methods hide(), cancel(), show(), build(), and any other + * presets which were set during the config phase. + */ + var publicService = { + hide: interimElementService.hide, + cancel: interimElementService.cancel, + show: showInterimElement, + + // Special internal method to destroy an interim element without animations + // used when navigation changes causes a $scope.$destroy() action + destroy : destroyInterimElement + }; + + + defaultMethods = providerConfig.methods || []; + // This must be invoked after the publicService is initialized + defaultOptions = invokeFactory(providerConfig.optionsFactory, {}); + + // Copy over the simple custom methods + angular.forEach(customMethods, function(fn, name) { + publicService[name] = fn; + }); + + angular.forEach(providerConfig.presets, function(definition, name) { + var presetDefaults = invokeFactory(definition.optionsFactory, {}); + var presetMethods = (definition.methods || []).concat(defaultMethods); + + // Every interimElement built with a preset has a field called `$type`, + // which matches the name of the preset. + // Eg in preset 'confirm', options.$type === 'confirm' + angular.extend(presetDefaults, { $type: name }); + + // This creates a preset class which has setter methods for every + // method given in the `.addPreset()` function, as well as every + // method given in the `.setDefaults()` function. + // + // @example + // .setDefaults({ + // methods: ['hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent'], + // options: dialogDefaultOptions + // }) + // .addPreset('alert', { + // methods: ['title', 'ok'], + // options: alertDialogOptions + // }) + // + // Set values will be passed to the options when interimElement.show() is called. + function Preset(opts) { + this._options = angular.extend({}, presetDefaults, opts); + } + angular.forEach(presetMethods, function(name) { + Preset.prototype[name] = function(value) { + this._options[name] = value; + return this; + }; + }); + + // Create shortcut method for one-linear methods + if (definition.argOption) { + var methodName = 'show' + name.charAt(0).toUpperCase() + name.slice(1); + publicService[methodName] = function(arg) { + var config = publicService[name](arg); + return publicService.show(config); + }; + } + + // eg $mdDialog.alert() will return a new alert preset + publicService[name] = function(arg) { + // If argOption is supplied, eg `argOption: 'content'`, then we assume + // if the argument is not an options object then it is the `argOption` option. + // + // @example `$mdToast.simple('hello')` // sets options.content to hello + // // because argOption === 'content' + if (arguments.length && definition.argOption && + !angular.isObject(arg) && !angular.isArray(arg)) { + + return (new Preset())[definition.argOption](arg); + + } else { + return new Preset(arg); + } + + }; + }); + + return publicService; + + /** + * + */ + function showInterimElement(opts) { + // opts is either a preset which stores its options on an _options field, + // or just an object made up of options + opts = opts || { }; + if (opts._options) opts = opts._options; + + return interimElementService.show( + angular.extend({}, defaultOptions, opts) + ); + } + + /** + * Special method to hide and destroy an interimElement WITHOUT + * any 'leave` or hide animations ( an immediate force hide/remove ) + * + * NOTE: This calls the onRemove() subclass method for each component... + * which must have code to respond to `options.$destroy == true` + */ + function destroyInterimElement(opts) { + return interimElementService.destroy(opts); + } + + /** + * Helper to call $injector.invoke with a local of the factory name for + * this provider. + * If an $mdDialog is providing options for a dialog and tries to inject + * $mdDialog, a circular dependency error will happen. + * We get around that by manually injecting $mdDialog as a local. + */ + function invokeFactory(factory, defaultVal) { + var locals = {}; + locals[interimFactoryName] = publicService; + return $injector.invoke(factory || function() { return defaultVal; }, {}, locals); + } + } + } + + /* @ngInject */ + function InterimElementFactory($document, $q, $rootScope, $timeout, $rootElement, $animate, + $mdUtil, $mdCompiler, $mdTheming, $injector, $exceptionHandler) { + return function createInterimElementService() { + var SHOW_CANCELLED = false; + + /** + * @ngdoc service + * @name $$interimElementProvider.$service + * + * @description + * A service used to control inserting and removing of an element from the DOM. + * It is used by $mdBottomSheet, $mdDialog, $mdToast, $mdMenu, $mdPanel, and $mdSelect. + */ + var service; + + var showPromises = []; // Promises for the interim's which are currently opening. + var hidePromises = []; // Promises for the interim's which are currently hiding. + var showingInterims = []; // Interim elements which are currently showing up. + + // Publish instance $$interimElement service; + return service = { + show: show, + hide: waitForInterim(hide), + cancel: waitForInterim(cancel), + destroy : destroy, + $injector_: $injector + }; + + /** + * @ngdoc method + * @name $$interimElementProvider.$service#show + * @kind function + * + * @description + * Adds the `$interimElement` to the DOM and returns a special promise that will be resolved + * or rejected with hide or cancel, respectively. + * + * @param {Object} options map of options and values + * @returns {Promise} a Promise that will be resolved when hide() is called or rejected when + * cancel() is called. + */ + function show(options) { + options = options || {}; + var interimElement = new InterimElement(options || {}); + + // When an interim element is currently showing, we have to cancel it. + // Just hiding it, will resolve the InterimElement's promise, the promise should be + // rejected instead. + var hideAction = options.multiple ? $q.resolve() : $q.all(showPromises); + + if (!options.multiple) { + // Wait for all opening interim's to finish their transition. + hideAction = hideAction.then(function() { + // Wait for all closing and showing interim's to be completely closed. + var promiseArray = hidePromises.concat(showingInterims.map(service.cancel)); + return $q.all(promiseArray); + }); + } + + var showAction = hideAction.then(function() { + + return interimElement + .show() + .then(function () { + showingInterims.push(interimElement); + }) + .catch(function (reason) { + return reason; + }) + .finally(function() { + showPromises.splice(showPromises.indexOf(showAction), 1); + }); + + }); + + showPromises.push(showAction); + + // In AngularJS 1.6+, exceptions inside promises will cause a rejection. We need to handle + // the rejection and only log it if it's an error. + interimElement.deferred.promise.catch(function(fault) { + if (fault instanceof Error) { + $exceptionHandler(fault); + } + + return fault; + }); + + // Return a promise that will be resolved when the interim + // element is hidden or cancelled... + return interimElement.deferred.promise; + } + + /** + * @ngdoc method + * @name $$interimElementProvider.$service#hide + * @kind function + * + * @description + * Removes the `$interimElement` from the DOM and resolves the Promise returned from `show()`. + * + * @param {*} reason Data used to resolve the Promise + * @param {object} options map of options and values + * @returns {Promise} a Promise that will be resolved after the element has been removed + * from the DOM. + */ + function hide(reason, options) { + options = options || {}; + + if (options.closeAll) { + // We have to make a shallow copy of the array, because otherwise the map will break. + return $q.all(showingInterims.slice().reverse().map(closeElement)); + } else if (options.closeTo !== undefined) { + return $q.all(showingInterims.slice(options.closeTo).map(closeElement)); + } + + // Hide the latest showing interim element. + return closeElement(showingInterims[showingInterims.length - 1]); + + /** + * @param {InterimElement} interim element to close + * @returns {Promise} + */ + function closeElement(interim) { + if (!interim) { + return $q.when(reason); + } + + var hideAction = interim + .remove(reason, false, options || { }) + .catch(function(reason) { return reason; }) + .finally(function() { + hidePromises.splice(hidePromises.indexOf(hideAction), 1); + }); + + showingInterims.splice(showingInterims.indexOf(interim), 1); + hidePromises.push(hideAction); + + return interim.deferred.promise; + } + } + + /** + * @ngdoc method + * @name $$interimElementProvider.$service#cancel + * @kind function + * + * @description + * Removes the `$interimElement` from the DOM and rejects the Promise returned from `show()`. + * + * @param {*} reason Data used to resolve the Promise + * @param {object} options map of options and values + * @returns {Promise} Promise that will be resolved after the element has been removed + * from the DOM. + */ + function cancel(reason, options) { + var interim = showingInterims.pop(); + if (!interim) { + return $q.when(reason); + } + + var cancelAction = interim + .remove(reason, true, options || {}) + .catch(function(reason) { return reason; }) + .finally(function() { + hidePromises.splice(hidePromises.indexOf(cancelAction), 1); + }); + + hidePromises.push(cancelAction); + + // Since AngularJS 1.6.7, promises will be logged to $exceptionHandler when the promise + // is not handling the rejection. We create a pseudo catch handler, which will prevent the + // promise from being logged to the $exceptionHandler. + return interim.deferred.promise.catch(angular.noop); + } + + /** + * Creates a function to wait for at least one interim element to be available. + * @param callbackFn Function to be used as callback + * @returns {Function} + */ + function waitForInterim(callbackFn) { + return function() { + var fnArguments = arguments; + + if (!showingInterims.length) { + // When there are still interim's opening, then wait for the first interim element to + // finish its open animation. + if (showPromises.length) { + return showPromises[0].finally(function () { + return callbackFn.apply(service, fnArguments); + }); + } + + return $q.when("No interim elements currently showing up."); + } + + return callbackFn.apply(service, fnArguments); + }; + } + + /** + * @ngdoc method + * @name $$interimElementProvider.$service#destroy + * @kind function + * + * Special method to quick-remove the interim element without running animations. This is + * useful when the parent component has been or is being destroyed. + * + * Note: interim elements are in "interim containers". + */ + function destroy(targetEl) { + var interim = !targetEl ? showingInterims.shift() : null; + + var parentEl = angular.element(targetEl).length && angular.element(targetEl)[0].parentNode; + + if (parentEl) { + // Try to find the interim in the stack which corresponds to the supplied DOM element. + var filtered = showingInterims.filter(function(entry) { + return entry.options.element[0] === parentEl; + }); + + // Note: This function might be called when the element already has been removed, + // in which case we won't find any matches. + if (filtered.length) { + interim = filtered[0]; + showingInterims.splice(showingInterims.indexOf(interim), 1); + } + } + + return interim ? interim.remove(SHOW_CANCELLED, false, { '$destroy': true }) : + $q.when(SHOW_CANCELLED); + } + + /* + * Internal Interim Element Object + * Used internally to manage the DOM element and related data + */ + function InterimElement(options) { + var self, element, showAction = $q.when(true); + + options = configureScopeAndTransitions(options); + + return self = { + options : options, + deferred: $q.defer(), + show : createAndTransitionIn, + remove : transitionOutAndRemove + }; + + /** + * Compile, link, and show this interim element. Use optional autoHide and transition-in + * effects. + * @return {Q.Promise} + */ + function createAndTransitionIn() { + return $q(function(resolve, reject) { + + // Trigger onCompiling callback before the compilation starts. + // This is useful, when modifying options, which can be influenced by developers. + options.onCompiling && options.onCompiling(options); + + compileElement(options) + .then(function(compiledData) { + element = linkElement(compiledData, options); + + // Expose the cleanup function from the compiler. + options.cleanupElement = compiledData.cleanup; + + showAction = showElement(element, options, compiledData.controller) + .then(resolve, rejectAll); + }).catch(rejectAll); + + function rejectAll(fault) { + // Force the '$md.show()' promise to reject + self.deferred.reject(fault); + + // Continue rejection propagation + reject(fault); + } + }); + } + + /** + * After the show process has finished/rejected: + * - announce 'removing', + * - perform the transition-out, and + * - perform optional clean up scope. + */ + function transitionOutAndRemove(response, isCancelled, opts) { + + // abort if the show() and compile failed + if (!element) return $q.when(false); + + options = angular.extend(options || {}, opts || {}); + options.cancelAutoHide && options.cancelAutoHide(); + options.element.triggerHandler('$mdInterimElementRemove'); + + if (options.$destroy === true) { + + return hideElement(options.element, options).then(function(){ + (isCancelled && rejectAll(response)) || resolveAll(response); + }); + + } else { + $q.when(showAction).finally(function() { + hideElement(options.element, options).then(function() { + isCancelled ? rejectAll(response) : resolveAll(response); + }, rejectAll); + }); + + return self.deferred.promise; + } + + + /** + * The `show()` returns a promise that will be resolved when the interim + * element is hidden or cancelled... + */ + function resolveAll(response) { + self.deferred.resolve(response); + } + + /** + * Force the '$md.show()' promise to reject + */ + function rejectAll(fault) { + self.deferred.reject(fault); + } + } + + /** + * Prepare optional isolated scope and prepare $animate with default enter and leave + * transitions for the new element instance. + */ + function configureScopeAndTransitions(options) { + options = options || { }; + if (options.template) { + options.template = $mdUtil.processTemplate(options.template); + } + + return angular.extend({ + preserveScope: false, + cancelAutoHide : angular.noop, + scope: options.scope || $rootScope.$new(options.isolateScope), + + /** + * Default usage to enable $animate to transition-in; can be easily overridden via 'options' + */ + onShow: function transitionIn(scope, element, options) { + return $animate.enter(element, options.parent); + }, + + /** + * Default usage to enable $animate to transition-out; can be easily overridden via 'options' + */ + onRemove: function transitionOut(scope, element) { + // Element could be undefined if a new element is shown before + // the old one finishes compiling. + return element && $animate.leave(element) || $q.when(); + } + }, options); + + } + + /** + * Compile an element with a templateUrl, controller, and locals + * @param {Object} options + * @return {Q.Promise<{element: JQLite=, link: Function, locals: Object, cleanup: any=, + * controller: Object=}>} + */ + function compileElement(options) { + + var compiled = !options.skipCompile ? $mdCompiler.compile(options) : null; + + return compiled || $q(function (resolve) { + resolve({ + locals: {}, + link: function () { + return options.element; + } + }); + }); + } + + /** + * Link an element with compiled configuration + * @param {{element: JQLite=, link: Function, locals: Object, controller: Object=}} compileData + * @param {Object} options + * @return {JQLite} + */ + function linkElement(compileData, options) { + angular.extend(compileData.locals, options); + + var element = compileData.link(options.scope); + + // Search for parent at insertion time, if not specified + options.element = element; + options.parent = findParent(element, options); + if (options.themable) $mdTheming(element); + + return element; + } + + /** + * Search for parent at insertion time, if not specified. + * @param {JQLite} element + * @param {Object} options + * @return {JQLite} + */ + function findParent(element, options) { + var parent = options.parent; + + // Search for parent at insertion time, if not specified + if (angular.isFunction(parent)) { + parent = parent(options.scope, element, options); + } else if (angular.isString(parent)) { + parent = angular.element($document[0].querySelector(parent)); + } else { + parent = angular.element(parent); + } + + // If parent querySelector/getter function fails, or it's just null, + // find a default. + if (!(parent || {}).length) { + var el; + if ($rootElement[0] && $rootElement[0].querySelector) { + el = $rootElement[0].querySelector(':not(svg) > body'); + } + if (!el) el = $rootElement[0]; + if (el.nodeName === '#comment') { + el = $document[0].body; + } + return angular.element(el); + } + + return parent; + } + + /** + * If auto-hide is enabled, start timer and prepare cancel function + */ + function startAutoHide() { + var autoHideTimer, cancelAutoHide = angular.noop; + + if (options.hideDelay) { + autoHideTimer = $timeout(service.hide, options.hideDelay) ; + cancelAutoHide = function() { + $timeout.cancel(autoHideTimer); + }; + } + + // Cache for subsequent use + options.cancelAutoHide = function() { + cancelAutoHide(); + options.cancelAutoHide = undefined; + }; + } + + /** + * Show the element (with transitions), notify complete and start optional auto hiding + * timer. + * @param {JQLite} element + * @param {Object} options + * @param {Object} controller + * @return {Q.Promise} + */ + function showElement(element, options, controller) { + // Trigger onShowing callback before the `show()` starts + var notifyShowing = options.onShowing || angular.noop; + // Trigger onComplete callback when the `show()` finishes + var notifyComplete = options.onComplete || angular.noop; + + // Necessary for consistency between AngularJS 1.5 and 1.6. + try { + // This fourth controller parameter is used by $mdDialog in beforeShow(). + notifyShowing(options.scope, element, options, controller); + } catch (e) { + return $q.reject(e); + } + + return $q(function (resolve, reject) { + try { + // Start transitionIn + $q.when(options.onShow(options.scope, element, options)) + .then(function () { + notifyComplete(options.scope, element, options); + startAutoHide(); + + resolve(element); + }, reject); + + } catch (e) { + reject(e.message); + } + }); + } + + function hideElement(element, options) { + var announceRemoving = options.onRemoving || angular.noop; + + return $q(function (resolve, reject) { + try { + // Start transitionIn + var action = $q.when(options.onRemove(options.scope, element, options) || true); + + // Trigger callback *before* the remove operation starts + announceRemoving(element, action); + + if (options.$destroy) { + // For $destroy, onRemove should be synchronous + resolve(element); + + if (!options.preserveScope && options.scope) { + // scope destroy should still be be done after the current digest is done + action.then(function() { options.scope.$destroy(); }); + } + } else { + // Wait until transition-out is done + action.then(function () { + if (!options.preserveScope && options.scope) { + options.scope.$destroy(); + } + + resolve(element); + }, reject); + } + } catch (e) { + reject(e.message); + } + }); + } + + } + }; + } +} + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + var $mdUtil, $interpolate, $log; + + var SUFFIXES = /(-gt)?-(sm|md|lg|print)/g; + var WHITESPACE = /\s+/g; + + var FLEX_OPTIONS = ['grow', 'initial', 'auto', 'none', 'noshrink', 'nogrow']; + var LAYOUT_OPTIONS = ['row', 'column']; + var ALIGNMENT_MAIN_AXIS= ["", "start", "center", "end", "stretch", "space-around", "space-between"]; + var ALIGNMENT_CROSS_AXIS= ["", "start", "center", "end", "stretch"]; + + var config = { + /** + * Enable directive attribute-to-class conversions + * Developers can use `` to quickly + * disable the Layout directives and prohibit the injection of Layout classNames + */ + enabled: true, + + /** + * List of mediaQuery breakpoints and associated suffixes + * [ + * { suffix: "sm", mediaQuery: "screen and (max-width: 599px)" }, + * { suffix: "md", mediaQuery: "screen and (min-width: 600px) and (max-width: 959px)" } + * ] + */ + breakpoints: [] + }; + + registerLayoutAPI(angular.module('material.core.layout', ['ng'])); + + /** + * registerLayoutAPI() + * + * The original AngularJS Material Layout solution used attribute selectors and CSS. + * + * ```html + *
    My Content
    + * ``` + * + * ```css + * [layout] { + * box-sizing: border-box; + * display:flex; + * } + * [layout=column] { + * flex-direction : column + * } + * ``` + * + * Use of attribute selectors creates significant performance impacts in some + * browsers... mainly IE. + * + * This module registers directives that allow the same layout attributes to be + * interpreted and converted to class selectors. The directive will add equivalent classes to + * each element that contains a Layout directive. + * + * ```html + *
    My Content
    + * ``` + * + * ```css + * .layout { + * box-sizing: border-box; + * display:flex; + * } + * .layout-column { + * flex-direction : column + * } + * ``` + */ + function registerLayoutAPI(module){ + var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i; + var SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g; + + // NOTE: these are also defined in constants::MEDIA_PRIORITY and constants::MEDIA + var BREAKPOINTS = ["", "xs", "gt-xs", "sm", "gt-sm", "md", "gt-md", "lg", "gt-lg", "xl", "print"]; + var API_WITH_VALUES = ["layout", "flex", "flex-order", "flex-offset", "layout-align"]; + var API_NO_VALUES = ["show", "hide", "layout-padding", "layout-margin"]; + + + // Build directive registration functions for the standard Layout API... for all breakpoints. + angular.forEach(BREAKPOINTS, function(mqb) { + + // Attribute directives with expected, observable value(s) + angular.forEach(API_WITH_VALUES, function(name){ + var fullName = mqb ? name + "-" + mqb : name; + module.directive(directiveNormalize(fullName), attributeWithObserve(fullName)); + }); + + // Attribute directives with no expected value(s) + angular.forEach(API_NO_VALUES, function(name){ + var fullName = mqb ? name + "-" + mqb : name; + module.directive(directiveNormalize(fullName), attributeWithoutValue(fullName)); + }); + + }); + + // Register other, special directive functions for the Layout features: + module + .provider('$$mdLayout', function() { + // Publish internal service for Layouts + return { + $get : angular.noop, + validateAttributeValue : validateAttributeValue, + validateAttributeUsage : validateAttributeUsage, + /** + * Easy way to disable/enable the Layout API. + * When disabled, this stops all attribute-to-classname generations + */ + disableLayouts : function(isDisabled) { + config.enabled = (isDisabled !== true); + } + }; + }) + + .directive('mdLayoutCss' , disableLayoutDirective) + .directive('ngCloak' , buildCloakInterceptor('ng-cloak')) + + .directive('layoutWrap' , attributeWithoutValue('layout-wrap')) + .directive('layoutNowrap' , attributeWithoutValue('layout-nowrap')) + .directive('layoutNoWrap' , attributeWithoutValue('layout-no-wrap')) + .directive('layoutFill' , attributeWithoutValue('layout-fill')) + + // Determine if + .config(detectDisabledLayouts); + + /** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ + function directiveNormalize(name) { + return name + .replace(PREFIX_REGEXP, '') + .replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); + } + } + + + /** + * Detect if any of the HTML tags has a [md-layouts-disabled] attribute; + * If yes, then immediately disable all layout API features + * + * Note: this attribute should be specified on either the HTML or BODY tags + * @ngInject + */ + function detectDisabledLayouts() { + var isDisabled = !!document.querySelector('[md-layouts-disabled]'); + config.enabled = !isDisabled; + } + + /** + * Special directive that will disable ALL Layout conversions of layout + * attribute(s) to classname(s). + * + * + * + * + * + * ... + * + * + * Note: Using md-layout-css directive requires the developer to load the Material + * Layout Attribute stylesheet (which only uses attribute selectors): + * + * `angular-material.layout.css` + * + * Another option is to use the LayoutProvider to configure and disable the attribute + * conversions; this would obviate the use of the `md-layout-css` directive + */ + function disableLayoutDirective() { + // Return a 1x-only, first-match attribute directive + config.enabled = false; + + return { + restrict : 'A', + priority : '900' + }; + } + + /** + * Tail-hook ngCloak to delay the uncloaking while Layout transformers + * finish processing. Eliminates flicker with Material.Layouts + */ + function buildCloakInterceptor(className) { + return ['$timeout', function($timeout){ + return { + restrict : 'A', + priority : -10, // run after normal ng-cloak + compile : function(element) { + if (!config.enabled) return angular.noop; + + // Re-add the cloak + element.addClass(className); + + return function(scope, element) { + // Wait while layout injectors configure, then uncloak + // NOTE: $rAF does not delay enough... and this is a 1x-only event, + // $timeout is acceptable. + $timeout(function(){ + element.removeClass(className); + }, 10, false); + }; + } + }; + }]; + } + + + // ********************************************************************************* + // + // These functions create registration functions for AngularJS Material Layout attribute + // directives. This provides easy translation to switch AngularJS Material attribute selectors to + // CLASS selectors and directives; which has huge performance implications for IE Browsers. + // + // ********************************************************************************* + + /** + * Creates a directive registration function where a possible dynamic attribute + * value will be observed/watched. + * @param {string} className attribute name; eg `layout-gt-md` with value ="row" + */ + function attributeWithObserve(className) { + + return ['$mdUtil', '$interpolate', "$log", function(_$mdUtil_, _$interpolate_, _$log_) { + $mdUtil = _$mdUtil_; + $interpolate = _$interpolate_; + $log = _$log_; + + return { + restrict: 'A', + compile: function(element, attr) { + var linkFn; + if (config.enabled) { + // immediately replace static (non-interpolated) invalid values... + + validateAttributeUsage(className, attr, element, $log); + + validateAttributeValue(className, + getNormalizedAttrValue(className, attr, ""), + buildUpdateFn(element, className, attr) + ); + + linkFn = translateWithValueToCssClass; + } + + // Use for postLink to account for transforms after ng-transclude. + return linkFn || angular.noop; + } + }; + }]; + + /** + * Observe deprecated layout attributes and update the element's layout classes to match. + */ + function translateWithValueToCssClass(scope, element, attrs) { + var updateFn = updateClassWithValue(element, className, attrs); + var unwatch = attrs.$observe(attrs.$normalize(className), updateFn); + + updateFn(getNormalizedAttrValue(className, attrs, "")); + scope.$on("$destroy", function() { unwatch(); }); + } + } + + /** + * Creates a registration function for AngularJS Material Layout attribute directive. + * This is a `simple` transpose of attribute usage to class usage; where we ignore + * any attribute value. + */ + function attributeWithoutValue(className) { + return ['$mdUtil', '$interpolate', "$log", function(_$mdUtil_, _$interpolate_, _$log_) { + $mdUtil = _$mdUtil_; + $interpolate = _$interpolate_; + $log = _$log_; + + return { + restrict: 'A', + compile: function(element, attr) { + var linkFn; + if (config.enabled) { + // immediately replace static (non-interpolated) invalid values... + + validateAttributeValue(className, + getNormalizedAttrValue(className, attr, ""), + buildUpdateFn(element, className, attr) + ); + + translateToCssClass(null, element); + + // Use for postLink to account for transforms after ng-transclude. + linkFn = translateToCssClass; + } + + return linkFn || angular.noop; + } + }; + }]; + + /** + * Add transformed class selector. + */ + function translateToCssClass(scope, element) { + element.addClass(className); + } + } + + /** + * After link-phase, do NOT remove deprecated layout attribute selector. + * Instead watch the attribute so interpolated data-bindings to layout + * selectors will continue to be supported. + * + * $observe() the className and update with new class (after removing the last one) + * + * e.g. `layout="{{layoutDemo.direction}}"` will update... + * + * NOTE: The value must match one of the specified styles in the CSS. + * For example `flex-gt-md="{{size}}` where `scope.size == 47` will NOT work since + * only breakpoints for 0, 5, 10, 15... 100, 33, 34, 66, 67 are defined. + */ + function updateClassWithValue(element, className) { + var lastClass; + + return function updateClassFn(newValue) { + var value = validateAttributeValue(className, newValue || ""); + if (angular.isDefined(value)) { + if (lastClass) element.removeClass(lastClass); + lastClass = !value ? className : className + "-" + value.trim().replace(WHITESPACE, "-"); + element.addClass(lastClass); + } + }; + } + + /** + * Centralize warnings for known flexbox issues (especially IE-related issues) + */ + function validateAttributeUsage(className, attr, element, $log){ + var message, usage, url; + var nodeName = element[0].nodeName.toLowerCase(); + + switch (className.replace(SUFFIXES,"")) { + case "flex": + if ((nodeName === "md-button") || (nodeName === "fieldset")){ + // @see https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers + // Use
    wrapper inside (preferred) or outside + + usage = "<" + nodeName + " " + className + ">"; + url = "https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers"; + message = "Markup '{0}' may not work as expected in IE Browsers. Consult '{1}' for details."; + + $log.warn($mdUtil.supplant(message, [usage, url])); + } + } + } + + + /** + * For the Layout attribute value, validate or replace with default fallback value. + */ + function validateAttributeValue(className, value, updateFn) { + var origValue = value; + + if (!needsInterpolation(value)) { + switch (className.replace(SUFFIXES,"")) { + case 'layout' : + if (!findIn(value, LAYOUT_OPTIONS)) { + value = LAYOUT_OPTIONS[0]; // 'row'; + } + break; + + case 'flex' : + if (!findIn(value, FLEX_OPTIONS)) { + if (isNaN(value)) { + value = ''; + } + } + break; + + case 'flex-offset' : + case 'flex-order' : + if (!value || isNaN(+value)) { + value = '0'; + } + break; + + case 'layout-align' : + var axis = extractAlignAxis(value); + value = $mdUtil.supplant("{main}-{cross}",axis); + break; + + case 'layout-padding' : + case 'layout-margin' : + case 'layout-fill' : + case 'layout-wrap' : + case 'layout-nowrap' : + value = ''; + break; + } + + if (value !== origValue) { + (updateFn || angular.noop)(value); + } + } + + return value ? value.trim() : ""; + } + + /** + * Replace current attribute value with fallback value + */ + function buildUpdateFn(element, className, attrs) { + return function updateAttrValue(fallback) { + if (!needsInterpolation(fallback)) { + // Do not modify the element's attribute value; so + // uses '' will not + // be affected. Just update the attrs value. + attrs[attrs.$normalize(className)] = fallback; + } + }; + } + + /** + * See if the original value has interpolation symbols: + * e.g. flex-gt-md="{{triggerPoint}}" + */ + function needsInterpolation(value) { + return (value || "").indexOf($interpolate.startSymbol()) > -1; + } + + function getNormalizedAttrValue(className, attrs, defaultVal) { + var normalizedAttr = attrs.$normalize(className); + return attrs[normalizedAttr] ? attrs[normalizedAttr].trim().replace(WHITESPACE, "-") : + defaultVal || null; + } + + function findIn(item, list, replaceWith) { + item = replaceWith && item ? item.replace(WHITESPACE, replaceWith) : item; + + var found = false; + if (item) { + list.forEach(function(it) { + it = replaceWith ? it.replace(WHITESPACE, replaceWith) : it; + found = found || (it === item); + }); + } + return found; + } + + function extractAlignAxis(attrValue) { + var axis = { + main : "start", + cross: "stretch" + }, values; + + attrValue = (attrValue || ""); + + if (attrValue.indexOf("-") === 0 || attrValue.indexOf(" ") === 0) { + // For missing main-axis values + attrValue = "none" + attrValue; + } + + values = attrValue.toLowerCase().trim().replace(WHITESPACE, "-").split("-"); + if (values.length && (values[0] === "space")) { + // for main-axis values of "space-around" or "space-between" + values = [values[0]+"-"+values[1],values[2]]; + } + + if (values.length > 0) axis.main = values[0] || axis.main; + if (values.length > 1) axis.cross = values[1] || axis.cross; + + if (ALIGNMENT_MAIN_AXIS.indexOf(axis.main) < 0) axis.main = "start"; + if (ALIGNMENT_CROSS_AXIS.indexOf(axis.cross) < 0) axis.cross = "stretch"; + + return axis; + } +})(); + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.core.liveannouncer + * @description + * AngularJS Material Live Announcer to provide accessibility for Voice Readers. + */ +MdLiveAnnouncer.$inject = ["$timeout"]; +angular + .module('material.core') + .service('$mdLiveAnnouncer', MdLiveAnnouncer); + +/** + * @ngdoc service + * @name $mdLiveAnnouncer + * @module material.core.liveannouncer + * + * @description + * + * Service to announce messages to supported screenreaders. + * + * > The `$mdLiveAnnouncer` service is internally used for components to provide proper accessibility. + * + * + * module.controller('AppCtrl', function($mdLiveAnnouncer) { + * // Basic announcement (Polite Mode) + * $mdLiveAnnouncer.announce('Hey Google'); + * + * // Custom announcement (Assertive Mode) + * $mdLiveAnnouncer.announce('Hey Google', 'assertive'); + * }); + * + * + */ +function MdLiveAnnouncer($timeout) { + /** @private @const @type {!angular.$timeout} */ + this._$timeout = $timeout; + + /** @private @const @type {!HTMLElement} */ + this._liveElement = this._createLiveElement(); + + /** @private @const @type {!number} */ + this._announceTimeout = 100; +} + +/** + * @ngdoc method + * @name $mdLiveAnnouncer#announce + * @description Announces messages to supported screenreaders. + * @param {string} message Message to be announced to the screenreader + * @param {'off'|'polite'|'assertive'} politeness The politeness of the announcer element. + */ +MdLiveAnnouncer.prototype.announce = function(message, politeness) { + if (!politeness) { + politeness = 'polite'; + } + + var self = this; + + self._liveElement.textContent = ''; + self._liveElement.setAttribute('aria-live', politeness); + + // This 100ms timeout is necessary for some browser + screen-reader combinations: + // - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout. + // - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a + // second time without clearing and then using a non-zero delay. + // (using JAWS 17 at time of this writing). + self._$timeout(function() { + self._liveElement.textContent = message; + }, self._announceTimeout, false); +}; + +/** + * Creates a live announcer element, which listens for DOM changes and announces them + * to the screenreaders. + * @returns {!HTMLElement} + * @private + */ +MdLiveAnnouncer.prototype._createLiveElement = function() { + var liveEl = document.createElement('div'); + + liveEl.classList.add('md-visually-hidden'); + liveEl.setAttribute('role', 'status'); + liveEl.setAttribute('aria-atomic', 'true'); + liveEl.setAttribute('aria-live', 'polite'); + + document.body.appendChild(liveEl); + + return liveEl; +}; + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc service + * @name $$mdMeta + * @module material.core.meta + * + * @description + * + * A provider and a service that simplifies meta tags access + * + * Note: This is intended only for use with dynamic meta tags such as browser color and title. + * Tags that are only processed when the page is rendered (such as `charset`, and `http-equiv`) + * will not work since `$$mdMeta` adds the tags after the page has already been loaded. + * + * ```js + * app.config(function($$mdMetaProvider) { + * var removeMeta = $$mdMetaProvider.setMeta('meta-name', 'content'); + * var metaValue = $$mdMetaProvider.getMeta('meta-name'); // -> 'content' + * + * removeMeta(); + * }); + * + * app.controller('myController', function($$mdMeta) { + * var removeMeta = $$mdMeta.setMeta('meta-name', 'content'); + * var metaValue = $$mdMeta.getMeta('meta-name'); // -> 'content' + * + * removeMeta(); + * }); + * ``` + * + * @returns {$$mdMeta.$service} + * + */ +angular.module('material.core.meta', []) + .provider('$$mdMeta', function () { + var head = angular.element(document.head); + var metaElements = {}; + + /** + * Checks if the requested element was written manually and maps it + * + * @param {string} name meta tag 'name' attribute value + * @returns {boolean} returns true if there is an element with the requested name + */ + function mapExistingElement(name) { + if (metaElements[name]) { + return true; + } + + var element = document.getElementsByName(name)[0]; + + if (!element) { + return false; + } + + metaElements[name] = angular.element(element); + + return true; + } + + /** + * @ngdoc method + * @name $$mdMeta#setMeta + * + * @description + * Creates meta element with the 'name' and 'content' attributes, + * if the meta tag is already created than we replace the 'content' value + * + * @param {string} name meta tag 'name' attribute value + * @param {string} content meta tag 'content' attribute value + * @returns {function} remove function + * + */ + function setMeta(name, content) { + mapExistingElement(name); + + if (!metaElements[name]) { + var newMeta = angular.element(''); + head.append(newMeta); + metaElements[name] = newMeta; + } + else { + metaElements[name].attr('content', content); + } + + return function () { + metaElements[name].attr('content', ''); + metaElements[name].remove(); + delete metaElements[name]; + }; + } + + /** + * @ngdoc method + * @name $$mdMeta#getMeta + * + * @description + * Gets the 'content' attribute value of the wanted meta element + * + * @param {string} name meta tag 'name' attribute value + * @returns {string} content attribute value + */ + function getMeta(name) { + if (!mapExistingElement(name)) { + throw Error('$$mdMeta: could not find a meta tag with the name \'' + name + '\''); + } + + return metaElements[name].attr('content'); + } + + var module = { + setMeta: setMeta, + getMeta: getMeta + }; + + return angular.extend({}, module, { + $get: function () { + return module; + } + }); + }); +})(); +(function(){ +"use strict"; + + /** + * @ngdoc module + * @name material.core.componentRegistry + * + * @description + * A component instance registration service. + * Note: currently this as a private service in the SideNav component. + */ + ComponentRegistry.$inject = ["$log", "$q"]; + angular.module('material.core') + .factory('$mdComponentRegistry', ComponentRegistry); + + /* + * @private + * @ngdoc factory + * @name ComponentRegistry + * @module material.core.componentRegistry + * + */ + function ComponentRegistry($log, $q) { + + var self; + var instances = []; + var pendings = { }; + + return self = { + /** + * Used to print an error when an instance for a handle isn't found. + */ + notFoundError: function(handle, msgContext) { + $log.error((msgContext || "") + 'No instance found for handle', handle); + }, + /** + * Return all registered instances as an array. + */ + getInstances: function() { + return instances; + }, + + /** + * Get a registered instance. + * @param handle the String handle to look up for a registered instance. + */ + get: function(handle) { + if (!isValidID(handle)) return null; + + var i, j, instance; + for (i = 0, j = instances.length; i < j; i++) { + instance = instances[i]; + if (instance.$$mdHandle === handle) { + return instance; + } + } + return null; + }, + + /** + * Register an instance. + * @param instance the instance to register + * @param handle the handle to identify the instance under. + */ + register: function(instance, handle) { + if (!handle) return angular.noop; + + instance.$$mdHandle = handle; + instances.push(instance); + resolveWhen(); + + return deregister; + + /** + * Remove registration for an instance + */ + function deregister() { + var index = instances.indexOf(instance); + if (index !== -1) { + instances.splice(index, 1); + } + } + + /** + * Resolve any pending promises for this instance + */ + function resolveWhen() { + var dfd = pendings[handle]; + if (dfd) { + dfd.forEach(function (promise) { + promise.resolve(instance); + }); + delete pendings[handle]; + } + } + }, + + /** + * Async accessor to registered component instance + * If not available then a promise is created to notify + * all listeners when the instance is registered. + */ + when : function(handle) { + if (isValidID(handle)) { + var deferred = $q.defer(); + var instance = self.get(handle); + + if (instance) { + deferred.resolve(instance); + } else { + if (pendings[handle] === undefined) { + pendings[handle] = []; + } + pendings[handle].push(deferred); + } + + return deferred.promise; + } + return $q.reject("Invalid `md-component-id` value."); + } + + }; + + function isValidID(handle){ + return handle && (handle !== ""); + } + + } + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * @ngdoc service + * @name $mdButtonInkRipple + * @module material.core + * + * @description + * Provides ripple effects for md-button. See $mdInkRipple service for all possible configuration options. + * + * @param {object=} scope Scope within the current context + * @param {object=} element The element the ripple effect should be applied to + * @param {object=} options (Optional) Configuration options to override the default ripple configuration + */ + + MdButtonInkRipple.$inject = ["$mdInkRipple"]; + angular.module('material.core') + .factory('$mdButtonInkRipple', MdButtonInkRipple); + + function MdButtonInkRipple($mdInkRipple) { + return { + attach: function attachRipple(scope, element, options) { + options = angular.extend(optionsForElement(element), options); + + return $mdInkRipple.attach(scope, element, options); + } + }; + + function optionsForElement(element) { + if (element.hasClass('md-icon-button')) { + return { + isMenuItem: element.hasClass('md-menu-item'), + fitRipple: true, + center: true + }; + } else { + return { + isMenuItem: element.hasClass('md-menu-item'), + dimBackground: true + }; + } + } + } +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * @ngdoc service + * @name $mdCheckboxInkRipple + * @module material.core + * + * @description + * Provides ripple effects for md-checkbox. See $mdInkRipple service for all possible configuration options. + * + * @param {object=} scope Scope within the current context + * @param {object=} element The element the ripple effect should be applied to + * @param {object=} options (Optional) Configuration options to override the defaultripple configuration + */ + + MdCheckboxInkRipple.$inject = ["$mdInkRipple"]; + angular.module('material.core') + .factory('$mdCheckboxInkRipple', MdCheckboxInkRipple); + + function MdCheckboxInkRipple($mdInkRipple) { + return { + attach: attach + }; + + function attach(scope, element, options) { + return $mdInkRipple.attach(scope, element, angular.extend({ + center: true, + dimBackground: false, + fitRipple: true + }, options)); + } + } +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * @ngdoc service + * @name $mdListInkRipple + * @module material.core + * + * @description + * Provides ripple effects for md-list. See $mdInkRipple service for all possible configuration options. + * + * @param {object=} scope Scope within the current context + * @param {object=} element The element the ripple effect should be applied to + * @param {object=} options (Optional) Configuration options to override the defaultripple configuration + */ + + MdListInkRipple.$inject = ["$mdInkRipple"]; + angular.module('material.core') + .factory('$mdListInkRipple', MdListInkRipple); + + function MdListInkRipple($mdInkRipple) { + return { + attach: attach + }; + + function attach(scope, element, options) { + return $mdInkRipple.attach(scope, element, angular.extend({ + center: false, + dimBackground: true, + outline: false, + rippleSize: 'full' + }, options)); + } + } +})(); + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.core.ripple + * @description + * Ripple + */ +InkRippleCtrl.$inject = ["$scope", "$element", "rippleOptions", "$window", "$timeout", "$mdUtil", "$mdColorUtil"]; +InkRippleDirective.$inject = ["$mdButtonInkRipple", "$mdCheckboxInkRipple"]; +angular.module('material.core') + .provider('$mdInkRipple', InkRippleProvider) + .directive('mdInkRipple', InkRippleDirective) + .directive('mdNoInk', attrNoDirective) + .directive('mdNoBar', attrNoDirective) + .directive('mdNoStretch', attrNoDirective); + +var DURATION = 450; + +/** + * @ngdoc directive + * @name mdInkRipple + * @module material.core.ripple + * + * @description + * The `md-ink-ripple` directive allows you to specify the ripple color or if a ripple is allowed. + * + * @param {string|boolean} md-ink-ripple A color string `#FF0000` or boolean (`false` or `0`) for + * preventing ripple + * + * @usage + * ### String values + * + * + * Ripples in red + * + * + * + * Not rippling + * + * + * + * ### Interpolated values + * + * + * Ripples with the return value of 'randomColor' function + * + * + * + * Ripples if 'canRipple' function return value is not 'false' or '0' + * + * + */ +function InkRippleDirective ($mdButtonInkRipple, $mdCheckboxInkRipple) { + return { + controller: angular.noop, + link: function (scope, element, attr) { + attr.hasOwnProperty('mdInkRippleCheckbox') + ? $mdCheckboxInkRipple.attach(scope, element) + : $mdButtonInkRipple.attach(scope, element); + } + }; +} + +/** + * @ngdoc service + * @name $mdInkRipple + * @module material.core.ripple + * + * @description + * `$mdInkRipple` is a service for adding ripples to any element. + * + * @usage + * + * app.factory('$myElementInkRipple', function($mdInkRipple) { + * return { + * attach: function (scope, element, options) { + * return $mdInkRipple.attach(scope, element, angular.extend({ + * center: false, + * dimBackground: true + * }, options)); + * } + * }; + * }); + * + * app.controller('myController', function ($scope, $element, $myElementInkRipple) { + * $scope.onClick = function (ev) { + * $myElementInkRipple.attach($scope, angular.element(ev.target), { center: true }); + * } + * }); + * + */ + +/** + * @ngdoc service + * @name $mdInkRippleProvider + * @module material.core.ripple + * + * @description + * If you want to disable ink ripples globally, for all components, you can call the + * `disableInkRipple` method in your app's config. + * + * + * @usage + * + * app.config(function ($mdInkRippleProvider) { + * $mdInkRippleProvider.disableInkRipple(); + * }); + * + */ + +function InkRippleProvider () { + var isDisabledGlobally = false; + + return { + disableInkRipple: disableInkRipple, + $get: ["$injector", function($injector) { + return { attach: attach }; + + /** + * @ngdoc method + * @name $mdInkRipple#attach + * + * @description + * Attaching given scope, element and options to inkRipple controller + * + * @param {object=} scope Scope within the current context + * @param {object=} element The element the ripple effect should be applied to + * @param {object=} options (Optional) Configuration options to override the defaultRipple configuration + * * `center` - Whether the ripple should start from the center of the container element + * * `dimBackground` - Whether the background should be dimmed with the ripple color + * * `colorElement` - The element the ripple should take its color from, defined by css property `color` + * * `fitRipple` - Whether the ripple should fill the element + */ + function attach (scope, element, options) { + if (isDisabledGlobally || element.controller('mdNoInk')) return angular.noop; + return $injector.instantiate(InkRippleCtrl, { + $scope: scope, + $element: element, + rippleOptions: options + }); + } + }] + }; + + /** + * @ngdoc method + * @name $mdInkRippleProvider#disableInkRipple + * + * @description + * A config-time method that, when called, disables ripples globally. + */ + function disableInkRipple () { + isDisabledGlobally = true; + } +} + +/** + * Controller used by the ripple service in order to apply ripples + * @ngInject + */ +function InkRippleCtrl ($scope, $element, rippleOptions, $window, $timeout, $mdUtil, $mdColorUtil) { + this.$window = $window; + this.$timeout = $timeout; + this.$mdUtil = $mdUtil; + this.$mdColorUtil = $mdColorUtil; + this.$scope = $scope; + this.$element = $element; + this.options = rippleOptions; + this.mousedown = false; + this.ripples = []; + this.timeout = null; // Stores a reference to the most-recent ripple timeout + this.lastRipple = null; + + $mdUtil.valueOnUse(this, 'container', this.createContainer); + + this.$element.addClass('md-ink-ripple'); + + // attach method for unit tests + ($element.controller('mdInkRipple') || {}).createRipple = angular.bind(this, this.createRipple); + ($element.controller('mdInkRipple') || {}).setColor = angular.bind(this, this.color); + + this.bindEvents(); +} + + +/** + * Either remove or unlock any remaining ripples when the user mouses off of the element (either by + * mouseup or mouseleave event) + */ +function autoCleanup (self, cleanupFn) { + if (self.mousedown || self.lastRipple) { + self.mousedown = false; + self.$mdUtil.nextTick(angular.bind(self, cleanupFn), false); + } +} + + +/** + * Returns the color that the ripple should be (either based on CSS or hard-coded) + * @returns {string} + */ +InkRippleCtrl.prototype.color = function (value) { + var self = this; + + // If assigning a color value, apply it to background and the ripple color + if (angular.isDefined(value)) { + self._color = self._parseColor(value); + } + + // If color lookup, use assigned, defined, or inherited + return self._color || self._parseColor(self.inkRipple()) || self._parseColor(getElementColor()); + + /** + * Finds the color element and returns its text color for use as default ripple color + * @returns {string} + */ + function getElementColor () { + var items = self.options && self.options.colorElement ? self.options.colorElement : []; + var elem = items.length ? items[ 0 ] : self.$element[ 0 ]; + + return elem ? self.$window.getComputedStyle(elem).color : 'rgb(0,0,0)'; + } +}; + +/** + * Updating the ripple colors based on the current inkRipple value + * or the element's computed style color + */ +InkRippleCtrl.prototype.calculateColor = function () { + return this.color(); +}; + + +/** + * Takes a string color and converts it to RGBA format + * @param {string} color + * @param {number} multiplier + * @returns {string} + */ +InkRippleCtrl.prototype._parseColor = function parseColor (color, multiplier) { + multiplier = multiplier || 1; + var colorUtil = this.$mdColorUtil; + + if (!color) return; + if (color.indexOf('rgba') === 0) return color.replace(/\d?\.?\d*\s*\)\s*$/, (0.1 * multiplier).toString() + ')'); + if (color.indexOf('rgb') === 0) return colorUtil.rgbToRgba(color); + if (color.indexOf('#') === 0) return colorUtil.hexToRgba(color); + +}; + +/** + * Binds events to the root element for + */ +InkRippleCtrl.prototype.bindEvents = function () { + this.$element.on('mousedown', angular.bind(this, this.handleMousedown)); + this.$element.on('mouseup touchend', angular.bind(this, this.handleMouseup)); + this.$element.on('mouseleave', angular.bind(this, this.handleMouseup)); + this.$element.on('touchmove', angular.bind(this, this.handleTouchmove)); +}; + +/** + * Create a new ripple on every mousedown event from the root element + * @param event {MouseEvent} + */ +InkRippleCtrl.prototype.handleMousedown = function (event) { + if (this.mousedown) return; + + // When jQuery is loaded, we have to get the original event + if (event.hasOwnProperty('originalEvent')) event = event.originalEvent; + this.mousedown = true; + if (this.options.center) { + this.createRipple(this.container.prop('clientWidth') / 2, this.container.prop('clientWidth') / 2); + } else { + + // We need to calculate the relative coordinates if the target is a sublayer of the ripple element + if (event.srcElement !== this.$element[0]) { + var layerRect = this.$element[0].getBoundingClientRect(); + var layerX = event.clientX - layerRect.left; + var layerY = event.clientY - layerRect.top; + + this.createRipple(layerX, layerY); + } else { + this.createRipple(event.offsetX, event.offsetY); + } + } +}; + +/** + * Either remove or unlock any remaining ripples when the user mouses off of the element (either by + * mouseup, touchend or mouseleave event) + */ +InkRippleCtrl.prototype.handleMouseup = function () { + this.$timeout(function () { + autoCleanup(this, this.clearRipples); + }.bind(this)); +}; + +/** + * Either remove or unlock any remaining ripples when the user mouses off of the element (by + * touchmove) + */ +InkRippleCtrl.prototype.handleTouchmove = function () { + autoCleanup(this, this.deleteRipples); +}; + +/** + * Cycles through all ripples and attempts to remove them. + */ +InkRippleCtrl.prototype.deleteRipples = function () { + for (var i = 0; i < this.ripples.length; i++) { + this.ripples[ i ].remove(); + } +}; + +/** + * Cycles through all ripples and attempts to remove them with fade. + * Depending on logic within `fadeInComplete`, some removals will be postponed. + */ +InkRippleCtrl.prototype.clearRipples = function () { + for (var i = 0; i < this.ripples.length; i++) { + this.fadeInComplete(this.ripples[ i ]); + } +}; + +/** + * Creates the ripple container element + * @returns {*} + */ +InkRippleCtrl.prototype.createContainer = function () { + var container = angular.element('
    '); + this.$element.append(container); + return container; +}; + +InkRippleCtrl.prototype.clearTimeout = function () { + if (this.timeout) { + this.$timeout.cancel(this.timeout); + this.timeout = null; + } +}; + +InkRippleCtrl.prototype.isRippleAllowed = function () { + var element = this.$element[0]; + do { + if (!element.tagName || element.tagName === 'BODY') break; + + if (element && angular.isFunction(element.hasAttribute)) { + if (element.hasAttribute('disabled')) return false; + if (this.inkRipple() === 'false' || this.inkRipple() === '0') return false; + } + + } while (element = element.parentNode); + return true; +}; + +/** + * The attribute `md-ink-ripple` may be a static or interpolated + * color value OR a boolean indicator (used to disable ripples) + */ +InkRippleCtrl.prototype.inkRipple = function () { + return this.$element.attr('md-ink-ripple'); +}; + +/** + * Creates a new ripple and adds it to the container. Also tracks ripple in `this.ripples`. + * @param left + * @param top + */ +InkRippleCtrl.prototype.createRipple = function (left, top) { + if (!this.isRippleAllowed()) return; + + var ctrl = this; + var colorUtil = ctrl.$mdColorUtil; + var ripple = angular.element('
    '); + var width = this.$element.prop('clientWidth'); + var height = this.$element.prop('clientHeight'); + var x = Math.max(Math.abs(width - left), left) * 2; + var y = Math.max(Math.abs(height - top), top) * 2; + var size = getSize(this.options.fitRipple, x, y); + var color = this.calculateColor(); + + ripple.css({ + left: left + 'px', + top: top + 'px', + background: 'black', + width: size + 'px', + height: size + 'px', + backgroundColor: colorUtil.rgbaToRgb(color), + borderColor: colorUtil.rgbaToRgb(color) + }); + this.lastRipple = ripple; + + // we only want one timeout to be running at a time + this.clearTimeout(); + this.timeout = this.$timeout(function () { + ctrl.clearTimeout(); + if (!ctrl.mousedown) ctrl.fadeInComplete(ripple); + }, DURATION * 0.35, false); + + if (this.options.dimBackground) this.container.css({ backgroundColor: color }); + this.container.append(ripple); + this.ripples.push(ripple); + ripple.addClass('md-ripple-placed'); + + this.$mdUtil.nextTick(function () { + + ripple.addClass('md-ripple-scaled md-ripple-active'); + ctrl.$timeout(function () { + ctrl.clearRipples(); + }, DURATION, false); + + }, false); + + function getSize (fit, x, y) { + return fit + ? Math.max(x, y) + : Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + } +}; + + + +/** + * After fadeIn finishes, either kicks off the fade-out animation or queues the element for removal on mouseup + * @param ripple + */ +InkRippleCtrl.prototype.fadeInComplete = function (ripple) { + if (this.lastRipple === ripple) { + if (!this.timeout && !this.mousedown) { + this.removeRipple(ripple); + } + } else { + this.removeRipple(ripple); + } +}; + +/** + * Kicks off the animation for removing a ripple + * @param ripple {Element} + */ +InkRippleCtrl.prototype.removeRipple = function (ripple) { + var ctrl = this; + var index = this.ripples.indexOf(ripple); + if (index < 0) return; + this.ripples.splice(this.ripples.indexOf(ripple), 1); + ripple.removeClass('md-ripple-active'); + ripple.addClass('md-ripple-remove'); + if (this.ripples.length === 0) this.container.css({ backgroundColor: '' }); + // use a 2-second timeout in order to allow for the animation to finish + // we don't actually care how long the animation takes + this.$timeout(function () { + ctrl.fadeOutComplete(ripple); + }, DURATION, false); +}; + +/** + * Removes the provided ripple from the DOM + * @param ripple + */ +InkRippleCtrl.prototype.fadeOutComplete = function (ripple) { + ripple.remove(); + this.lastRipple = null; +}; + +/** + * Used to create an empty directive. This is used to track flag-directives whose children may have + * functionality based on them. + * + * Example: `md-no-ink` will potentially be used by all child directives. + */ +function attrNoDirective () { + return { controller: angular.noop }; +} + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * @ngdoc service + * @name $mdTabInkRipple + * @module material.core + * + * @description + * Provides ripple effects for md-tabs. See $mdInkRipple service for all possible configuration options. + * + * @param {object=} scope Scope within the current context + * @param {object=} element The element the ripple effect should be applied to + * @param {object=} options (Optional) Configuration options to override the defaultripple configuration + */ + + MdTabInkRipple.$inject = ["$mdInkRipple"]; + angular.module('material.core') + .factory('$mdTabInkRipple', MdTabInkRipple); + + function MdTabInkRipple($mdInkRipple) { + return { + attach: attach + }; + + function attach(scope, element, options) { + return $mdInkRipple.attach(scope, element, angular.extend({ + center: false, + dimBackground: true, + outline: false, + rippleSize: 'full' + }, options)); + } + } +})(); + +})(); +(function(){ +"use strict"; + +angular.module('material.core.theming.palette', []) +.constant('$mdColorPalette', { + 'red': { + '50': '#ffebee', + '100': '#ffcdd2', + '200': '#ef9a9a', + '300': '#e57373', + '400': '#ef5350', + '500': '#f44336', + '600': '#e53935', + '700': '#d32f2f', + '800': '#c62828', + '900': '#b71c1c', + 'A100': '#ff8a80', + 'A200': '#ff5252', + 'A400': '#ff1744', + 'A700': '#d50000', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 400 500 600 A100 A200 A400', + 'contrastStrongLightColors': '700 800 900 A700' + }, + 'pink': { + '50': '#fce4ec', + '100': '#f8bbd0', + '200': '#f48fb1', + '300': '#f06292', + '400': '#ec407a', + '500': '#e91e63', + '600': '#d81b60', + '700': '#c2185b', + '800': '#ad1457', + '900': '#880e4f', + 'A100': '#ff80ab', + 'A200': '#ff4081', + 'A400': '#f50057', + 'A700': '#c51162', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 400 A100 A200 A400', + // White on 500 does not meet the minimum 4.5 contrast ratio (at 4.34), + // but it's worse with a dark foreground (3.61). + 'contrastStrongLightColors': '500 600 700 800 900 A700' + }, + 'purple': { + '50': '#f3e5f5', + '100': '#e1bee7', + '200': '#ce93d8', + '300': '#ba68c8', + '400': '#ab47bc', + '500': '#9c27b0', + '600': '#8e24aa', + '700': '#7b1fa2', + '800': '#6a1b9a', + '900': '#4a148c', + 'A100': '#ea80fc', + 'A200': '#e040fb', + 'A400': '#d500f9', + 'A700': '#aa00ff', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 A100 A200 A400', + 'contrastStrongLightColors': '400 500 600 700 800 900 A700' + }, + 'deep-purple': { + '50': '#ede7f6', + '100': '#d1c4e9', + '200': '#b39ddb', + '300': '#9575cd', + '400': '#7e57c2', + '500': '#673ab7', + '600': '#5e35b1', + '700': '#512da8', + '800': '#4527a0', + '900': '#311b92', + 'A100': '#b388ff', + 'A200': '#7c4dff', + 'A400': '#651fff', + 'A700': '#6200ea', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 A100', + 'contrastStrongLightColors': '400 500 600 700 800 900 A200 A400 A700' + }, + 'indigo': { + '50': '#e8eaf6', + '100': '#c5cae9', + '200': '#9fa8da', + '300': '#7986cb', + '400': '#5c6bc0', + '500': '#3f51b5', + '600': '#3949ab', + '700': '#303f9f', + '800': '#283593', + '900': '#1a237e', + 'A100': '#8c9eff', + 'A200': '#536dfe', + 'A400': '#3d5afe', + 'A700': '#304ffe', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 A100 A200', + 'contrastStrongLightColors': '400 500 600 700 800 900 A400 A700' + }, + 'blue': { + '50': '#e3f2fd', + '100': '#bbdefb', + '200': '#90caf9', + '300': '#64b5f6', + '400': '#42a5f5', + '500': '#2196f3', + '600': '#1e88e5', + '700': '#1976d2', + '800': '#1565c0', + '900': '#0d47a1', + 'A100': '#82b1ff', + 'A200': '#448aff', + 'A400': '#2979ff', + 'A700': '#2962ff', + 'contrastDefaultColor': 'light', + // White on A400 does not meet the minimum 4.5 contrast ratio (at 3.98), + // but it's worse with a dark foreground (3.94). + 'contrastDarkColors': '50 100 200 300 400 500 600 A100 A200', + 'contrastStrongLightColors': '700 800 900 A400 A700' + }, + 'light-blue': { + '50': '#e1f5fe', + '100': '#b3e5fc', + '200': '#81d4fa', + '300': '#4fc3f7', + '400': '#29b6f6', + '500': '#03a9f4', + '600': '#039be5', + '700': '#0288d1', + '800': '#0277bd', + '900': '#01579b', + 'A100': '#80d8ff', + 'A200': '#40c4ff', + 'A400': '#00b0ff', + 'A700': '#0091ea', + 'contrastDefaultColor': 'dark', + // Dark on 700 does not meet the minimum 4.5 contrast ratio (at 4.07), + // but it's worse with a white foreground (3.85). + 'contrastStrongLightColors': '800 900 A700' + }, + 'cyan': { + '50': '#e0f7fa', + '100': '#b2ebf2', + '200': '#80deea', + '300': '#4dd0e1', + '400': '#26c6da', + '500': '#00bcd4', + '600': '#00acc1', + '700': '#0097a7', + '800': '#00838f', + '900': '#006064', + 'A100': '#84ffff', + 'A200': '#18ffff', + 'A400': '#00e5ff', + 'A700': '#00b8d4', + 'contrastDefaultColor': 'dark', + // Dark on 700 does not meet the minimum 4.5 contrast ratio (at 4.47), + // but it's worse with a white foreground (3.5). + 'contrastStrongLightColors': '800 900' + }, + 'teal': { + '50': '#e0f2f1', + '100': '#b2dfdb', + '200': '#80cbc4', + '300': '#4db6ac', + '400': '#26a69a', + '500': '#009688', + '600': '#00897b', + '700': '#00796b', + '800': '#00695c', + '900': '#004d40', + 'A100': '#a7ffeb', + 'A200': '#64ffda', + 'A400': '#1de9b6', + 'A700': '#00bfa5', + 'contrastDefaultColor': 'dark', + // Dark on 500 does not meet the minimum 4.5 contrast ratio (at 4.27), + // but it's worse with a white foreground (3.67). + // White on 600 does not meet the minimum 4.5 contrast ratio (at 4.31), + // but it's worse with a dark foreground (3.64). + 'contrastStrongLightColors': '600 700 800 900' + }, + 'green': { + '50': '#e8f5e9', + '100': '#c8e6c9', + '200': '#a5d6a7', + '300': '#81c784', + '400': '#66bb6a', + '500': '#4caf50', + '600': '#43a047', + '700': '#388e3c', + '800': '#2e7d32', + '900': '#1b5e20', + 'A100': '#b9f6ca', + 'A200': '#69f0ae', + 'A400': '#00e676', + 'A700': '#00c853', + 'contrastDefaultColor': 'dark', + // White on 700 does not meet the minimum 4.5 contrast ratio (at 4.11), + // but it's worse with a dark foreground (3.81). + 'contrastStrongLightColors': '700 800 900' + }, + 'light-green': { + '50': '#f1f8e9', + '100': '#dcedc8', + '200': '#c5e1a5', + '300': '#aed581', + '400': '#9ccc65', + '500': '#8bc34a', + '600': '#7cb342', + '700': '#689f38', + '800': '#558b2f', + '900': '#33691e', + 'A100': '#ccff90', + 'A200': '#b2ff59', + 'A400': '#76ff03', + 'A700': '#64dd17', + 'contrastDefaultColor': 'dark', + 'contrastStrongLightColors': '800 900' + }, + 'lime': { + '50': '#f9fbe7', + '100': '#f0f4c3', + '200': '#e6ee9c', + '300': '#dce775', + '400': '#d4e157', + '500': '#cddc39', + '600': '#c0ca33', + '700': '#afb42b', + '800': '#9e9d24', + '900': '#827717', + 'A100': '#f4ff81', + 'A200': '#eeff41', + 'A400': '#c6ff00', + 'A700': '#aeea00', + 'contrastDefaultColor': 'dark', + 'contrastStrongLightColors': '900' + }, + 'yellow': { + '50': '#fffde7', + '100': '#fff9c4', + '200': '#fff59d', + '300': '#fff176', + '400': '#ffee58', + '500': '#ffeb3b', + '600': '#fdd835', + '700': '#fbc02d', + '800': '#f9a825', + '900': '#f57f17', + 'A100': '#ffff8d', + 'A200': '#ffff00', + 'A400': '#ffea00', + 'A700': '#ffd600', + 'contrastDefaultColor': 'dark' + }, + 'amber': { + '50': '#fff8e1', + '100': '#ffecb3', + '200': '#ffe082', + '300': '#ffd54f', + '400': '#ffca28', + '500': '#ffc107', + '600': '#ffb300', + '700': '#ffa000', + '800': '#ff8f00', + '900': '#ff6f00', + 'A100': '#ffe57f', + 'A200': '#ffd740', + 'A400': '#ffc400', + 'A700': '#ffab00', + 'contrastDefaultColor': 'dark' + }, + 'orange': { + '50': '#fff3e0', + '100': '#ffe0b2', + '200': '#ffcc80', + '300': '#ffb74d', + '400': '#ffa726', + '500': '#ff9800', + '600': '#fb8c00', + '700': '#f57c00', + '800': '#ef6c00', + '900': '#e65100', + 'A100': '#ffd180', + 'A200': '#ffab40', + 'A400': '#ff9100', + 'A700': '#ff6d00', + 'contrastDefaultColor': 'dark', + 'contrastStrongLightColors': '900' + }, + 'deep-orange': { + '50': '#fbe9e7', + '100': '#ffccbc', + '200': '#ffab91', + '300': '#ff8a65', + '400': '#ff7043', + '500': '#ff5722', + '600': '#f4511e', + '700': '#e64a19', + '800': '#d84315', + '900': '#bf360c', + 'A100': '#ff9e80', + 'A200': '#ff6e40', + 'A400': '#ff3d00', + 'A700': '#dd2c00', + 'contrastDefaultColor': 'dark', + // Dark on 700 does not meet the minimum 4.5 contrast ratio (at 4.01), + // but it's worse with a white foreground (3.91). + // White on 800 does not meet the minimum 4.5 contrast ratio (at 4.43), + // but it's worse with a dark foreground (3.54). + 'contrastStrongLightColors': '800 900 A400 A700', + }, + 'brown': { + '50': '#efebe9', + '100': '#d7ccc8', + '200': '#bcaaa4', + '300': '#a1887f', + '400': '#8d6e63', + '500': '#795548', + '600': '#6d4c41', + '700': '#5d4037', + '800': '#4e342e', + '900': '#3e2723', + 'A100': '#d7ccc8', + 'A200': '#bcaaa4', + 'A400': '#8d6e63', + 'A700': '#5d4037', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 A100 A200', + 'contrastStrongLightColors': '400 500 600 700 800 900 A400 A700' + }, + 'grey': { + '50': '#fafafa', + '100': '#f5f5f5', + '200': '#eeeeee', + '300': '#e0e0e0', + '400': '#bdbdbd', + '500': '#9e9e9e', + '600': '#757575', + '700': '#616161', + '800': '#424242', + '900': '#212121', + 'A100': '#ffffff', + 'A200': '#000000', + 'A400': '#303030', + 'A700': '#616161', + 'contrastDefaultColor': 'dark', + 'contrastLightColors': '700 800 900 A200 A400 A700', + 'contrastStrongLightColors': '600' + }, + 'blue-grey': { + '50': '#eceff1', + '100': '#cfd8dc', + '200': '#b0bec5', + '300': '#90a4ae', + '400': '#78909c', + '500': '#607d8b', + '600': '#546e7a', + '700': '#455a64', + '800': '#37474f', + '900': '#263238', + 'A100': '#cfd8dc', + 'A200': '#b0bec5', + 'A400': '#78909c', + 'A700': '#455a64', + 'contrastDefaultColor': 'light', + 'contrastDarkColors': '50 100 200 300 400 A100 A200 A400', + // White on 500 does not meet the minimum 4.5 contrast ratio (at 4.37), + // but it's worse with a dark foreground. + 'contrastStrongLightColors': '500 600 700 800 900 A700' + } +}); + +})(); +(function(){ +"use strict"; + +(function(angular) { + 'use strict'; +/** + * @ngdoc module + * @name material.core.theming + * @description + * Theming + */ +detectDisabledThemes.$inject = ["$mdThemingProvider"]; +ThemingDirective.$inject = ["$mdTheming", "$interpolate", "$parse", "$mdUtil", "$q", "$log"]; +ThemableDirective.$inject = ["$mdTheming"]; +ThemingProvider.$inject = ["$mdColorPalette", "$$mdMetaProvider"]; +generateAllThemes.$inject = ["$injector", "$mdTheming"]; +angular.module('material.core.theming', ['material.core.theming.palette', 'material.core.meta']) + .directive('mdTheme', ThemingDirective) + .directive('mdThemable', ThemableDirective) + .directive('mdThemesDisabled', disableThemesDirective) + .provider('$mdTheming', ThemingProvider) + .config(detectDisabledThemes) + .run(generateAllThemes); + +/** + * Detect if the HTML or the BODY tags has a [md-themes-disabled] attribute + * If yes, then immediately disable all theme stylesheet generation and DOM injection + */ +/** + * @ngInject + */ +function detectDisabledThemes($mdThemingProvider) { + var isDisabled = !!document.querySelector('[md-themes-disabled]'); + $mdThemingProvider.disableTheming(isDisabled); +} + +/** + * @ngdoc service + * @name $mdThemingProvider + * @module material.core.theming + * + * @description Provider to configure the `$mdTheming` service. + * + * ### Default Theme + * The `$mdThemingProvider` uses by default the following theme configuration: + * + * - Primary Palette: `Blue` + * - Accent Palette: `Pink` + * - Warn Palette: `Deep-Orange` + * - Background Palette: `Grey` + * + * If you don't want to use the `md-theme` directive on the elements itself, you may want to overwrite + * the default theme.
    + * This can be done by using the following markup. + * + * + * myAppModule.config(function($mdThemingProvider) { + * $mdThemingProvider + * .theme('default') + * .primaryPalette('blue') + * .accentPalette('teal') + * .warnPalette('red') + * .backgroundPalette('grey'); + * }); + * + * + + * ### Dynamic Themes + * + * By default, if you change a theme at runtime, the `$mdTheming` service will not detect those changes.
    + * If you have an application, which changes its theme on runtime, you have to enable theme watching. + * + * + * myAppModule.config(function($mdThemingProvider) { + * // Enable theme watching. + * $mdThemingProvider.alwaysWatchTheme(true); + * }); + * + * + * ### Custom Theme Styles + * + * Sometimes you may want to use your own theme styles for some custom components.
    + * You are able to register your own styles by using the following markup. + * + * + * myAppModule.config(function($mdThemingProvider) { + * // Register our custom stylesheet into the theming provider. + * $mdThemingProvider.registerStyles(STYLESHEET); + * }); + * + * + * The `registerStyles` method only accepts strings as value, so you're actually not able to load an external + * stylesheet file into the `$mdThemingProvider`. + * + * If it's necessary to load an external stylesheet, we suggest using a bundler, which supports including raw content, + * like [raw-loader](https://github.com/webpack/raw-loader) for `webpack`. + * + * + * myAppModule.config(function($mdThemingProvider) { + * // Register your custom stylesheet into the theming provider. + * $mdThemingProvider.registerStyles(require('../styles/my-component.theme.css')); + * }); + * + * + * ### Browser color + * + * Enables browser header coloring + * for more info please visit: + * https://developers.google.com/web/fundamentals/design-and-ui/browser-customization/theme-color + * + * Options parameter:
    + * `theme` - A defined theme via `$mdThemeProvider` to use the palettes from. Default is `default` theme.
    + * `palette` - Can be any one of the basic material design palettes, extended defined palettes and 'primary', + * 'accent', 'background' and 'warn'. Default is `primary`.
    + * `hue` - The hue from the selected palette. Default is `800`
    + * + * + * myAppModule.config(function($mdThemingProvider) { + * // Enable browser color + * $mdThemingProvider.enableBrowserColor({ + * theme: 'myTheme', // Default is 'default' + * palette: 'accent', // Default is 'primary', any basic material palette and extended palettes are available + * hue: '200' // Default is '800' + * }); + * }); + * + */ + +/** + * Some Example Valid Theming Expressions + * ======================================= + * + * Intention group expansion: (valid for primary, accent, warn, background) + * + * {{primary-100}} - grab shade 100 from the primary palette + * {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7 + * {{primary-100-contrast}} - grab shade 100's contrast color + * {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette + * {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1 + * {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue + * {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules + * {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue + * {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules + * {{primary-contrast-divider}} - Apply divider opacity to contrast color + * + * Foreground expansion: Applies rgba to black/white foreground text + * + * Old Foreground Expressions: + * {{foreground-1}} - used for primary text + * {{foreground-2}} - used for secondary text/divider + * {{foreground-3}} - used for disabled text + * {{foreground-4}} - used for dividers + * + * New Foreground Expressions: + * + * Apply primary text color for contrasting with default background + * {{background-default-contrast}} - default opacity + * {{background-default-contrast-secondary}} - opacity for secondary text + * {{background-default-contrast-hint}} - opacity for hints and placeholders + * {{background-default-contrast-disabled}} - opacity for disabled text + * {{background-default-contrast-divider}} - opacity for dividers + * + * Apply contrast color for specific shades + * {{background-50-contrast-icon}} - Apply contrast color for icon on background's shade 50 hue + */ + +// In memory generated CSS rules; registered by theme.name +var GENERATED = { }; + +// In memory storage of defined themes and color palettes (both loaded by CSS, and user specified) +var PALETTES; + +// Text colors are automatically generated based on background color when not specified +// Custom palettes can provide override colors +// @see https://material.io/archive/guidelines/style/color.html#color-usability +var DARK_FOREGROUND = { + name: 'dark', +}; +var LIGHT_FOREGROUND = { + name: 'light', +}; + +var DARK_SHADOW = '1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)'; +var LIGHT_SHADOW = ''; + +var DARK_CONTRAST_COLOR = colorToRgbaArray('rgba(0,0,0,0.87)'); +var LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgba(255,255,255,0.87)'); +var STRONG_LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgb(255,255,255)'); + +var THEME_COLOR_TYPES = ['primary', 'accent', 'warn', 'background']; +var DEFAULT_COLOR_TYPE = 'primary'; + +// A color in a theme will use these hues by default, if not specified by user. +var LIGHT_DEFAULT_HUES = { + 'accent': { + 'default': 'A200', + 'hue-1': 'A100', + 'hue-2': 'A400', + 'hue-3': 'A700' + }, + 'background': { + 'default': '50', + 'hue-1': 'A100', + 'hue-2': '100', + 'hue-3': '300' + } +}; + +var DARK_DEFAULT_HUES = { + 'background': { + 'default': 'A400', + 'hue-1': '800', + 'hue-2': '900', + 'hue-3': 'A200' + } +}; + +// Icon opacity values (active/inactive) from +// https://material.io/archive/guidelines/style/color.html#color-usability +var DARK_CONTRAST_OPACITY = { + 'icon': 0.54, + 'secondary': 0.54, + 'disabled': 0.38, + 'hint': 0.38, + 'divider': 0.12, +}; + +var LIGHT_CONTRAST_OPACITY = { + 'icon': 0.87, + 'secondary': 0.7, + 'disabled': 0.5, + 'hint': 0.5, + 'divider': 0.12 +}; + +// Icon opacity values (active/inactive) from +// https://material.io/archive/guidelines/style/color.html#color-usability +var STRONG_LIGHT_CONTRAST_OPACITY = { + 'icon': 1.0, + 'secondary': 0.7, + 'disabled': 0.5, + 'hint': 0.5, + 'divider': 0.12 +}; + +THEME_COLOR_TYPES.forEach(function(colorType) { + // Color types with unspecified default hues will use these default hue values + var defaultDefaultHues = { + 'default': '500', + 'hue-1': '300', + 'hue-2': '800', + 'hue-3': 'A100' + }; + if (!LIGHT_DEFAULT_HUES[colorType]) LIGHT_DEFAULT_HUES[colorType] = defaultDefaultHues; + if (!DARK_DEFAULT_HUES[colorType]) DARK_DEFAULT_HUES[colorType] = defaultDefaultHues; +}); + +var VALID_HUE_VALUES = [ + '50', '100', '200', '300', '400', '500', '600', + '700', '800', '900', 'A100', 'A200', 'A400', 'A700' +]; + +var themeConfig = { + disableTheming : false, // Generate our themes at run time; also disable stylesheet DOM injection + generateOnDemand : false, // Whether or not themes are to be generated on-demand (vs. eagerly). + registeredStyles : [], // Custom styles registered to be used in the theming of custom components. + nonce : null // Nonce to be added as an attribute to the generated themes style tags. +}; + +/** + * + */ +function ThemingProvider($mdColorPalette, $$mdMetaProvider) { + ThemingService.$inject = ["$rootScope", "$mdUtil", "$q", "$log"]; + PALETTES = { }; + var THEMES = { }; + + var themingProvider; + + var alwaysWatchTheme = false; + var defaultTheme = 'default'; + + // Load JS Defined Palettes + angular.extend(PALETTES, $mdColorPalette); + + // Default theme defined in core.js + + /** + * Adds `theme-color` and `msapplication-navbutton-color` meta tags with the color parameter + * @param {string} color Hex value of the wanted browser color + * @returns {function} Remove function of the meta tags + */ + var setBrowserColor = function (color) { + // Chrome, Firefox OS and Opera + var removeChrome = $$mdMetaProvider.setMeta('theme-color', color); + // Windows Phone + var removeWindows = $$mdMetaProvider.setMeta('msapplication-navbutton-color', color); + + return function () { + removeChrome(); + removeWindows(); + }; + }; + + /** + * @ngdoc method + * @name $mdThemingProvider#enableBrowserColor + * @description + * Enables browser header coloring. For more info please visit + * + * Web Fundamentals. + * @param {object=} options Options for the browser color, which include:
    + * - `theme` - `{string}`: A defined theme via `$mdThemeProvider` to use the palettes from. Default is `default` theme.
    + * - `palette` - `{string}`: Can be any one of the basic material design palettes, extended defined palettes, or `primary`, + * `accent`, `background`, and `warn`. Default is `primary`.
    + * - `hue` - `{string}`: The hue from the selected palette. Default is `800`.
    + * @returns {function} Function that removes the browser coloring when called. + */ + var enableBrowserColor = function (options) { + options = angular.isObject(options) ? options : {}; + + var theme = options.theme || 'default'; + var hue = options.hue || '800'; + + var palette = PALETTES[options.palette] || + PALETTES[THEMES[theme].colors[options.palette || 'primary'].name]; + + var color = angular.isObject(palette[hue]) ? palette[hue].hex : palette[hue]; + if (color.substr(0, 1) !== '#') color = '#' + color; + + return setBrowserColor(color); + }; + + return themingProvider = { + definePalette: definePalette, + extendPalette: extendPalette, + theme: registerTheme, + + /** + * return a read-only clone of the current theme configuration + */ + configuration : function() { + return angular.extend({ }, themeConfig, { + defaultTheme : defaultTheme, + alwaysWatchTheme : alwaysWatchTheme, + registeredStyles : [].concat(themeConfig.registeredStyles) + }); + }, + + /** + * @ngdoc method + * @name $mdThemingProvider#disableTheming + * @description + * An easier way to disable theming without having to use `.constant("$MD_THEME_CSS","");`. + * This disables all dynamic theme style sheet generations and injections. + * @param {boolean=} isDisabled Disable all dynamic theme style sheet generations and injections + * if `true` or `undefined`. + */ + disableTheming: function(isDisabled) { + themeConfig.disableTheming = angular.isUndefined(isDisabled) || !!isDisabled; + }, + + /** + * @ngdoc method + * @name $mdThemingProvider#registerStyles + * @param {string} styles The styles to be appended to AngularJS Material's built in theme CSS. + */ + registerStyles: function(styles) { + themeConfig.registeredStyles.push(styles); + }, + + /** + * @ngdoc method + * @name $mdThemingProvider#setNonce + * @param {string} nonceValue The nonce to be added as an attribute to the theme style tags. + * Setting a value allows the use of CSP policy without using the `'unsafe-inline'` directive. + * The string must already be base64 encoded. You can use `btoa(string)` to do this encoding. + * In your CSP's `style-src`, you would then add an entry for `'nonce-nonceValue'`. + */ + setNonce: function(nonceValue) { + themeConfig.nonce = nonceValue; + }, + + generateThemesOnDemand: function(onDemand) { + themeConfig.generateOnDemand = onDemand; + }, + + /** + * @ngdoc method + * @name $mdThemingProvider#setDefaultTheme + * @param {string} theme Default theme name to be applied to elements. + * Default value is `default`. + */ + setDefaultTheme: function(theme) { + defaultTheme = theme; + }, + + /** + * @ngdoc method + * @name $mdThemingProvider#alwaysWatchTheme + * @param {boolean} alwaysWatch Whether or not to always watch themes for changes and re-apply + * classes when they change. Default is `false`. Enabling can reduce performance. + */ + alwaysWatchTheme: function(alwaysWatch) { + alwaysWatchTheme = alwaysWatch; + }, + + enableBrowserColor: enableBrowserColor, + + $get: ThemingService, + _LIGHT_DEFAULT_HUES: LIGHT_DEFAULT_HUES, + _DARK_DEFAULT_HUES: DARK_DEFAULT_HUES, + _PALETTES: PALETTES, + _THEMES: THEMES, + _parseRules: parseRules, + _rgba: rgba + }; + + /** + * @ngdoc method + * @name $mdThemingProvider#definePalette + * @description + * In the event that you need to define a custom color palette, you can use this function to + * make it available to your theme for use in its intention groups.
    + * Note that you must specify all hues in the definition map. + * @param {string} name Name of palette being defined + * @param {object} map Palette definition that includes hue definitions and contrast colors: + * - `'50'` - `{string}`: HEX color + * - `'100'` - `{string}`: HEX color + * - `'200'` - `{string}`: HEX color + * - `'300'` - `{string}`: HEX color + * - `'400'` - `{string}`: HEX color + * - `'500'` - `{string}`: HEX color + * - `'600'` - `{string}`: HEX color + * - `'700'` - `{string}`: HEX color + * - `'800'` - `{string}`: HEX color + * - `'900'` - `{string}`: HEX color + * - `'A100'` - `{string}`: HEX color + * - `'A200'` - `{string}`: HEX color + * - `'A400'` - `{string}`: HEX color + * - `'A700'` - `{string}`: HEX color + * - `'contrastDefaultColor'` - `{string}`: `light` or `dark` + * - `'contrastDarkColors'` - `{string[]}`: Hues which should use dark contrast colors (i.e. raised button text). + * For example: `['50', '100', '200', '300', '400', 'A100']`. + * - `'contrastLightColors'` - `{string[]}`: Hues which should use light contrast colors (i.e. raised button text). + * For example: `['500', '600', '700', '800', '900', 'A200', 'A400', 'A700']`. + */ + function definePalette(name, map) { + map = map || {}; + PALETTES[name] = checkPaletteValid(name, map); + return themingProvider; + } + + /** + * @ngdoc method + * @name $mdThemingProvider#extendPalette + * @description + * Sometimes it is easier to extend an existing color palette and then change a few properties, + * rather than defining a whole new palette. + * @param {string} name Name of palette being extended + * @param {object} map Palette definition that includes optional hue definitions and contrast colors: + * - `'50'` - `{string}`: HEX color + * - `'100'` - `{string}`: HEX color + * - `'200'` - `{string}`: HEX color + * - `'300'` - `{string}`: HEX color + * - `'400'` - `{string}`: HEX color + * - `'500'` - `{string}`: HEX color + * - `'600'` - `{string}`: HEX color + * - `'700'` - `{string}`: HEX color + * - `'800'` - `{string}`: HEX color + * - `'900'` - `{string}`: HEX color + * - `'A100'` - `{string}`: HEX color + * - `'A200'` - `{string}`: HEX color + * - `'A400'` - `{string}`: HEX color + * - `'A700'` - `{string}`: HEX color + * - `'contrastDefaultColor'` - `{string}`: `light` or `dark` + * - `'contrastDarkColors'` - `{string[]}`: Hues which should use dark contrast colors (i.e. raised button text). + * For example: `['50', '100', '200', '300', '400', 'A100']`. + * - `'contrastLightColors'` - `{string[]}`: Hues which should use light contrast colors (i.e. raised button text). + * For example: `['500', '600', '700', '800', '900', 'A200', 'A400', 'A700']`. + * @returns {object} A new object which is a copy of the given palette, `name`, + * with variables from `map` overwritten. + */ + function extendPalette(name, map) { + return checkPaletteValid(name, angular.extend({}, PALETTES[name] || {}, map)); + } + + // Make sure that palette has all required hues + function checkPaletteValid(name, map) { + var missingColors = VALID_HUE_VALUES.filter(function(field) { + return !map[field]; + }); + if (missingColors.length) { + throw new Error("Missing colors %1 in palette %2!" + .replace('%1', missingColors.join(', ')) + .replace('%2', name)); + } + + return map; + } + + /** + * @ngdoc method + * @name $mdThemingProvider#theme + * @description + * Register a theme (which is a collection of color palettes); i.e. `warn`, `accent`, + * `background`, and `primary`.
    + * Optionally inherit from an existing theme. + * @param {string} name Name of theme being registered + * @param {string=} inheritFrom Existing theme name to inherit from + */ + function registerTheme(name, inheritFrom) { + if (THEMES[name]) return THEMES[name]; + + inheritFrom = inheritFrom || 'default'; + + var parentTheme = typeof inheritFrom === 'string' ? THEMES[inheritFrom] : inheritFrom; + var theme = new Theme(name); + + if (parentTheme) { + angular.forEach(parentTheme.colors, function(color, colorType) { + theme.colors[colorType] = { + name: color.name, + // Make sure a COPY of the hues is given to the child color, + // not the same reference. + hues: angular.extend({}, color.hues) + }; + }); + } + THEMES[name] = theme; + + return theme; + } + + function Theme(name) { + var self = this; + self.name = name; + self.colors = {}; + + self.dark = setDark; + setDark(false); + + function setDark(isDark) { + isDark = arguments.length === 0 ? true : !!isDark; + + // If no change, abort + if (isDark === self.isDark) return; + + self.isDark = isDark; + + self.foregroundPalette = self.isDark ? LIGHT_FOREGROUND : DARK_FOREGROUND; + self.foregroundShadow = self.isDark ? DARK_SHADOW : LIGHT_SHADOW; + + // Light and dark themes have different default hues. + // Go through each existing color type for this theme, and for every + // hue value that is still the default hue value from the previous light/dark setting, + // set it to the default hue value from the new light/dark setting. + var newDefaultHues = self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES; + var oldDefaultHues = self.isDark ? LIGHT_DEFAULT_HUES : DARK_DEFAULT_HUES; + angular.forEach(newDefaultHues, function(newDefaults, colorType) { + var color = self.colors[colorType]; + var oldDefaults = oldDefaultHues[colorType]; + if (color) { + for (var hueName in color.hues) { + if (color.hues[hueName] === oldDefaults[hueName]) { + color.hues[hueName] = newDefaults[hueName]; + } + } + } + }); + + return self; + } + + THEME_COLOR_TYPES.forEach(function(colorType) { + var defaultHues = (self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES)[colorType]; + self[colorType + 'Palette'] = function setPaletteType(paletteName, hues) { + var color = self.colors[colorType] = { + name: paletteName, + hues: angular.extend({}, defaultHues, hues) + }; + + Object.keys(color.hues).forEach(function(name) { + if (!defaultHues[name]) { + throw new Error("Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4" + .replace('%1', name) + .replace('%2', self.name) + .replace('%3', paletteName) + .replace('%4', Object.keys(defaultHues).join(', ')) + ); + } + }); + Object.keys(color.hues).map(function(key) { + return color.hues[key]; + }).forEach(function(hueValue) { + if (VALID_HUE_VALUES.indexOf(hueValue) === -1) { + throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5" + .replace('%1', hueValue) + .replace('%2', self.name) + .replace('%3', colorType) + .replace('%4', paletteName) + .replace('%5', VALID_HUE_VALUES.join(', ')) + ); + } + }); + return self; + }; + }); + } + + /** + * @ngdoc service + * @name $mdTheming + * @module material.core.theming + * @description + * Service that makes an element apply theming related classes to itself. + * + * For more information on the hue objects, their default values, as well as valid hue values, please visit the custom hues section of Configuring a Theme. + * + * + * // Example component directive that we want to apply theming classes to. + * app.directive('myFancyDirective', function($mdTheming) { + * return { + * restrict: 'AE', + * link: function(scope, element, attrs) { + * // Initialize the service using our directive's element + * $mdTheming(element); + * + * $mdTheming.defineTheme('myTheme', { + * primary: 'blue', + * primaryHues: { + * default: '500', + * hue-1: '300', + * hue-2: '900', + * hue-3: 'A100' + * }, + * accent: 'pink', + * accentHues: { + * default: '600', + * hue-1: '300', + * hue-2: '200', + * hue-3: 'A500' + * }, + * warn: 'red', + * // It's not necessary to specify all hues in the object. + * warnHues: { + * default: '200', + * hue-3: 'A100' + * }, + * // It's not necessary to specify custom hues at all. + * background: 'grey', + * dark: true + * }); + * // Your directive's custom code here. + * } + * }; + * }); + * + * @param {element=} element Element that will have theming classes applied to it. + */ + + /** + * @ngdoc property + * @name $mdTheming#THEMES + * @description + * Property to get all the themes defined + * @returns {object} All the themes defined with their properties. + */ + + /** + * @ngdoc property + * @name $mdTheming#PALETTES + * @description + * Property to get all the palettes defined + * @returns {object} All the palettes defined with their colors. + */ + + /** + * @ngdoc method + * @name $mdTheming#registered + * @description + * Determine is specified theme name is a valid, registered theme + * @param {string} themeName the theme to check if registered + * @returns {boolean} whether the theme is registered or not + */ + + /** + * @ngdoc method + * @name $mdTheming#defaultTheme + * @description + * Returns the default theme + * @returns {string} The default theme + */ + + /** + * @ngdoc method + * @name $mdTheming#generateTheme + * @description + * Lazy generate themes - by default, every theme is generated when defined. + * You can disable this in the configuration section using the + * `$mdThemingProvider.generateThemesOnDemand(true);` + * + * The theme name that is passed in must match the name of the theme that was defined as part of + * the configuration block. + * + * @param {string} name theme name to generate + */ + + /** + * @ngdoc method + * @name $mdTheming#setBrowserColor + * @description + * Enables browser header coloring. For more info please visit + * + * Web Fundamentals. + * @param {object=} options Options for the browser color, which include:
    + * - `theme` - `{string}`: A defined theme via `$mdThemeProvider` to use the palettes from. + * Default is `default` theme.
    + * - `palette` - `{string}`: Can be any one of the basic material design palettes, extended + * defined palettes, or `primary`, `accent`, `background`, and `warn`. Default is `primary`. + *
    + * - `hue` - `{string}`: The hue from the selected palette. Default is `800`.
    + * @returns {function} Function that removes the browser coloring when called. + */ + + /** + * @ngdoc method + * @name $mdTheming#defineTheme + * @description + * Dynamically define a theme by using an options object that contains palette names. + * + * @param {string} name Theme name to define + * @param {object} options Theme definition options + * + * Options are:
    + * - `primary` - `{string}`: The name of the primary palette to use in the theme.
    + * - `primaryHues` - `{object=}`: Override hues for primary palette.
    + * - `accent` - `{string}`: The name of the accent palette to use in the theme.
    + * - `accentHues` - `{object=}`: Override hues for accent palette.
    + * - `warn` - `{string}`: The name of the warn palette to use in the theme.
    + * - `warnHues` - `{object=}`: Override hues for warn palette.
    + * - `background` - `{string}`: The name of the background palette to use in the theme.
    + * - `backgroundHues` - `{object=}`: Override hues for background palette.
    + * - `dark` - `{boolean}`: Indicates if it's a dark theme.
    + * @returns {Promise} A resolved promise with the new theme name. + */ + + /* @ngInject */ + function ThemingService($rootScope, $mdUtil, $q, $log) { + // Allow us to be invoked via a linking function signature. + var applyTheme = function (scope, el) { + if (el === undefined) { el = scope; scope = undefined; } + if (scope === undefined) { scope = $rootScope; } + applyTheme.inherit(el, el); + }; + + Object.defineProperty(applyTheme, 'THEMES', { + get: function () { + return angular.extend({}, THEMES); + } + }); + Object.defineProperty(applyTheme, 'PALETTES', { + get: function () { + return angular.extend({}, PALETTES); + } + }); + Object.defineProperty(applyTheme, 'ALWAYS_WATCH', { + get: function () { + return alwaysWatchTheme; + } + }); + applyTheme.inherit = inheritTheme; + applyTheme.registered = registered; + applyTheme.defaultTheme = function() { return defaultTheme; }; + applyTheme.generateTheme = function(name) { generateTheme(THEMES[name], name, themeConfig.nonce); }; + applyTheme.defineTheme = function(name, options) { + options = options || {}; + + var theme = registerTheme(name); + + if (options.primary) { + theme.primaryPalette(options.primary, options.primaryHues); + } + if (options.accent) { + theme.accentPalette(options.accent, options.accentHues); + } + if (options.warn) { + theme.warnPalette(options.warn, options.warnHues); + } + if (options.background) { + theme.backgroundPalette(options.background, options.backgroundHues); + } + if (options.dark){ + theme.dark(); + } + + this.generateTheme(name); + + return $q.resolve(name); + }; + applyTheme.setBrowserColor = enableBrowserColor; + + return applyTheme; + + /** + * Determine is specified theme name is a valid, registered theme + */ + function registered(themeName) { + if (themeName === undefined || themeName === '') return true; + return applyTheme.THEMES[themeName] !== undefined; + } + + /** + * Get theme name for the element, then update with Theme CSS class + */ + function inheritTheme (el, parent) { + var ctrl = parent.controller('mdTheme') || el.data('$mdThemeController'); + var scope = el.scope(); + + updateThemeClass(lookupThemeName()); + + if (ctrl) { + var watchTheme = alwaysWatchTheme || + ctrl.$shouldWatch || + $mdUtil.parseAttributeBoolean(el.attr('md-theme-watch')); + + if (watchTheme || ctrl.isAsyncTheme) { + var clearNameWatcher = function () { + if (unwatch) { + unwatch(); + unwatch = undefined; + } + }; + + var unwatch = ctrl.registerChanges(function(name) { + updateThemeClass(name); + + if (!watchTheme) { + clearNameWatcher(); + } + }); + + if (scope) { + scope.$on('$destroy', clearNameWatcher); + } else { + el.on('$destroy', clearNameWatcher); + } + } + } + + /** + * Find the theme name from the parent controller or element data + */ + function lookupThemeName() { + // As a few components (dialog) add their controllers later, we should also watch for a controller init. + return ctrl && ctrl.$mdTheme || (defaultTheme === 'default' ? '' : defaultTheme); + } + + /** + * Remove old theme class and apply a new one + * NOTE: if not a valid theme name, then the current name is not changed + */ + function updateThemeClass(theme) { + if (!theme) return; + if (!registered(theme)) { + $log.warn('Attempted to use unregistered theme \'' + theme + '\'. ' + + 'Register it with $mdThemingProvider.theme().'); + } + + var oldTheme = el.data('$mdThemeName'); + if (oldTheme) el.removeClass('md-' + oldTheme +'-theme'); + el.addClass('md-' + theme + '-theme'); + el.data('$mdThemeName', theme); + if (ctrl) { + el.data('$mdThemeController', ctrl); + } + } + } + + } +} + +function ThemingDirective($mdTheming, $interpolate, $parse, $mdUtil, $q, $log) { + return { + priority: 101, // has to be more than 100 to be before interpolation (issue on IE) + link: { + pre: function(scope, el, attrs) { + var registeredCallbacks = []; + + var startSymbol = $interpolate.startSymbol(); + var endSymbol = $interpolate.endSymbol(); + + var theme = attrs.mdTheme.trim(); + + var hasInterpolation = + theme.substr(0, startSymbol.length) === startSymbol && + theme.lastIndexOf(endSymbol) === theme.length - endSymbol.length; + + var oneTimeOperator = '::'; + var oneTimeBind = attrs.mdTheme + .split(startSymbol).join('') + .split(endSymbol).join('') + .trim() + .substr(0, oneTimeOperator.length) === oneTimeOperator; + + var getTheme = function () { + var interpolation = $interpolate(attrs.mdTheme)(scope); + return $parse(interpolation)(scope) || interpolation; + }; + + var ctrl = { + isAsyncTheme: angular.isFunction(getTheme()) || angular.isFunction(getTheme().then), + registerChanges: function (cb, context) { + if (context) { + cb = angular.bind(context, cb); + } + + registeredCallbacks.push(cb); + + return function () { + var index = registeredCallbacks.indexOf(cb); + + if (index > -1) { + registeredCallbacks.splice(index, 1); + } + }; + }, + $setTheme: function (theme) { + if (!$mdTheming.registered(theme)) { + $log.warn('attempted to use unregistered theme \'' + theme + '\''); + } + + ctrl.$mdTheme = theme; + + // Iterating backwards to support unregistering during iteration + // http://stackoverflow.com/a/9882349/890293 + // we don't use `reverse()` of array because it mutates the array and we don't want it + // to get re-indexed + for (var i = registeredCallbacks.length; i--;) { + registeredCallbacks[i](theme); + } + }, + $shouldWatch: $mdUtil.parseAttributeBoolean(el.attr('md-theme-watch')) || + $mdTheming.ALWAYS_WATCH || + (hasInterpolation && !oneTimeBind) + }; + + el.data('$mdThemeController', ctrl); + + var setParsedTheme = function (theme) { + if (typeof theme === 'string') { + return ctrl.$setTheme(theme); + } + + $q.when(angular.isFunction(theme) ? theme() : theme) + .then(function(name) { + ctrl.$setTheme(name); + }); + }; + + setParsedTheme(getTheme()); + + var unwatch = scope.$watch(getTheme, function(theme) { + if (theme) { + setParsedTheme(theme); + + if (!ctrl.$shouldWatch) { + unwatch(); + } + } + }); + } + } + }; +} + +/** + * Special directive that will disable ALL runtime Theme style generation and DOM injection + * + * + * + * + * + * ... + * + * + * Note: Using md-themes-css directive requires the developer to load external + * theme stylesheets; e.g. custom themes from Material-Tools: + * + * `angular-material.themes.css` + * + * Another option is to use the ThemingProvider to configure and disable the attribute + * conversions; this would obviate the use of the `md-themes-css` directive + * + */ +function disableThemesDirective() { + themeConfig.disableTheming = true; + + // Return a 1x-only, first-match attribute directive + return { + restrict : 'A', + priority : '900' + }; +} + +function ThemableDirective($mdTheming) { + return $mdTheming; +} + +function parseRules(theme, colorType, rules) { + checkValidPalette(theme, colorType); + + rules = rules.replace(/THEME_NAME/g, theme.name); + var themeNameRegex = new RegExp('\\.md-' + theme.name + '-theme', 'g'); + // Matches '{{ primary-color }}', etc + var hueRegex = new RegExp('([\'"])?{{\\s*([a-zA-Z]+)-?(color|default)?-?(contrast)?-?((?:\\d\\.?\\d*)|(?:[a-zA-Z]+))?\\s*}}(["\'])?','g'); + var simpleVariableRegex = /'?"?{{\s*([a-zA-Z]+)-(A?\d+|hue-[0-3]|shadow|default)-?(contrast)?-?((?:\d\.?\d*)|(?:[a-zA-Z]+))?\s*}}'?"?/g; + var defaultBgHue = theme.colors['background'].hues['default']; + var defaultBgContrastType = PALETTES[theme.colors['background'].name][defaultBgHue].contrastType; + + // find and replace simple variables where we use a specific hue, not an entire palette + // eg. "{{primary-100}}" + // \(' + THEME_COLOR_TYPES.join('\|') + '\)' + rules = rules.replace(simpleVariableRegex, function(match, colorType, hue, contrast, opacity) { + var regexColorType = colorType; + if (colorType === 'foreground') { + if (hue === 'shadow') { + return theme.foregroundShadow; + } else if (theme.foregroundPalette[hue]) { + // Use user defined palette number (ie: foreground-2) + return rgba(colorToRgbaArray(theme.foregroundPalette[hue])); + } else if (theme.foregroundPalette['1']){ + return rgba(colorToRgbaArray(theme.foregroundPalette['1'])); + } + // Default to background-default-contrast-{opacity} + colorType = 'background'; + contrast = 'contrast'; + if (!opacity && hue) { + // Convert references to legacy hues to opacities (i.e. foreground-4 to *-divider) + switch (hue) { + // hue-1 uses default opacity + case '2': + opacity = 'secondary'; + break; + case '3': + opacity = 'disabled'; + break; + case '4': + opacity = 'divider'; + } + } + hue = 'default'; + } + + // `default` is also accepted as a hue-value, because the background palettes are + // using it as a name for the default hue. + if (hue.indexOf('hue') === 0 || hue === 'default') { + hue = theme.colors[colorType].hues[hue]; + } + + var colorDetails = (PALETTES[ theme.colors[colorType].name ][hue] || ''); + + // If user has specified a foreground color, use those + if (colorType === 'background' && contrast && regexColorType !== 'foreground' && + colorDetails.contrastType === defaultBgContrastType) { + // Don't process if colorType was changed + switch (opacity) { + case 'secondary': + case 'icon': + if (theme.foregroundPalette['2']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['2'])); + } + break; + case 'disabled': + case 'hint': + if (theme.foregroundPalette['3']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['3'])); + } + break; + case 'divider': + if (theme.foregroundPalette['4']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['4'])); + } + break; + default: + if (theme.foregroundPalette['1']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['1'])); + } + break; + } + } + + if (contrast && opacity) { + opacity = colorDetails.opacity[opacity] || opacity; + } + + return rgba(colorDetails[contrast ? 'contrast' : 'value'], opacity); + }); + + var generatedRules = []; + + // For each type, generate rules for each hue (ie. default, md-hue-1, md-hue-2, md-hue-3) + angular.forEach(['default', 'hue-1', 'hue-2', 'hue-3'], function(hueName) { + var newRule = rules + .replace(hueRegex, function(match, _, matchedColorType, hueType, contrast, opacity) { + var color = theme.colors[matchedColorType]; + var palette = PALETTES[color.name]; + var hueValue = color.hues[hueName]; + if (contrast && opacity) { + opacity = palette[hueValue].opacity[opacity] || opacity; + } + return rgba(palette[hueValue][hueType === 'color' ? 'value' : 'contrast'], opacity); + }); + if (hueName !== 'default') { + newRule = newRule.replace(themeNameRegex, '.md-' + theme.name + '-theme.md-' + hueName); + } + + // Don't apply a selector rule to the default theme, making it easier to override + // styles of the base-component + if (theme.name === 'default') { + var themeRuleRegex = /((?:\s|>|\.|\w|-|:|\(|\)|\[|]|"|'|=)*)\.md-default-theme((?:\s|>|\.|\w|-|:|\(|\)|\[|]|"|'|=)*)/g; + + newRule = newRule.replace(themeRuleRegex, function(match, start, end) { + return match + ', ' + start + end; + }); + } + generatedRules.push(newRule); + }); + + return generatedRules; +} + +var rulesByType = {}; + +// Generate our themes at run time given the state of THEMES and PALETTES +function generateAllThemes($injector, $mdTheming) { + var head = document.head; + var firstChild = head ? head.firstElementChild : null; + var themeCss = !themeConfig.disableTheming && $injector.has('$MD_THEME_CSS') ? $injector.get('$MD_THEME_CSS') : ''; + + // Append our custom registered styles to the theme stylesheet. + themeCss += themeConfig.registeredStyles.join(''); + + if (!firstChild) return; + if (themeCss.length === 0) return; // no rules, so no point in running this expensive task + + // Expose contrast colors for palettes to ensure that text is always readable + angular.forEach(PALETTES, sanitizePalette); + + // MD_THEME_CSS is a string generated by the build process that includes all the themeable + // components as templates + + // Break the CSS into individual rules + var rules = splitCss(themeCss).map(function(rule) { + return rule.trim(); + }); + + THEME_COLOR_TYPES.forEach(function(type) { + rulesByType[type] = ''; + }); + + // Sort the rules based on type, allowing us to do color substitution on a per-type basis + rules.forEach(function(rule) { + // First: test that if the rule has '.md-accent', it goes into the accent set of rules + for (var i = 0, type; type = THEME_COLOR_TYPES[i]; i++) { + if (rule.indexOf('.md-' + type) > -1) { + return rulesByType[type] += rule; + } + } + + // If no eg 'md-accent' class is found, try to just find 'accent' in the rule and guess from + // there + for (i = 0; type = THEME_COLOR_TYPES[i]; i++) { + if (rule.indexOf(type) > -1) { + return rulesByType[type] += rule; + } + } + + // Default to the primary array + return rulesByType[DEFAULT_COLOR_TYPE] += rule; + }); + + // If themes are being generated on-demand, quit here. The user will later manually + // call generateTheme to do this on a theme-by-theme basis. + if (themeConfig.generateOnDemand) return; + + angular.forEach($mdTheming.THEMES, function(theme) { + if (!GENERATED[theme.name] && !($mdTheming.defaultTheme() !== 'default' && theme.name === 'default')) { + generateTheme(theme, theme.name, themeConfig.nonce); + } + }); + + + // ************************* + // Internal functions + // ************************* + + /** + * The user specifies a 'default' contrast color as either light or dark, then explicitly lists + * which hues are the opposite contrast (eg. A100 has dark, A200 has light). + * @param {!object} palette to sanitize + */ + function sanitizePalette(palette) { + var defaultContrast = palette.contrastDefaultColor; + var lightColors = palette.contrastLightColors || []; + var strongLightColors = palette.contrastStrongLightColors || []; + var darkColors = palette.contrastDarkColors || []; + + // These colors are provided as space-separated lists + if (typeof lightColors === 'string') lightColors = lightColors.split(' '); + if (typeof strongLightColors === 'string') strongLightColors = strongLightColors.split(' '); + if (typeof darkColors === 'string') darkColors = darkColors.split(' '); + + // Cleanup after ourselves + delete palette.contrastDefaultColor; + delete palette.contrastLightColors; + delete palette.contrastStrongLightColors; + delete palette.contrastDarkColors; + + /** + * @param {string} hueName + * @return {'dark'|'light'|'strongLight'} + */ + function getContrastType(hueName) { + if (defaultContrast === 'light' ? darkColors.indexOf(hueName) !== -1 : + (lightColors.indexOf(hueName) === -1 && strongLightColors.indexOf(hueName) === -1)) { + return 'dark'; + } + if (strongLightColors.indexOf(hueName) !== -1) { + return 'strongLight'; + } + return 'light'; + } + + /** + * @param {'dark'|'light'|'strongLight'} contrastType + * @return {[number, number, number]} [red, green, blue] array + */ + function getContrastColor(contrastType) { + switch (contrastType) { + default: + case 'strongLight': + return STRONG_LIGHT_CONTRAST_COLOR; + case 'light': + return LIGHT_CONTRAST_COLOR; + case 'dark': + return DARK_CONTRAST_COLOR; + } + } + + /** + * @param {'dark'|'light'|'strongLight'} contrastType + * @return {{secondary: number, divider: number, hint: number, icon: number, disabled: number}} + */ + function getOpacityValues(contrastType) { + switch (contrastType) { + default: + case 'strongLight': + return STRONG_LIGHT_CONTRAST_OPACITY; + case 'light': + return LIGHT_CONTRAST_OPACITY; + case 'dark': + return DARK_CONTRAST_OPACITY; + } + } + // Change { 'A100': '#fffeee' } to { 'A100': { value: '#fffeee', contrast:DARK_CONTRAST_COLOR } + angular.forEach(palette, function(hueValue, hueName) { + if (angular.isObject(hueValue)) return; // Already converted + // Map everything to rgb colors + var rgbValue = colorToRgbaArray(hueValue); + if (!rgbValue) { + throw new Error("Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected." + .replace('%1', hueValue) + .replace('%2', palette.name) + .replace('%3', hueName)); + } + + var contrastType = getContrastType(hueName); + palette[hueName] = { + hex: palette[hueName], + value: rgbValue, + contrastType: contrastType, + contrast: getContrastColor(contrastType), + opacity: getOpacityValues(contrastType) + }; + }); + } + + /** + * @param {string} themeCss + * @returns {[]} a string representing a CSS file that is split, producing an array with a rule + * at each index. + */ + function splitCss(themeCss) { + var result = []; + var currentRule = ''; + var openedCurlyBrackets = 0; + var closedCurlyBrackets = 0; + + for (var i = 0; i < themeCss.length; i++) { + var character = themeCss.charAt(i); + + // Check for content in quotes + if (character === '\'' || character === '"') { + // Append text in quotes to current rule + var textInQuotes = themeCss.substring(i, themeCss.indexOf(character, i + 1)); + currentRule += textInQuotes; + + // Jump to the closing quote char + i += textInQuotes.length; + } else { + currentRule += character; + + if (character === '}') { + closedCurlyBrackets++; + if (closedCurlyBrackets === openedCurlyBrackets) { + closedCurlyBrackets = 0; + openedCurlyBrackets = 0; + result.push(currentRule); + currentRule = ''; + } + } else if (character === '{') { + openedCurlyBrackets++; + } + } + } + // Add comments added after last valid rule. + if (currentRule !== '') { + result.push(currentRule); + } + + return result; + } +} + +function generateTheme(theme, name, nonce) { + var head = document.head; + var firstChild = head ? head.firstElementChild : null; + + if (!GENERATED[name]) { + // For each theme, use the color palettes specified for + // `primary`, `warn` and `accent` to generate CSS rules. + THEME_COLOR_TYPES.forEach(function(colorType) { + var styleStrings = parseRules(theme, colorType, rulesByType[colorType]); + while (styleStrings.length) { + var styleContent = styleStrings.shift(); + if (styleContent) { + var style = document.createElement('style'); + style.setAttribute('md-theme-style', ''); + if (nonce) { + style.setAttribute('nonce', nonce); + } + style.appendChild(document.createTextNode(styleContent)); + head.insertBefore(style, firstChild); + } + } + }); + + GENERATED[theme.name] = true; + } + +} + + +function checkValidPalette(theme, colorType) { + // If theme attempts to use a palette that doesnt exist, throw error + if (!PALETTES[ (theme.colors[colorType] || {}).name ]) { + throw new Error( + "You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3" + .replace('%1', theme.name) + .replace('%2', colorType) + .replace('%3', Object.keys(PALETTES).join(', ')) + ); + } +} + +/** + * @param {string} clr rbg or rgba color + * @return {number[]|undefined} [red, green, blue] array if it can be computed + */ +function colorToRgbaArray(clr) { + if (angular.isArray(clr) && clr.length === 3) return clr; + if (/^rgb/.test(clr)) { + return clr.replace(/(^\s*rgba?\(|\)\s*$)/g, '').split(',').map(function(value, i) { + return i === 3 ? parseFloat(value) : parseInt(value, 10); + }); + } + if (clr.charAt(0) === '#') clr = clr.substring(1); + if (!/^([a-fA-F0-9]{3}){1,2}$/g.test(clr)) return; + + var dig = clr.length / 3; + var red = clr.substr(0, dig); + var grn = clr.substr(dig, dig); + var blu = clr.substr(dig * 2); + if (dig === 1) { + red += red; + grn += grn; + blu += blu; + } + return [parseInt(red, 16), parseInt(grn, 16), parseInt(blu, 16)]; +} + +function rgba(rgbArray, opacity) { + if (!rgbArray) return "rgb('0,0,0')"; + + if (rgbArray.length === 4) { + rgbArray = angular.copy(rgbArray); + opacity ? rgbArray.pop() : opacity = rgbArray.pop(); + } + return opacity && (typeof opacity == 'number' || (typeof opacity == 'string' && opacity.length)) ? + 'rgba(' + rgbArray.join(',') + ',' + opacity + ')' : + 'rgb(' + rgbArray.join(',') + ')'; +} + + +})(window.angular); + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.autocomplete + */ +/* + * @see js folder for autocomplete implementation + */ +angular.module('material.components.autocomplete', [ + 'material.core', + 'material.components.icon', + 'material.components.virtualRepeat' +]); + +})(); +(function(){ +"use strict"; + + +MdAutocompleteCtrl.$inject = ["$scope", "$element", "$mdUtil", "$mdConstant", "$mdTheming", "$window", "$animate", "$rootElement", "$attrs", "$q", "$log", "$mdLiveAnnouncer"];angular + .module('material.components.autocomplete') + .controller('MdAutocompleteCtrl', MdAutocompleteCtrl); + +var ITEM_HEIGHT = 48, + MAX_ITEMS = 5, + MENU_PADDING = 8, + INPUT_PADDING = 2, // Padding provided by `md-input-container` + MODE_STANDARD = 'standard', + MODE_VIRTUAL = 'virtual'; + +function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming, $window, + $animate, $rootElement, $attrs, $q, $log, $mdLiveAnnouncer) { + + // Internal Variables. + var ctrl = this, + itemParts = $scope.itemsExpr.split(/ in /i), + itemExpr = itemParts[ 1 ], + elements = null, + cache = {}, + noBlur = false, + selectedItemWatchers = [], + hasFocus = false, + fetchesInProgress = 0, + enableWrapScroll = null, + inputModelCtrl = null, + debouncedOnResize = $mdUtil.debounce(onWindowResize), + mode = MODE_VIRTUAL; // default + + /** + * The root document element. This is used for attaching a top-level click handler to + * close the options panel when a click outside said panel occurs. We use `documentElement` + * instead of body because, when scrolling is disabled, some browsers consider the body element + * to be completely off the screen and propagate events directly to the html element. + * @type {!Object} angular.JQLite + */ + ctrl.documentElement = angular.element(document.documentElement); + + // Public Exported Variables with handlers + defineProperty('hidden', handleHiddenChange, true); + + // Public Exported Variables + ctrl.scope = $scope; + ctrl.parent = $scope.$parent; + ctrl.itemName = itemParts[0]; + ctrl.matches = []; + ctrl.loading = false; + ctrl.hidden = true; + ctrl.index = -1; + ctrl.activeOption = null; + ctrl.id = $mdUtil.nextUid(); + ctrl.isDisabled = null; + ctrl.isRequired = null; + ctrl.isReadonly = null; + ctrl.hasNotFound = false; + ctrl.selectedMessage = $scope.selectedMessage || 'selected'; + ctrl.noMatchMessage = $scope.noMatchMessage || 'There are no matches available.'; + ctrl.singleMatchMessage = $scope.singleMatchMessage || 'There is 1 match available.'; + ctrl.multipleMatchStartMessage = $scope.multipleMatchStartMessage || 'There are '; + ctrl.multipleMatchEndMessage = $scope.multipleMatchEndMessage || ' matches available.'; + ctrl.defaultEscapeOptions = 'clear'; + + // Public Exported Methods + ctrl.keydown = keydown; + ctrl.blur = blur; + ctrl.focus = focus; + ctrl.clear = clearValue; + ctrl.select = select; + ctrl.listEnter = onListEnter; + ctrl.listLeave = onListLeave; + ctrl.focusInput = focusInputElement; + ctrl.getCurrentDisplayValue = getCurrentDisplayValue; + ctrl.registerSelectedItemWatcher = registerSelectedItemWatcher; + ctrl.unregisterSelectedItemWatcher = unregisterSelectedItemWatcher; + ctrl.notFoundVisible = notFoundVisible; + ctrl.loadingIsVisible = loadingIsVisible; + ctrl.positionDropdown = positionDropdown; + + /** + * Report types to be used for the $mdLiveAnnouncer + * @enum {number} Unique flag id. + */ + var ReportType = { + Count: 1, + Selected: 2 + }; + + return init(); + + // initialization methods + + /** + * Initialize the controller, setup watchers, gather elements + */ + function init () { + + $mdUtil.initOptionalProperties($scope, $attrs, { + searchText: '', + selectedItem: null, + clearButton: false, + disableVirtualRepeat: false, + }); + + $mdTheming($element); + configureWatchers(); + $mdUtil.nextTick(function () { + + gatherElements(); + moveDropdown(); + + // Touch devices often do not send a click event on tap. We still want to focus the input + // and open the options pop-up in these cases. + $element.on('touchstart', focusInputElement); + + // Forward all focus events to the input element when autofocus is enabled + if ($scope.autofocus) { + $element.on('focus', focusInputElement); + } + if ($scope.inputAriaDescribedBy) { + elements.input.setAttribute('aria-describedby', $scope.inputAriaDescribedBy); + } + if (!$scope.floatingLabel) { + if ($scope.inputAriaLabel) { + elements.input.setAttribute('aria-label', $scope.inputAriaLabel); + } else if ($scope.inputAriaLabelledBy) { + elements.input.setAttribute('aria-labelledby', $scope.inputAriaLabelledBy); + } else if ($scope.placeholder) { + // If no aria-label or aria-labelledby references are defined, then just label using the + // placeholder. + elements.input.setAttribute('aria-label', $scope.placeholder); + } + } + }); + } + + function updateModelValidators() { + if (!$scope.requireMatch || !inputModelCtrl) return; + + inputModelCtrl.$setValidity('md-require-match', !!$scope.selectedItem || !$scope.searchText); + } + + /** + * Calculates the dropdown's position and applies the new styles to the menu element + * @returns {*} + */ + function positionDropdown () { + if (!elements) { + return $mdUtil.nextTick(positionDropdown, false, $scope); + } + + var dropdownHeight = ($scope.dropdownItems || MAX_ITEMS) * ITEM_HEIGHT; + var hrect = elements.wrap.getBoundingClientRect(), + vrect = elements.snap.getBoundingClientRect(), + root = elements.root.getBoundingClientRect(), + top = vrect.bottom - root.top, + bot = root.bottom - vrect.top, + left = hrect.left - root.left, + width = hrect.width, + offset = getVerticalOffset(), + position = $scope.dropdownPosition, + styles, enoughBottomSpace, enoughTopSpace; + var bottomSpace = root.bottom - vrect.bottom - MENU_PADDING + $mdUtil.getViewportTop(); + var topSpace = vrect.top - MENU_PADDING; + + // Automatically determine dropdown placement based on available space in viewport. + if (!position) { + enoughTopSpace = topSpace > dropdownHeight; + enoughBottomSpace = bottomSpace > dropdownHeight; + if (enoughBottomSpace) { + position = 'bottom'; + } else if (enoughTopSpace) { + position = 'top'; + } else { + position = topSpace > bottomSpace ? 'top' : 'bottom'; + } + } + // Adjust the width to account for the padding provided by `md-input-container` + if ($attrs.mdFloatingLabel) { + left += INPUT_PADDING; + width -= INPUT_PADDING * 2; + } + styles = { + left: left + 'px', + minWidth: width + 'px', + maxWidth: Math.max(hrect.right - root.left, root.right - hrect.left) - MENU_PADDING + 'px' + }; + + if (position === 'top') { + styles.top = 'auto'; + styles.bottom = bot + 'px'; + styles.maxHeight = Math.min(dropdownHeight, topSpace) + 'px'; + } else { + bottomSpace = root.bottom - hrect.bottom - MENU_PADDING + $mdUtil.getViewportTop(); + + styles.top = (top - offset) + 'px'; + styles.bottom = 'auto'; + styles.maxHeight = Math.min(dropdownHeight, bottomSpace) + 'px'; + } + + elements.$.scrollContainer.css(styles); + $mdUtil.nextTick(correctHorizontalAlignment, false, $scope); + + /** + * Calculates the vertical offset for floating label examples to account for ngMessages + * @returns {number} + */ + function getVerticalOffset () { + var offset = 0; + var inputContainer = $element.find('md-input-container'); + if (inputContainer.length) { + var input = inputContainer.find('input'); + offset = inputContainer.prop('offsetHeight'); + offset -= input.prop('offsetTop'); + offset -= input.prop('offsetHeight'); + // add in the height left up top for the floating label text + offset += inputContainer.prop('offsetTop'); + } + return offset; + } + + /** + * Makes sure that the menu doesn't go off of the screen on either side. + */ + function correctHorizontalAlignment () { + var dropdown = elements.scrollContainer.getBoundingClientRect(), + styles = {}; + if (dropdown.right > root.right) { + styles.left = (hrect.right - dropdown.width) + 'px'; + } + elements.$.scrollContainer.css(styles); + } + } + + /** + * Moves the dropdown menu to the body tag in order to avoid z-index and overflow issues. + */ + function moveDropdown () { + if (!elements.$.root.length) return; + $mdTheming(elements.$.scrollContainer); + elements.$.scrollContainer.detach(); + elements.$.root.append(elements.$.scrollContainer); + if ($animate.pin) $animate.pin(elements.$.scrollContainer, $rootElement); + } + + /** + * Sends focus to the input element. + */ + function focusInputElement () { + elements.input.focus(); + } + + /** + * Update the activeOption based on the selected item in the listbox. + * The activeOption is used in the template to set the aria-activedescendant attribute, which + * enables screen readers to properly handle visual focus within the listbox and announce the + * item's place in the list. I.e. "List item 3 of 50". Anytime that `ctrl.index` changes, this + * function needs to be called to update the activeOption. + */ + function updateActiveOption() { + var selectedOption = elements.scroller.querySelector('.selected'); + if (selectedOption) { + ctrl.activeOption = selectedOption.id; + } else { + ctrl.activeOption = null; + } + } + + /** + * Sets up any watchers used by autocomplete + */ + function configureWatchers () { + var wait = parseInt($scope.delay, 10) || 0; + + $attrs.$observe('disabled', function (value) { ctrl.isDisabled = $mdUtil.parseAttributeBoolean(value, false); }); + $attrs.$observe('required', function (value) { ctrl.isRequired = $mdUtil.parseAttributeBoolean(value, false); }); + $attrs.$observe('readonly', function (value) { ctrl.isReadonly = $mdUtil.parseAttributeBoolean(value, false); }); + + $scope.$watch('searchText', wait ? $mdUtil.debounce(handleSearchText, wait) : handleSearchText); + $scope.$watch('selectedItem', selectedItemChange); + + angular.element($window).on('resize', debouncedOnResize); + + $scope.$on('$destroy', cleanup); + } + + /** + * Removes any events or leftover elements created by this controller + */ + function cleanup () { + if (!ctrl.hidden) { + $mdUtil.enableScrolling(); + } + + angular.element($window).off('resize', debouncedOnResize); + + if (elements){ + var items = ['ul', 'scroller', 'scrollContainer', 'input']; + angular.forEach(items, function(key){ + elements.$[key].remove(); + }); + } + } + + /** + * Event handler to be called whenever the window resizes. + */ + function onWindowResize() { + if (!ctrl.hidden) { + positionDropdown(); + } + } + + /** + * Gathers all of the elements needed for this controller + */ + function gatherElements () { + + var snapWrap = gatherSnapWrap(); + + elements = { + main: $element[0], + scrollContainer: $element[0].querySelector('.md-virtual-repeat-container, .md-standard-list-container'), + scroller: $element[0].querySelector('.md-virtual-repeat-scroller, .md-standard-list-scroller'), + ul: $element.find('ul')[0], + input: $element.find('input')[0], + wrap: snapWrap.wrap, + snap: snapWrap.snap, + root: document.body, + }; + + elements.li = elements.ul.getElementsByTagName('li'); + elements.$ = getAngularElements(elements); + mode = elements.scrollContainer.classList.contains('md-standard-list-container') ? MODE_STANDARD : MODE_VIRTUAL; + inputModelCtrl = elements.$.input.controller('ngModel'); + } + + /** + * Gathers the snap and wrap elements + * + */ + function gatherSnapWrap() { + var element; + var value; + for (element = $element; element.length; element = element.parent()) { + value = element.attr('md-autocomplete-snap'); + if (angular.isDefined(value)) break; + } + + if (element.length) { + return { + snap: element[0], + wrap: (value.toLowerCase() === 'width') ? element[0] : $element.find('md-autocomplete-wrap')[0] + }; + } + + var wrap = $element.find('md-autocomplete-wrap')[0]; + return { + snap: wrap, + wrap: wrap + }; + } + + /** + * Gathers angular-wrapped versions of each element + * @param elements + * @returns {{}} + */ + function getAngularElements (elements) { + var obj = {}; + for (var key in elements) { + if (elements.hasOwnProperty(key)) obj[ key ] = angular.element(elements[ key ]); + } + return obj; + } + + // event/change handlers + + /** + * @param {Event} $event + */ + function preventDefault($event) { + $event.preventDefault(); + } + + /** + * @param {Event} $event + */ + function stopPropagation($event) { + $event.stopPropagation(); + } + + /** + * Handles changes to the `hidden` property. + * @param {boolean} hidden true to hide the options pop-up, false to show it. + * @param {boolean} oldHidden the previous value of hidden + */ + function handleHiddenChange (hidden, oldHidden) { + var scrollContainerElement; + + if (elements) { + scrollContainerElement = angular.element(elements.scrollContainer); + } + if (!hidden && oldHidden) { + positionDropdown(); + + // Report in polite mode, because the screen reader should finish the default description of + // the input element. + reportMessages(true, ReportType.Count | ReportType.Selected); + + if (elements) { + $mdUtil.disableScrollAround(elements.scrollContainer); + enableWrapScroll = disableElementScrollEvents(elements.wrap); + if ($mdUtil.isIos) { + ctrl.documentElement.on('touchend', handleTouchOutsidePanel); + if (scrollContainerElement) { + scrollContainerElement.on('touchstart touchmove touchend', stopPropagation); + } + } + ctrl.index = getDefaultIndex(); + $mdUtil.nextTick(function() { + updateActiveOption(); + updateScroll(); + }); + } + } else if (hidden && !oldHidden) { + if ($mdUtil.isIos) { + ctrl.documentElement.off('touchend', handleTouchOutsidePanel); + if (scrollContainerElement) { + scrollContainerElement.off('touchstart touchmove touchend', stopPropagation); + } + } + $mdUtil.enableScrolling(); + + if (enableWrapScroll) { + enableWrapScroll(); + enableWrapScroll = null; + } + } + } + + /** + * Handling touch events that bubble up to the document is required for closing the dropdown + * panel on touch outside of the options pop-up panel on iOS. + * @param {Event} $event + */ + function handleTouchOutsidePanel($event) { + ctrl.hidden = true; + // iOS does not blur the pop-up for touches on the scroll mask, so we have to do it. + doBlur(true); + } + + /** + * Disables scrolling for a specific element. + * @param {!string|!DOMElement} element to disable scrolling + * @return {Function} function to call to re-enable scrolling for the element + */ + function disableElementScrollEvents(element) { + var elementToDisable = angular.element(element); + elementToDisable.on('wheel touchmove', preventDefault); + + return function() { + elementToDisable.off('wheel touchmove', preventDefault); + }; + } + + /** + * When the user mouses over the dropdown menu, ignore blur events. + */ + function onListEnter () { + noBlur = true; + } + + /** + * When the user's mouse leaves the menu, blur events may hide the menu again. + */ + function onListLeave () { + if (!hasFocus && !ctrl.hidden) elements.input.focus(); + noBlur = false; + ctrl.hidden = shouldHide(); + } + + /** + * Handles changes to the selected item. + * @param selectedItem + * @param previousSelectedItem + */ + function selectedItemChange (selectedItem, previousSelectedItem) { + + updateModelValidators(); + + if (selectedItem) { + getDisplayValue(selectedItem).then(function (val) { + $scope.searchText = val; + handleSelectedItemChange(selectedItem, previousSelectedItem); + }); + } else if (previousSelectedItem && $scope.searchText) { + getDisplayValue(previousSelectedItem).then(function(displayValue) { + // Clear the searchText, when the selectedItem is set to null. + // Do not clear the searchText, when the searchText isn't matching with the previous + // selected item. + if (angular.isString($scope.searchText) + && displayValue.toString().toLowerCase() === $scope.searchText.toLowerCase()) { + $scope.searchText = ''; + } + }); + } + + if (selectedItem !== previousSelectedItem) { + announceItemChange(); + } + } + + /** + * Use the user-defined expression to announce changes each time a new item is selected + */ + function announceItemChange () { + angular.isFunction($scope.itemChange) && + $scope.itemChange(getItemAsNameVal($scope.selectedItem)); + } + + /** + * Use the user-defined expression to announce changes each time the search text is changed + */ + function announceTextChange () { + angular.isFunction($scope.textChange) && $scope.textChange(); + } + + /** + * Calls any external watchers listening for the selected item. Used in conjunction with + * `registerSelectedItemWatcher`. + * @param selectedItem + * @param previousSelectedItem + */ + function handleSelectedItemChange (selectedItem, previousSelectedItem) { + selectedItemWatchers.forEach(function (watcher) { + watcher(selectedItem, previousSelectedItem); + }); + } + + /** + * Register a function to be called when the selected item changes. + * @param cb + */ + function registerSelectedItemWatcher (cb) { + if (selectedItemWatchers.indexOf(cb) === -1) { + selectedItemWatchers.push(cb); + } + } + + /** + * Unregister a function previously registered for selected item changes. + * @param cb + */ + function unregisterSelectedItemWatcher (cb) { + var i = selectedItemWatchers.indexOf(cb); + if (i !== -1) { + selectedItemWatchers.splice(i, 1); + } + } + + /** + * Handles changes to the searchText property. + * @param {string} searchText + * @param {string} previousSearchText + */ + function handleSearchText (searchText, previousSearchText) { + ctrl.index = getDefaultIndex(); + + // do nothing on init + if (searchText === previousSearchText) return; + + updateModelValidators(); + + getDisplayValue($scope.selectedItem).then(function (val) { + // clear selected item if search text no longer matches it + if (searchText !== val) { + $scope.selectedItem = null; + + // trigger change event if available + if (searchText !== previousSearchText) { + announceTextChange(); + } + + // cancel results if search text is not long enough + if (!isMinLengthMet()) { + ctrl.matches = []; + + setLoading(false); + reportMessages(true, ReportType.Count); + + } else { + handleQuery(); + } + } + }); + + } + + /** + * Handles input blur event, determines if the dropdown should hide. + * @param {Event=} $event + */ + function blur($event) { + hasFocus = false; + + if (!noBlur) { + ctrl.hidden = shouldHide(); + evalAttr('ngBlur', { $event: $event }); + } else if (angular.isObject($event)) { + $event.stopImmediatePropagation(); + } + } + + /** + * Force blur on input element + * @param {boolean} forceBlur + */ + function doBlur(forceBlur) { + if (forceBlur) { + noBlur = false; + hasFocus = false; + } + elements.input.blur(); + } + + /** + * Handles input focus event, determines if the dropdown should show. + */ + function focus($event) { + hasFocus = true; + + if (isSearchable() && isMinLengthMet()) { + handleQuery(); + } + + ctrl.hidden = shouldHide(); + + evalAttr('ngFocus', { $event: $event }); + } + + /** + * Handles keyboard input. + * @param event + */ + function keydown (event) { + switch (event.keyCode) { + case $mdConstant.KEY_CODE.DOWN_ARROW: + if (ctrl.loading || hasSelection()) return; + event.stopPropagation(); + event.preventDefault(); + ctrl.index = ctrl.index + 1 > ctrl.matches.length - 1 ? 0 : Math.min(ctrl.index + 1, ctrl.matches.length - 1); + $mdUtil.nextTick(updateActiveOption); + updateScroll(); + break; + case $mdConstant.KEY_CODE.UP_ARROW: + if (ctrl.loading || hasSelection()) return; + event.stopPropagation(); + event.preventDefault(); + ctrl.index = ctrl.index - 1 < 0 ? ctrl.matches.length - 1 : Math.max(0, ctrl.index - 1); + $mdUtil.nextTick(updateActiveOption); + updateScroll(); + break; + case $mdConstant.KEY_CODE.TAB: + // If we hit tab, assume that we've left the list so it will close + onListLeave(); + + if (ctrl.hidden || ctrl.loading || ctrl.index < 0 || ctrl.matches.length < 1) return; + select(ctrl.index); + break; + case $mdConstant.KEY_CODE.ENTER: + if (ctrl.hidden || ctrl.loading || ctrl.index < 0 || ctrl.matches.length < 1) return; + if (hasSelection()) return; + event.stopImmediatePropagation(); + event.preventDefault(); + select(ctrl.index); + break; + case $mdConstant.KEY_CODE.ESCAPE: + event.preventDefault(); // Prevent browser from always clearing input + if (!shouldProcessEscape()) return; + event.stopPropagation(); + + clearSelectedItem(); + if ($scope.searchText && hasEscapeOption('clear')) { + clearSearchText(); + } + + // Manually hide (needed for mdNotFound support) + ctrl.hidden = true; + + if (hasEscapeOption('blur')) { + // Force the component to blur if they hit escape + doBlur(true); + } + + break; + default: + } + } + + // getters + + /** + * Returns the minimum length needed to display the dropdown. + * @returns {*} + */ + function getMinLength () { + return angular.isNumber($scope.minLength) ? $scope.minLength : 1; + } + + /** + * Returns the display value for an item. + * @param {*} item + * @returns {*} + */ + function getDisplayValue (item) { + return $q.when(getItemText(item) || item).then(function(itemText) { + if (itemText && !angular.isString(itemText)) { + $log.warn('md-autocomplete: Could not resolve display value to a string. ' + + 'Please check the `md-item-text` attribute.'); + } + + return itemText; + }); + + /** + * Getter function to invoke user-defined expression (in the directive) + * to convert your object to a single string. + * @param {*} item + * @returns {string|null} + */ + function getItemText (item) { + return (item && $scope.itemText) ? $scope.itemText(getItemAsNameVal(item)) : null; + } + } + + /** + * Returns the locals object for compiling item templates. + * @param {*} item + * @returns {Object|undefined} + */ + function getItemAsNameVal (item) { + if (!item) { + return undefined; + } + + var locals = {}; + if (ctrl.itemName) { + locals[ ctrl.itemName ] = item; + } + + return locals; + } + + /** + * Returns the default index based on whether or not autoselect is enabled. + * @returns {number} 0 if autoselect is enabled, -1 if not. + */ + function getDefaultIndex () { + return $scope.autoselect ? 0 : -1; + } + + /** + * Sets the loading parameter and updates the hidden state. + * @param value {boolean} Whether or not the component is currently loading. + */ + function setLoading(value) { + if (ctrl.loading !== value) { + ctrl.loading = value; + } + + // Always refresh the hidden variable as something else might have changed + ctrl.hidden = shouldHide(); + } + + /** + * Determines if the menu should be hidden. + * @returns {boolean} true if the menu should be hidden + */ + function shouldHide () { + return !shouldShow(); + } + + /** + * Determines whether the autocomplete is able to query within the current state. + * @returns {boolean} true if the query can be run + */ + function isSearchable() { + if (ctrl.loading && !hasMatches()) { + // No query when query is in progress. + return false; + } else if (hasSelection()) { + // No query if there is already a selection + return false; + } + else if (!hasFocus) { + // No query if the input does not have focus + return false; + } + return true; + } + + /** + * @returns {boolean} if the escape keydown should be processed, return true. + * Otherwise return false. + */ + function shouldProcessEscape() { + return hasEscapeOption('blur') || !ctrl.hidden || ctrl.loading || hasEscapeOption('clear') && $scope.searchText; + } + + /** + * @param {string} option check if this option is set + * @returns {boolean} if the specified escape option is set, return true. Return false otherwise. + */ + function hasEscapeOption(option) { + if (!angular.isString($scope.escapeOptions)) { + return ctrl.defaultEscapeOptions.indexOf(option) !== -1; + } else { + return $scope.escapeOptions.toLowerCase().indexOf(option) !== -1; + } + } + + /** + * Determines if the menu should be shown. + * @returns {boolean} true if the menu should be shown + */ + function shouldShow() { + if (ctrl.isReadonly) { + // Don't show if read only is set + return false; + } else if (!isSearchable()) { + // Don't show if a query is in progress, there is already a selection, + // or the input is not focused. + return false; + } + return (isMinLengthMet() && hasMatches()) || notFoundVisible(); + } + + /** + * @returns {boolean} true if the search text has matches. + */ + function hasMatches() { + return ctrl.matches.length ? true : false; + } + + /** + * @returns {boolean} true if the autocomplete has a valid selection. + */ + function hasSelection() { + return ctrl.scope.selectedItem ? true : false; + } + + /** + * @returns {boolean} true if the loading indicator is, or should be, visible. + */ + function loadingIsVisible() { + return ctrl.loading && !hasSelection(); + } + + /** + * @returns {*} the display value of the current item. + */ + function getCurrentDisplayValue () { + return getDisplayValue(ctrl.matches[ ctrl.index ]); + } + + /** + * Determines if the minimum length is met by the search text. + * @returns {*} true if the minimum length is met by the search text + */ + function isMinLengthMet () { + return ($scope.searchText || '').length >= getMinLength(); + } + + // actions + + /** + * Defines a public property with a handler and a default value. + * @param {string} key + * @param {Function} handler function + * @param {*} defaultValue default value + */ + function defineProperty (key, handler, defaultValue) { + Object.defineProperty(ctrl, key, { + get: function () { return defaultValue; }, + set: function (newValue) { + var oldValue = defaultValue; + defaultValue = newValue; + handler(newValue, oldValue); + } + }); + } + + /** + * Selects the item at the given index. + * @param {number} index to select + */ + function select (index) { + // force form to update state for validation + $mdUtil.nextTick(function () { + getDisplayValue(ctrl.matches[ index ]).then(function (val) { + var ngModel = elements.$.input.controller('ngModel'); + $mdLiveAnnouncer.announce(val + ' ' + ctrl.selectedMessage, 'assertive'); + ngModel.$setViewValue(val); + ngModel.$render(); + }).finally(function () { + $scope.selectedItem = ctrl.matches[ index ]; + setLoading(false); + }); + }, false); + } + + /** + * Clears the searchText value and selected item. + * @param {Event} $event + */ + function clearValue ($event) { + if ($event) { + $event.stopPropagation(); + } + clearSelectedItem(); + clearSearchText(); + } + + /** + * Clears the selected item + */ + function clearSelectedItem () { + // Reset our variables + ctrl.index = -1; + $mdUtil.nextTick(updateActiveOption); + ctrl.matches = []; + } + + /** + * Clears the searchText value + */ + function clearSearchText () { + // Set the loading to true so we don't see flashes of content. + // The flashing will only occur when an async request is running. + // So the loading process will stop when the results had been retrieved. + setLoading(true); + + $scope.searchText = ''; + + // Normally, triggering the change / input event is unnecessary, because the browser detects it properly. + // But some browsers are not detecting it properly, which means that we have to trigger the event. + // Using the `input` is not working properly, because for example IE11 is not supporting the `input` event. + // The `change` event is a good alternative and is supported by all supported browsers. + var eventObj = document.createEvent('CustomEvent'); + eventObj.initCustomEvent('change', true, true, { value: '' }); + elements.input.dispatchEvent(eventObj); + + // For some reason, firing the above event resets the value of $scope.searchText if + // $scope.searchText has a space character at the end, so we blank it one more time and then + // focus. + elements.input.blur(); + $scope.searchText = ''; + elements.input.focus(); + } + + /** + * Fetches the results for the provided search text. + * @param searchText + */ + function fetchResults (searchText) { + var items = $scope.$parent.$eval(itemExpr), + term = searchText.toLowerCase(), + isList = angular.isArray(items), + isPromise = !!items.then; // Every promise should contain a `then` property + + if (isList) onResultsRetrieved(items); + else if (isPromise) handleAsyncResults(items); + + function handleAsyncResults(items) { + if (!items) return; + + items = $q.when(items); + fetchesInProgress++; + setLoading(true); + + $mdUtil.nextTick(function () { + items + .then(onResultsRetrieved) + .finally(function(){ + if (--fetchesInProgress === 0) { + setLoading(false); + } + }); + },true, $scope); + } + + function onResultsRetrieved(matches) { + cache[term] = matches; + + // Just cache the results if the request is now outdated. + // The request becomes outdated, when the new searchText has changed during the result fetching. + if ((searchText || '') !== ($scope.searchText || '')) { + return; + } + + handleResults(matches); + } + } + + + /** + * Reports given message types to supported screen readers. + * @param {boolean} isPolite Whether the announcement should be polite. + * @param {!number} types Message flags to be reported to the screen reader. + */ + function reportMessages(isPolite, types) { + var politeness = isPolite ? 'polite' : 'assertive'; + var messages = []; + + if (types & ReportType.Selected && ctrl.index !== -1) { + messages.push(getCurrentDisplayValue()); + } + + if (types & ReportType.Count) { + messages.push($q.resolve(getCountMessage())); + } + + $q.all(messages).then(function(data) { + $mdLiveAnnouncer.announce(data.join(' '), politeness); + }); + } + + /** + * @returns {string} the ARIA message for how many results match the current query. + */ + function getCountMessage () { + switch (ctrl.matches.length) { + case 0: + return ctrl.noMatchMessage; + case 1: + return ctrl.singleMatchMessage; + default: + return ctrl.multipleMatchStartMessage + ctrl.matches.length + ctrl.multipleMatchEndMessage; + } + } + + /** + * Makes sure that the focused element is within view. + */ + function updateScroll () { + if (!elements.li[0]) return; + if (mode === MODE_STANDARD) { + updateStandardScroll(); + } else { + updateVirtualScroll(); + } + } + + function updateVirtualScroll() { + // elements in virtual scroll have consistent heights + var optionHeight = elements.li[0].offsetHeight, + top = optionHeight * Math.max(0, ctrl.index), + bottom = top + optionHeight, + containerHeight = elements.scroller.clientHeight, + scrollTop = elements.scroller.scrollTop; + + if (top < scrollTop) { + scrollTo(top); + } else if (bottom > scrollTop + containerHeight) { + scrollTo(bottom - containerHeight); + } + } + + function updateStandardScroll() { + // elements in standard scroll have variable heights + var selected = elements.li[Math.max(0, ctrl.index)]; + var containerHeight = elements.scrollContainer.offsetHeight, + top = selected && selected.offsetTop || 0, + bottom = top + selected.clientHeight, + scrollTop = elements.scrollContainer.scrollTop; + + if (top < scrollTop) { + scrollTo(top); + } else if (bottom > scrollTop + containerHeight) { + scrollTo(bottom - containerHeight); + } + } + + function isPromiseFetching() { + return fetchesInProgress !== 0; + } + + function scrollTo (offset) { + if (mode === MODE_STANDARD) { + elements.scrollContainer.scrollTop = offset; + } else { + elements.$.scrollContainer.controller('mdVirtualRepeatContainer').scrollTo(offset); + } + } + + function notFoundVisible () { + var textLength = (ctrl.scope.searchText || '').length; + + return ctrl.hasNotFound && !hasMatches() && (!ctrl.loading || isPromiseFetching()) && textLength >= getMinLength() && (hasFocus || noBlur) && !hasSelection(); + } + + /** + * Starts the query to gather the results for the current searchText. Attempts to return cached + * results first, then forwards the process to `fetchResults` if necessary. + */ + function handleQuery () { + var searchText = $scope.searchText || ''; + var term = searchText.toLowerCase(); + + // If caching is enabled and the current searchText is stored in the cache + if (!$scope.noCache && cache[term]) { + // The results should be handled as same as a normal un-cached request does. + handleResults(cache[term]); + } else { + fetchResults(searchText); + } + + ctrl.hidden = shouldHide(); + } + + /** + * Handles the retrieved results by showing them in the autocompletes dropdown. + * @param results Retrieved results + */ + function handleResults(results) { + ctrl.matches = results; + ctrl.hidden = shouldHide(); + + // If loading is in progress, then we'll end the progress. This is needed for example, + // when the `clear` button was clicked, because there we always show the loading process, to prevent flashing. + if (ctrl.loading) setLoading(false); + + if ($scope.selectOnMatch) selectItemOnMatch(); + + positionDropdown(); + reportMessages(true, ReportType.Count); + } + + /** + * If there is only one matching item and the search text matches its display value exactly, + * automatically select that item. Note: This function is only called if the user uses the + * `md-select-on-match` flag. + */ + function selectItemOnMatch () { + var searchText = $scope.searchText, + matches = ctrl.matches, + item = matches[ 0 ]; + if (matches.length === 1) getDisplayValue(item).then(function (displayValue) { + var isMatching = searchText === displayValue; + if ($scope.matchInsensitive && !isMatching) { + isMatching = searchText.toLowerCase() === displayValue.toLowerCase(); + } + + if (isMatching) { + select(0); + } + }); + } + + /** + * Evaluates an attribute expression against the parent scope. + * @param {String} attr Name of the attribute to be evaluated. + * @param {Object?} locals Properties to be injected into the evaluation context. + */ + function evalAttr(attr, locals) { + if ($attrs[attr]) { + $scope.$parent.$eval($attrs[attr], locals || {}); + } + } + +} + +})(); +(function(){ +"use strict"; + + +MdAutocomplete.$inject = ["$$mdSvgRegistry"];angular + .module('material.components.autocomplete') + .directive('mdAutocomplete', MdAutocomplete); + +/** + * @ngdoc directive + * @name mdAutocomplete + * @module material.components.autocomplete + * + * @description + * `` is a special input component with a drop-down of all possible matches to a + * custom query. This component allows you to provide real-time suggestions as the user types + * in the input area. + * + * To start, you will need to specify the required parameters and provide a template for your + * results. The content inside `md-autocomplete` will be treated as a template. + * + * In more complex cases, you may want to include other content such as a message to display when + * no matches were found. You can do this by wrapping your template in `md-item-template` and + * adding a tag for `md-not-found`. An example of this is shown below. + * + * To reset the displayed value you must clear both values for `md-search-text` and + * `md-selected-item`. + * + * ### Validation + * + * You can use `ng-messages` to include validation the same way that you would normally validate; + * however, if you want to replicate a standard input with a floating label, you will have to + * do the following: + * + * - Make sure that your template is wrapped in `md-item-template` + * - Add your `ng-messages` code inside of `md-autocomplete` + * - Add your validation properties to `md-autocomplete` (ie. `required`) + * - Add a `name` to `md-autocomplete` (to be used on the generated `input`) + * + * There is an example below of how this should look. + * + * ### Snapping Drop-Down + * + * You can cause the autocomplete drop-down to snap to an ancestor element by applying the + * `md-autocomplete-snap` attribute to that element. You can also snap to the width of + * the `md-autocomplete-snap` element by setting the attribute's value to `width` + * (ie. `md-autocomplete-snap="width"`). + * + * ### Notes + * + * **Autocomplete Dropdown Items Rendering** + * + * The `md-autocomplete` uses the the + * mdVirtualRepeat directive for displaying the results inside of the dropdown.
    + * + * > When encountering issues regarding the item template please take a look at the + * VirtualRepeatContainer documentation. + * + * **Autocomplete inside of a Virtual Repeat** + * + * When using the `md-autocomplete` directive inside of a + * VirtualRepeatContainer the dropdown items + * might not update properly, because caching of the results is enabled by default. + * + * The autocomplete will then show invalid dropdown items, because the Virtual Repeat only updates + * the scope bindings rather than re-creating the `md-autocomplete`. This means that the previous + * cached results will be used. + * + * > To avoid such problems, ensure that the autocomplete does not cache any results via + * `md-no-cache="true"`: + * + * + * + * {{ item.display }} + * + * + * + * + * @param {expression} md-items An expression in the format of `item in results` to iterate over + * matches for your search.

    + * The `results` expression can be also a function, which returns the results synchronously + * or asynchronously (per Promise). + * @param {expression=} md-selected-item-change An expression to be run each time a new item is + * selected. + * @param {expression=} md-search-text-change An expression to be run each time the search text + * updates. + * @param {expression=} md-search-text A model to bind the search query text to. + * @param {object=} md-selected-item A model to bind the selected item to. + * @param {expression=} md-item-text An expression that will convert your object to a single string. + * @param {string=} placeholder Placeholder text that will be forwarded to the input. + * @param {boolean=} md-no-cache Disables the internal caching that happens in autocomplete. + * @param {boolean=} ng-disabled Determines whether or not to disable the input field. + * @param {boolean=} md-require-match When set to true, the autocomplete will add a validator, + * which will evaluate to false, when no item is currently selected. + * @param {number=} md-min-length Specifies the minimum length of text before autocomplete will + * make suggestions. + * @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking + * for results. + * @param {boolean=} md-clear-button Whether the clear button for the autocomplete input should show + * up or not. When `md-floating-label` is set, defaults to false, defaults to true otherwise. + * @param {boolean=} md-autofocus If true, the autocomplete will be automatically focused when a + * `$mdDialog`, `$mdBottomsheet` or `$mdSidenav`, which contains the autocomplete, is opening. + *

    + * Also the autocomplete will immediately focus the input element. + * @param {boolean=} md-no-asterisk When present, asterisk will not be appended to the floating + * label. + * @param {boolean=} md-autoselect If set to true, the first item will be automatically selected + * in the dropdown upon open. + * @param {string=} md-input-name The name attribute given to the input element to be used with + * FormController. + * @param {string=} md-menu-class This class will be applied to the dropdown menu for styling. + * @param {string=} md-menu-container-class This class will be applied to the parent container + * of the dropdown panel. + * @param {string=} md-input-class This will be applied to the input for styling. This attribute + * is only valid when a `md-floating-label` is defined. + * @param {string=} md-floating-label This will add a floating label to autocomplete and wrap it in + * `md-input-container`. + * @param {string=} md-select-on-focus When present the input's text will be automatically selected + * on focus. + * @param {string=} md-input-id An ID to be added to the input element. + * @param {number=} md-input-minlength The minimum length for the input's value for validation. + * @param {number=} md-input-maxlength The maximum length for the input's value for validation. + * @param {boolean=} md-select-on-match When set, autocomplete will automatically select + * the item if the search text is an exact match.

    + * An exact match is when only one match is displayed. + * @param {boolean=} md-match-case-insensitive When set and using `md-select-on-match`, autocomplete + * will select on case-insensitive match. + * @param {string=} md-escape-options Override escape key logic. Default is `clear`.
    + * Options: `blur`, `clear`, `none`. + * @param {string=} md-dropdown-items Specifies the maximum amount of items to be shown in + * the dropdown.

    + * When the dropdown doesn't fit into the viewport, the dropdown will shrink + * as much as possible. + * @param {string=} md-dropdown-position Overrides the default dropdown position. Options: `top`, + * `bottom`. + * @param {string=} input-aria-describedby A space-separated list of element IDs. This should + * contain the IDs of any elements that describe this autocomplete. Screen readers will read the + * content of these elements at the end of announcing that the autocomplete has been selected + * and describing its current state. The descriptive elements do not need to be visible on the + * page. + * @param {string=} input-aria-labelledby A space-separated list of element IDs. The ideal use case + * is that this would contain the ID of a `
    ' : ''; + } + + function getRepeatType(repeatMode) { + return isVirtualRepeatDisabled(repeatMode) ? + 'ng-repeat' : 'md-virtual-repeat'; + } + + function isVirtualRepeatDisabled(repeatMode) { + // ensure we have a valid repeat mode + var correctedRepeatMode = getRepeatMode(repeatMode); + return correctedRepeatMode !== REPEAT_VIRTUAL; + } + + function getInputElement () { + if (attr.mdFloatingLabel) { + return '\ + \ + \ + \ +
    ' + leftover + '
    \ +
    '; + } else { + return '\ + '; + } + } + + function getClearButton() { + return '' + + ''; + } + } + }; +} + +})(); +(function(){ +"use strict"; + + +MdAutocompleteItemScopeDirective.$inject = ["$compile", "$mdUtil"];angular + .module('material.components.autocomplete') + .directive('mdAutocompleteParentScope', MdAutocompleteItemScopeDirective); + +function MdAutocompleteItemScopeDirective($compile, $mdUtil) { + return { + restrict: 'AE', + compile: compile, + terminal: true, + transclude: 'element' + }; + + function compile(tElement, tAttr, transclude) { + return function postLink(scope, element, attr) { + var ctrl = scope.$mdAutocompleteCtrl; + var newScope = ctrl.parent.$new(); + var itemName = ctrl.itemName; + + // Watch for changes to our scope's variables and copy them to the new scope + watchVariable('$index', '$index'); + watchVariable('item', itemName); + + // Ensure that $digest calls on our scope trigger $digest on newScope. + connectScopes(); + + // Link the element against newScope. + transclude(newScope, function(clone) { + element.after(clone); + }); + + /** + * Creates a watcher for variables that are copied from the parent scope + * @param variable + * @param alias + */ + function watchVariable(variable, alias) { + newScope[alias] = scope[variable]; + + scope.$watch(variable, function(value) { + $mdUtil.nextTick(function() { + newScope[alias] = value; + }); + }); + } + + /** + * Creates watchers on scope and newScope that ensure that for any + * $digest of scope, newScope is also $digested. + */ + function connectScopes() { + var scopeDigesting = false; + var newScopeDigesting = false; + + scope.$watch(function() { + if (newScopeDigesting || scopeDigesting) { + return; + } + + scopeDigesting = true; + scope.$$postDigest(function() { + if (!newScopeDigesting) { + newScope.$digest(); + } + + scopeDigesting = newScopeDigesting = false; + }); + }); + + newScope.$watch(function() { + newScopeDigesting = true; + }); + } + }; + } +} +})(); +(function(){ +"use strict"; + + +MdHighlightCtrl.$inject = ["$scope", "$element", "$attrs", "$mdUtil"];angular + .module('material.components.autocomplete') + .controller('MdHighlightCtrl', MdHighlightCtrl); + +function MdHighlightCtrl ($scope, $element, $attrs, $mdUtil) { + this.$scope = $scope; + this.$element = $element; + this.$attrs = $attrs; + this.$mdUtil = $mdUtil; + + // Cache the Regex to avoid rebuilding each time. + this.regex = null; +} + +MdHighlightCtrl.prototype.init = function(unsafeTermFn, unsafeContentFn) { + + this.flags = this.$attrs.mdHighlightFlags || ''; + + this.unregisterFn = this.$scope.$watch(function($scope) { + return { + term: unsafeTermFn($scope), + contentText: unsafeContentFn($scope) + }; + }.bind(this), this.onRender.bind(this), true); + + this.$element.on('$destroy', this.unregisterFn); +}; + +/** + * Triggered once a new change has been recognized and the highlighted + * text needs to be updated. + */ +MdHighlightCtrl.prototype.onRender = function(state, prevState) { + + var contentText = state.contentText; + + /* Update the regex if it's outdated, because we don't want to rebuilt it constantly. */ + if (this.regex === null || state.term !== prevState.term) { + this.regex = this.createRegex(state.term, this.flags); + } + + /* If a term is available apply the regex to the content */ + if (state.term) { + this.applyRegex(contentText); + } else { + this.$element.text(contentText); + } + +}; + +/** + * Decomposes the specified text into different tokens (whether match or not). + * Breaking down the string guarantees proper XSS protection due to the native browser + * escaping of unsafe text. + */ +MdHighlightCtrl.prototype.applyRegex = function(text) { + var tokens = this.resolveTokens(text); + + this.$element.empty(); + + tokens.forEach(function (token) { + + if (token.isMatch) { + var tokenEl = angular.element('').text(token.text); + + this.$element.append(tokenEl); + } else { + this.$element.append(document.createTextNode(token)); + } + + }.bind(this)); + +}; + + /** + * Decomposes the specified text into different tokens by running the regex against the text. + */ +MdHighlightCtrl.prototype.resolveTokens = function(string) { + var tokens = []; + var lastIndex = 0; + + // Use replace here, because it supports global and single regular expressions at same time. + string.replace(this.regex, function(match, index) { + appendToken(lastIndex, index); + + tokens.push({ + text: match, + isMatch: true + }); + + lastIndex = index + match.length; + }); + + // Append the missing text as a token. + appendToken(lastIndex); + + return tokens; + + function appendToken(from, to) { + var targetText = string.slice(from, to); + targetText && tokens.push(targetText); + } +}; + +/** Creates a regex for the specified text with the given flags. */ +MdHighlightCtrl.prototype.createRegex = function(term, flags) { + var startFlag = '', endFlag = ''; + var regexTerm = this.$mdUtil.sanitize(term); + + if (flags.indexOf('^') >= 0) startFlag = '^'; + if (flags.indexOf('$') >= 0) endFlag = '$'; + + return new RegExp(startFlag + regexTerm + endFlag, flags.replace(/[$^]/g, '')); +}; + +})(); +(function(){ +"use strict"; + + +MdHighlight.$inject = ["$interpolate", "$parse"];angular + .module('material.components.autocomplete') + .directive('mdHighlightText', MdHighlight); + +/** + * @ngdoc directive + * @name mdHighlightText + * @module material.components.autocomplete + * + * @description + * The `md-highlight-text` directive allows you to specify text that should be highlighted within + * an element. Highlighted text will be wrapped in `` which can + * be styled through CSS. Please note that child elements may not be used with this directive. + * + * @param {string} md-highlight-text A model to be searched for + * @param {string=} md-highlight-flags A list of flags (loosely based on JavaScript RexExp flags). + * #### **Supported flags**: + * - `g`: Find all matches within the provided text + * - `i`: Ignore case when searching for matches + * - `$`: Only match if the text ends with the search term + * - `^`: Only match if the text begins with the search term + * + * @usage + * + * + *
      + *
    • + * {{result.text}} + *
    • + *
    + *
    + */ + +function MdHighlight ($interpolate, $parse) { + return { + terminal: true, + controller: 'MdHighlightCtrl', + compile: function mdHighlightCompile(tElement, tAttr) { + var termExpr = $parse(tAttr.mdHighlightText); + var unsafeContentExpr = $interpolate(tElement.html()); + + return function mdHighlightLink(scope, element, attr, ctrl) { + ctrl.init(termExpr, unsafeContentExpr); + }; + } + }; +} + +})(); +(function(){ +"use strict"; + +/* + * @ngdoc module + * @name material.components.backdrop + * @description Backdrop + */ + +/** + * @ngdoc directive + * @name mdBackdrop + * @module material.components.backdrop + * + * @restrict E + * + * @description + * `` is a backdrop element used by other components, such as dialog and bottom sheet. + * Apply class `opaque` to make the backdrop use the theme backdrop color. + * + */ + +angular + .module('material.components.backdrop', ['material.core']) + .directive('mdBackdrop', ["$mdTheming", "$mdUtil", "$animate", "$rootElement", "$window", "$log", "$$rAF", "$document", function BackdropDirective($mdTheming, $mdUtil, $animate, $rootElement, $window, $log, $$rAF, $document) { + var ERROR_CSS_POSITION = ' may not work properly in a scrolled, static-positioned parent container.'; + + return { + restrict: 'E', + link: postLink + }; + + function postLink(scope, element, attrs) { + // backdrop may be outside the $rootElement, tell ngAnimate to animate regardless + if ($animate.pin) $animate.pin(element, $rootElement); + + var bodyStyles; + + $$rAF(function() { + // If body scrolling has been disabled using mdUtil.disableBodyScroll(), + // adjust the 'backdrop' height to account for the fixed 'body' top offset. + // Note that this can be pretty expensive and is better done inside the $$rAF. + bodyStyles = $window.getComputedStyle($document[0].body); + + if (bodyStyles.position === 'fixed') { + var resizeHandler = $mdUtil.debounce(function(){ + bodyStyles = $window.getComputedStyle($document[0].body); + resize(); + }, 60, null, false); + + resize(); + angular.element($window).on('resize', resizeHandler); + + scope.$on('$destroy', function() { + angular.element($window).off('resize', resizeHandler); + }); + } + + // Often $animate.enter() is used to append the backDrop element + // so let's wait until $animate is done... + var parent = element.parent(); + + if (parent.length) { + if (parent[0].nodeName === 'BODY') { + element.css('position', 'fixed'); + } + + var styles = $window.getComputedStyle(parent[0]); + + if (styles.position === 'static') { + // backdrop uses position:absolute and will not work properly with parent position:static (default) + $log.warn(ERROR_CSS_POSITION); + } + + // Only inherit the parent if the backdrop has a parent. + $mdTheming.inherit(element, parent); + } + }); + + function resize() { + var viewportHeight = parseInt(bodyStyles.height, 10) + Math.abs(parseInt(bodyStyles.top, 10)); + element.css('height', viewportHeight + 'px'); + } + } + + }]); + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.bottomSheet + * @description + * BottomSheet + */ +MdBottomSheetDirective.$inject = ["$mdBottomSheet"]; +MdBottomSheetProvider.$inject = ["$$interimElementProvider"]; +angular + .module('material.components.bottomSheet', [ + 'material.core', + 'material.components.backdrop' + ]) + .directive('mdBottomSheet', MdBottomSheetDirective) + .provider('$mdBottomSheet', MdBottomSheetProvider); + +/* @ngInject */ +function MdBottomSheetDirective($mdBottomSheet) { + return { + restrict: 'E', + link : function postLink(scope, element) { + element.addClass('_md'); // private md component indicator for styling + + // When navigation force destroys an interimElement, then + // listen and $destroy() that interim instance... + scope.$on('$destroy', function() { + $mdBottomSheet.destroy(); + }); + } + }; +} + + +/** + * @ngdoc service + * @name $mdBottomSheet + * @module material.components.bottomSheet + * + * @description + * `$mdBottomSheet` opens a bottom sheet over the app and provides a simple promise API. + * + * ## Restrictions + * + * - The bottom sheet's template must have an outer `` element. + * - Add the `md-grid` class to the bottom sheet for a grid layout. + * - Add the `md-list` class to the bottom sheet for a list layout. + * + * @usage + * + *
    + * + * Open a Bottom Sheet! + * + *
    + *
    + * + * var app = angular.module('app', ['ngMaterial']); + * app.controller('MyController', function($scope, $mdBottomSheet) { + * $scope.openBottomSheet = function() { + * $mdBottomSheet.show({ + * template: '' + + * 'Hello! Close' + + * '' + * }) + * + * // Fires when the hide() method is used + * .then(function() { + * console.log('You clicked the button to close the bottom sheet!'); + * }) + * + * // Fires when the cancel() method is used + * .catch(function() { + * console.log('You hit escape or clicked the backdrop to close.'); + * }); + * }; + * + * $scope.closeBottomSheet = function($scope, $mdBottomSheet) { + * $mdBottomSheet.hide(); + * } + * + * }); + * + * + * ### Custom Presets + * Developers are also able to create their own preset, which can be easily used without repeating + * their options each time. + * + * + * $mdBottomSheetProvider.addPreset('testPreset', { + * options: function() { + * return { + * template: + * '' + + * 'This is a custom preset' + + * '', + * controllerAs: 'bottomSheet', + * bindToController: true, + * clickOutsideToClose: true, + * escapeToClose: true + * }; + * } + * }); + * + * + * After you create your preset during the config phase, you can easily access it. + * + * + * $mdBottomSheet.show( + * $mdBottomSheet.testPreset() + * ); + * + */ + +/** + * @ngdoc method + * @name $mdBottomSheet#show + * + * @description + * Show a bottom sheet with the specified options. + * + * Note: You should always provide a `.catch()` method in case the user hits the + * `esc` key or clicks the background to close. In this case, the `cancel()` method will + * automatically be called on the bottom sheet which will `reject()` the promise. See the @usage + * section above for an example. + * + * Newer versions of Angular will throw a `Possibly unhandled rejection` exception if you forget + * this. + * + * @param {Object} optionsOrPreset Either provide an `$mdBottomSheetPreset` defined during the + * config phase or an options object, with the following properties: + * + * - `templateUrl` - `{string=}`: The url of an html template file that will + * be used as the content of the bottom sheet. Restrictions: the template must + * have an outer `md-bottom-sheet` element. + * - `template` - `{string=}`: Same as templateUrl, except this is an actual + * template string. + * - `scope` - `{Object=}`: the scope to link the template / controller to. If none is specified, + * it will create a new child scope. This scope will be destroyed when the bottom sheet is + * removed unless `preserveScope` is set to true. + * - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. + * Default is false + * - `controller` - `{string=}`: The controller to associate with this bottom sheet. + * - `locals` - `{string=}`: An object containing key/value pairs. The keys will be used as names + * of values to inject into the controller. For example, `locals: {three: 3}` would inject + * `three` into the controller with the value of 3. + * - `clickOutsideToClose` - `{boolean=}`: Whether the user can click outside the bottom sheet to + * close it. Default true. + * - `bindToController` - `{boolean=}`: When set to true, the locals will be bound to the + * controller instance and available in it's $onInit function. + * - `disableBackdrop` - `{boolean=}`: When set to true, the bottomsheet will not show a backdrop. + * - `escapeToClose` - `{boolean=}`: Whether the user can press escape to close the bottom sheet. + * Default true. + * - `isLockedOpen` - `{boolean=}`: Disables all default ways of closing the bottom sheet. + * **Note:** this will override the `clickOutsideToClose` and `escapeToClose` options, leaving + * only the `hide` and `cancel` methods as ways of closing the bottom sheet. Defaults to false. + * - `resolve` - `{Object=}`: Similar to locals, except it takes promises as values + * and the bottom sheet will not open until the promises resolve. + * - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope. + * - `parent` - `{element=}`: The element to append the bottom sheet to. The `parent` may be a + * `function`, `string`, `Object`, or null. Defaults to appending to the body of the root element + * (or the root element) of the application. + * e.g. angular.element(document.getElementById('content')) or "#content" + * - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the bottom sheet is + * open. Default true. + * + * @returns {promise} A promise that can be resolved with `$mdBottomSheet.hide()` or + * rejected with `$mdBottomSheet.cancel()`. + */ + +/** + * @ngdoc method + * @name $mdBottomSheet#hide + * + * @description + * Hide the existing bottom sheet and resolve the promise returned from + * `$mdBottomSheet.show()`. This call will close the most recently opened/current bottom sheet (if + * any). + * + * Note: Use a `.then()` on your `.show()` to handle this callback. + * + * @param {*=} response An argument for the resolved promise. + * + */ + +/** + * @ngdoc method + * @name $mdBottomSheet#cancel + * + * @description + * Hide the existing bottom sheet and reject the promise returned from + * `$mdBottomSheet.show()`. + * + * Note: Use a `.catch()` on your `.show()` to handle this callback. + * + * @param {*=} response An argument for the rejected promise. + * + */ + +function MdBottomSheetProvider($$interimElementProvider) { + // how fast we need to flick down to close the sheet, pixels/ms + bottomSheetDefaults.$inject = ["$animate", "$mdConstant", "$mdUtil", "$mdTheming", "$mdBottomSheet", "$rootElement", "$mdGesture", "$log"]; + var CLOSING_VELOCITY = 0.5; + var PADDING = 80; // same as css + + return $$interimElementProvider('$mdBottomSheet') + .setDefaults({ + methods: ['disableParentScroll', 'escapeToClose', 'clickOutsideToClose'], + options: bottomSheetDefaults + }); + + /* @ngInject */ + function bottomSheetDefaults($animate, $mdConstant, $mdUtil, $mdTheming, $mdBottomSheet, $rootElement, + $mdGesture, $log) { + var backdrop; + + return { + themable: true, + onShow: onShow, + onRemove: onRemove, + disableBackdrop: false, + escapeToClose: true, + clickOutsideToClose: true, + disableParentScroll: true, + isLockedOpen: false + }; + + function onShow(scope, element, options) { + element = $mdUtil.extractElementByName(element, 'md-bottom-sheet'); + + // prevent tab focus or click focus on the bottom-sheet container + element.attr('tabindex', '-1'); + + // Once the md-bottom-sheet has `ng-cloak` applied on his template the opening animation will not work properly. + // This is a very common problem, so we have to notify the developer about this. + if (element.hasClass('ng-cloak')) { + var message = '$mdBottomSheet: using `` will affect the bottom-sheet opening animations.'; + $log.warn(message, element[0]); + } + + if (options.isLockedOpen) { + options.clickOutsideToClose = false; + options.escapeToClose = false; + } else { + options.cleanupGestures = registerGestures(element, options.parent); + } + + if (!options.disableBackdrop) { + // Add a backdrop that will close on click + backdrop = $mdUtil.createBackdrop(scope, "md-bottom-sheet-backdrop md-opaque"); + + // Prevent mouse focus on backdrop; ONLY programmatic focus allowed. + // This allows clicks on backdrop to propagate to the $rootElement and + // ESC key events to be detected properly. + backdrop[0].tabIndex = -1; + + if (options.clickOutsideToClose) { + backdrop.on('click', function() { + $mdUtil.nextTick($mdBottomSheet.cancel, true); + }); + } + + $mdTheming.inherit(backdrop, options.parent); + + $animate.enter(backdrop, options.parent, null); + } + + $mdTheming.inherit(element, options.parent); + + if (options.disableParentScroll) { + options.restoreScroll = $mdUtil.disableScrollAround(element, options.parent); + } + + return $animate.enter(element, options.parent, backdrop) + .then(function() { + var focusable = $mdUtil.findFocusTarget(element) || angular.element( + element[0].querySelector('button') || + element[0].querySelector('a') || + element[0].querySelector($mdUtil.prefixer('ng-click', true)) + ) || backdrop; + + if (options.escapeToClose) { + options.rootElementKeyupCallback = function(e) { + if (e.keyCode === $mdConstant.KEY_CODE.ESCAPE) { + $mdUtil.nextTick($mdBottomSheet.cancel, true); + } + }; + + $rootElement.on('keyup', options.rootElementKeyupCallback); + focusable && focusable.focus(); + } + }); + + } + + function onRemove(scope, element, options) { + if (!options.disableBackdrop) $animate.leave(backdrop); + + return $animate.leave(element).then(function() { + if (options.disableParentScroll) { + options.restoreScroll(); + delete options.restoreScroll; + } + + options.cleanupGestures && options.cleanupGestures(); + }); + } + + /** + * Adds the drag gestures to the bottom sheet. + * @param {JQLite} element where CSS transitions will be applied + * @param {JQLite} parent used for registering gesture listeners + * @return {Function} function that removes gesture listeners that were set up by + * registerGestures() + */ + function registerGestures(element, parent) { + var deregister = $mdGesture.register(parent, 'drag', { horizontal: false }); + parent.on('$md.dragstart', onDragStart) + .on('$md.drag', onDrag) + .on('$md.dragend', onDragEnd); + + return function cleanupGestures() { + deregister(); + parent.off('$md.dragstart', onDragStart); + parent.off('$md.drag', onDrag); + parent.off('$md.dragend', onDragEnd); + }; + + function onDragStart() { + // Disable transitions on transform so that it feels fast + element.css($mdConstant.CSS.TRANSITION_DURATION, '0ms'); + } + + function onDrag(ev) { + var transform = ev.pointer.distanceY; + if (transform < 5) { + // Slow down drag when trying to drag up, and stop after PADDING + transform = Math.max(-PADDING, transform / 2); + } + element.css($mdConstant.CSS.TRANSFORM, 'translate3d(0,' + (PADDING + transform) + 'px,0)'); + } + + function onDragEnd(ev) { + if (ev.pointer.distanceY > 0 && + (ev.pointer.distanceY > 20 || Math.abs(ev.pointer.velocityY) > CLOSING_VELOCITY)) { + var distanceRemaining = element.prop('offsetHeight') - ev.pointer.distanceY; + var transitionDuration = Math.min(distanceRemaining / ev.pointer.velocityY * 0.75, 500); + element.css($mdConstant.CSS.TRANSITION_DURATION, transitionDuration + 'ms'); + $mdUtil.nextTick($mdBottomSheet.cancel, true); + } else { + element.css($mdConstant.CSS.TRANSITION_DURATION, ''); + element.css($mdConstant.CSS.TRANSFORM, ''); + } + } + } + } +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.button + * @description + * + * Button + */ +MdButtonDirective.$inject = ["$mdButtonInkRipple", "$mdTheming", "$mdAria", "$mdInteraction"]; +MdAnchorDirective.$inject = ["$mdTheming"]; +angular + .module('material.components.button', ['material.core']) + .directive('mdButton', MdButtonDirective) + .directive('a', MdAnchorDirective); + + +/** + * @private + * @restrict E + * + * @description + * `a` is an anchor directive used to inherit theme colors for md-primary, md-accent, etc. + * + * @usage + * + * + * + * + * + * + */ +function MdAnchorDirective($mdTheming) { + return { + restrict : 'E', + link : function postLink(scope, element) { + // Make sure to inherit theme so stand-alone anchors + // support theme colors for md-primary, md-accent, etc. + $mdTheming(element); + } + }; +} + + +/** + * @ngdoc directive + * @name mdButton + * @module material.components.button + * + * @restrict E + * + * @description + * `` is a button directive with optional ink ripples (default enabled). + * + * If you supply a `href` or `ng-href` attribute, it will become an `` element. Otherwise, it + * will become a `'; + } + } + + function postLink(scope, element, attr) { + $mdTheming(element); + $mdButtonInkRipple.attach(scope, element); + + // Use async expect to support possible bindings in the button label + $mdAria.expectWithoutText(element, 'aria-label'); + + // For anchor elements, we have to set tabindex manually when the element is disabled. + // We don't do this for md-nav-bar anchors as the component manages its own tabindex values. + if (isAnchor(attr) && angular.isDefined(attr.ngDisabled) && + !element.hasClass('_md-nav-button')) { + scope.$watch(attr.ngDisabled, function(isDisabled) { + element.attr('tabindex', isDisabled ? -1 : 0); + }); + } + + // disabling click event when disabled is true + element.on('click', function(e){ + if (attr.disabled === true) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + }); + + if (!element.hasClass('md-no-focus')) { + + element.on('focus', function() { + + // Only show the focus effect when being focused through keyboard interaction or programmatically + if (!$mdInteraction.isUserInvoked() || $mdInteraction.getLastInteractionType() === 'keyboard') { + element.addClass('md-focused'); + } + + }); + + element.on('blur', function() { + element.removeClass('md-focused'); + }); + } + + } + +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.card + * + * @description + * Card components. + */ +mdCardDirective.$inject = ["$mdTheming"]; +angular.module('material.components.card', [ + 'material.core' + ]) + .directive('mdCard', mdCardDirective); + + +/** + * @ngdoc directive + * @name mdCard + * @module material.components.card + * + * @restrict E + * + * @description + * The `` directive is a container element used within `` containers. + * + * An image included as a direct descendant will fill the card's width. If you want to avoid this, + * you can add the `md-image-no-fill` class to the parent element. The `` + * container will wrap text content and provide padding. An `` element can be + * optionally included to put content flush against the bottom edge of the card. + * + * Action buttons can be included in an `` element, similar to ``. + * You can then position buttons using layout attributes. + * + * Card is built with: + * * `` - Header for the card, holds avatar, text and squared image + * - `` - Card avatar + * - `md-user-avatar` - Class for user image + * - `` + * - `` - Contains elements for the card description + * - `md-title` - Class for the card title + * - `md-subhead` - Class for the card sub header + * * `` - Image for the card + * * `` - Card content title + * - `` + * - `md-headline` - Class for the card content title + * - `md-subhead` - Class for the card content sub header + * - `` - Squared image within the title + * - `md-media-sm` - Class for small image + * - `md-media-md` - Class for medium image + * - `md-media-lg` - Class for large image + * - `md-media-xl` - Class for extra large image + * * `` - Card content + * * `` - Card actions + * - `` - Icon actions + * + * Cards have constant width and variable heights; where the maximum height is limited to what can + * fit within a single view on a platform, but it can temporarily expand as needed. + * + * @usage + * ### Card with optional footer + * + * + * image caption + * + *

    Card headline

    + *

    Card content

    + *
    + * + * Card footer + * + *
    + *
    + * + * ### Card with actions + * + * + * image caption + * + *

    Card headline

    + *

    Card content

    + *
    + * + * Action 1 + * Action 2 + * + *
    + *
    + * + * ### Card with header, image, title actions and content + * + * + * + * + * + * + * + * Title + * Sub header + * + * + * image caption + * + * + * Card headline + * Card subheader + * + * + * + * Action 1 + * Action 2 + * + * + * + * + * + * + * + *

    + * Card content + *

    + *
    + *
    + *
    + */ +function mdCardDirective($mdTheming) { + return { + restrict: 'E', + link: function ($scope, $element, attr) { + $element.addClass('_md'); // private md component indicator for styling + $mdTheming($element); + } + }; +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.checkbox + * @description Checkbox module! + */ +MdCheckboxDirective.$inject = ["inputDirective", "$mdAria", "$mdConstant", "$mdTheming", "$mdUtil", "$mdInteraction"]; +angular + .module('material.components.checkbox', ['material.core']) + .directive('mdCheckbox', MdCheckboxDirective); + +/** + * @ngdoc directive + * @name mdCheckbox + * @module material.components.checkbox + * @restrict E + * + * @description + * The checkbox directive is used like the normal + * [angular checkbox](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D). + * + * As per the [Material Design spec](https://material.io/archive/guidelines/style/color.html#color-color-palette) + * the checkbox is in the accent color by default. The primary color palette may be used with + * the `md-primary` class. + * + * @param {expression} ng-model Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {expression=} ng-true-value The value to which the expression should be set when selected. + * @param {expression=} ng-false-value The value to which the expression should be set when not + * selected. + * @param {expression=} ng-change Expression to be executed when the model value changes. + * @param {boolean=} md-no-ink If present, disable ink ripple effects. + * @param {string=} aria-label Adds label to checkbox for accessibility. + * Defaults to checkbox's text. If no default text is found, a warning will be logged. + * @param {expression=} md-indeterminate This determines when the checkbox should be rendered as + * 'indeterminate'. If a truthy expression or no value is passed in the checkbox renders in the + * md-indeterminate state. If falsy expression is passed in it just looks like a normal unchecked + * checkbox. The indeterminate, checked, and unchecked states are mutually exclusive. A box + * cannot be in any two states at the same time. Adding the 'md-indeterminate' attribute + * overrides any checked/unchecked rendering logic. When using the 'md-indeterminate' attribute + * use 'ng-checked' to define rendering logic instead of using 'ng-model'. + * @param {expression=} ng-checked If this expression evaluates as truthy, the 'md-checked' css + * class is added to the checkbox and it will appear checked. + * + * @usage + * + * + * Finished ? + * + * + * + * No Ink Effects + * + * + * + * Disabled + * + * + * + * + */ +function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $mdUtil, $mdInteraction) { + inputDirective = inputDirective[0]; + + return { + restrict: 'E', + transclude: true, + require: ['^?mdInputContainer', '?ngModel', '?^form'], + priority: $mdConstant.BEFORE_NG_ARIA, + template: + '
    ' + + '
    ' + + '
    ' + + '
    ', + compile: compile + }; + + // ********************************************************** + // Private Methods + // ********************************************************** + + function compile (tElement, tAttrs) { + tAttrs.$set('tabindex', tAttrs.tabindex || '0'); + tAttrs.$set('type', 'checkbox'); + tAttrs.$set('role', tAttrs.type); + tElement.addClass('md-auto-horizontal-margin'); + + return { + pre: function(scope, element) { + // Attach a click handler during preLink, in order to immediately stop propagation + // (especially for ng-click) when the checkbox is disabled. + element.on('click', function(e) { + if (this.hasAttribute('disabled')) { + e.stopImmediatePropagation(); + } + }); + }, + post: postLink + }; + + function postLink(scope, element, attr, ctrls) { + var isIndeterminate; + var containerCtrl = ctrls[0]; + var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); + var formCtrl = ctrls[2]; + var labelHasLink = element.find('a').length > 0; + + // The original component structure is not accessible when the checkbox's label contains a link. + // In order to keep backwards compatibility, we're only changing the structure of the component + // when we detect a link within the label. Using a span after the md-checkbox and attaching it + // via aria-labelledby allows screen readers to find and work with the link within the label. + if (labelHasLink) { + var labelId = 'label-' + $mdUtil.nextUid(); + attr.$set('aria-labelledby', labelId); + + var label = element.children()[1]; + // Use jQLite here since ChildNode.remove() is not supported in IE11. + angular.element(label).remove(); + label.removeAttribute('ng-transclude'); + label.className = 'md-checkbox-link-label'; + label.setAttribute('id', labelId); + element.after(label); + // Make sure that clicking on the label still causes the checkbox to be toggled, when appropriate. + var externalLabel = element.next(); + externalLabel.on('click', listener); + } + + if (containerCtrl) { + var isErrorGetter = containerCtrl.isErrorGetter || function() { + return ngModelCtrl.$invalid && (ngModelCtrl.$touched || (formCtrl && formCtrl.$submitted)); + }; + + containerCtrl.input = element; + + scope.$watch(isErrorGetter, containerCtrl.setInvalid); + } + + $mdTheming(element); + + // Redirect focus events to the root element, because IE11 is always focusing the container element instead + // of the md-checkbox element. This causes issues when using ngModelOptions: `updateOnBlur` + element.children().on('focus', function() { + element.focus(); + }); + + if ($mdUtil.parseAttributeBoolean(attr.mdIndeterminate)) { + setIndeterminateState(); + scope.$watch(attr.mdIndeterminate, setIndeterminateState); + } + + if (attr.ngChecked) { + scope.$watch(scope.$eval.bind(scope, attr.ngChecked), function(value) { + ngModelCtrl.$setViewValue(value); + ngModelCtrl.$render(); + }); + } + + $$watchExpr('ngDisabled', 'tabindex', { + true: '-1', + false: attr.tabindex + }); + + // Don't emit a warning when the label has a link within it. In that case we'll use + // aria-labelledby to point to another span that should be read as the label. + if (!labelHasLink) { + $mdAria.expectWithText(element, 'aria-label'); + } + + // Reuse the original input[type=checkbox] directive from AngularJS core. + // This is a bit hacky as we need our own event listener and own render + // function. + inputDirective.link.pre(scope, { + on: angular.noop, + 0: {} + }, attr, [ngModelCtrl]); + + element.on('click', listener) + .on('keypress', keypressHandler) + .on('focus', function() { + if ($mdInteraction.getLastInteractionType() === 'keyboard') { + element.addClass('md-focused'); + } + }) + .on('blur', function() { + element.removeClass('md-focused'); + }); + + ngModelCtrl.$render = render; + + function $$watchExpr(expr, htmlAttr, valueOpts) { + if (attr[expr]) { + scope.$watch(attr[expr], function(val) { + if (valueOpts[val]) { + element.attr(htmlAttr, valueOpts[val]); + } + }); + } + } + + /** + * @param {KeyboardEvent} ev 'keypress' event to handle + */ + function keypressHandler(ev) { + var keyCode = ev.which || ev.keyCode; + var submit, form; + + ev.preventDefault(); + switch (keyCode) { + case $mdConstant.KEY_CODE.SPACE: + element.addClass('md-focused'); + listener(ev); + break; + case $mdConstant.KEY_CODE.ENTER: + // Match the behavior of the native . + // When the enter key is pressed while focusing a native checkbox inside a form, + // the browser will trigger a `click` on the first non-disabled submit button/input + // in the form. Note that this is different from text inputs, which + // will directly submit the form without needing a submit button/input to be present. + form = $mdUtil.getClosest(ev.target, 'form'); + if (form) { + submit = form.querySelector('button[type="submit"]:enabled, input[type="submit"]:enabled'); + if (submit) { + submit.click(); + } + } + break; + } + } + + function listener(ev) { + // skipToggle boolean is used by the switch directive to prevent the click event + // when releasing the drag. There will be always a click if releasing the drag over the checkbox. + // If the click came from a link in the checkbox, don't toggle the value. + // We want the link to be opened without changing the value in this case. + if (element[0].hasAttribute('disabled') || scope.skipToggle || ev.target.tagName === 'A') { + return; + } + + scope.$apply(function() { + // Toggle the checkbox value... + var viewValue = attr.ngChecked && attr.ngClick ? attr.checked : !ngModelCtrl.$viewValue; + + ngModelCtrl.$setViewValue(viewValue, ev && ev.type); + ngModelCtrl.$render(); + }); + } + + function render() { + // Cast the $viewValue to a boolean since it could be undefined + var checked = !!ngModelCtrl.$viewValue && !isIndeterminate; + element.toggleClass('md-checked', checked); + if (!isIndeterminate) { + if (checked) { + element.attr('aria-checked', 'true'); + } else { + element.attr('aria-checked', 'false'); + } + } + } + + /** + * @param {string=} newValue + */ + function setIndeterminateState(newValue) { + isIndeterminate = newValue !== false; + if (isIndeterminate) { + element.attr('aria-checked', 'mixed'); + } + element.toggleClass('md-indeterminate', isIndeterminate); + ngModelCtrl.$render(); + } + } + } +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.chips + */ +/* + * @see js folder for chips implementation + */ +angular.module('material.components.chips', [ + 'material.core', + 'material.components.autocomplete' +]); + +})(); +(function(){ +"use strict"; + + +MdChipCtrl.$inject = ["$scope", "$element", "$mdConstant", "$timeout", "$mdUtil"];angular + .module('material.components.chips') + .controller('MdChipCtrl', MdChipCtrl); + +/** + * Controller for the MdChip component. Responsible for handling keyboard + * events and editing the chip if needed. + * + * @param $scope + * @param $element + * @param $mdConstant + * @param $timeout + * @param $mdUtil + * @constructor + */ +function MdChipCtrl ($scope, $element, $mdConstant, $timeout, $mdUtil) { + /** + * @type {$scope} + */ + this.$scope = $scope; + + /** + * @type {$element} + */ + this.$element = $element; + + /** + * @type {$mdConstant} + */ + this.$mdConstant = $mdConstant; + + /** + * @type {$timeout} + */ + this.$timeout = $timeout; + + /** + * @type {$mdUtil} + */ + this.$mdUtil = $mdUtil; + + /** + * @type {boolean} + */ + this.isEditing = false; + + /** + * @type {MdChipsCtrl} + */ + this.parentController = undefined; + + /** + * @type {boolean} + */ + this.enableChipEdit = false; +} + + +/** + * @param {MdChipsCtrl} controller + */ +MdChipCtrl.prototype.init = function(controller) { + this.parentController = controller; + this.enableChipEdit = this.parentController.enableChipEdit; + + if (this.enableChipEdit) { + this.$element.on('keydown', this.chipKeyDown.bind(this)); + this.$element.on('dblclick', this.chipMouseDoubleClick.bind(this)); + this.getChipContent().addClass('_md-chip-content-edit-is-enabled'); + } +}; + + +/** + * @return {Object} first element with the md-chip-content class + */ +MdChipCtrl.prototype.getChipContent = function() { + var chipContents = this.$element[0].getElementsByClassName('md-chip-content'); + return angular.element(chipContents[0]); +}; + + +/** + * When editing the chip, if the user modifies the existing contents, we'll get a span back and + * need to ignore text elements as they only contain blank space. + * `children()` ignores text elements. + * + * When editing the chip, if the user deletes the contents and then enters some new content + * we'll only get a text element back. + * @return {Object} jQuery object representing the content element of the chip + */ +MdChipCtrl.prototype.getContentElement = function() { + var contentElement = angular.element(this.getChipContent().children()[0]); + if (!contentElement || contentElement.length === 0) { + contentElement = angular.element(this.getChipContent().contents()[0]); + } + return contentElement; +}; + + +/** + * @return {number} index of this chip + */ +MdChipCtrl.prototype.getChipIndex = function() { + return parseInt(this.$element.attr('index')); +}; + + +/** + * Update the chip's contents, focus the chip if it's selected, and exit edit mode. + * If the contents were updated to be empty, remove the chip and re-focus the input element. + */ +MdChipCtrl.prototype.goOutOfEditMode = function() { + if (!this.isEditing) { + return; + } + + this.isEditing = false; + this.$element.removeClass('_md-chip-editing'); + this.getChipContent()[0].contentEditable = 'false'; + var chipIndex = this.getChipIndex(); + + var content = this.getContentElement().text(); + if (content) { + this.parentController.updateChipContents(chipIndex, content); + + this.$mdUtil.nextTick(function() { + if (this.parentController.selectedChip === chipIndex) { + this.parentController.focusChip(chipIndex); + } + }.bind(this)); + } else { + this.parentController.removeChipAndFocusInput(chipIndex); + } +}; + + +/** + * Given an HTML element. Selects contents of it. + * @param {Element} node + */ +MdChipCtrl.prototype.selectNodeContents = function(node) { + var range, selection; + if (document.body.createTextRange) { + range = document.body.createTextRange(); + range.moveToElementText(node); + range.select(); + } else if (window.getSelection) { + selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents(node); + selection.removeAllRanges(); + selection.addRange(range); + } +}; + + +/** + * Presents an input element to edit the contents of the chip. + */ +MdChipCtrl.prototype.goInEditMode = function() { + this.isEditing = true; + this.$element.addClass('_md-chip-editing'); + this.getChipContent()[0].contentEditable = 'true'; + this.getChipContent().on('blur', function() { + this.goOutOfEditMode(); + }.bind(this)); + + this.selectNodeContents(this.getChipContent()[0]); +}; + + +/** + * Handles the keydown event on the chip element. If enable-chip-edit attribute is + * set to true, space or enter keys can trigger going into edit mode. Enter can also + * trigger submitting if the chip is already being edited. + * @param {KeyboardEvent} event + */ +MdChipCtrl.prototype.chipKeyDown = function(event) { + if (!this.isEditing && + (event.keyCode === this.$mdConstant.KEY_CODE.ENTER || + event.keyCode === this.$mdConstant.KEY_CODE.SPACE)) { + event.preventDefault(); + this.goInEditMode(); + } else if (this.isEditing && event.keyCode === this.$mdConstant.KEY_CODE.ENTER) { + event.preventDefault(); + this.goOutOfEditMode(); + } +}; + + +/** + * Enter edit mode if we're not already editing and the enable-chip-edit attribute is enabled. + */ +MdChipCtrl.prototype.chipMouseDoubleClick = function() { + if (this.enableChipEdit && !this.isEditing) { + this.goInEditMode(); + } +}; + +})(); +(function(){ +"use strict"; + + +MdChip.$inject = ["$mdTheming", "$mdUtil", "$compile", "$timeout"];angular + .module('material.components.chips') + .directive('mdChip', MdChip); + +/** + * @ngdoc directive + * @name mdChip + * @module material.components.chips + * + * @description + * `` is a component used within ``. It is responsible for rendering an + * individual chip. + * + * + * @usage + * + * + * {{$chip}} + * + * + * + */ + +/** + * MDChip Directive Definition + * + * @param $mdTheming + * @param $mdUtil + * @param $compile + * @param $timeout + * @ngInject + */ +function MdChip($mdTheming, $mdUtil, $compile, $timeout) { + return { + restrict: 'E', + require: ['^?mdChips', 'mdChip'], + link: postLink, + controller: 'MdChipCtrl' + }; + + function postLink(scope, element, attr, ctrls) { + var chipsController = ctrls.shift(); + var chipController = ctrls.shift(); + var chipContentElement = angular.element(element[0].querySelector('.md-chip-content')); + + $mdTheming(element); + + if (chipsController) { + chipController.init(chipsController); + + // When a chip is blurred, make sure to unset (or reset) the selected chip so that tabbing + // through elements works properly + chipContentElement.on('blur', function() { + chipsController.resetSelectedChip(); + chipsController.$scope.$applyAsync(); + }); + } + + // Use $timeout to ensure we run AFTER the element has been added to the DOM so we can focus it. + $timeout(function() { + if (!chipsController) { + return; + } + + if (chipsController.shouldFocusLastChip) { + chipsController.focusLastChipThenInput(); + } + }); + } +} + +})(); +(function(){ +"use strict"; + + +MdChipRemove.$inject = ["$timeout"];angular + .module('material.components.chips') + .directive('mdChipRemove', MdChipRemove); + +/** + * @ngdoc directive + * @name mdChipRemove + * @restrict A + * @module material.components.chips + * + * @description + * Indicates that the associated element should be used as the delete button template for all chips. + * The associated element must be a child of `md-chips`. + * + * The provided button template will be appended to each chip and will remove the associated chip + * on click. + * + * The button is not styled or themed based on the theme set on the `md-chips` component. A theme + * class and custom icon can be specified in your template. + * + * You can also specify the `type` of the button in your template. + * + * @usage + * ### With Standard Chips + * + * + * + * + * + * + * ### With Object Chips + * + * + * + * + * + */ + + +/** + * MdChipRemove Directive Definition. + * + * @param $timeout + * @returns {{restrict: string, require: string[], link: Function, scope: boolean}} + * @constructor + */ +function MdChipRemove ($timeout) { + return { + restrict: 'A', + require: '^mdChips', + scope: false, + link: postLink + }; + + function postLink(scope, element, attr, ctrl) { + element.on('click', function() { + scope.$apply(function() { + ctrl.removeChip(scope.$$replacedScope.$index); + }); + }); + + // Child elements aren't available until after a $timeout tick as they are hidden by an + // `ng-if`. see http://goo.gl/zIWfuw + $timeout(function() { + element.attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); + element.find('button').attr('tabindex', '-1'); + }); + } +} + +})(); +(function(){ +"use strict"; + + +MdChipTransclude.$inject = ["$compile"];angular + .module('material.components.chips') + .directive('mdChipTransclude', MdChipTransclude); + +function MdChipTransclude ($compile) { + return { + restrict: 'EA', + terminal: true, + link: link, + scope: false + }; + function link (scope, element, attr) { + var ctrl = scope.$parent.$mdChipsCtrl, + newScope = ctrl.parent.$new(false, ctrl.parent); + newScope.$$replacedScope = scope; + newScope.$chip = scope.$chip; + newScope.$index = scope.$index; + newScope.$mdChipsCtrl = ctrl; + + var newHtml = ctrl.$scope.$eval(attr.mdChipTransclude); + + element.html(newHtml); + $compile(element.contents())(newScope); + } +} + +})(); +(function(){ +"use strict"; + +/** + * The default chip append delay. + * + * @type {number} + */ +MdChipsCtrl.$inject = ["$scope", "$attrs", "$mdConstant", "$log", "$element", "$timeout", "$mdUtil", "$mdLiveAnnouncer", "$exceptionHandler"]; +var DEFAULT_CHIP_APPEND_DELAY = 300; + +angular + .module('material.components.chips') + .controller('MdChipsCtrl', MdChipsCtrl); + +/** + * Controller for the MdChips component. Responsible for adding to and + * removing from the list of chips, marking chips as selected, and binding to + * the models of various input components. + * + * @param $scope + * @param $attrs + * @param $mdConstant + * @param $log + * @param $element + * @param $timeout + * @param $mdUtil + * @param $mdLiveAnnouncer + * @param $exceptionHandler + * @constructor + */ +function MdChipsCtrl ($scope, $attrs, $mdConstant, $log, $element, $timeout, $mdUtil, + $mdLiveAnnouncer, $exceptionHandler) { + /** @type {Function} **/ + this.$timeout = $timeout; + + /** @type {Object} */ + this.$mdConstant = $mdConstant; + + /** @type {angular.$scope} */ + this.$scope = $scope; + + /** @type {angular.$scope} */ + this.parent = $scope.$parent; + + /** @type {$mdUtil} */ + this.$mdUtil = $mdUtil; + + /** @type {$log} */ + this.$log = $log; + + /** @type {$mdLiveAnnouncer} */ + this.$mdLiveAnnouncer = $mdLiveAnnouncer; + + /** @type {$exceptionHandler} */ + this.$exceptionHandler = $exceptionHandler; + + /** @type {$element} */ + this.$element = $element; + + /** @type {$attrs} */ + this.$attrs = $attrs; + + /** @type {angular.NgModelController} */ + this.ngModelCtrl = null; + + /** @type {angular.NgModelController} */ + this.userInputNgModelCtrl = null; + + /** @type {MdAutocompleteCtrl} */ + this.autocompleteCtrl = null; + + /** @type {Element} */ + this.userInputElement = null; + + /** @type {Array.} */ + this.items = []; + + /** @type {number} */ + this.selectedChip = -1; + + /** @type {string} */ + this.enableChipEdit = $mdUtil.parseAttributeBoolean($attrs.mdEnableChipEdit); + + /** @type {string} */ + this.addOnBlur = $mdUtil.parseAttributeBoolean($attrs.mdAddOnBlur); + + /** + * The class names to apply to the autocomplete or input. + * @type {string} + */ + this.inputClass = ''; + + /** + * The text to be used as the aria-label for the input. + * @type {string} + */ + this.inputAriaLabel = 'Chips input.'; + + /** + * Label text to describe the chips container. Used to give context and instructions to screen + * reader users when the chips container is selected. + * @type {string} + */ + this.containerHint = 'Chips container. Use arrow keys to select chips.'; + + /** + * Label text to describe the chips container when it is empty. Used to give context and + * instructions to screen reader users when the chips container is selected and it contains + * no chips. + * @type {string} + */ + this.containerEmptyHint = + 'Chips container. Enter the text area, then type text, and press enter to add a chip.'; + + /** + * Hidden hint text for how to delete a chip. Used to give context to screen readers. + * @type {string} + */ + this.deleteHint = 'Press delete to remove this chip.'; + + /** + * Hidden label for the delete button. Used to give context to screen readers. + * @type {string} + */ + this.deleteButtonLabel = 'Remove'; + + /** + * Model used by the input element. + * @type {string} + */ + this.chipBuffer = ''; + + /** + * Whether to use the transformChip expression to transform the chip buffer + * before appending it to the list. + * @type {boolean} + */ + this.useTransformChip = false; + + /** + * Whether to use the onAdd expression to notify of chip additions. + * @type {boolean} + */ + this.useOnAdd = false; + + /** + * Whether to use the onRemove expression to notify of chip removals. + * @type {boolean} + */ + this.useOnRemove = false; + + /** + * The ID of the chips wrapper which is used to build unique IDs for the chips and the aria-owns + * attribute. + * + * Defaults to '_md-chips-wrapper-' plus a unique number. + * + * @type {string} + */ + this.wrapperId = ''; + + /** + * Array of unique numbers which will be auto-generated any time the items change, and is used to + * create unique IDs for the aria-owns attribute. + * + * @type {Array} + */ + this.contentIds = []; + + /** + * The index of the chip that should have it's `tabindex` property set to `0` so it is selectable + * via the keyboard. + * + * @type {number|null} + */ + this.ariaTabIndex = null; + + /** + * After appending a chip, the chip will be focused for this number of milliseconds before the + * input is refocused. + * + * **Note:** This is **required** for compatibility with certain screen readers in order for + * them to properly allow keyboard access. + * + * @type {number} + */ + this.chipAppendDelay = DEFAULT_CHIP_APPEND_DELAY; + + /** + * Collection of functions to call to un-register watchers + * + * @type {Array} + */ + this.deRegister = []; + + /** + * The screen reader will announce the chip content followed by this message when a chip is added. + * @type {string} + */ + this.addedMessage = 'added'; + + /** + * The screen reader will announce the chip content followed by this message when a chip is + * removed. + * @type {string} + */ + this.removedMessage = 'removed'; + + this.init(); +} + +/** + * Initializes variables and sets up watchers + */ +MdChipsCtrl.prototype.init = function() { + var ctrl = this; + + // Set the wrapper ID + this.wrapperId = '_md-chips-wrapper-' + this.$mdUtil.nextUid(); + + // If we're using static chips, then we need to initialize a few things. + if (!this.$element.attr('ng-model')) { + this.setupStaticChips(); + } + + // Setup a watcher which manages the role and aria-owns attributes. + // This is never called for static chips since items is not defined. + this.deRegister.push( + this.$scope.$watchCollection('$mdChipsCtrl.items', function() { + // Make sure our input and wrapper have the correct ARIA attributes + ctrl.setupInputAria(); + ctrl.setupWrapperAria(); + }) + ); + + this.deRegister.push( + this.$attrs.$observe('mdChipAppendDelay', function(newValue) { + ctrl.chipAppendDelay = parseInt(newValue) || DEFAULT_CHIP_APPEND_DELAY; + }) + ); +}; + +/** + * Destructor for cleanup + */ +MdChipsCtrl.prototype.$onDestroy = function $onDestroy() { + var $destroyFn; + while (($destroyFn = this.deRegister.pop())) { + $destroyFn.call(this); + } +}; + +/** + * If we have an input, ensure it has the appropriate ARIA attributes. + */ +MdChipsCtrl.prototype.setupInputAria = function() { + var input = this.$element.find('input'); + + // If we have no input, just return + if (!input) { + return; + } + + input.attr('role', 'textbox'); + input.attr('aria-multiline', true); + if (this.inputAriaDescribedBy) { + input.attr('aria-describedby', this.inputAriaDescribedBy); + } + if (this.inputAriaLabelledBy) { + input.attr('aria-labelledby', this.inputAriaLabelledBy); + input.removeAttr('aria-label'); + } else { + input.attr('aria-label', this.inputAriaLabel); + } +}; + +/** + * Ensure our wrapper has the appropriate ARIA attributes. + */ +MdChipsCtrl.prototype.setupWrapperAria = function() { + var ctrl = this, + wrapper = this.$element.find('md-chips-wrap'); + + if (this.items && this.items.length) { + // Dynamically add the listbox role on every change because it must be removed when there are + // no items. + wrapper.attr('role', 'listbox'); + + // Generate some random (but unique) IDs for each chip + this.contentIds = this.items.map(function() { + return ctrl.wrapperId + '-chip-' + ctrl.$mdUtil.nextUid(); + }); + + // Use the contentIDs above to generate the aria-owns attribute + wrapper.attr('aria-owns', this.contentIds.join(' ')); + wrapper.attr('aria-label', this.containerHint); + } else { + // If we have no items, then the role and aria-owns attributes MUST be removed + wrapper.removeAttr('role'); + wrapper.removeAttr('aria-owns'); + wrapper.attr('aria-label', this.containerEmptyHint); + } +}; + +/** + * Apply specific roles and aria attributes for static chips + */ +MdChipsCtrl.prototype.setupStaticChips = function() { + var ctrl = this, i, staticChips; + var wrapper = this.$element.find('md-chips-wrap'); + + this.$timeout(function() { + wrapper.attr('role', 'list'); + staticChips = wrapper[0].children; + for (i = 0; i < staticChips.length; i++) { + staticChips[i].setAttribute('role', 'listitem'); + staticChips[i].setAttribute('aria-setsize', staticChips.length); + } + if (ctrl.inputAriaDescribedBy) { + wrapper.attr('aria-describedby', ctrl.inputAriaDescribedBy); + } + if (ctrl.inputAriaLabelledBy) { + wrapper.attr('aria-labelledby', ctrl.inputAriaLabelledBy); + wrapper.removeAttr('aria-label'); + } else { + wrapper.attr('aria-label', ctrl.inputAriaLabel); + } + }, 10); +}; + +/** + * Handles the keydown event on the input element: by default appends + * the buffer to the chip list, while backspace removes the last chip in the + * list if the current buffer is empty. + * @param {jQuery.Event|KeyboardEvent} event + */ +MdChipsCtrl.prototype.inputKeydown = function(event) { + var chipBuffer = this.getChipBuffer(); + + // If we have an autocomplete, and it handled the event, we have nothing to do + if (this.autocompleteCtrl && event.isDefaultPrevented && event.isDefaultPrevented()) { + return; + } + + if (event.keyCode === this.$mdConstant.KEY_CODE.BACKSPACE) { + // Only select and focus the previous chip, if the current caret position of the + // input element is at the beginning. + if (this.getCursorPosition(event.target) !== 0) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + if (this.items.length) { + this.selectAndFocusChipSafe(this.items.length - 1); + } + + return; + } + + // By default appends the buffer to the chip list. + if (!this.separatorKeys || this.separatorKeys.length < 1) { + this.separatorKeys = [this.$mdConstant.KEY_CODE.ENTER]; + } + + // Support additional separator key codes in an array of `md-separator-keys`. + if (this.separatorKeys.indexOf(event.keyCode) !== -1) { + if ((this.autocompleteCtrl && this.requireMatch) || !chipBuffer) return; + event.preventDefault(); + + // Only append the chip and reset the chip buffer if the max chips limit isn't reached. + if (this.hasMaxChipsReached()) return; + + this.appendChip(chipBuffer.trim()); + this.resetChipBuffer(); + + return false; + } +}; + +/** + * Returns the cursor position of the specified input element. + * @param {HTMLInputElement} element relevant input element + * @returns {Number} Cursor Position of the input. + */ +MdChipsCtrl.prototype.getCursorPosition = function(element) { + /* + * Figure out whether the current input for the chips buffer is valid for using + * the selectionStart / end property to retrieve the cursor position. + * Some browsers do not allow the use of those attributes, on different input types. + */ + try { + if (element.selectionStart === element.selectionEnd) { + return element.selectionStart; + } + } catch (e) { + if (!element.value) { + return 0; + } + } +}; + + +/** + * Updates the content of the chip at given index + * @param {number} chipIndex + * @param {string} chipContents + */ +MdChipsCtrl.prototype.updateChipContents = function(chipIndex, chipContents) { + if (chipIndex >= 0 && chipIndex < this.items.length) { + this.items[chipIndex] = chipContents; + this.updateNgModel(true); + } +}; + + +/** + * @return {boolean} true if a chip is currently being edited. False otherwise. + */ +MdChipsCtrl.prototype.isEditingChip = function() { + return !!this.$element[0].querySelector('._md-chip-editing'); +}; + +/** + * @param {string|Object} chip contents of a single chip + * @returns {boolean} true if the chip is an Object, false otherwise. + * @private + */ +MdChipsCtrl.prototype._isChipObject = function(chip) { + return angular.isObject(chip); +}; + +/** + * @returns {boolean} true if chips can be removed, false otherwise. + */ +MdChipsCtrl.prototype.isRemovable = function() { + // Return false if we have static chips + if (!this.ngModelCtrl) { + return false; + } + + return this.readonly ? this.removable : + angular.isDefined(this.removable) ? this.removable : true; +}; + +/** + * Handles the keydown event on the chip elements: backspace removes the selected chip, arrow + * keys switch which chip is active. + * @param {KeyboardEvent} event + */ +MdChipsCtrl.prototype.chipKeydown = function (event) { + if (this.getChipBuffer()) return; + if (this.isEditingChip()) return; + + switch (event.keyCode) { + case this.$mdConstant.KEY_CODE.BACKSPACE: + case this.$mdConstant.KEY_CODE.DELETE: + if (this.selectedChip < 0) return; + event.preventDefault(); + // Cancel the delete action only after the event cancel. Otherwise the page will go back. + if (!this.isRemovable()) return; + this.removeAndSelectAdjacentChip(this.selectedChip, event); + break; + case this.$mdConstant.KEY_CODE.LEFT_ARROW: + event.preventDefault(); + // By default, allow selection of -1 which will focus the input; if we're readonly, don't go + // below 0. + if (this.selectedChip < 0 || (this.readonly && this.selectedChip === 0)) { + this.selectedChip = this.items.length; + } + if (this.items.length) this.selectAndFocusChipSafe(this.selectedChip - 1); + break; + case this.$mdConstant.KEY_CODE.RIGHT_ARROW: + event.preventDefault(); + this.selectAndFocusChipSafe(this.selectedChip + 1); + break; + case this.$mdConstant.KEY_CODE.ESCAPE: + case this.$mdConstant.KEY_CODE.TAB: + if (this.selectedChip < 0) return; + event.preventDefault(); + this.onFocus(); + break; + } +}; + +/** + * Get the input's placeholder - uses `placeholder` when list is empty and `secondary-placeholder` + * when the list is non-empty. If `secondary-placeholder` is not provided, `placeholder` is used + * always. + * @returns {string} + */ +MdChipsCtrl.prototype.getPlaceholder = function() { + // Allow `secondary-placeholder` to be blank. + var useSecondary = (this.items && this.items.length && + (this.secondaryPlaceholder === '' || this.secondaryPlaceholder)); + return useSecondary ? this.secondaryPlaceholder : this.placeholder; +}; + +/** + * Removes chip at {@code index} and selects the adjacent chip. + * @param {number} index adjacent chip to select + * @param {Event=} event + */ +MdChipsCtrl.prototype.removeAndSelectAdjacentChip = function(index, event) { + var self = this; + var selIndex = self.getAdjacentChipIndex(index); + var wrap = this.$element[0].querySelector('md-chips-wrap'); + var chip = this.$element[0].querySelector('md-chip[index="' + index + '"]'); + + self.removeChip(index, event); + + // The double-timeout is currently necessary to ensure that the DOM has finalized and the select() + // will find the proper chip since the selection is index-based. + // + // TODO: Investigate calling from within chip $scope.$on('$destroy') to reduce/remove timeouts + self.$timeout(function() { + self.$timeout(function() { + self.selectAndFocusChipSafe(selIndex); + }); + }); +}; + +/** + * Sets the selected chip index to -1. + */ +MdChipsCtrl.prototype.resetSelectedChip = function() { + this.selectedChip = -1; + this.ariaTabIndex = null; +}; + +/** + * Gets the index of an adjacent chip to select after deletion. Adjacency is + * determined as the next chip in the list, unless the target chip is the + * last in the list, then it is the chip immediately preceding the target. If + * there is only one item in the list, -1 is returned (select none). + * The number returned is the index to select AFTER the target has been removed. + * If the current chip is not selected, then -1 is returned to select none. + * @param {number} index + * @returns {number} + */ +MdChipsCtrl.prototype.getAdjacentChipIndex = function(index) { + var len = this.items.length - 1; + return (len === 0) ? -1 : + (index === len) ? index - 1 : index; +}; + +/** + * Append the contents of the buffer to the chip list. This method will first + * call out to the md-transform-chip method, if provided. + * @param {string} newChip chip buffer contents that will be used to create the new chip + */ +MdChipsCtrl.prototype.appendChip = function(newChip) { + this.shouldFocusLastChip = !this.addOnBlur; + if (this.useTransformChip && this.transformChip) { + var transformedChip = this.transformChip({'$chip': newChip}); + + // Check to make sure the chip is defined before assigning it, otherwise, we'll just assume + // they want the string version. + if (angular.isDefined(transformedChip)) { + newChip = transformedChip; + } + } + + // If items contains an identical object to newChip, do not append + if (angular.isObject(newChip)) { + var identical = this.items.some(function(item) { + return angular.equals(newChip, item); + }); + if (identical) return; + } + + // Check for a null (but not undefined), or existing chip and cancel appending + if (newChip == null || this.items.indexOf(newChip) + 1) return; + + // Append the new chip onto our list + var length = this.items.push(newChip); + var index = length - 1; + + this.updateNgModel(); + + // Tell screen reader users that the chip was successfully added. + // TODO add a way for developers to specify which field of the object should be announced here. + var chipContent = angular.isObject(newChip) ? '' : newChip; + this.$mdLiveAnnouncer.announce(chipContent + ' ' + this.addedMessage, 'assertive'); + + // If the md-on-add attribute is specified, send a chip addition event + if (this.useOnAdd && this.onAdd) { + this.onAdd({ '$chip': newChip, '$index': index }); + } +}; + +/** + * Sets whether to use the md-transform-chip expression. This expression is + * bound to scope and controller in {@code MdChipsDirective} as + * {@code transformChip}. Due to the nature of directive scope bindings, the + * controller cannot know on its own/from the scope whether an expression was + * actually provided. + */ +MdChipsCtrl.prototype.useTransformChipExpression = function() { + this.useTransformChip = true; +}; + +/** + * Sets whether to use the md-on-add expression. This expression is + * bound to scope and controller in {@code MdChipsDirective} as + * {@code onAdd}. Due to the nature of directive scope bindings, the + * controller cannot know on its own/from the scope whether an expression was + * actually provided. + */ +MdChipsCtrl.prototype.useOnAddExpression = function() { + this.useOnAdd = true; +}; + +/** + * Sets whether to use the md-on-remove expression. This expression is + * bound to scope and controller in {@code MdChipsDirective} as + * {@code onRemove}. Due to the nature of directive scope bindings, the + * controller cannot know on its own/from the scope whether an expression was + * actually provided. + */ +MdChipsCtrl.prototype.useOnRemoveExpression = function() { + this.useOnRemove = true; +}; + +/** + * Sets whether to use the md-on-select expression. This expression is + * bound to scope and controller in {@code MdChipsDirective} as + * {@code onSelect}. Due to the nature of directive scope bindings, the + * controller cannot know on its own/from the scope whether an expression was + * actually provided. + */ +MdChipsCtrl.prototype.useOnSelectExpression = function() { + this.useOnSelect = true; +}; + +/** + * Gets the input buffer. The input buffer can be the model bound to the + * default input item {@code this.chipBuffer}, the {@code selectedItem} + * model of an {@code md-autocomplete}, or, through some magic, the model + * bound to any input or text area element found within a + * {@code md-input-container} element. + * @return {string} the input buffer + */ +MdChipsCtrl.prototype.getChipBuffer = function() { + var chipBuffer = !this.userInputElement ? this.chipBuffer : + this.userInputNgModelCtrl ? this.userInputNgModelCtrl.$viewValue : + this.userInputElement[0].value; + + // Ensure that the chip buffer is always a string. For example, the input element buffer + // might be falsy. + return angular.isString(chipBuffer) ? chipBuffer : ''; +}; + +/** + * Resets the input buffer for either the internal input or user provided input element. + */ +MdChipsCtrl.prototype.resetChipBuffer = function() { + if (this.userInputElement) { + if (this.userInputNgModelCtrl) { + this.userInputNgModelCtrl.$setViewValue(''); + this.userInputNgModelCtrl.$render(); + } else { + this.userInputElement[0].value = ''; + } + } else { + this.chipBuffer = ''; + } +}; + +/** + * @returns {boolean} true if the max chips limit has been reached, false otherwise. + */ +MdChipsCtrl.prototype.hasMaxChipsReached = function() { + if (angular.isString(this.maxChips)) { + this.maxChips = parseInt(this.maxChips, 10) || 0; + } + + return this.maxChips > 0 && this.items.length >= this.maxChips; +}; + +/** + * Updates the validity properties for the ngModel. + * + * TODO add the md-max-chips validator to this.ngModelCtrl.validators so that the validation will + * be performed automatically. + */ +MdChipsCtrl.prototype.validateModel = function() { + this.ngModelCtrl.$setValidity('md-max-chips', !this.hasMaxChipsReached()); + this.ngModelCtrl.$validate(); // rerun any registered validators +}; + +/** + * Function to handle updating the model, validation, and change notification when a chip + * is added, removed, or changed. + * @param {boolean=} skipValidation true to skip calling validateModel() + */ +MdChipsCtrl.prototype.updateNgModel = function(skipValidation) { + if (!skipValidation) { + this.validateModel(); + } + // This will trigger ng-change to fire, even in cases where $setViewValue() would not. + angular.forEach(this.ngModelCtrl.$viewChangeListeners, function(listener) { + try { + listener(); + } catch (e) { + this.$exceptionHandler(e); + } + }); +}; + +/** + * Removes the chip at the given index. + * @param {number} index of chip to remove + * @param {Event=} event optionally passed to the onRemove callback + */ +MdChipsCtrl.prototype.removeChip = function(index, event) { + var removed = this.items.splice(index, 1); + + this.updateNgModel(); + this.ngModelCtrl.$setDirty(); + + // Tell screen reader users that the chip was successfully removed. + // TODO add a way for developers to specify which field of the object should be announced here. + var chipContent = angular.isObject(removed[0]) ? '' : removed[0]; + this.$mdLiveAnnouncer.announce(chipContent + ' ' + this.removedMessage, 'assertive'); + + if (removed && removed.length && this.useOnRemove && this.onRemove) { + this.onRemove({ '$chip': removed[0], '$index': index, '$event': event }); + } +}; + +/** + * @param {number} index location of chip to remove + * @param {Event=} $event + */ +MdChipsCtrl.prototype.removeChipAndFocusInput = function (index, $event) { + this.removeChip(index, $event); + + if (this.autocompleteCtrl) { + // Always hide the autocomplete dropdown before focusing the autocomplete input. + // Wait for the input to move horizontally, because the chip was removed. + // This can lead to an incorrect dropdown position. + this.autocompleteCtrl.hidden = true; + this.$mdUtil.nextTick(this.onFocus.bind(this)); + } else { + this.onFocus(); + } + +}; +/** + * Selects the chip at `index`, + * @param {number} index location of chip to select and focus + */ +MdChipsCtrl.prototype.selectAndFocusChipSafe = function(index) { + // If we have no chips, or are asked to select a chip before the first, just focus the input + if (!this.items.length || index === -1) { + return this.focusInput(); + } + + // If we are asked to select a chip greater than the number of chips... + if (index >= this.items.length) { + if (this.readonly) { + // If we are readonly, jump back to the start (because we have no input) + index = 0; + } else { + // If we are not readonly, we should attempt to focus the input + return this.onFocus(); + } + } + + index = Math.max(index, 0); + index = Math.min(index, this.items.length - 1); + + this.selectChip(index); + this.focusChip(index); +}; + +/** + * Focus last chip, then focus the input. This is needed for screen reader support. + */ +MdChipsCtrl.prototype.focusLastChipThenInput = function() { + var ctrl = this; + + ctrl.shouldFocusLastChip = false; + + ctrl.focusChip(this.items.length - 1); + + ctrl.$timeout(function() { + ctrl.focusInput(); + }, ctrl.chipAppendDelay); +}; + +/** + * Focus the input element. + */ +MdChipsCtrl.prototype.focusInput = function() { + this.selectChip(-1); + this.onFocus(); +}; + +/** + * Marks the chip at the given index as selected. + * @param {number} index location of chip to select + */ +MdChipsCtrl.prototype.selectChip = function(index) { + if (index >= -1 && index <= this.items.length) { + this.selectedChip = index; + + // Fire the onSelect if provided + if (this.useOnSelect && this.onSelect) { + this.onSelect({'$chip': this.items[index] }); + } + } else { + this.$log.warn('Selected Chip index out of bounds; ignoring.'); + } +}; + +/** + * Call {@code focus()} on the chip at {@code index} + * @param {number} index location of chip to focus + */ +MdChipsCtrl.prototype.focusChip = function(index) { + var chipContent = this.$element[0].querySelector( + 'md-chip[index="' + index + '"] .md-chip-content' + ); + + this.ariaTabIndex = index; + + chipContent.focus(); +}; + +/** + * Configures the required interactions with the ngModel Controller. + * Specifically, set {@code this.items} to the {@code NgModelController#$viewValue}. + * @param {NgModelController} ngModelCtrl + */ +MdChipsCtrl.prototype.configureNgModel = function(ngModelCtrl) { + this.ngModelCtrl = ngModelCtrl; + + var self = this; + + // in chips the meaning of $isEmpty changes + ngModelCtrl.$isEmpty = function(value) { + return !value || value.length === 0; + }; + + ngModelCtrl.$render = function() { + // model is updated. do something. + self.items = self.ngModelCtrl.$viewValue; + }; +}; + +MdChipsCtrl.prototype.onFocus = function () { + var input = this.$element[0].querySelector('input'); + input && input.focus(); + this.resetSelectedChip(); +}; + +MdChipsCtrl.prototype.onInputFocus = function () { + this.inputHasFocus = true; + + // Make sure we have the appropriate ARIA attributes + this.setupInputAria(); + + // Make sure we don't have any chips selected + this.resetSelectedChip(); +}; + +MdChipsCtrl.prototype.onInputBlur = function () { + this.inputHasFocus = false; + + if (this.shouldAddOnBlur()) { + this.appendChip(this.getChipBuffer().trim()); + this.resetChipBuffer(); + } +}; + +/** + * Configure event bindings on input element. + * @param {angular.element} inputElement + */ +MdChipsCtrl.prototype.configureInput = function configureInput(inputElement) { + // Find the NgModelCtrl for the input element + var ngModelCtrl = inputElement.controller('ngModel'); + var ctrl = this; + + if (ngModelCtrl) { + + // sync touched-state from inner input to chips-element + this.deRegister.push( + this.$scope.$watch( + function() { + return ngModelCtrl.$touched; + }, + function(isTouched) { + isTouched && ctrl.ngModelCtrl.$setTouched(); + } + ) + ); + + // sync dirty-state from inner input to chips-element + this.deRegister.push( + this.$scope.$watch( + function() { + return ngModelCtrl.$dirty; + }, + function(isDirty) { + isDirty && ctrl.ngModelCtrl.$setDirty(); + } + ) + ); + } +}; + +/** + * Configure event bindings on a user-provided input element. + * @param {angular.element} inputElement + */ +MdChipsCtrl.prototype.configureUserInput = function(inputElement) { + this.userInputElement = inputElement; + + // Find the NgModelCtrl for the input element + var ngModelCtrl = inputElement.controller('ngModel'); + // `.controller` will look in the parent as well. + if (ngModelCtrl !== this.ngModelCtrl) { + this.userInputNgModelCtrl = ngModelCtrl; + } + + var scope = this.$scope; + var ctrl = this; + + // Run all of the events using evalAsync because a focus may fire a blur in the same digest loop + var scopeApplyFn = function(event, fn) { + scope.$evalAsync(angular.bind(ctrl, fn, event)); + }; + + // Bind to keydown and focus events of input + inputElement + .attr({ tabindex: 0 }) + .on('keydown', function(event) { scopeApplyFn(event, ctrl.inputKeydown); }) + .on('focus', function(event) { scopeApplyFn(event, ctrl.onInputFocus); }) + .on('blur', function(event) { scopeApplyFn(event, ctrl.onInputBlur); }); +}; + +/** + * @param {MdAutocompleteCtrl} ctrl controller from the autocomplete component + */ +MdChipsCtrl.prototype.configureAutocomplete = function(ctrl) { + if (ctrl) { + this.autocompleteCtrl = ctrl; + // Update the default container empty hint when we're inside of an autocomplete. + if (!this.$element.attr('container-empty-hint')) { + this.containerEmptyHint = 'Chips container with autocompletion. Enter the text area, ' + + 'type text to search, and then use the up and down arrow keys to select an option. ' + + 'Press enter to add the selected option as a chip.'; + this.setupWrapperAria(); + } + + ctrl.registerSelectedItemWatcher(angular.bind(this, function (item) { + if (item) { + // Only append the chip and reset the chip buffer if the max chips limit isn't reached. + if (this.hasMaxChipsReached()) return; + + this.appendChip(item); + this.resetChipBuffer(); + } + })); + + this.$element.find('input') + .on('focus',angular.bind(this, this.onInputFocus)) + .on('blur', angular.bind(this, this.onInputBlur)); + } +}; + +/** + * @returns {boolean} Whether the current chip buffer should be added on input blur or not. + */ +MdChipsCtrl.prototype.shouldAddOnBlur = function() { + + // Update the custom ngModel validators from the chips component. + this.validateModel(); + + var chipBuffer = this.getChipBuffer().trim(); + // If the model value is empty and required is set on the element, then the model will be invalid. + // In that case, we still want to allow adding the chip. The main (but not only) case we want + // to disallow is adding a chip on blur when md-max-chips validation fails. + var isModelValid = this.ngModelCtrl.$isEmpty(this.ngModelCtrl.$modelValue) || + this.ngModelCtrl.$valid; + var isAutocompleteShowing = this.autocompleteCtrl && !this.autocompleteCtrl.hidden; + + if (this.userInputNgModelCtrl) { + isModelValid = isModelValid && this.userInputNgModelCtrl.$valid; + } + + return this.addOnBlur && !this.requireMatch && chipBuffer && isModelValid && + !isAutocompleteShowing; +}; + +/** + * @returns {boolean} true if the input or a chip is focused. False otherwise. + */ +MdChipsCtrl.prototype.hasFocus = function () { + return this.inputHasFocus || this.selectedChip >= 0; +}; + +/** + * @param {number} index location of content id + * @returns {number} unique id for the aria-owns attribute + */ +MdChipsCtrl.prototype.contentIdFor = function(index) { + return this.contentIds[index]; +}; + +})(); +(function(){ +"use strict"; + + + MdChips.$inject = ["$mdTheming", "$mdUtil", "$compile", "$log", "$timeout", "$$mdSvgRegistry"];angular + .module('material.components.chips') + .directive('mdChips', MdChips); + + /** + * @ngdoc directive + * @name mdChips + * @module material.components.chips + * + * @description + * `` is an input component for building lists of strings or objects. The list items are + * displayed as 'chips'. This component can make use of an `` element or an + * `` element. + * + * ### Custom templates + * A custom template may be provided to render the content of each chip. This is achieved by + * specifying an `` element containing the custom content as a child of + * ``. + * + * Note: Any attributes on + * `` will be dropped as only the innerHTML is used for the chip template. The + * variables `$chip` and `$index` are available in the scope of ``, representing + * the chip object and its index in the list of chips, respectively. + * To override the chip delete control, include an element (ideally a button) with the attribute + * `md-chip-remove`. A click listener to remove the chip will be added automatically. The element + * is also placed as a sibling to the chip content (on which there are also click listeners) to + * avoid a nested ng-click situation. + * + * + * + * Sometimes developers want to limit the amount of possible chips.
    + * You can specify the maximum amount of chips by using the following markup. + * + * + * + * + * + * + * In some cases, you have an autocomplete inside of the `md-chips`.
    + * When the maximum amount of chips has been reached, you can also disable the autocomplete + * selection.
    + * Here is an example markup. + * + * + * + * + * + * + * + * ### Accessibility + * + * The `md-chips` component supports keyboard and screen reader users since Version 1.1.2. In + * order to achieve this, we modified the chips behavior to select newly appended chips for + * `300ms` before re-focusing the input and allowing the user to type. + * + * For most users, this delay is small enough that it will not be noticeable but allows certain + * screen readers to function properly (JAWS and NVDA in particular). + * + * We introduced a new `md-chip-append-delay` option to allow developers to better control this + * behavior. + * + * Please refer to the documentation of this option (below) for more information. + * + * @param {expression} ng-model Assignable AngularJS expression to be data-bound to the list of + * chips. The expression should evaluate to a `string` or `Object` Array. The type of this + * array should align with the return value of `md-transform-chip`. + * @param {expression=} ng-change AngularJS expression to be executed on chip addition, removal, + * or content change. + * @param {string=} placeholder Placeholder text that will be forwarded to the input. + * @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input, + * displayed when there is at least one item in the list + * @param {boolean=} md-removable Enables or disables the deletion of chips through the + * removal icon or the Delete/Backspace key. Defaults to true. + * @param {boolean=} readonly Disables list manipulation (deleting or adding list items), hiding + * the input and delete buttons. If no `ng-model` is provided, the chips will automatically be + * marked as readonly.

    + * When `md-removable` is not defined, the `md-remove` behavior will be overwritten and + * disabled. + * @param {boolean=} md-enable-chip-edit Set this to `"true"` to enable editing of chip contents. + * The user can go into edit mode by pressing the `space` or `enter` keys, or by double + * clicking on the chip. Chip editing is only supported for chips using the basic template. + * **Note:** This attribute is only evaluated once; it is not watched. + * @param {boolean=} ng-required Whether ng-model is allowed to be empty or not. + * @param {number=} md-max-chips The maximum number of chips allowed to add through user input. + *

    The validation property `md-max-chips` can be used when the max chips + * amount is reached. + * @param {boolean=} md-add-on-blur When set to `"true"`, the remaining text inside of the input + * will be converted into a new chip on blur. + * **Note:** This attribute is only evaluated once; it is not watched. + * @param {expression} md-transform-chip An expression of form `myFunction($chip)` that when + * called expects one of the following return values: + * - an object representing the `$chip` input string + * - `undefined` to simply add the `$chip` input string, or + * - `null` to prevent the chip from being appended + * @param {expression=} md-on-add An expression which will be called when a chip has been + * added with `$chip` and `$index` available as parameters. + * @param {expression=} md-on-remove An expression which will be called when a chip has been + * removed with `$chip`, `$index`, and `$event` available as parameters. + * @param {expression=} md-on-select An expression which will be called when a chip is selected. + * @param {boolean=} md-require-match If true, and the chips template contains an autocomplete, + * only allow selection of pre-defined chips (i.e. you cannot add new ones). + * @param {string=} md-input-class This class will be applied to the child input for custom + * styling. If you are using an `md-autocomplete`, then you need to put this attribute on the + * `md-autocomplete` rather than the `md-chips`. + * @param {string=} input-aria-describedby A space-separated list of element IDs. This should + * contain the IDs of any elements that describe this autocomplete. Screen readers will read + * the content of these elements at the end of announcing that the chips input has been + * selected and describing its current state. The descriptive elements do not need to be + * visible on the page. + * @param {string=} input-aria-labelledby A space-separated list of element IDs. The ideal use + * case is that this would contain the ID of a `
    \ + \ +
    \ + {{$chip[$mdContactChipsCtrl.contactName]}}\ +
    \ +
    \ + {{$chip[$mdContactChipsCtrl.contactName]}}\ +
    \ +
    \ +
    '; + + +/** + * MDContactChips Directive Definition + * + * @param $mdTheming + * @param $mdUtil + * @returns {*} + * @ngInject + */ +function MdContactChips($mdTheming, $mdUtil) { + return { + template: function(element, attrs) { + return MD_CONTACT_CHIPS_TEMPLATE; + }, + restrict: 'E', + controller: 'MdContactChipsCtrl', + controllerAs: '$mdContactChipsCtrl', + bindToController: true, + compile: compile, + scope: { + contactQuery: '&mdContacts', + placeholder: '@?', + secondaryPlaceholder: '@?', + contactName: '@mdContactName', + contactImage: '@mdContactImage', + contactEmail: '@mdContactEmail', + contacts: '=ngModel', + ngChange: '&?', + requireMatch: '=?mdRequireMatch', + minLength: '=?mdMinLength', + maxChips: '=?mdMaxChips', + highlightFlags: '@?mdHighlightFlags', + chipAppendDelay: '@?mdChipAppendDelay', + separatorKeys: '=?mdSeparatorKeys', + removedMessage: '@?mdRemovedMessage', + inputClass: '@?mdInputClass', + inputAriaDescribedBy: '@?inputAriaDescribedby', + inputAriaLabelledBy: '@?inputAriaLabelledby', + inputAriaLabel: '@?', + containerHint: '@?', + containerEmptyHint: '@?', + deleteHint: '@?' + } + }; + + function compile(element, attr) { + return function postLink(scope, element, attrs, controllers) { + var contactChipsController = controllers; + + $mdUtil.initOptionalProperties(scope, attr); + $mdTheming(element); + + element.attr('tabindex', '-1'); + + attrs.$observe('mdChipAppendDelay', function(newValue) { + contactChipsController.chipAppendDelay = newValue; + }); + }; + } +} + +})(); +(function(){ +"use strict"; + +(function () { + "use strict"; + + /** + * Use a RegExp to check if the `md-colors=""` is static string + * or one that should be observed and dynamically interpolated. + */ + MdColorsDirective.$inject = ["$mdColors", "$mdUtil", "$log", "$parse"]; + MdColorsService.$inject = ["$mdTheming", "$mdUtil", "$log"]; + var STATIC_COLOR_EXPRESSION = /^{((\s|,)*?["'a-zA-Z-]+?\s*?:\s*?(['"])[a-zA-Z0-9-.]*(['"]))+\s*}$/; + var colorPalettes = null; + + /** + * @ngdoc module + * @name material.components.colors + * + * @description + * Define $mdColors service and a `md-colors=""` attribute directive + */ + angular + .module('material.components.colors', ['material.core']) + .directive('mdColors', MdColorsDirective) + .service('$mdColors', MdColorsService); + + /** + * @ngdoc service + * @name $mdColors + * @module material.components.colors + * + * @description + * By default, defining a theme does not make its colors available for applying to non AngularJS + * Material elements. The `$mdColors` service is used by the `md-color` directive to convert a + * set of color expressions to RGBA values and then apply those values to the element as CSS + * property values. + * + * @usage + * Getting a color based on a theme + * + * + * angular.controller('myCtrl', function ($mdColors) { + * var color = $mdColors.getThemeColor('myTheme-primary-900-0.5'); + * ... + * }); + * + * + * Applying a color from a palette to an element + * + * app.directive('myDirective', function($mdColors) { + * return { + * ... + * link: function (scope, elem) { + * $mdColors.applyThemeColors(elem, {color: 'red-A200-0.2'}); + * } + * } + * }); + * + */ + function MdColorsService($mdTheming, $mdUtil, $log) { + colorPalettes = colorPalettes || Object.keys($mdTheming.PALETTES); + + // Publish service instance + return { + applyThemeColors: applyThemeColors, + getThemeColor: getThemeColor, + hasTheme: hasTheme + }; + + // ******************************************** + // Internal Methods + // ******************************************** + + /** + * @ngdoc method + * @name $mdColors#applyThemeColors + * + * @description + * Lookup a set of colors by hue, theme, and palette, then apply those colors + * with the provided opacity (via `rgba()`) to the specified CSS property. + * + * @param {angular.element} element the element to apply the styles to + * @param {Object} colorExpression Keys are CSS properties and values are strings representing + * the `theme-palette-hue-opacity` of the desired color. For example: + * `{'color': 'red-A200-0.3', 'background-color': 'myTheme-primary-700-0.8'}`. Theme, hue, and + * opacity are optional. + */ + function applyThemeColors(element, colorExpression) { + try { + if (colorExpression) { + // Assign the calculate RGBA color values directly as inline CSS + element.css(interpolateColors(colorExpression)); + } + } catch (e) { + $log.error(e.message); + } + } + + /** + * @ngdoc method + * @name $mdColors#getThemeColor + * + * @description + * Get a parsed RGBA color using a string representing the `theme-palette-hue-opacity` of the + * desired color. + * + * @param {string} expression color expression like `'red-A200-0.3'` or + * `'myTheme-primary-700-0.8'`. Theme, hue, and opacity are optional. + * @returns {string} a CSS color value like `rgba(211, 47, 47, 0.8)` + */ + function getThemeColor(expression) { + var color = extractColorOptions(expression); + + return parseColor(color); + } + + /** + * Return the parsed color + * @param {{hue: *, theme: any, palette: *, opacity: (*|string|number)}} color hash map of color + * definitions + * @param {boolean=} contrast whether use contrast color for foreground. Defaults to false. + * @returns {string} rgba color string + */ + function parseColor(color, contrast) { + contrast = contrast || false; + var rgbValues = $mdTheming.PALETTES[color.palette][color.hue]; + + rgbValues = contrast ? rgbValues.contrast : rgbValues.value; + + return $mdUtil.supplant('rgba({0}, {1}, {2}, {3})', + [rgbValues[0], rgbValues[1], rgbValues[2], rgbValues[3] || color.opacity] + ); + } + + /** + * Convert the color expression into an object with scope-interpolated values + * Then calculate the rgba() values based on the theme color parts + * @param {Object} themeColors json object, keys are css properties and values are string of + * the wanted color, for example: `{color: 'red-A200-0.3'}`. + * @return {Object} Hashmap of CSS properties with associated `rgba()` string values + */ + function interpolateColors(themeColors) { + var rgbColors = {}; + + var hasColorProperty = themeColors.hasOwnProperty('color'); + + angular.forEach(themeColors, function (value, key) { + var color = extractColorOptions(value); + var hasBackground = key.indexOf('background') > -1; + + rgbColors[key] = parseColor(color); + if (hasBackground && !hasColorProperty) { + rgbColors.color = parseColor(color, true); + } + }); + + return rgbColors; + } + + /** + * Check if expression has defined theme + * For instance: + * 'myTheme-primary' => true + * 'red-800' => false + * @param {string} expression color expression like 'red-800', 'red-A200-0.3', + * 'myTheme-primary', or 'myTheme-primary-400' + * @return {boolean} true if the expression has a theme part, false otherwise. + */ + function hasTheme(expression) { + return angular.isDefined($mdTheming.THEMES[expression.split('-')[0]]); + } + + /** + * For the evaluated expression, extract the color parts into a hash map + * @param {string} expression color expression like 'red-800', 'red-A200-0.3', + * 'myTheme-primary', or 'myTheme-primary-400' + * @returns {{hue: *, theme: any, palette: *, opacity: (*|string|number)}} + */ + function extractColorOptions(expression) { + var parts = expression.split('-'); + var hasTheme = angular.isDefined($mdTheming.THEMES[parts[0]]); + var theme = hasTheme ? parts.splice(0, 1)[0] : $mdTheming.defaultTheme(); + + return { + theme: theme, + palette: extractPalette(parts, theme), + hue: extractHue(parts, theme), + opacity: parts[2] || 1 + }; + } + + /** + * Calculate the theme palette name + * @param {Array} parts + * @param {string} theme name + * @return {string} + */ + function extractPalette(parts, theme) { + // If the next section is one of the palettes we assume it's a two word palette + // Two word palette can be also written in camelCase, forming camelCase to dash-case + + var isTwoWord = parts.length > 1 && colorPalettes.indexOf(parts[1]) !== -1; + var palette = parts[0].replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + + if (isTwoWord) palette = parts[0] + '-' + parts.splice(1, 1); + + if (colorPalettes.indexOf(palette) === -1) { + // If the palette is not in the palette list it's one of primary/accent/warn/background + var scheme = $mdTheming.THEMES[theme].colors[palette]; + if (!scheme) { + throw new Error($mdUtil.supplant( + 'mdColors: couldn\'t find \'{palette}\' in the palettes.', + {palette: palette})); + } + palette = scheme.name; + } + + return palette; + } + + /** + * @param {Array} parts + * @param {string} theme name + * @return {*} + */ + function extractHue(parts, theme) { + var themeColors = $mdTheming.THEMES[theme].colors; + + if (parts[1] === 'hue') { + var hueNumber = parseInt(parts.splice(2, 1)[0], 10); + + if (hueNumber < 1 || hueNumber > 3) { + throw new Error($mdUtil.supplant( + 'mdColors: \'hue-{hueNumber}\' is not a valid hue, can be only \'hue-1\', \'hue-2\' and \'hue-3\'', + {hueNumber: hueNumber})); + } + parts[1] = 'hue-' + hueNumber; + + if (!(parts[0] in themeColors)) { + throw new Error($mdUtil.supplant( + 'mdColors: \'hue-x\' can only be used with [{availableThemes}], but was used with \'{usedTheme}\'', + { + availableThemes: Object.keys(themeColors).join(', '), + usedTheme: parts[0] + })); + } + + return themeColors[parts[0]].hues[parts[1]]; + } + + return parts[1] || themeColors[parts[0] in themeColors ? parts[0] : 'primary'].hues['default']; + } + } + + /** + * @ngdoc directive + * @name mdColors + * @module material.components.colors + * + * @restrict A + * + * @description + * `mdColors` directive will apply the theme-based color expression as RGBA CSS style values. + * + * The format will be similar to the colors defined in the Sass files: + * + * ## `[?theme]-[palette]-[?hue]-[?opacity]` + * - [theme] - default value is the default theme + * - [palette] - can be either palette name or primary/accent/warn/background + * - [hue] - default is 500 (hue-x can be used with primary/accent/warn/background) + * - [opacity] - default is 1 + * + * + * > `?` indicates optional parameter + * + * @usage + * + *
    + *
    + * Color demo + *
    + *
    + *
    + * + * The `mdColors` directive will automatically watch for changes in the expression if it recognizes + * an interpolation expression or a function. For performance options, you can use `::` prefix to + * the `md-colors` expression to indicate a one-time data binding. + * + * + * + * + * + */ + function MdColorsDirective($mdColors, $mdUtil, $log, $parse) { + return { + restrict: 'A', + require: ['^?mdTheme'], + compile: function (tElem, tAttrs) { + var shouldWatch = shouldColorsWatch(); + + return function (scope, element, attrs, ctrl) { + var mdThemeController = ctrl[0]; + + var lastColors = {}; + + /** + * @param {string=} theme + * @return {Object} colors found in the specified theme + */ + var parseColors = function (theme) { + if (typeof theme !== 'string') { + theme = ''; + } + + if (!attrs.mdColors) { + attrs.mdColors = '{}'; + } + + /** + * Json.parse() does not work because the keys are not quoted; + * use $parse to convert to a hash map + */ + var colors = $parse(attrs.mdColors)(scope); + + /** + * If mdTheme is defined higher up the DOM tree, + * we add mdTheme's theme to the colors which don't specify a theme. + * + * @example + * + *
    + *
    + * Color demo + *
    + *
    + *
    + * + * 'primary-600' will be changed to 'myTheme-primary-600', + * but 'mySecondTheme-accent-200' will not be changed since it has a theme defined. + */ + if (mdThemeController) { + Object.keys(colors).forEach(function (prop) { + var color = colors[prop]; + if (!$mdColors.hasTheme(color)) { + colors[prop] = (theme || mdThemeController.$mdTheme) + '-' + color; + } + }); + } + + cleanElement(colors); + + return colors; + }; + + /** + * @param {Object} colors + */ + var cleanElement = function (colors) { + if (!angular.equals(colors, lastColors)) { + var keys = Object.keys(lastColors); + + if (lastColors.background && !keys.color) { + keys.push('color'); + } + + keys.forEach(function (key) { + element.css(key, ''); + }); + } + + lastColors = colors; + }; + + /** + * Registering for mgTheme changes and asking mdTheme controller run our callback whenever + * a theme changes. + */ + var unregisterChanges = angular.noop; + + if (mdThemeController) { + unregisterChanges = mdThemeController.registerChanges(function (theme) { + $mdColors.applyThemeColors(element, parseColors(theme)); + }); + } + + scope.$on('$destroy', function () { + unregisterChanges(); + }); + + try { + if (shouldWatch) { + scope.$watch(parseColors, angular.bind(this, + $mdColors.applyThemeColors, element + ), true); + } + else { + $mdColors.applyThemeColors(element, parseColors()); + } + + } + catch (e) { + $log.error(e.message); + } + + }; + + /** + * @return {boolean} + */ + function shouldColorsWatch() { + // Simulate 1x binding and mark mdColorsWatch == false + var rawColorExpression = tAttrs.mdColors; + var bindOnce = rawColorExpression.indexOf('::') > -1; + var isStatic = bindOnce ? true : STATIC_COLOR_EXPRESSION.test(tAttrs.mdColors); + + // Remove it for the postLink... + tAttrs.mdColors = rawColorExpression.replace('::', ''); + + var hasWatchAttr = angular.isDefined(tAttrs.mdColorsWatch); + + return (bindOnce || isStatic) ? false : + hasWatchAttr ? $mdUtil.parseAttributeBoolean(tAttrs.mdColorsWatch) : true; + } + } + }; + } +})(); + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.content + * + * @description + * Scrollable content + */ +mdContentDirective.$inject = ["$mdTheming"]; +angular.module('material.components.content', [ + 'material.core' +]) + .directive('mdContent', mdContentDirective); + +/** + * @ngdoc directive + * @name mdContent + * @module material.components.content + * + * @restrict E + * + * @description + * + * The `` directive is a container element useful for scrollable content. It achieves + * this by setting the CSS `overflow` property to `auto` so that content can properly scroll. + * + * In general, `` components are not designed to be nested inside one another. If + * possible, it is better to make them siblings. This often results in a better user experience as + * having nested scrollbars may confuse the user. + * + * ## Troubleshooting + * + * In some cases, you may wish to apply the `md-no-momentum` class to ensure that Safari's + * momentum scrolling is disabled. Momentum scrolling can cause flickering issues while scrolling + * SVG icons and some other components. + * + * Additionally, we now also offer the `md-no-flicker` class which can be applied to any element + * and uses a Webkit-specific filter of `blur(0px)` that forces GPU rendering of all elements + * inside (which eliminates the flicker on iOS devices). + * + * _Note: Forcing an element to render on the GPU can have unintended side-effects, especially + * related to the z-index of elements. Please use with caution and only on the elements needed._ + * + * @usage + * + * Add the `[layout-padding]` attribute to make the content padded. + * + * + * + * Lorem ipsum dolor sit amet, ne quod novum mei. + * + * + */ + +function mdContentDirective($mdTheming) { + return { + restrict: 'E', + controller: ['$scope', '$element', ContentController], + link: function(scope, element) { + element.addClass('_md'); // private md component indicator for styling + + $mdTheming(element); + scope.$broadcast('$mdContentLoaded', element); + + iosScrollFix(element[0]); + } + }; + + function ContentController($scope, $element) { + this.$scope = $scope; + this.$element = $element; + } +} + +function iosScrollFix(node) { + // IOS FIX: + // If we scroll where there is no more room for the webview to scroll, + // by default the webview itself will scroll up and down, this looks really + // bad. So if we are scrolling to the very top or bottom, add/subtract one + angular.element(node).on('$md.pressdown', function(ev) { + // Only touch events + if (ev.pointer.type !== 't') return; + // Don't let a child content's touchstart ruin it for us. + if (ev.$materialScrollFixed) return; + ev.$materialScrollFixed = true; + + if (node.scrollTop === 0) { + node.scrollTop = 1; + } else if (node.scrollHeight === node.scrollTop + node.offsetHeight) { + node.scrollTop -= 1; + } + }); +} + +})(); +(function(){ +"use strict"; + +/** + * @ngdoc module + * @name material.components.datepicker + * @description Module for the datepicker component. + */ + +angular.module('material.components.datepicker', [ + 'material.core', + 'material.components.icon', + 'material.components.virtualRepeat' +]); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * @ngdoc directive + * @name mdCalendar + * @module material.components.datepicker + * + * @param {Date} ng-model The component's model. Should be a Date object. + * @param {Object=} ng-model-options Allows tuning of the way in which `ng-model` is being + * updated. Also allows for a timezone to be specified. + * Read more at the + * ngModelOptions docs. + * @param {Date=} md-min-date Expression representing the minimum date. + * @param {Date=} md-max-date Expression representing the maximum date. + * @param {(function(Date): boolean)=} md-date-filter Function expecting a date and returning a + * boolean whether it can be selected in "day" mode or not. + * @param {(function(Date): boolean)=} md-month-filter Function expecting a date and returning a + * boolean whether it can be selected in "month" mode or not. + * @param {String=} md-current-view Current view of the calendar. Can be either "month" or "year". + * @param {String=} md-mode Restricts the user to only selecting a value from a particular view. + * This option can be used if the user is only supposed to choose from a certain date type + * (e.g. only selecting the month). Can be either "month" or "day". **Note** that this will + * overwrite the `md-current-view` value. + * + * @description + * `` is a component that renders a calendar that can be used to select a date. + * It is a part of the `` pane, however it can also be used on it's own. + * + * @usage + * + * + * + * + */ + CalendarCtrl.$inject = ["$element", "$scope", "$$mdDateUtil", "$mdUtil", "$mdConstant", "$mdTheming", "$$rAF", "$attrs", "$mdDateLocale", "$filter", "$document"]; + calendarDirective.$inject = ["inputDirective"]; + angular.module('material.components.datepicker') + .directive('mdCalendar', calendarDirective); + + // TODO(jelbourn): Mac Cmd + left / right == Home / End + // TODO(jelbourn): Refactor month element creation to use cloneNode (performance). + // TODO(jelbourn): Define virtual scrolling constants (compactness) users can override. + // TODO(jelbourn): Animated month transition on ng-model change (virtual-repeat) + // TODO(jelbourn): Scroll snapping (virtual repeat) + // TODO(jelbourn): Remove superfluous row from short months (virtual-repeat) + // TODO(jelbourn): Month headers stick to top when scrolling. + // TODO(jelbourn): Previous month opacity is lowered when partially scrolled out of view. + // TODO(jelbourn): Support md-calendar standalone on a page (as a tabstop w/ aria-live + // announcement and key handling). + // TODO Read-only calendar (not just date-picker). + + function calendarDirective(inputDirective) { + return { + template: function(tElement, tAttr) { + // This allows the calendar to work, without a datepicker. This ensures that the virtual + // repeater scrolls to the proper place on load by deferring the execution until the next + // digest. It's necessary only if the calendar is used without a datepicker, otherwise it's + // already wrapped in an ngIf. + var extraAttrs = tAttr.hasOwnProperty('ngIf') ? '' : 'ng-if="calendarCtrl.isInitialized"'; + return '' + + '
    ' + + '' + + '' + + '
    '; + }, + scope: { + minDate: '=mdMinDate', + maxDate: '=mdMaxDate', + dateFilter: '=mdDateFilter', + monthFilter: '=mdMonthFilter', + + // These need to be prefixed, because Angular resets + // any changes to the value due to bindToController. + _mode: '@mdMode', + _currentView: '@mdCurrentView' + }, + require: ['ngModel', 'mdCalendar'], + controller: CalendarCtrl, + controllerAs: 'calendarCtrl', + bindToController: true, + link: function(scope, element, attrs, controllers) { + var ngModelCtrl = controllers[0]; + var mdCalendarCtrl = controllers[1]; + mdCalendarCtrl.configureNgModel(ngModelCtrl, inputDirective); + } + }; + } + + /** + * Occasionally the hideVerticalScrollbar method might read an element's + * width as 0, because it hasn't been laid out yet. This value will be used + * as a fallback, in order to prevent scenarios where the element's width + * would otherwise have been set to 0. This value is the "usual" width of a + * calendar within a floating calendar pane. + */ + var FALLBACK_WIDTH = 340; + + /** Next identifier for calendar instance. */ + var nextUniqueId = 0; + + /** Maps the `md-mode` values to their corresponding calendar views. */ + var MODE_MAP = { + day: 'month', + month: 'year' + }; + + /** + * Controller for the mdCalendar component. + * @ngInject @constructor + */ + function CalendarCtrl($element, $scope, $$mdDateUtil, $mdUtil, $mdConstant, $mdTheming, $$rAF, + $attrs, $mdDateLocale, $filter, $document) { + $mdTheming($element); + + /** + * @final + * @type {!JQLite} + */ + this.$element = $element; + + /** + * @final + * @type {!angular.Scope} + */ + this.$scope = $scope; + + /** + * @final + * @type {!angular.$attrs} Current attributes object for the element + */ + this.$attrs = $attrs; + + /** @final */ + this.dateUtil = $$mdDateUtil; + + /** @final */ + this.$mdUtil = $mdUtil; + + /** @final */ + this.keyCode = $mdConstant.KEY_CODE; + + /** @final */ + this.$$rAF = $$rAF; + + /** @final */ + this.$mdDateLocale = $mdDateLocale; + + /** @final The built-in Angular date filter. */ + this.ngDateFilter = $filter('date'); + + /** + * @final + * @type {Date} + */ + this.today = this.dateUtil.createDateAtMidnight(); + + /** @type {!ngModel.NgModelController} */ + this.ngModelCtrl = undefined; + + /** @type {string} Class applied to the selected date cell. */ + this.SELECTED_DATE_CLASS = 'md-calendar-selected-date'; + + /** @type {string} Class applied to the cell for today. */ + this.TODAY_CLASS = 'md-calendar-date-today'; + + /** @type {string} Class applied to the focused cell. */ + this.FOCUSED_DATE_CLASS = 'md-focus'; + + /** + * @final + * @type {number} Unique ID for this calendar instance. + */ + this.id = nextUniqueId++; + + /** + * The date that is currently focused or showing in the calendar. This will initially be set + * to the ng-model value if set, otherwise to today. It will be updated as the user navigates + * to other months. The cell corresponding to the displayDate does not necessarily always have + * focus in the document (such as for cases when the user is scrolling the calendar). + * @type {Date} + */ + this.displayDate = null; + + /** + * Allows restricting the calendar to only allow selecting a month or a day. + * @type {'month'|'day'|null} + */ + this.mode = null; + + /** + * The selected date. Keep track of this separately from the ng-model value so that we + * can know, when the ng-model value changes, what the previous value was before it's updated + * in the component's UI. + * + * @type {Date} + */ + this.selectedDate = null; + + /** + * The first date that can be rendered by the calendar. The default is taken + * from the mdDateLocale provider and is limited by the mdMinDate. + * @type {Date} + */ + this.firstRenderableDate = null; + + /** + * The last date that can be rendered by the calendar. The default comes + * from the mdDateLocale provider and is limited by the maxDate. + * @type {Date} + */ + this.lastRenderableDate = null; + + /** + * Used to toggle initialize the root element in the next digest. + * @type {boolean} + */ + this.isInitialized = false; + + /** + * Cache for the width of the element without a scrollbar. Used to hide the scrollbar later on + * and to avoid extra reflows when switching between views. + * @type {number} + */ + this.width = 0; + + /** + * Caches the width of the scrollbar in order to be used when hiding it and to avoid extra reflows. + * @type {number} + */ + this.scrollbarWidth = 0; + + /** + * @type {boolean} set to true if the calendar is being used "standalone" (outside of a + * md-datepicker). + */ + this.standaloneMode = false; + + // Unless the user specifies so, the calendar should not be a tab stop. + // This is necessary because ngAria might add a tabindex to anything with an ng-model + // (based on whether or not the user has turned that particular feature on/off). + if (!$attrs.tabindex) { + $element.attr('tabindex', '-1'); + } + + var boundKeyHandler = angular.bind(this, this.handleKeyEvent); + + // If use the md-calendar directly in the body without datepicker, + // handleKeyEvent will disable other inputs on the page. + // So only apply the handleKeyEvent on the body when the md-calendar inside datepicker, + // otherwise apply on the calendar element only. + + var handleKeyElement; + if ($element.parent().hasClass('md-datepicker-calendar')) { + handleKeyElement = angular.element($document[0].body); + } else { + this.standaloneMode = true; + handleKeyElement = $element; + } + + // Bind the keydown handler to the body, in order to handle cases where the focused + // element gets removed from the DOM and stops propagating click events. + handleKeyElement.on('keydown', boundKeyHandler); + + $scope.$on('$destroy', function() { + handleKeyElement.off('keydown', boundKeyHandler); + }); + + // For AngularJS 1.4 and older, where there are no lifecycle hooks but bindings are pre-assigned, + // manually call the $onInit hook. + if (angular.version.major === 1 && angular.version.minor <= 4) { + this.$onInit(); + } + } + + /** + * AngularJS Lifecycle hook for newer AngularJS versions. + * Bindings are not guaranteed to have been assigned in the controller, but they are in the + * $onInit hook. + */ + CalendarCtrl.prototype.$onInit = function() { + /** + * The currently visible calendar view. Note the prefix on the scope value, + * which is necessary, because the datepicker seems to reset the real one value if the + * calendar is open, but the `currentView` on the datepicker's scope is empty. + * @type {String} + */ + if (this._mode && MODE_MAP.hasOwnProperty(this._mode)) { + this.currentView = MODE_MAP[this._mode]; + this.mode = this._mode; + } else { + this.currentView = this._currentView || 'month'; + this.mode = null; + } + + if (this.minDate && this.minDate > this.$mdDateLocale.firstRenderableDate) { + this.firstRenderableDate = this.minDate; + } else { + this.firstRenderableDate = this.$mdDateLocale.firstRenderableDate; + } + + if (this.maxDate && this.maxDate < this.$mdDateLocale.lastRenderableDate) { + this.lastRenderableDate = this.maxDate; + } else { + this.lastRenderableDate = this.$mdDateLocale.lastRenderableDate; + } + }; + + /** + * Sets up the controller's reference to ngModelController. + * @param {!ngModel.NgModelController} ngModelCtrl Instance of the ngModel controller. + * @param {Object} inputDirective Config for AngularJS's `input` directive. + */ + CalendarCtrl.prototype.configureNgModel = function(ngModelCtrl, inputDirective) { + var self = this; + self.ngModelCtrl = ngModelCtrl; + + // The component needs to be [type="date"] in order to be picked up by AngularJS. + this.$attrs.$set('type', 'date'); + + // Invoke the `input` directive link function, adding a stub for the element. + // This allows us to re-use AngularJS' logic for setting the timezone via ng-model-options. + // It works by calling the link function directly which then adds the proper `$parsers` and + // `$formatters` to the NgModelController. + inputDirective[0].link.pre(this.$scope, { + on: angular.noop, + val: angular.noop, + 0: {} + }, this.$attrs, [ngModelCtrl]); + + ngModelCtrl.$render = function() { + var value = this.$viewValue, convertedDate; + + // In the case where a conversion is needed, the $viewValue here will be a string like + // "2020-05-10" instead of a Date object. + if (!self.dateUtil.isValidDate(value)) { + convertedDate = self.dateUtil.removeLocalTzAndReparseDate(new Date(value)); + if (self.dateUtil.isValidDate(convertedDate)) { + value = convertedDate; + } + } + + // Notify the child scopes of any changes. + self.$scope.$broadcast('md-calendar-parent-changed', value); + + // Set up the selectedDate if it hasn't been already. + if (!self.selectedDate) { + self.selectedDate = value; + } + + // Also set up the displayDate. + if (!self.displayDate) { + self.displayDate = self.selectedDate || self.today; + } + }; + + self.$mdUtil.nextTick(function() { + self.isInitialized = true; + }); + }; + + /** + * Sets the ng-model value for the calendar and emits a change event. + * @param {Date} date new value for the calendar + */ + CalendarCtrl.prototype.setNgModelValue = function(date) { + var timezone = this.$mdUtil.getModelOption(this.ngModelCtrl, 'timezone'); + var value = this.dateUtil.createDateAtMidnight(date); + this.focusDate(value); + this.$scope.$emit('md-calendar-change', value); + // Using the timezone when the offset is negative (GMT+X) causes the previous day to be + // selected here. This check avoids that. + if (timezone == null || value.getTimezoneOffset() < 0) { + this.ngModelCtrl.$setViewValue(this.ngDateFilter(value, 'yyyy-MM-dd'), 'default'); + } else { + this.ngModelCtrl.$setViewValue(this.ngDateFilter(value, 'yyyy-MM-dd', timezone), 'default'); + } + this.ngModelCtrl.$render(); + return value; + }; + + /** + * Sets the current view that should be visible in the calendar + * @param {string} newView View name to be set. + * @param {number|Date} time Date object or a timestamp for the new display date. + */ + CalendarCtrl.prototype.setCurrentView = function(newView, time) { + var self = this; + + self.$mdUtil.nextTick(function() { + self.currentView = newView; + + if (time) { + self.displayDate = angular.isDate(time) ? time : new Date(time); + } + }); + }; + + /** + * Focus the cell corresponding to the given date. + * @param {Date=} date The date to be focused. + */ + CalendarCtrl.prototype.focusDate = function(date) { + if (this.dateUtil.isValidDate(date)) { + var previousFocus = this.$element[0].querySelector('.' + this.FOCUSED_DATE_CLASS); + if (previousFocus) { + previousFocus.classList.remove(this.FOCUSED_DATE_CLASS); + } + + var cellId = this.getDateId(date, this.currentView); + var cell = document.getElementById(cellId); + if (cell) { + cell.classList.add(this.FOCUSED_DATE_CLASS); + cell.focus(); + this.displayDate = date; + } + } else { + var rootElement = this.$element[0].querySelector('[ng-switch]'); + + if (rootElement) { + rootElement.focus(); + } + } + }; + + /** + * Highlights a date cell on the calendar and changes the selected date. + * @param {Date=} date Date to be marked as selected. + */ + CalendarCtrl.prototype.changeSelectedDate = function(date) { + var selectedDateClass = this.SELECTED_DATE_CLASS; + var prevDateCell = this.$element[0].querySelector('.' + selectedDateClass); + + // Remove the selected class from the previously selected date, if any. + if (prevDateCell) { + prevDateCell.classList.remove(selectedDateClass); + prevDateCell.setAttribute('aria-selected', 'false'); + } + + // Apply the select class to the new selected date if it is set. + if (date) { + var dateCell = document.getElementById(this.getDateId(date, this.currentView)); + if (dateCell) { + dateCell.classList.add(selectedDateClass); + dateCell.setAttribute('aria-selected', 'true'); + } + } + + this.selectedDate = date; + }; + + /** + * Normalizes the key event into an action name. The action will be broadcast + * to the child controllers. + * @param {KeyboardEvent} event + * @returns {string} The action that should be taken, or null if the key + * does not match a calendar shortcut. + */ + CalendarCtrl.prototype.getActionFromKeyEvent = function(event) { + var keyCode = this.keyCode; + + switch (event.which) { + case keyCode.ENTER: return 'select'; + + case keyCode.RIGHT_ARROW: return 'move-right'; + case keyCode.LEFT_ARROW: return 'move-left'; + + case keyCode.DOWN_ARROW: return event.metaKey ? 'move-page-down' : 'move-row-down'; + case keyCode.UP_ARROW: return event.metaKey ? 'move-page-up' : 'move-row-up'; + + case keyCode.PAGE_DOWN: return 'move-page-down'; + case keyCode.PAGE_UP: return 'move-page-up'; + + case keyCode.HOME: return 'start'; + case keyCode.END: return 'end'; + + default: return null; + } + }; + + /** + * Handles a key event in the calendar with the appropriate action. + * The action will either + * - select the focused date + * - navigate to focus a new date + * - emit a md-calendar-close event if in a md-datepicker panel + * - emit a md-calendar-parent-action + * - delegate to normal tab order if the TAB key is pressed in standalone mode + * @param {KeyboardEvent} event + */ + CalendarCtrl.prototype.handleKeyEvent = function(event) { + var self = this; + + this.$scope.$apply(function() { + // Capture escape and emit back up so that a wrapping component + // (such as a date-picker) can decide to close. + if (event.which === self.keyCode.ESCAPE || + (event.which === self.keyCode.TAB && !self.standaloneMode)) { + self.$scope.$emit('md-calendar-close'); + + if (event.which === self.keyCode.TAB) { + event.preventDefault(); + } + + return; + } else if (event.which === self.keyCode.TAB && self.standaloneMode) { + // delegate to the normal tab order if the TAB key is pressed in standalone mode + return; + } + + // Broadcast the action that any child controllers should take. + var action = self.getActionFromKeyEvent(event); + if (action) { + event.preventDefault(); + event.stopPropagation(); + self.$scope.$broadcast('md-calendar-parent-action', action); + } + }); + }; + + /** + * Hides the vertical scrollbar on the calendar scroller of a child controller by + * setting the width on the calendar scroller and the `overflow: hidden` wrapper + * around the scroller, and then setting a padding-right on the scroller equal + * to the width of the browser's scrollbar. + * + * This will cause a reflow. + * + * @param {object} childCtrl The child controller whose scrollbar should be hidden. + */ + CalendarCtrl.prototype.hideVerticalScrollbar = function(childCtrl) { + var self = this; + var element = childCtrl.$element[0]; + var scrollMask = element.querySelector('.md-calendar-scroll-mask'); + + if (self.width > 0) { + setWidth(); + } else { + self.$$rAF(function() { + var scroller = childCtrl.calendarScroller; + + self.scrollbarWidth = scroller.offsetWidth - scroller.clientWidth; + self.width = element.querySelector('table').offsetWidth; + setWidth(); + }); + } + + function setWidth() { + var width = self.width || FALLBACK_WIDTH; + var scrollbarWidth = self.scrollbarWidth; + var scroller = childCtrl.calendarScroller; + + scrollMask.style.width = width + 'px'; + scroller.style.width = (width + scrollbarWidth) + 'px'; + scroller.style.paddingRight = scrollbarWidth + 'px'; + } + }; + + /** + * Gets an identifier for a date unique to the calendar instance for internal + * purposes. Not to be displayed. + * @param {Date} date The date for which the id is being generated + * @param {string} namespace Namespace for the id. (month, year etc.) + * @returns {string} + */ + CalendarCtrl.prototype.getDateId = function(date, namespace) { + if (!namespace) { + throw new Error('A namespace for the date id has to be specified.'); + } + + return [ + 'md', + this.id, + namespace, + date.getFullYear(), + date.getMonth(), + date.getDate() + ].join('-'); + }; + + /** + * Util to trigger an extra digest on a parent scope, in order to to ensure that + * any child virtual repeaters have updated. This is necessary, because the virtual + * repeater doesn't update the $index the first time around since the content isn't + * in place yet. The case, in which this is an issue, is when the repeater has less + * than a page of content (e.g. a month or year view has a min or max date). + */ + CalendarCtrl.prototype.updateVirtualRepeat = function() { + var scope = this.$scope; + var virtualRepeatResizeListener = scope.$on('$md-resize-enable', function() { + if (!scope.$$phase) { + scope.$apply(); + } + + virtualRepeatResizeListener(); + }); + }; +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + CalendarMonthCtrl.$inject = ["$element", "$scope", "$animate", "$q", "$$mdDateUtil", "$mdDateLocale"]; + angular.module('material.components.datepicker') + .directive('mdCalendarMonth', calendarDirective); + + /** + * Height of one calendar month tbody. This must be made known to the virtual-repeat and is + * subsequently used for scrolling to specific months. + */ + var TBODY_HEIGHT = 265; + + /** + * Height of a calendar month with a single row. This is needed to calculate the offset for + * rendering an extra month in virtual-repeat that only contains one row. + */ + var TBODY_SINGLE_ROW_HEIGHT = 45; + + /** Private directive that represents a list of months inside the calendar. */ + function calendarDirective() { + return { + template: + '' + + '
    ' + + '' + + '' + + '' + + + // The ensures that the will always have the + // proper height, even if it's empty. If it's content is + // compiled, the will be overwritten. + '' + + '' + + '
    ' + + '
    ' + + '
    ', + require: ['^^mdCalendar', 'mdCalendarMonth'], + controller: CalendarMonthCtrl, + controllerAs: 'monthCtrl', + bindToController: true, + link: function(scope, element, attrs, controllers) { + var calendarCtrl = controllers[0]; + var monthCtrl = controllers[1]; + monthCtrl.initialize(calendarCtrl); + } + }; + } + + /** + * Controller for the calendar month component. + * @ngInject @constructor + */ + function CalendarMonthCtrl($element, $scope, $animate, $q, + $$mdDateUtil, $mdDateLocale) { + + /** @final {!angular.JQLite} */ + this.$element = $element; + + /** @final {!angular.Scope} */ + this.$scope = $scope; + + /** @final {!angular.$animate} */ + this.$animate = $animate; + + /** @final {!angular.$q} */ + this.$q = $q; + + /** @final */ + this.dateUtil = $$mdDateUtil; + + /** @final */ + this.dateLocale = $mdDateLocale; + + /** @final {HTMLElement} */ + this.calendarScroller = $element[0].querySelector('.md-virtual-repeat-scroller'); + + /** @type {boolean} */ + this.isInitialized = false; + + /** @type {boolean} */ + this.isMonthTransitionInProgress = false; + + var self = this; + + /** + * Handles a click event on a date cell. + * Created here so that every cell can use the same function instance. + * @this {HTMLTableCellElement} The cell that was clicked. + */ + this.cellClickHandler = function() { + var timestamp = $$mdDateUtil.getTimestampFromNode(this); + self.$scope.$apply(function() { + // The timestamp has to be converted to a valid date. + self.calendarCtrl.setNgModelValue(new Date(timestamp)); + }); + }; + + /** + * Handles click events on the month headers. Switches + * the calendar to the year view. + * @this {HTMLTableCellElement} The cell that was clicked. + */ + this.headerClickHandler = function() { + self.calendarCtrl.setCurrentView('year', $$mdDateUtil.getTimestampFromNode(this)); + }; + } + + /** Initialization **/ + + /** + * Initialize the controller by saving a reference to the calendar and + * setting up the object that will be iterated by the virtual repeater. + */ + CalendarMonthCtrl.prototype.initialize = function(calendarCtrl) { + /** + * Dummy array-like object for virtual-repeat to iterate over. The length is the total + * number of months that can be viewed. We add 2 months: one to include the current month + * and one for the last dummy month. + * + * This is shorter than ideal because of a (potential) Firefox bug + * https://bugzilla.mozilla.org/show_bug.cgi?id=1181658. + */ + + this.items = { + length: this.dateUtil.getMonthDistance( + calendarCtrl.firstRenderableDate, + calendarCtrl.lastRenderableDate + ) + 2 + }; + + this.calendarCtrl = calendarCtrl; + this.attachScopeListeners(); + calendarCtrl.updateVirtualRepeat(); + + // Fire the initial render, since we might have missed it the first time it fired. + calendarCtrl.ngModelCtrl && calendarCtrl.ngModelCtrl.$render(); + }; + + /** + * Gets the "index" of the currently selected date as it would be in the virtual-repeat. + * @returns {number} the "index" of the currently selected date + */ + CalendarMonthCtrl.prototype.getSelectedMonthIndex = function() { + var calendarCtrl = this.calendarCtrl; + + return this.dateUtil.getMonthDistance( + calendarCtrl.firstRenderableDate, + calendarCtrl.displayDate || calendarCtrl.selectedDate || calendarCtrl.today + ); + }; + + /** + * Change the date that is being shown in the calendar. If the given date is in a different + * month, the displayed month will be transitioned. + * @param {Date} date + */ + CalendarMonthCtrl.prototype.changeDisplayDate = function(date) { + // Initialization is deferred until this function is called because we want to reflect + // the starting value of ngModel. + if (!this.isInitialized) { + this.buildWeekHeader(); + this.calendarCtrl.hideVerticalScrollbar(this); + this.isInitialized = true; + return this.$q.when(); + } + + // If trying to show an invalid date or a transition is in progress, do nothing. + if (!this.dateUtil.isValidDate(date) || this.isMonthTransitionInProgress) { + return this.$q.when(); + } + + this.isMonthTransitionInProgress = true; + var animationPromise = this.animateDateChange(date); + + this.calendarCtrl.displayDate = date; + + var self = this; + animationPromise.then(function() { + self.isMonthTransitionInProgress = false; + }); + + return animationPromise; + }; + + /** + * Animates the transition from the calendar's current month to the given month. + * @param {Date} date + * @returns {angular.$q.Promise} The animation promise. + */ + CalendarMonthCtrl.prototype.animateDateChange = function(date) { + if (this.dateUtil.isValidDate(date)) { + var monthDistance = this.dateUtil.getMonthDistance(this.calendarCtrl.firstRenderableDate, date); + this.calendarScroller.scrollTop = monthDistance * TBODY_HEIGHT; + } + + return this.$q.when(); + }; + + /** + * Builds and appends a day-of-the-week header to the calendar. + * This should only need to be called once during initialization. + */ + CalendarMonthCtrl.prototype.buildWeekHeader = function() { + var firstDayOfWeek = this.dateLocale.firstDayOfWeek; + var shortDays = this.dateLocale.shortDays; + + var row = document.createElement('tr'); + for (var i = 0; i < 7; i++) { + var th = document.createElement('th'); + th.textContent = shortDays[(i + firstDayOfWeek) % 7]; + row.appendChild(th); + } + + this.$element.find('thead').append(row); + }; + + /** + * Attaches listeners for the scope events that are broadcast by the calendar. + */ + CalendarMonthCtrl.prototype.attachScopeListeners = function() { + var self = this; + + self.$scope.$on('md-calendar-parent-changed', function(event, value) { + self.calendarCtrl.changeSelectedDate(value); + self.changeDisplayDate(value); + }); + + self.$scope.$on('md-calendar-parent-action', angular.bind(this, this.handleKeyEvent)); + }; + + /** + * Handles the month-specific keyboard interactions. + * @param {Object} event Scope event object passed by the calendar. + * @param {String} action Action, corresponding to the key that was pressed. + */ + CalendarMonthCtrl.prototype.handleKeyEvent = function(event, action) { + var calendarCtrl = this.calendarCtrl; + var displayDate = calendarCtrl.displayDate; + + if (action === 'select') { + calendarCtrl.setNgModelValue(displayDate); + } else { + var date = null; + var dateUtil = this.dateUtil; + + switch (action) { + case 'move-right': date = dateUtil.incrementDays(displayDate, 1); break; + case 'move-left': date = dateUtil.incrementDays(displayDate, -1); break; + + case 'move-page-down': date = dateUtil.incrementMonths(displayDate, 1); break; + case 'move-page-up': date = dateUtil.incrementMonths(displayDate, -1); break; + + case 'move-row-down': date = dateUtil.incrementDays(displayDate, 7); break; + case 'move-row-up': date = dateUtil.incrementDays(displayDate, -7); break; + + case 'start': date = dateUtil.getFirstDateOfMonth(displayDate); break; + case 'end': date = dateUtil.getLastDateOfMonth(displayDate); break; + } + + if (date) { + date = this.dateUtil.clampDate(date, calendarCtrl.minDate, calendarCtrl.maxDate); + + this.changeDisplayDate(date).then(function() { + calendarCtrl.focusDate(date); + }); + } + } + }; +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + mdCalendarMonthBodyDirective.$inject = ["$compile", "$$mdSvgRegistry"]; + CalendarMonthBodyCtrl.$inject = ["$element", "$$mdDateUtil", "$mdDateLocale"]; + angular.module('material.components.datepicker') + .directive('mdCalendarMonthBody', mdCalendarMonthBodyDirective); + + /** + * Private directive consumed by md-calendar-month. Having this directive lets the calender use + * md-virtual-repeat and also cleanly separates the month DOM construction functions from + * the rest of the calendar controller logic. + * @ngInject + */ + function mdCalendarMonthBodyDirective($compile, $$mdSvgRegistry) { + var ARROW_ICON = $compile('')({})[0]; + + return { + require: ['^^mdCalendar', '^^mdCalendarMonth', 'mdCalendarMonthBody'], + scope: { offset: '=mdMonthOffset' }, + controller: CalendarMonthBodyCtrl, + controllerAs: 'mdMonthBodyCtrl', + bindToController: true, + link: function(scope, element, attrs, controllers) { + var calendarCtrl = controllers[0]; + var monthCtrl = controllers[1]; + var monthBodyCtrl = controllers[2]; + + monthBodyCtrl.calendarCtrl = calendarCtrl; + monthBodyCtrl.monthCtrl = monthCtrl; + monthBodyCtrl.arrowIcon = ARROW_ICON.cloneNode(true); + + // The virtual-repeat re-uses the same DOM elements, so there are only a limited number + // of repeated items that are linked, and then those elements have their bindings updated. + // Since the months are not generated by bindings, we simply regenerate the entire thing + // when the binding (offset) changes. + scope.$watch(function() { return monthBodyCtrl.offset; }, function(offset) { + if (angular.isNumber(offset)) { + monthBodyCtrl.generateContent(); + } + }); + } + }; + } + + /** + * Controller for a single calendar month. + * @ngInject @constructor + */ + function CalendarMonthBodyCtrl($element, $$mdDateUtil, $mdDateLocale) { + /** + * @final + * @type {!JQLite} + */ + this.$element = $element; + + /** @final */ + this.dateUtil = $$mdDateUtil; + + /** @final */ + this.dateLocale = $mdDateLocale; + + /** @type {Object} Reference to the month view. */ + this.monthCtrl = null; + + /** @type {Object} Reference to the calendar. */ + this.calendarCtrl = null; + + /** + * Number of months from the start of the month "items" that the currently rendered month + * occurs. Set via angular data binding. + * @type {number|null} + */ + this.offset = null; + + /** + * Date cell to focus after appending the month to the document. + * @type {HTMLElement} + */ + this.focusAfterAppend = null; + } + + /** Generate and append the content for this month to the directive element. */ + CalendarMonthBodyCtrl.prototype.generateContent = function() { + var date = this.dateUtil.incrementMonths(this.calendarCtrl.firstRenderableDate, this.offset); + + this.$element + .empty() + .append(this.buildCalendarForMonth(date)); + + if (this.focusAfterAppend) { + this.focusAfterAppend.classList.add(this.calendarCtrl.FOCUSED_DATE_CLASS); + this.focusAfterAppend = null; + } + }; + + /** + * Creates a single cell to contain a date in the calendar with all appropriate + * attributes and classes added. If a date is given, the cell content will be set + * based on the date. + * @param {Date=} opt_date + * @returns {HTMLElement} + */ + CalendarMonthBodyCtrl.prototype.buildDateCell = function(opt_date) { + var monthCtrl = this.monthCtrl; + var calendarCtrl = this.calendarCtrl; + + // TODO(jelbourn): cloneNode is likely a faster way of doing this. + var cell = document.createElement('td'); + cell.tabIndex = -1; + cell.classList.add('md-calendar-date'); + cell.setAttribute('role', 'gridcell'); + + if (opt_date) { + cell.setAttribute('tabindex', '-1'); + cell.setAttribute('aria-label', this.dateLocale.longDateFormatter(opt_date)); + cell.id = calendarCtrl.getDateId(opt_date, 'month'); + + // Use `data-timestamp` attribute because IE10 does not support the `dataset` property. + cell.setAttribute('data-timestamp', opt_date.getTime()); + + // TODO(jelourn): Doing these comparisons for class addition during generation might be slow. + // It may be better to finish the construction and then query the node and add the class. + if (this.dateUtil.isSameDay(opt_date, calendarCtrl.today)) { + cell.classList.add(calendarCtrl.TODAY_CLASS); + } + + if (this.dateUtil.isValidDate(calendarCtrl.selectedDate) && + this.dateUtil.isSameDay(opt_date, calendarCtrl.selectedDate)) { + cell.classList.add(calendarCtrl.SELECTED_DATE_CLASS); + cell.setAttribute('aria-selected', 'true'); + } + + var cellText = this.dateLocale.dates[opt_date.getDate()]; + + if (this.isDateEnabled(opt_date)) { + // Add a indicator for select, hover, and focus states. + var selectionIndicator = document.createElement('span'); + selectionIndicator.classList.add('md-calendar-date-selection-indicator'); + selectionIndicator.textContent = cellText; + cell.appendChild(selectionIndicator); + cell.addEventListener('click', monthCtrl.cellClickHandler); + + if (calendarCtrl.displayDate && this.dateUtil.isSameDay(opt_date, calendarCtrl.displayDate)) { + this.focusAfterAppend = cell; + } + } else { + cell.classList.add('md-calendar-date-disabled'); + cell.textContent = cellText; + } + } + + return cell; + }; + + /** + * Check whether date is in range and enabled + * @param {Date=} opt_date + * @return {boolean} Whether the date is enabled. + */ + CalendarMonthBodyCtrl.prototype.isDateEnabled = function(opt_date) { + return this.dateUtil.isDateWithinRange(opt_date, + this.calendarCtrl.minDate, this.calendarCtrl.maxDate) && + (!angular.isFunction(this.calendarCtrl.dateFilter) + || this.calendarCtrl.dateFilter(opt_date)); + }; + + /** + * Builds a `tr` element for the calendar grid. + * @param rowNumber The week number within the month. + * @returns {HTMLElement} + */ + CalendarMonthBodyCtrl.prototype.buildDateRow = function(rowNumber) { + var row = document.createElement('tr'); + row.setAttribute('role', 'row'); + + // Because of an NVDA bug (with Firefox), the row needs an aria-label in order + // to prevent the entire row being read aloud when the user moves between rows. + // See http://community.nvda-project.org/ticket/4643. + row.setAttribute('aria-label', this.dateLocale.weekNumberFormatter(rowNumber)); + + return row; + }; + + /** + * Builds the content for the given date's month. + * @param {Date=} opt_dateInMonth + * @returns {DocumentFragment} A document fragment containing the elements. + */ + CalendarMonthBodyCtrl.prototype.buildCalendarForMonth = function(opt_dateInMonth) { + var date = this.dateUtil.isValidDate(opt_dateInMonth) ? opt_dateInMonth : new Date(); + + var firstDayOfMonth = this.dateUtil.getFirstDateOfMonth(date); + var firstDayOfTheWeek = this.getLocaleDay_(firstDayOfMonth); + var numberOfDaysInMonth = this.dateUtil.getNumberOfDaysInMonth(date); + + // Store rows for the month in a document fragment so that we can append them all at once. + var monthBody = document.createDocumentFragment(); + + var rowNumber = 1; + var row = this.buildDateRow(rowNumber); + monthBody.appendChild(row); + + // If this is the final month in the list of items, only the first week should render, + // so we should return immediately after the first row is complete and has been + // attached to the body. + var isFinalMonth = this.offset === this.monthCtrl.items.length - 1; + + // Add a label for the month. If the month starts on a Sun/Mon/Tues, the month label + // goes on a row above the first of the month. Otherwise, the month label takes up the first + // two cells of the first row. + var blankCellOffset = 0; + var monthLabelCell = document.createElement('td'); + var monthLabelCellContent = document.createElement('span'); + var calendarCtrl = this.calendarCtrl; + + monthLabelCellContent.textContent = this.dateLocale.monthHeaderFormatter(date); + monthLabelCell.appendChild(monthLabelCellContent); + monthLabelCell.classList.add('md-calendar-month-label'); + // If the entire month is after the max date, render the label as a disabled state. + if (calendarCtrl.maxDate && firstDayOfMonth > calendarCtrl.maxDate) { + monthLabelCell.classList.add('md-calendar-month-label-disabled'); + // If the user isn't supposed to be able to change views, render the + // label as usual, but disable the clicking functionality. + } else if (!calendarCtrl.mode) { + monthLabelCell.addEventListener('click', this.monthCtrl.headerClickHandler); + monthLabelCell.setAttribute('data-timestamp', firstDayOfMonth.getTime()); + monthLabelCell.setAttribute('aria-label', this.dateLocale.monthFormatter(date)); + monthLabelCell.classList.add('md-calendar-label-clickable'); + monthLabelCell.appendChild(this.arrowIcon.cloneNode(true)); + } + + if (firstDayOfTheWeek <= 2) { + monthLabelCell.setAttribute('colspan', '7'); + + var monthLabelRow = this.buildDateRow(); + monthLabelRow.appendChild(monthLabelCell); + monthBody.insertBefore(monthLabelRow, row); + + if (isFinalMonth) { + return monthBody; + } + } else { + blankCellOffset = 3; + monthLabelCell.setAttribute('colspan', '3'); + row.appendChild(monthLabelCell); + } + + // Add a blank cell for each day of the week that occurs before the first of the month. + // For example, if the first day of the month is a Tuesday, add blank cells for Sun and Mon. + // The blankCellOffset is needed in cases where the first N cells are used by the month label. + for (var i = blankCellOffset; i < firstDayOfTheWeek; i++) { + row.appendChild(this.buildDateCell()); + } + + // Add a cell for each day of the month, keeping track of the day of the week so that + // we know when to start a new row. + var dayOfWeek = firstDayOfTheWeek; + var iterationDate = firstDayOfMonth; + for (var d = 1; d <= numberOfDaysInMonth; d++) { + // If we've reached the end of the week, start a new row. + if (dayOfWeek === 7) { + // We've finished the first row, so we're done if this is the final month. + if (isFinalMonth) { + return monthBody; + } + dayOfWeek = 0; + rowNumber++; + row = this.buildDateRow(rowNumber); + monthBody.appendChild(row); + } + + iterationDate.setDate(d); + var cell = this.buildDateCell(iterationDate); + row.appendChild(cell); + + dayOfWeek++; + } + + // Ensure that the last row of the month has 7 cells. + while (row.childNodes.length < 7) { + row.appendChild(this.buildDateCell()); + } + + // Ensure that all months have 6 rows. This is necessary for now because the virtual-repeat + // requires that all items have exactly the same height. + while (monthBody.childNodes.length < 6) { + var whitespaceRow = this.buildDateRow(); + for (var j = 0; j < 7; j++) { + whitespaceRow.appendChild(this.buildDateCell()); + } + monthBody.appendChild(whitespaceRow); + } + + return monthBody; + }; + + /** + * Gets the day-of-the-week index for a date for the current locale. + * @private + * @param {Date} date + * @returns {number} The column index of the date in the calendar. + */ + CalendarMonthBodyCtrl.prototype.getLocaleDay_ = function(date) { + return (date.getDay() + (7 - this.dateLocale.firstDayOfWeek)) % 7; + }; +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + CalendarYearCtrl.$inject = ["$element", "$scope", "$animate", "$q", "$$mdDateUtil", "$mdUtil"]; + angular.module('material.components.datepicker') + .directive('mdCalendarYear', calendarDirective); + + /** + * Height of one calendar year tbody. This must be made known to the virtual-repeat and is + * subsequently used for scrolling to specific years. + */ + var TBODY_HEIGHT = 88; + + /** Private component, representing a list of years in the calendar. */ + function calendarDirective() { + return { + template: + '
    ' + + '' + + '' + + '' + + // The ensures that the will have the proper + // height, even though it may be empty. + '' + + '' + + '
    ' + + '
    ' + + '
    ', + require: ['^^mdCalendar', 'mdCalendarYear'], + controller: CalendarYearCtrl, + controllerAs: 'yearCtrl', + bindToController: true, + link: function(scope, element, attrs, controllers) { + var calendarCtrl = controllers[0]; + var yearCtrl = controllers[1]; + yearCtrl.initialize(calendarCtrl); + } + }; + } + + /** + * Controller for the mdCalendar component. + * @ngInject @constructor + */ + function CalendarYearCtrl($element, $scope, $animate, $q, $$mdDateUtil, $mdUtil) { + + /** @final {!angular.JQLite} */ + this.$element = $element; + + /** @final {!angular.Scope} */ + this.$scope = $scope; + + /** @final {!angular.$animate} */ + this.$animate = $animate; + + /** @final {!angular.$q} */ + this.$q = $q; + + /** @final */ + this.dateUtil = $$mdDateUtil; + + /** @final {HTMLElement} */ + this.calendarScroller = $element[0].querySelector('.md-virtual-repeat-scroller'); + + /** @type {boolean} */ + this.isInitialized = false; + + /** @type {boolean} */ + this.isMonthTransitionInProgress = false; + + /** @final */ + this.$mdUtil = $mdUtil; + + var self = this; + + /** + * Handles a click event on a date cell. + * Created here so that every cell can use the same function instance. + * @this {HTMLTableCellElement} The cell that was clicked. + */ + this.cellClickHandler = function() { + self.onTimestampSelected($$mdDateUtil.getTimestampFromNode(this)); + }; + } + + /** + * Initialize the controller by saving a reference to the calendar and + * setting up the object that will be iterated by the virtual repeater. + */ + CalendarYearCtrl.prototype.initialize = function(calendarCtrl) { + /** + * Dummy array-like object for virtual-repeat to iterate over. The length is the total + * number of years that can be viewed. We add 1 extra in order to include the current year. + */ + + this.items = { + length: this.dateUtil.getYearDistance( + calendarCtrl.firstRenderableDate, + calendarCtrl.lastRenderableDate + ) + 1 + }; + + this.calendarCtrl = calendarCtrl; + this.attachScopeListeners(); + calendarCtrl.updateVirtualRepeat(); + + // Fire the initial render, since we might have missed it the first time it fired. + calendarCtrl.ngModelCtrl && calendarCtrl.ngModelCtrl.$render(); + }; + + /** + * Gets the "index" of the currently selected date as it would be in the virtual-repeat. + * @returns {number} + */ + CalendarYearCtrl.prototype.getFocusedYearIndex = function() { + var calendarCtrl = this.calendarCtrl; + + return this.dateUtil.getYearDistance( + calendarCtrl.firstRenderableDate, + calendarCtrl.displayDate || calendarCtrl.selectedDate || calendarCtrl.today + ); + }; + + /** + * Change the date that is highlighted in the calendar. + * @param {Date} date + */ + CalendarYearCtrl.prototype.changeDate = function(date) { + // Initialization is deferred until this function is called because we want to reflect + // the starting value of ngModel. + if (!this.isInitialized) { + this.calendarCtrl.hideVerticalScrollbar(this); + this.isInitialized = true; + return this.$q.when(); + } else if (this.dateUtil.isValidDate(date) && !this.isMonthTransitionInProgress) { + var self = this; + var animationPromise = this.animateDateChange(date); + + self.isMonthTransitionInProgress = true; + self.calendarCtrl.displayDate = date; + + return animationPromise.then(function() { + self.isMonthTransitionInProgress = false; + }); + } + }; + + /** + * Animates the transition from the calendar's current month to the given month. + * @param {Date} date + * @returns {angular.$q.Promise} The animation promise. + */ + CalendarYearCtrl.prototype.animateDateChange = function(date) { + if (this.dateUtil.isValidDate(date)) { + var monthDistance = this.dateUtil.getYearDistance(this.calendarCtrl.firstRenderableDate, date); + this.calendarScroller.scrollTop = monthDistance * TBODY_HEIGHT; + } + + return this.$q.when(); + }; + + /** + * Handles the year-view-specific keyboard interactions. + * @param {Object} event Scope event object passed by the calendar. + * @param {String} action Action, corresponding to the key that was pressed. + */ + CalendarYearCtrl.prototype.handleKeyEvent = function(event, action) { + var self = this; + var calendarCtrl = self.calendarCtrl; + var displayDate = calendarCtrl.displayDate; + + if (action === 'select') { + self.changeDate(displayDate).then(function() { + self.onTimestampSelected(displayDate); + }); + } else { + var date = null; + var dateUtil = self.dateUtil; + + switch (action) { + case 'move-right': date = dateUtil.incrementMonths(displayDate, 1); break; + case 'move-left': date = dateUtil.incrementMonths(displayDate, -1); break; + + case 'move-row-down': date = dateUtil.incrementMonths(displayDate, 6); break; + case 'move-row-up': date = dateUtil.incrementMonths(displayDate, -6); break; + } + + if (date) { + var min = calendarCtrl.minDate ? dateUtil.getFirstDateOfMonth(calendarCtrl.minDate) : null; + var max = calendarCtrl.maxDate ? dateUtil.getFirstDateOfMonth(calendarCtrl.maxDate) : null; + date = dateUtil.getFirstDateOfMonth(self.dateUtil.clampDate(date, min, max)); + + self.changeDate(date).then(function() { + calendarCtrl.focusDate(date); + }); + } + } + }; + + /** + * Attaches listeners for the scope events that are broadcast by the calendar. + */ + CalendarYearCtrl.prototype.attachScopeListeners = function() { + var self = this; + + self.$scope.$on('md-calendar-parent-changed', function(event, value) { + self.calendarCtrl.changeSelectedDate(value ? self.dateUtil.getFirstDateOfMonth(value) : value); + self.changeDate(value); + }); + + self.$scope.$on('md-calendar-parent-action', angular.bind(self, self.handleKeyEvent)); + }; + + /** + * Handles the behavior when a date is selected. Depending on the `mode` + * of the calendar, this can either switch back to the calendar view or + * set the model value. + * @param {number} timestamp The selected timestamp. + */ + CalendarYearCtrl.prototype.onTimestampSelected = function(timestamp) { + var calendarCtrl = this.calendarCtrl; + + if (calendarCtrl.mode) { + this.$mdUtil.nextTick(function() { + // The timestamp has to be converted to a valid date. + calendarCtrl.setNgModelValue(new Date(timestamp)); + }); + } else { + calendarCtrl.setCurrentView('month', timestamp); + } + }; +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + CalendarYearBodyCtrl.$inject = ["$element", "$$mdDateUtil", "$mdDateLocale"]; + angular.module('material.components.datepicker') + .directive('mdCalendarYearBody', mdCalendarYearDirective); + + /** + * Private component, consumed by the md-calendar-year, which separates the DOM construction logic + * and allows for the year view to use md-virtual-repeat. + */ + function mdCalendarYearDirective() { + return { + require: ['^^mdCalendar', '^^mdCalendarYear', 'mdCalendarYearBody'], + scope: { offset: '=mdYearOffset' }, + controller: CalendarYearBodyCtrl, + controllerAs: 'mdYearBodyCtrl', + bindToController: true, + link: function(scope, element, attrs, controllers) { + var calendarCtrl = controllers[0]; + var yearCtrl = controllers[1]; + var yearBodyCtrl = controllers[2]; + + yearBodyCtrl.calendarCtrl = calendarCtrl; + yearBodyCtrl.yearCtrl = yearCtrl; + + scope.$watch(function() { return yearBodyCtrl.offset; }, function(offset) { + if (angular.isNumber(offset)) { + yearBodyCtrl.generateContent(); + } + }); + } + }; + } + + /** + * Controller for a single year. + * @ngInject @constructor + */ + function CalendarYearBodyCtrl($element, $$mdDateUtil, $mdDateLocale) { + /** + * @final + * @type {!JQLite} + */ + this.$element = $element; + + /** @final */ + this.dateUtil = $$mdDateUtil; + + /** @final */ + this.dateLocale = $mdDateLocale; + + /** @type {Object} Reference to the calendar. */ + this.calendarCtrl = null; + + /** @type {Object} Reference to the year view. */ + this.yearCtrl = null; + + /** + * Number of months from the start of the month "items" that the currently rendered month + * occurs. Set via angular data binding. + * @type {number|null} + */ + this.offset = null; + + /** + * Date cell to focus after appending the month to the document. + * @type {HTMLElement} + */ + this.focusAfterAppend = null; + } + + /** Generate and append the content for this year to the directive element. */ + CalendarYearBodyCtrl.prototype.generateContent = function() { + var date = this.dateUtil.incrementYears(this.calendarCtrl.firstRenderableDate, this.offset); + + this.$element + .empty() + .append(this.buildCalendarForYear(date)); + + if (this.focusAfterAppend) { + this.focusAfterAppend.classList.add(this.calendarCtrl.FOCUSED_DATE_CLASS); + this.focusAfterAppend = null; + } + }; + + /** + * Creates a single cell to contain a year in the calendar. + * @param {number} year Four-digit year. + * @param {number} month Zero-indexed month. + * @returns {HTMLElement} + */ + CalendarYearBodyCtrl.prototype.buildMonthCell = function(year, month) { + var calendarCtrl = this.calendarCtrl; + var yearCtrl = this.yearCtrl; + var cell = this.buildBlankCell(); + + // Represent this month/year as a date. + var firstOfMonth = new Date(year, month, 1); + cell.setAttribute('aria-label', this.dateLocale.monthFormatter(firstOfMonth)); + cell.id = calendarCtrl.getDateId(firstOfMonth, 'year'); + + // Use `data-timestamp` attribute because IE10 does not support the `dataset` property. + cell.setAttribute('data-timestamp', String(firstOfMonth.getTime())); + + if (this.dateUtil.isSameMonthAndYear(firstOfMonth, calendarCtrl.today)) { + cell.classList.add(calendarCtrl.TODAY_CLASS); + } + + if (this.dateUtil.isValidDate(calendarCtrl.selectedDate) && + this.dateUtil.isSameMonthAndYear(firstOfMonth, calendarCtrl.selectedDate)) { + cell.classList.add(calendarCtrl.SELECTED_DATE_CLASS); + cell.setAttribute('aria-selected', 'true'); + } + + var cellText = this.dateLocale.shortMonths[month]; + + if (this.dateUtil.isMonthWithinRange( + firstOfMonth, calendarCtrl.minDate, calendarCtrl.maxDate) && + (!angular.isFunction(calendarCtrl.monthFilter) || + calendarCtrl.monthFilter(firstOfMonth))) { + var selectionIndicator = document.createElement('span'); + selectionIndicator.classList.add('md-calendar-date-selection-indicator'); + selectionIndicator.textContent = cellText; + cell.appendChild(selectionIndicator); + cell.addEventListener('click', yearCtrl.cellClickHandler); + + if (calendarCtrl.displayDate && + this.dateUtil.isSameMonthAndYear(firstOfMonth, calendarCtrl.displayDate)) { + this.focusAfterAppend = cell; + } + } else { + cell.classList.add('md-calendar-date-disabled'); + cell.textContent = cellText; + } + + return cell; + }; + + /** + * Builds a blank cell. + * @return {HTMLElement} + */ + CalendarYearBodyCtrl.prototype.buildBlankCell = function() { + var cell = document.createElement('td'); + cell.tabIndex = -1; + cell.classList.add('md-calendar-date'); + cell.setAttribute('role', 'gridcell'); + + cell.setAttribute('tabindex', '-1'); + return cell; + }; + + /** + * Builds the content for the given year. + * @param {Date} date Date for which the content should be built. + * @returns {DocumentFragment} A document fragment containing the months within the year. + */ + CalendarYearBodyCtrl.prototype.buildCalendarForYear = function(date) { + // Store rows for the month in a document fragment so that we can append them all at once. + var year = date.getFullYear(); + var yearBody = document.createDocumentFragment(); + + var monthCell, i; + // First row contains label and Jan-Jun. + var firstRow = document.createElement('tr'); + var labelCell = document.createElement('td'); + labelCell.className = 'md-calendar-month-label'; + labelCell.textContent = String(year); + firstRow.appendChild(labelCell); + + for (i = 0; i < 6; i++) { + firstRow.appendChild(this.buildMonthCell(year, i)); + } + yearBody.appendChild(firstRow); + + // Second row contains a blank cell and Jul-Dec. + var secondRow = document.createElement('tr'); + secondRow.appendChild(this.buildBlankCell()); + for (i = 6; i < 12; i++) { + secondRow.appendChild(this.buildMonthCell(year, i)); + } + yearBody.appendChild(secondRow); + + return yearBody; + }; +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * @ngdoc service + * @name $mdDateLocaleProvider + * @module material.components.datepicker + * + * @description + * The `$mdDateLocaleProvider` is the provider that creates the `$mdDateLocale` service. + * This provider that allows the user to specify messages, formatters, and parsers for date + * internationalization. The `$mdDateLocale` service itself is consumed by AngularJS Material + * components that deal with dates + * (i.e. mdDatepicker). + * + * @property {Array} months Array of month names (in order). + * @property {Array} shortMonths Array of abbreviated month names. + * @property {Array} days Array of the days of the week (in order). + * @property {Array} shortDays Array of abbreviated days of the week. + * @property {Array} dates Array of dates of the month. Only necessary for locales + * using a numeral system other than [1, 2, 3...]. + * @property {Array} firstDayOfWeek The first day of the week. Sunday = 0, Monday = 1, + * etc. + * @property {function(string): Date} parseDate Function that converts a date string to a Date + * object (the date portion). + * @property {function(Date, string): string} formatDate Function to format a date object to a + * string. The datepicker directive also provides the time zone, if it was specified. + * @property {function(Date): string} monthHeaderFormatter Function that returns the label for + * a month given a date. + * @property {function(Date): string} monthFormatter Function that returns the full name of a month + * for a given date. + * @property {function(number): string} weekNumberFormatter Function that returns a label for + * a week given the week number. + * @property {function(Date): string} longDateFormatter Function that formats a date into a long + * `aria-label` that is read by the screen reader when the focused date changes. + * @property {string} msgCalendar Translation of the label "Calendar" for the current locale. + * @property {string} msgOpenCalendar Translation of the button label "Open calendar" for the + * current locale. + * @property {Date} firstRenderableDate The date from which the datepicker calendar will begin + * rendering. Note that this will be ignored if a minimum date is set. + * Defaults to January 1st 1880. + * @property {Date} lastRenderableDate The last date that will be rendered by the datepicker + * calendar. Note that this will be ignored if a maximum date is set. + * Defaults to January 1st 2130. + * @property {function(string): boolean} isDateComplete Function to determine whether a string + * makes sense to be parsed to a `Date` object. Returns `true` if the date appears to be complete + * and parsing should occur. By default, this checks for 3 groups of text or numbers separated + * by delimiters. This means that by default, date strings must include a month, day, and year + * to be parsed and for the model to be updated. + * + * @usage + * + * myAppModule.config(function($mdDateLocaleProvider) { + * + * // Example of a French localization. + * $mdDateLocaleProvider.months = ['janvier', 'février', 'mars', ...]; + * $mdDateLocaleProvider.shortMonths = ['janv', 'févr', 'mars', ...]; + * $mdDateLocaleProvider.days = ['dimanche', 'lundi', 'mardi', ...]; + * $mdDateLocaleProvider.shortDays = ['Di', 'Lu', 'Ma', ...]; + * + * // Can change week display to start on Monday. + * $mdDateLocaleProvider.firstDayOfWeek = 1; + * + * // Optional. + * $mdDateLocaleProvider.dates = [1, 2, 3, 4, 5, 6, ...]; + * + * // Example uses moment.js to parse and format dates. + * $mdDateLocaleProvider.parseDate = function(dateString) { + * var m = moment(dateString, 'L', true); + * return m.isValid() ? m.toDate() : new Date(NaN); + * }; + * + * $mdDateLocaleProvider.formatDate = function(date) { + * var m = moment(date); + * return m.isValid() ? m.format('L') : ''; + * }; + * + * // Allow only a day and month to be specified. + * // This is required if using the 'M/D' format with moment.js. + * $mdDateLocaleProvider.isDateComplete = function(dateString) { + * dateString = dateString.trim(); + * + * // Look for two chunks of content (either numbers or text) separated by delimiters. + * var re = /^(([a-zA-Z]{3,}|[0-9]{1,4})([ .,]+|[/-]))([a-zA-Z]{3,}|[0-9]{1,4})/; + * return re.test(dateString); + * }; + * + * $mdDateLocaleProvider.monthHeaderFormatter = function(date) { + * return myShortMonths[date.getMonth()] + ' ' + date.getFullYear(); + * }; + * + * // In addition to date display, date components also need localized messages + * // for aria-labels for screen-reader users. + * + * $mdDateLocaleProvider.weekNumberFormatter = function(weekNumber) { + * return 'Semaine ' + weekNumber; + * }; + * + * $mdDateLocaleProvider.msgCalendar = 'Calendrier'; + * $mdDateLocaleProvider.msgOpenCalendar = 'Ouvrir le calendrier'; + * + * // You can also set when your calendar begins and ends. + * $mdDateLocaleProvider.firstRenderableDate = new Date(1776, 6, 4); + * $mdDateLocaleProvider.lastRenderableDate = new Date(2012, 11, 21); + * }); + * + * + */ + angular.module('material.components.datepicker').config(["$provide", function($provide) { + // TODO(jelbourn): Assert provided values are correctly formatted. Need assertions. + + /** @constructor */ + function DateLocaleProvider() { + /** Array of full month names. E.g., ['January', 'February', ...] */ + this.months = null; + + /** Array of abbreviated month names. E.g., ['Jan', 'Feb', ...] */ + this.shortMonths = null; + + /** Array of full day of the week names. E.g., ['Monday', 'Tuesday', ...] */ + this.days = null; + + /** Array of abbreviated dat of the week names. E.g., ['M', 'T', ...] */ + this.shortDays = null; + + /** Array of dates of a month (1 - 31). Characters might be different in some locales. */ + this.dates = null; + + /** Index of the first day of the week. 0 = Sunday, 1 = Monday, etc. */ + this.firstDayOfWeek = 0; + + /** + * Function that converts the date portion of a Date to a string. + * @type {(function(Date): string)} + */ + this.formatDate = null; + + /** + * Function that converts a date string to a Date object (the date portion) + * @type {function(string): Date} + */ + this.parseDate = null; + + /** + * Function that formats a Date into a month header string. + * @type {function(Date): string} + */ + this.monthHeaderFormatter = null; + + /** + * Function that formats a week number into a label for the week. + * @type {function(number): string} + */ + this.weekNumberFormatter = null; + + /** + * Function that formats a date into a long aria-label that is read + * when the focused date changes. + * @type {function(Date): string} + */ + this.longDateFormatter = null; + + /** + * Function to determine whether a string makes sense to be + * parsed to a Date object. + * @type {function(string): boolean} + */ + this.isDateComplete = null; + + /** + * ARIA label for the calendar "dialog" used in the datepicker. + * @type {string} + */ + this.msgCalendar = ''; + + /** + * ARIA label for the datepicker's "Open calendar" buttons. + * @type {string} + */ + this.msgOpenCalendar = ''; + } + + /** + * Factory function that returns an instance of the dateLocale service. + * @ngInject + * @param $locale + * @param $filter + * @returns {DateLocale} + */ + DateLocaleProvider.prototype.$get = function($locale, $filter) { + /** + * Default date-to-string formatting function. + * @param {!Date} date + * @param {string=} timezone + * @returns {string} + */ + function defaultFormatDate(date, timezone) { + if (!date) { + return ''; + } + + // All of the dates created through ng-material *should* be set to midnight. + // If we encounter a date where the localeTime shows at 11pm instead of midnight, + // we have run into an issue with DST where we need to increment the hour by one: + // var d = new Date(1992, 9, 8, 0, 0, 0); + // d.toLocaleString(); // == "10/7/1992, 11:00:00 PM" + var localeTime = date.toLocaleTimeString(); + var formatDate = date; + if (date.getHours() === 0 && + (localeTime.indexOf('11:') !== -1 || localeTime.indexOf('23:') !== -1)) { + formatDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 1, 0, 0); + } + + return $filter('date')(formatDate, 'M/d/yyyy', timezone); + } + + /** + * Default string-to-date parsing function. + * @param {string|number} dateString + * @returns {!Date} + */ + function defaultParseDate(dateString) { + return new Date(dateString); + } + + /** + * Default function to determine whether a string makes sense to be + * parsed to a Date object. + * + * This is very permissive and is just a basic check to ensure that + * things like single integers aren't able to be parsed into dates. + * @param {string} dateString + * @returns {boolean} + */ + function defaultIsDateComplete(dateString) { + dateString = dateString.trim(); + + // Looks for three chunks of content (either numbers or text) separated + // by delimiters. + var re = /^(([a-zA-Z]{3,}|[0-9]{1,4})([ .,]+|[/-])){2}([a-zA-Z]{3,}|[0-9]{1,4})$/; + return re.test(dateString); + } + + /** + * Default date-to-string formatter to get a month header. + * @param {!Date} date + * @returns {string} + */ + function defaultMonthHeaderFormatter(date) { + return service.shortMonths[date.getMonth()] + ' ' + date.getFullYear(); + } + + /** + * Default formatter for a month. + * @param {!Date} date + * @returns {string} + */ + function defaultMonthFormatter(date) { + return service.months[date.getMonth()] + ' ' + date.getFullYear(); + } + + /** + * Default week number formatter. + * @param number + * @returns {string} + */ + function defaultWeekNumberFormatter(number) { + return 'Week ' + number; + } + + /** + * Default formatter for date cell aria-labels. + * @param {!Date} date + * @returns {string} + */ + function defaultLongDateFormatter(date) { + // Example: 'Thursday June 18 2015' + return [ + service.days[date.getDay()], + service.months[date.getMonth()], + service.dates[date.getDate()], + date.getFullYear() + ].join(' '); + } + + // The default "short" day strings are the first character of each day, + // e.g., "Monday" => "M". + var defaultShortDays = $locale.DATETIME_FORMATS.SHORTDAY.map(function(day) { + return day.substring(0, 1); + }); + + // The default dates are simply the numbers 1 through 31. + var defaultDates = Array(32); + for (var i = 1; i <= 31; i++) { + defaultDates[i] = i; + } + + // Default ARIA messages are in English (US). + var defaultMsgCalendar = 'Calendar'; + var defaultMsgOpenCalendar = 'Open calendar'; + + // Default start/end dates that are rendered in the calendar. + var defaultFirstRenderableDate = new Date(1880, 0, 1); + var defaultLastRendereableDate = new Date(defaultFirstRenderableDate.getFullYear() + 250, 0, 1); + + var service = { + months: this.months || $locale.DATETIME_FORMATS.MONTH, + shortMonths: this.shortMonths || $locale.DATETIME_FORMATS.SHORTMONTH, + days: this.days || $locale.DATETIME_FORMATS.DAY, + shortDays: this.shortDays || defaultShortDays, + dates: this.dates || defaultDates, + firstDayOfWeek: this.firstDayOfWeek || 0, + formatDate: this.formatDate || defaultFormatDate, + parseDate: this.parseDate || defaultParseDate, + isDateComplete: this.isDateComplete || defaultIsDateComplete, + monthHeaderFormatter: this.monthHeaderFormatter || defaultMonthHeaderFormatter, + monthFormatter: this.monthFormatter || defaultMonthFormatter, + weekNumberFormatter: this.weekNumberFormatter || defaultWeekNumberFormatter, + longDateFormatter: this.longDateFormatter || defaultLongDateFormatter, + msgCalendar: this.msgCalendar || defaultMsgCalendar, + msgOpenCalendar: this.msgOpenCalendar || defaultMsgOpenCalendar, + firstRenderableDate: this.firstRenderableDate || defaultFirstRenderableDate, + lastRenderableDate: this.lastRenderableDate || defaultLastRendereableDate + }; + + return service; + }; + DateLocaleProvider.prototype.$get.$inject = ["$locale", "$filter"]; + + $provide.provider('$mdDateLocale', new DateLocaleProvider()); + }]); +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + /** + * Utility for performing date calculations to facilitate operation of the calendar and + * datepicker. + */ + angular.module('material.components.datepicker').factory('$$mdDateUtil', ["$mdDateLocale", function($mdDateLocale) { + return { + getFirstDateOfMonth: getFirstDateOfMonth, + getNumberOfDaysInMonth: getNumberOfDaysInMonth, + getDateInNextMonth: getDateInNextMonth, + getDateInPreviousMonth: getDateInPreviousMonth, + isInNextMonth: isInNextMonth, + isInPreviousMonth: isInPreviousMonth, + getDateMidpoint: getDateMidpoint, + isSameMonthAndYear: isSameMonthAndYear, + getWeekOfMonth: getWeekOfMonth, + incrementDays: incrementDays, + incrementMonths: incrementMonths, + getLastDateOfMonth: getLastDateOfMonth, + isSameDay: isSameDay, + getMonthDistance: getMonthDistance, + isValidDate: isValidDate, + setDateTimeToMidnight: setDateTimeToMidnight, + createDateAtMidnight: createDateAtMidnight, + isDateWithinRange: isDateWithinRange, + incrementYears: incrementYears, + getYearDistance: getYearDistance, + clampDate: clampDate, + getTimestampFromNode: getTimestampFromNode, + isMonthWithinRange: isMonthWithinRange, + removeLocalTzAndReparseDate: removeLocalTzAndReparseDate + }; + + /** + * Gets the first day of the month for the given date's month. + * @param {Date} date + * @returns {Date} + */ + function getFirstDateOfMonth(date) { + return new Date(date.getFullYear(), date.getMonth(), 1); + } + + /** + * Gets the number of days in the month for the given date's month. + * @param date + * @returns {number} + */ + function getNumberOfDaysInMonth(date) { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); + } + + /** + * Get an arbitrary date in the month after the given date's month. + * @param date + * @returns {Date} + */ + function getDateInNextMonth(date) { + return new Date(date.getFullYear(), date.getMonth() + 1, 1); + } + + /** + * Get an arbitrary date in the month before the given date's month. + * @param date + * @returns {Date} + */ + function getDateInPreviousMonth(date) { + return new Date(date.getFullYear(), date.getMonth() - 1, 1); + } + + /** + * Gets whether two dates have the same month and year. + * @param {Date} d1 + * @param {Date} d2 + * @returns {boolean} + */ + function isSameMonthAndYear(d1, d2) { + return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth(); + } + + /** + * Gets whether two dates are the same day (not not necessarily the same time). + * @param {Date} d1 + * @param {Date} d2 + * @returns {boolean} + */ + function isSameDay(d1, d2) { + return d1.getDate() == d2.getDate() && isSameMonthAndYear(d1, d2); + } + + /** + * Gets whether a date is in the month immediately after some date. + * @param {Date} startDate The date from which to compare. + * @param {Date} endDate The date to check. + * @returns {boolean} + */ + function isInNextMonth(startDate, endDate) { + var nextMonth = getDateInNextMonth(startDate); + return isSameMonthAndYear(nextMonth, endDate); + } + + /** + * Gets whether a date is in the month immediately before some date. + * @param {Date} startDate The date from which to compare. + * @param {Date} endDate The date to check. + * @returns {boolean} + */ + function isInPreviousMonth(startDate, endDate) { + var previousMonth = getDateInPreviousMonth(startDate); + return isSameMonthAndYear(endDate, previousMonth); + } + + /** + * Gets the midpoint between two dates. + * @param {Date} d1 + * @param {Date} d2 + * @returns {Date} + */ + function getDateMidpoint(d1, d2) { + return createDateAtMidnight((d1.getTime() + d2.getTime()) / 2); + } + + /** + * Gets the week of the month that a given date occurs in. + * @param {Date} date + * @returns {number} Index of the week of the month (zero-based). + */ + function getWeekOfMonth(date) { + var firstDayOfMonth = getFirstDateOfMonth(date); + return Math.floor((firstDayOfMonth.getDay() + date.getDate() - 1) / 7); + } + + /** + * Gets a new date incremented by the given number of days. Number of days can be negative. + * @param {Date} date + * @param {number} numberOfDays + * @returns {Date} + */ + function incrementDays(date, numberOfDays) { + return new Date(date.getFullYear(), date.getMonth(), date.getDate() + numberOfDays); + } + + /** + * Gets a new date incremented by the given number of months. Number of months can be negative. + * If the date of the given month does not match the target month, the date will be set to the + * last day of the month. + * @param {Date} date + * @param {number} numberOfMonths + * @returns {Date} + */ + function incrementMonths(date, numberOfMonths) { + // If the same date in the target month does not actually exist, the Date object will + // automatically advance *another* month by the number of missing days. + // For example, if you try to go from Jan. 30 to Feb. 30, you'll end up on March 2. + // So, we check if the month overflowed and go to the last day of the target month instead. + var dateInTargetMonth = new Date(date.getFullYear(), date.getMonth() + numberOfMonths, 1); + var numberOfDaysInMonth = getNumberOfDaysInMonth(dateInTargetMonth); + if (numberOfDaysInMonth < date.getDate()) { + dateInTargetMonth.setDate(numberOfDaysInMonth); + } else { + dateInTargetMonth.setDate(date.getDate()); + } + + return dateInTargetMonth; + } + + /** + * Get the integer distance between two months. This *only* considers the month and year + * portion of the Date instances. + * + * @param {Date} start + * @param {Date} end + * @returns {number} Number of months between `start` and `end`. If `end` is before `start` + * chronologically, this number will be negative. + */ + function getMonthDistance(start, end) { + return (12 * (end.getFullYear() - start.getFullYear())) + (end.getMonth() - start.getMonth()); + } + + /** + * Gets the last day of the month for the given date. + * @param {Date} date + * @returns {Date} + */ + function getLastDateOfMonth(date) { + return new Date(date.getFullYear(), date.getMonth(), getNumberOfDaysInMonth(date)); + } + + /** + * Checks whether a date is valid. + * @param {Date} date + * @return {boolean} Whether the date is a valid Date. + */ + function isValidDate(date) { + return date && date.getTime && !isNaN(date.getTime()); + } + + /** + * Sets a date's time to midnight. + * @param {Date} date + */ + function setDateTimeToMidnight(date) { + if (isValidDate(date)) { + date.setHours(0, 0, 0, 0); + } + } + + /** + * Creates a date with the time set to midnight. + * Drop-in replacement for two forms of the Date constructor via opt_value. + * @param {number|Date=} opt_value Leave undefined for a Date representing now. Or use a + * single value representing the number of seconds since the Unix Epoch or a Date object. + * @return {Date} New date with time set to midnight. + */ + function createDateAtMidnight(opt_value) { + var date; + if (angular.isDate(opt_value)) { + date = opt_value; + } else if (angular.isNumber(opt_value)) { + date = new Date(opt_value); + } else { + date = new Date(); + } + setDateTimeToMidnight(date); + return date; + } + + /** + * Checks if a date is within a min and max range, ignoring the time component. + * If minDate or maxDate are not dates, they are ignored. + * @param {Date} date + * @param {Date} minDate + * @param {Date} maxDate + */ + function isDateWithinRange(date, minDate, maxDate) { + var dateAtMidnight = createDateAtMidnight(date); + var minDateAtMidnight = isValidDate(minDate) ? createDateAtMidnight(minDate) : null; + var maxDateAtMidnight = isValidDate(maxDate) ? createDateAtMidnight(maxDate) : null; + return (!minDateAtMidnight || minDateAtMidnight <= dateAtMidnight) && + (!maxDateAtMidnight || maxDateAtMidnight >= dateAtMidnight); + } + + /** + * Gets a new date incremented by the given number of years. Number of years can be negative. + * See `incrementMonths` for notes on overflow for specific dates. + * @param {Date} date + * @param {number} numberOfYears + * @returns {Date} + */ + function incrementYears(date, numberOfYears) { + return incrementMonths(date, numberOfYears * 12); + } + + /** + * Get the integer distance between two years. This *only* considers the year portion of the + * Date instances. + * + * @param {Date} start + * @param {Date} end + * @returns {number} Number of months between `start` and `end`. If `end` is before `start` + * chronologically, this number will be negative. + */ + function getYearDistance(start, end) { + return end.getFullYear() - start.getFullYear(); + } + + /** + * Clamps a date between a minimum and a maximum date. + * @param {Date} date Date to be clamped + * @param {Date=} minDate Minimum date + * @param {Date=} maxDate Maximum date + * @return {Date} + */ + function clampDate(date, minDate, maxDate) { + var boundDate = date; + if (minDate && date < minDate) { + boundDate = new Date(minDate.getTime()); + } + if (maxDate && date > maxDate) { + boundDate = new Date(maxDate.getTime()); + } + return boundDate; + } + + /** + * Extracts and parses the timestamp from a DOM node. + * @param {HTMLElement} node Node from which the timestamp will be extracted. + * @return {number} Time since epoch. + */ + function getTimestampFromNode(node) { + if (node && node.hasAttribute('data-timestamp')) { + return Number(node.getAttribute('data-timestamp')); + } + } + + /** + * Checks if a month is within a min and max range, ignoring the date and time components. + * If minDate or maxDate are not dates, they are ignored. + * @param {Date} date + * @param {Date} minDate + * @param {Date} maxDate + */ + function isMonthWithinRange(date, minDate, maxDate) { + var month = date.getMonth(); + var year = date.getFullYear(); + + return (!minDate || minDate.getFullYear() < year || minDate.getMonth() <= month) && + (!maxDate || maxDate.getFullYear() > year || maxDate.getMonth() >= month); + } + + /** + * @param {Date} value date in local timezone + * @return {Date} date with local timezone offset removed + */ + function removeLocalTzAndReparseDate(value) { + var dateValue, formattedDate; + // Remove the local timezone offset before calling formatDate. + dateValue = new Date(value.getTime() + 60000 * value.getTimezoneOffset()); + formattedDate = $mdDateLocale.formatDate(dateValue); + // parseDate only works with a date formatted by formatDate when using Moment validation. + return $mdDateLocale.parseDate(formattedDate); + } + }]); +})(); + +})(); +(function(){ +"use strict"; + +(function() { + 'use strict'; + + // TODO(jelbourn): forward more attributes to the internal input (required, autofocus, etc.) + // TODO(jelbourn): something better for mobile (calendar panel takes up entire screen?) + // TODO(jelbourn): input behavior (masking? auto-complete?) + + DatePickerCtrl.$inject = ["$scope", "$element", "$attrs", "$window", "$mdConstant", "$mdTheming", "$mdUtil", "$mdDateLocale", "$$mdDateUtil", "$$rAF", "$filter", "$timeout"]; + datePickerDirective.$inject = ["$$mdSvgRegistry", "$mdUtil", "$mdAria", "inputDirective"]; + angular.module('material.components.datepicker') + .directive('mdDatepicker', datePickerDirective); + + /** + * @ngdoc directive + * @name mdDatepicker + * @module material.components.datepicker + * + * @param {Date} ng-model The component's model. Expects either a JavaScript Date object or a + * value that can be parsed into one (e.g. a ISO 8601 string). + * @param {Object=} ng-model-options Allows tuning of the way in which `ng-model` is being + * updated. Also allows for a timezone to be specified. + * + * Read more at the ngModelOptions docs. + * @param {expression=} ng-change Expression evaluated when the model value changes. + * @param {expression=} ng-focus Expression evaluated when the input is focused or the calendar + * is opened. + * @param {expression=} ng-blur Expression evaluated when focus is removed from the input or the + * calendar is closed. + * @param {boolean=} ng-disabled Whether the datepicker is disabled. + * @param {boolean=} ng-required Whether a value is required for the datepicker. + * @param {Date=} md-min-date Expression representing a min date (inclusive). + * @param {Date=} md-max-date Expression representing a max date (inclusive). + * @param {(function(Date): boolean)=} md-date-filter Function expecting a date and returning a + * boolean whether it can be selected in "day" mode or not. Returning false will also trigger a + * `filtered` model validation error. + * @param {(function(Date): boolean)=} md-month-filter Function expecting a date and returning a + * boolean whether it can be selected in "month" mode or not. Returning false will also trigger a + * `filtered` model validation error. + * @param {string=} md-placeholder The date input placeholder value. + * @param {string=} md-open-on-focus When present, the calendar will be opened when the input + * is focused. + * @param {Boolean=} md-is-open Expression that can be used to open the datepicker's calendar + * on-demand. + * @param {string=} md-current-view Default open view of the calendar pane. Can be either + * "month" or "year". + * @param {string=} md-mode Restricts the user to only selecting a value from a particular view. + * This option can be used if the user is only supposed to choose from a certain date type + * (e.g. only selecting the month). + * Can be either "month" or "day". **Note** that this will overwrite the `md-current-view` value. + * @param {string=} md-hide-icons Determines which datepicker icons should be hidden. Note that + * this may cause the datepicker to not align properly with other components. + * **Use at your own risk.** Possible values are: + * * `"all"` - Hides all icons. + * * `"calendar"` - Only hides the calendar icon. + * * `"triangle"` - Only hides the triangle icon. + * @param {Object=} md-date-locale Allows for the values from the `$mdDateLocaleProvider` to be + * overwritten on a per-element basis (e.g. `msgOpenCalendar` can be overwritten with + * `md-date-locale="{ msgOpenCalendar: 'Open a special calendar' }"`). + * @param {string=} input-aria-describedby A space-separated list of element IDs. This should + * contain the IDs of any elements that describe this datepicker. Screen readers will read the + * content of these elements at the end of announcing that the datepicker has been selected + * and describing its current state. The descriptive elements do not need to be visible on the + * page. + * @param {string=} input-aria-labelledby A space-separated list of element IDs. The ideal use + * case is that this would contain the ID of a `