var _typeof2=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj;}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj;};//'use strict'; // modules are defined as an array // [ module function, map of requireuires ] // // map of requireuires is short require name -> numeric require // // anything defined in a previous bundle is accessed via the // orig method which is the requireuire for previous bundles (function outer(modules,cache,entry){// Save the require from previous bundle to this closure if any var previousRequire=typeof require=='function'&&require;function newRequire(name,jumped){// UNSET DEFINE, BUT KEEP A REF var oldDefine=window.define;window.define=undefined;if(!cache[name]){if(!modules[name]){// if we cannot find the the module within our internal map or // cache jump to the current global require ie. the last bundle // that was added to the page. var currentRequire=typeof require=='function'&&require;if(!jumped&¤tRequire)return currentRequire(name,true);// If there are other bundles on this page the require from the // previous one is saved to 'previousRequire'. Repeat this as // many times as there are bundles until the module is found or // we exhaust the require chain. if(previousRequire)return previousRequire(name,true);var err=new Error('Cannot find module \''+name+'\'');err.code='MODULE_NOT_FOUND';throw err;}var m=cache[name]={exports:{}};modules[name][0].call(m.exports,function(x){var id=modules[name][1][x];return newRequire(id?id:x);},m,m.exports,outer,modules,cache,entry);}// RESET DEFINE window.define=oldDefine;return cache[name].exports;}for(var i=0;i-1;}//function setPreferences() { //PreferenceService.set( // 'hide_ignored_rules', _user.settings.hide_ignored_rules, false); //if (_user.settings.dashboard_mode && isBeta()) { //PreferenceService.set( // 'dashboard_mode', _user.settings.dashboard_mode, false); //if (_user.settings.dashboard_mode === 'osp') { //PreferenceService.set( // 'osp_deployment', _user.settings.osp_deployment, false); //} //} //} function _init(){$http.get(InsightsConfig.apiRoot+'me').success(function(user){angular.extend(_user,user);_user.permissions=indexBy(_user.permissions,'code');// used to keep up with is_internal in cases where is_internal is modified by // an internal user _user.is_super=_user.is_internal;//setPreferences(); if(_user.current_entitlements&&_user.current_entitlements.unlimitedRHEL){_user.current_entitlements.systemLimitReached=false;}if(window.localStorage.getItem('insights:user:isInternal')!==null&&_user.is_super===true){_user.is_internal=window.localStorage.getItem('insights:user:isInternal')==='true';}_user.loaded=true;PreferenceService.set('loaded',true,false);priv.tryFakeUser(_user);$rootScope.$broadcast('user:loaded');if(window.localStorage.getItem('tapi:demo')==='true'){_user.is_internal=false;_user.demo_mode=true;}_userDfd.resolve(_user);});}return{init:function init(){if(!angular.isDefined(_userDfd)){_userDfd=$q.defer();_init();}return _userDfd.promise;},current:_user,asyncCurrent:function asyncCurrent(cb){if(_user&&_user.loaded){return cb(_user);}else if(_userDfd&&_userDfd.promise){_userDfd.promise.then(cb);}},isOnOSPWhitelist:function isOnOSPWhitelist(){var onOSPWhitelist=false;if(!isBeta()){return false;}if(_user&&_user.current_entitlements){onOSPWhitelist=_user.current_entitlements.whitelist.osp;}return onOSPWhitelist;}};}apiModule.factory('User',User);},{"./":15,"lodash/keyBy":518}],27:[function(require,module,exports){'use strict';UserPermissions.$inject=['$resource','InsightsConfig'];var apiModule=require('./');/** * @ngInject */function UserPermissions($resource,InsightsConfig){var endpoint=InsightsConfig.apiRoot+'user_permissions/:userPermissionId';var params={userPermissionId:'@id'};var resource=$resource(endpoint,params);resource.init=function(){resource.initial=resource.query().$promise;};return resource;}apiModule.service('UserPermissions',UserPermissions);},{"./":15}],28:[function(require,module,exports){'use strict';UserSettings.$inject=['$http','InsightsConfig'];var apiModule=require('./');/** * @ngInject */function UserSettings($http,InsightsConfig){var root=InsightsConfig.apiRoot;return{update:function update(settings){return $http.post(root+'me/settings',settings);}};}apiModule.factory('UserSettings',UserSettings);},{"./":15}],29:[function(require,module,exports){'use strict';Webhooks.$inject=['$http','AccountService','InsightsConfig'];var apiModule=require('./');var URI=require('urijs');var pick=require('lodash/pick');var WEBHOOK_ATTRS=['active','url','firehose','event_types','certificate'];/** * @ngInject */function Webhooks($http,AccountService,InsightsConfig){var root=InsightsConfig.apiRoot;function url(){var url=URI(root);url.segment('webhooks');for(var _len2=arguments.length,segments=Array(_len2),_key2=0;_key2<_len2;_key2++){segments[_key2]=arguments[_key2];}segments.forEach(function(segment){return url.segment(String(segment));});url.addSearch(AccountService.queryParam());return url.toString();}return{get:function get(){return $http.get(url.apply(undefined,arguments));},create:function create(webhook){return $http.post(url(),pick(webhook,WEBHOOK_ATTRS));},ping:function ping(){return $http.post(url('ping'));},update:function update(webhook){return $http.put(url(webhook.id),pick(webhook,WEBHOOK_ATTRS));},delete:function _delete(webhook){return $http.delete(url(webhook.id));}};}apiModule.factory('Webhooks',Webhooks);},{"./":15,"lodash/pick":533,"urijs":826}],30:[function(require,module,exports){'use strict';/** * @ngInject */Routes.$inject=['$stateProvider'];function Routes($stateProvider){// Base States $stateProvider.state('app',{templateUrl:'js/states/base/base.html',abstract:true,controller:'AppCtrl'});$stateProvider.state('info',{templateUrl:'js/states/base/info.html',abstract:true});}module.exports=Routes;},{}],31:[function(require,module,exports){/*global window*/'use strict';accountSelectCtrl.$inject=['$scope','User','InsightsConfig','Utils','PermissionService'];var componentsModule=require('../');/** * @ngInject */function accountSelectCtrl($scope,User,InsightsConfig,Utils,PermissionService){var initialAcct=window.sessionStorage.getItem(InsightsConfig.acctKey);// The permission service mangles permissions if // you are not internal so I check them manually here. $scope.showAccountSelector=function(){var user=$scope.user;var PERMS=PermissionService.PERMS;if(!user||!user.permissions){return false;}if(user.permissions[PERMS.SU]){return true;}if(user.permissions[PERMS.ACCOUNT_SWITCHER]){return true;}return false;};$scope.Utils=Utils;$scope.accountChange=function(acct){if(acct){window.sessionStorage.setItem(InsightsConfig.acctKey,acct);window.location.reload();}};$scope.reset=function(){if(!$scope.user){return;}$scope.account_number=$scope.user.account_number;if($scope.user.is_internal){$scope.account_number=''+$scope.user.account_number;}$scope.accountChange($scope.account_number);};if(window.localStorage.getItem('tapi:demo')==='true'){console.log('Insights is running in demo mode');initialAcct='6';}if(initialAcct){$scope.account_number=initialAcct;}User.asyncCurrent(function(){$scope.user=User.current;if($scope.isInternal!==$scope.user.is_internal){$scope.isInternal=$scope.user.is_internal;}if(!initialAcct){$scope.account_number=$scope.user.account_number;}});$scope.changeInternal=function(){$scope.user.isInternal=$scope.isInternal;window.localStorage.setItem('insights:user:isInternal',JSON.stringify($scope.user.isInternal));window.location.reload();};}function accountSelect(){return{templateUrl:'js/components/accountSelect/accountSelect.html',restrict:'E',controller:accountSelectCtrl};}componentsModule.directive('accountSelect',accountSelect);},{"../":83}],32:[function(require,module,exports){'use strict';actionbarCtrl.$inject=['$scope','$rootScope','ActionbarService'];var componentsModule=require('../');/** * @ngInject */function actionbarCtrl($scope,$rootScope,ActionbarService){$scope.myActions=ActionbarService.actions;$rootScope.$on('$stateChangeStart',function(){ActionbarService.clear();});}function actionbar(){return{templateUrl:'js/components/actionbar/actionbar.html',restrict:'E',replace:true,controller:actionbarCtrl};}componentsModule.directive('actionbar',actionbar);},{"../":83}],33:[function(require,module,exports){/*global require*/'use strict';ActionsDirectiveCtrl.$inject=['$scope','RhaTelemetryActionsService'];var componentsModule=require('../');/** * @ngInject */function ActionsDirectiveCtrl($scope,RhaTelemetryActionsService){$scope.loading=RhaTelemetryActionsService.vars.loading;$scope.getTotal=RhaTelemetryActionsService.getTotal;$scope.isActions=RhaTelemetryActionsService.isActions;$scope.getCategory=RhaTelemetryActionsService.getCategory;}function rhaTelemetryActions(){return{templateUrl:'js/components/actions/actions.html',restrict:'E',replace:true,scope:{},controller:ActionsDirectiveCtrl};}componentsModule.directive('rhaTelemetryActions',rhaTelemetryActions);},{"../":83}],34:[function(require,module,exports){'use strict';actionsBackCtrl.$inject=['$scope','$state','$stateParams'];var componentsModule=require('../../');/** * @ngInject */function actionsBackCtrl($scope,$state,$stateParams){$scope.goBack=function(){if($state.current.name==='app.actions-rule'){$state.go('app.topic',{id:$stateParams.category});}else if($state.current.name==='app.topic'){$state.go('app.actions');}};$scope.$on('telemetry:esc',function($event){if($event.defaultPrevented){return;}$scope.goBack();});}function actionsBack(){return{restrict:'EC',scope:{},controller:actionsBackCtrl};}componentsModule.directive('actionsBack',actionsBack);},{"../../":83}],35:[function(require,module,exports){'use strict';actionsBreadcrumbsCtrl.$inject=['$scope','$state','ActionsBreadcrumbs'];var componentsModule=require('../../');/** * @ngInject */function actionsBreadcrumbsCtrl($scope,$state,ActionsBreadcrumbs){$scope.crumbs=ActionsBreadcrumbs.crumbs;$scope.getCrumbs=ActionsBreadcrumbs.get;$scope.crumbClick=function(crumb){$state.go(crumb.state,crumb.params);};}function actionsBreadcrumbs(){return{templateUrl:'js/components/actions/actionsBreadcrumbs/actionsBreadcrumbs.html',restrict:'E',replace:true,controller:actionsBreadcrumbsCtrl};}componentsModule.directive('actionsBreadcrumbs',actionsBreadcrumbs);},{"../../":83}],36:[function(require,module,exports){/*global require*/'use strict';ActionsRuleCtrl.$inject=['$filter','$location','$modal','$q','$rootScope','$scope','$state','$stateParams','$timeout','ActionsBreadcrumbs','FilterService','IncidentsService','InsightsConfig','InventoryService','ListTypeService','MaintenanceService','PermalinkService','PreferenceService','QuickFilters','Report','RhaTelemetryActionsService','System','SystemsService','Topic','User','Utils','ActionbarService','Export','Group'];var componentsModule=require('../../');/** * @ngInject */function ActionsRuleCtrl($filter,$location,$modal,$q,$rootScope,$scope,$state,$stateParams,$timeout,ActionsBreadcrumbs,FilterService,IncidentsService,InsightsConfig,InventoryService,ListTypeService,MaintenanceService,PermalinkService,PreferenceService,QuickFilters,Report,RhaTelemetryActionsService,System,SystemsService,Topic,User,Utils,ActionbarService,Export,Group){var REVERSE_TO_DIRECTION={false:'ASC',true:'DESC'};var category=$stateParams.category;$scope.allSelected=false;$scope.config=InsightsConfig;$scope.getListType=ListTypeService.getType;$scope.listTypes=ListTypeService.types();$scope.loading=true;$scope.noSystemsSelected=false;$scope.permalink=PermalinkService.make;$scope.predicate='toString';$scope.reallyAllSelected=false;$scope.reverse=false;$scope.ruleSystems=[];$scope.QuickFilters=QuickFilters;FilterService.parseBrowserQueryParams();FilterService.setShowFilters(false);FilterService.setSearchTerm('');function rejectIfNoSystemSelected(fn){return function(){var selectedSystems=$scope.checkboxes.getSelected($scope.ruleSystems);$scope.setNoSystemsSelected(selectedSystems.length===0);if(selectedSystems.length){return fn.apply(null,arguments);}};}$scope.setNoSystemsSelected=function(noSystemsSelected){$scope.noSystemsSelected=noSystemsSelected;if(noSystemsSelected){$timeout(function(){$scope.permalink('noSystemsSelected',true,50,500);},100);}};/* Temporarily disabled. Should be fixed as part of https://trello.com/c/CFOHQSd1/135 $state.transitionTo( 'app.actions-rule', FilterService.updateParams(params), { notify: false }); */$scope.$on('group:change',getData);$scope.$on('filterService:doFilter',function(){if($state.current.name==='app.actions-rule'){getData();}});ActionsBreadcrumbs.init($stateParams);RhaTelemetryActionsService.setCategory(category);RhaTelemetryActionsService.setRule($stateParams.rule);$scope.loading=true;$scope.loadingSystems=true;$scope.search=function(model){FilterService.setSearchTerm(model);FilterService.doFilter();};$scope.checkboxes=new Utils.Checkboxes('system_id');$scope.$watchCollection('checkboxes.items',updateCheckboxes);function updateCheckboxes(){$scope.checkboxes.update($scope.ruleSystems);if($scope.checkboxes.totalChecked>0){$scope.noSystemsSelected=false;}$scope.allSelected=$scope.checkboxes.totalChecked>0&&!$scope.checkboxes.indeterminate;if(!$scope.allSelected){$scope.reallyAllSelected=false;}}$scope.plans=MaintenanceService.plans;$scope.showSystem=function(system){InventoryService.showSystemModal(system);};/* * Sets pager.current_page to ruleSystems * * @param paginate gets all ruleSystems if set to false */function getData(paginate){$scope.loadingSystems=true;return RhaTelemetryActionsService.getActionsRulePage(paginate,$scope.pager).then(function(){$scope.ruleSystems=RhaTelemetryActionsService.getRuleSystems();$scope.totalRuleSystems=RhaTelemetryActionsService.getTotalRuleSystems();$scope.loadingSystems=false;});}$scope.paginate=function(){$scope.pager.update();$location.search('page',$scope.pager.currentPage);$location.search('pageSize',$scope.pager.perPage);getData(true).then(function(){if($scope.reallyAllSelected){$scope.checkboxes.checkboxChecked(true,$scope.ruleSystems);}else{$scope.checkboxes.reset();}});};/* * Get first page of systems * If url has a machine_id then open up the systemModal with given machine_id */function initialDisplay(){$scope.loading=true;$scope.loadingSystems=true;var incidentsPromise=IncidentsService.init();var topicBreadCrumbPromise=Topic.get($stateParams.category).success(function(topic){ActionsBreadcrumbs.setCrumb({label:topic.title,state:'app.topic',params:{id:topic.slug?topic.slug:topic.id}},1);});var productSpecific=System.getProductSpecificData();var systemsPromise=RhaTelemetryActionsService.initActionsRule($scope.pager).then(function(){var ruleDetails=RhaTelemetryActionsService.getRuleDetails();if(ruleDetails){$scope.ruleDetails=ruleDetails;ActionsBreadcrumbs.setCrumb({label:ruleDetails.description,params:{category:$stateParams.category,rule:ruleDetails.rule_id}},2);}$scope.ruleSystems=RhaTelemetryActionsService.getRuleSystems();$scope.totalRuleSystems=RhaTelemetryActionsService.getTotalRuleSystems();$scope.loadingSystems=false;});$q.all([SystemsService.getSystemTypesAsync(),incidentsPromise,topicBreadCrumbPromise,productSpecific,systemsPromise]).finally(function(){$scope.loading=false;var machine_id=$location.search().machine;if(!machine_id){return;}var system={system_id:machine_id};InventoryService.showSystemModal(system,true);});}function initCtrl(){User.asyncCurrent(function(){$scope.isInternal=User.current.is_internal;});$scope.checkboxes.reset();$scope.predicate='toString';$scope.reverse=false;FilterService.setQueryParam('sort_field',$scope.predicate);FilterService.setQueryParam('sort_direction',REVERSE_TO_DIRECTION[$scope.reverse]);//initialize pager and grab paging params from url $scope.pager=new Utils.Pager();$scope.pager.currentPage=$location.search().page?$location.search().page:$scope.pager.currentPage;$scope.pager.perPage=$location.search().pageSize?$location.search().pageSize:$scope.pager.perPage;initialDisplay();}$rootScope.$on('productFilter:change',function(){if($state.current.name==='app.actions-rule'){getData();}});$scope.$on('account:change',getData);if(InsightsConfig.authenticate&&!PreferenceService.get('loaded')){$rootScope.$on('user:loaded',initCtrl);}else{initCtrl();}$scope.addToPlan=rejectIfNoSystemSelected(function(existingPlan){var systems=systemsToAction();if(!systems.length){return;}var rule=RhaTelemetryActionsService.getRuleDetails();MaintenanceService.showMaintenanceModal(systems,rule,existingPlan);});$scope.sort=function(column){// just changing the sort direction if(column===$scope.predicate){$scope.reverse=!$scope.reverse;}else{$scope.reverse=false;// special case where we are sorting by timestamp but visually // showing timeago if(column==='last_check_in'){$scope.reverse=!$scope.reverse;}}$scope.predicate=column;// store sort fields in FilterService for systems query FilterService.setQueryParam('sort_field',$scope.predicate);FilterService.setQueryParam('sort_direction',REVERSE_TO_DIRECTION[$scope.reverse]);// reset dataset $scope.resetPaging();$scope.loadingSystems=true;return RhaTelemetryActionsService.sortActionsRulePage($scope.pager,$scope.predicate,$scope.reverse).then(function(){$scope.ruleSystems=RhaTelemetryActionsService.getRuleSystems();$scope.totalRuleSystems=RhaTelemetryActionsService.getTotalRuleSystems();$scope.loadingSystems=false;});};// reset checkboxes and pager when sorting $scope.resetPaging=function(){$scope.checkboxes.reset();$scope.pager.reset();};// really select all // keeps track of messages to display to the user $scope.selectAll=function(){// if we previously had all systems selected, we no longer do if($scope.reallyAllSelected&&$scope.pager.perPage<$scope.totalRuleSystems){$scope.reallyAllSelected=false;}};/* * Fetches all systems and assigns them to checkboxes */$scope.reallySelectAll=function(){$scope.allSelected=true;$scope.reallyAllSelected=true;getData(false);};/* * Resets checkboxes and selected variables */$scope.deselectAll=function(){$scope.reallyAllSelected=false;$scope.allSelected=false;$scope.checkboxes.reset();};function systemsToAction(){// assume true if the system is not shown in the view if($scope.reallyAllSelected){return RhaTelemetryActionsService.getAllSystems().filter(function(system){return!$scope.checkboxes.items.hasOwnProperty(system.system_id)||$scope.checkboxes.items.hasOwnProperty(system.system_id)&&$scope.checkboxes.items[system.system_id]===true;});}else{return $scope.checkboxes.getSelected($scope.ruleSystems);}}$scope.numberOfSelected=function(){if($scope.reallyAllSelected){return RhaTelemetryActionsService.getAllSystems().length;}else{return $scope.checkboxes.totalChecked;}};$scope.isIncident=IncidentsService.isIncident;if(InsightsConfig.allowExport){ActionbarService.addExportAction(function(){Export.getReports(null,$stateParams.rule,Group.current().id);});}}componentsModule.controller('ActionsRuleCtrl',ActionsRuleCtrl);},{"../../":83}],37:[function(require,module,exports){'use strict';var componentsModule=require('../../');function actionsRuleFilters(){return{templateUrl:'js/components/actions/actionsRuleFilters/actionsRuleFilters.html',restrict:'E',replace:true};}componentsModule.directive('actionsRuleFilters',actionsRuleFilters);},{"../../":83}],38:[function(require,module,exports){'use strict';SeveritySummaryCtrl.$inject=['$scope'];var componentsModule=require('../../');/** * @ngInject */function SeveritySummaryCtrl($scope){$scope.loaded=false;$scope.ruleCount={total:0,info:0,warn:0,error:0,critical:0};$scope.max=0;$scope.$watch('stats.rules',function(value){if(!value){return;}$scope.ruleCount=value;$scope.max=Math.max(value.info,value.warn,value.error,value.critical);$scope.loaded=true;},true);}function severitySummary(){return{templateUrl:'js/components/actions/severitySummary/severitySummary.html',restrict:'E',scope:{stats:'='},controller:SeveritySummaryCtrl};}componentsModule.directive('severitySummary',severitySummary);},{"../../":83}],39:[function(require,module,exports){/*global require*/'use strict';var componentsModule=require('../../');/** * @ngInject */function systemSummary(){return{templateUrl:'js/components/actions/systemSummary/systemSummary.html',restrict:'E',scope:{stats:'='},link:function link($scope){$scope.loaded=false;$scope.ratio=0;$scope.$watch('stats.systems',function(value){if(value&&value.total){$scope.ratio=100*value.affected/value.total;$scope.loaded=true;}},true);}};}componentsModule.directive('systemSummary',systemSummary);},{"../../":83}],40:[function(require,module,exports){'use strict';announcementsScrollCtrl.$inject=['$scope','$state','$stateParams','$timeout','sweetAlert','AnnouncementService','User','PermalinkService','PermissionService'];var componentsModule=require('../../');/** * @ngInject */function announcementsScrollCtrl($scope,$state,$stateParams,$timeout,sweetAlert,AnnouncementService,User,PermalinkService,PermissionService){$scope.params={showAll:true};AnnouncementService.init($scope.params);$scope.announcements=AnnouncementService.announcements;$scope.canCreate=false;$scope.permalink=PermalinkService.make;if($stateParams.announcementId){// wait for view to render before scrolling to announcement $timeout(function(){$scope.permalink($stateParams.announcementId.toString(),10,30);},100);}$scope.reload=function(){AnnouncementService.reload($scope.params);};$scope.ackAnnouncement=function(announcement){AnnouncementService.acknowledge(announcement);};$scope.editAnnouncement=function(announcement){$state.go('app.edit-announcement',{id:announcement.id});};$scope.deleteAnnouncement=function(announcement){sweetAlert().then(function(){AnnouncementService.deleteAnnouncement(announcement);});};User.asyncCurrent(function(user){$scope.canCreate=user&&user.is_internal&&PermissionService.has(user,PermissionService.PERMS.CREATE_ANNOUNCEMENT);});}function announcementsScroll(){return{templateUrl:'js/components/announcements/announcementsScroll/announcementsScroll.html',restrict:'E',replace:true,controller:announcementsScrollCtrl};}componentsModule.directive('announcementsScroll',announcementsScroll);},{"../../":83}],41:[function(require,module,exports){/*global require*/'use strict';ansibleIconCtrl.$inject=['$scope','gettextCatalog'];var componentsModule=require('../');/** * @ngInject */function ansibleIconCtrl($scope,gettextCatalog){var plannerLine=gettextCatalog.getString('Use the Planner to generate an Ansible Playbook.');var positive=gettextCatalog.getString('This rule has Ansible support.');var negative=gettextCatalog.getString('This rules does not have Ansible support');$scope.$watch('value',function(value){if(value){if($scope.white){$scope.icon='/assets/insights/l_ansible-white.svg';}else{$scope.icon='/assets/insights/l_ansible-blue.svg';}}else{$scope.icon='/assets/insights/l_ansible-unsupported.svg';}if($scope.showTooltip||$scope.showTooltip===undefined){if(value){$scope.tooltip=positive;if($scope.showPlannerLine||$scope.showPlannerLine===undefined){$scope.tooltip+=' '+plannerLine;}}else{$scope.tooltip=negative;}}});$scope.svgClass=$scope.white?'ansible-icon-white':'ansible-icon';}function ansibleIcon(){return{scope:{value:'=',showTooltip:'=',showPlannerLine:'=',white:'<'},templateUrl:'js/components/ansibleIcon/ansibleIcon.html',restrict:'E',replace:true,controller:ansibleIconCtrl};}componentsModule.directive('ansibleIcon',ansibleIcon);},{"../":83}],42:[function(require,module,exports){'use strict';betaSwitchButtonCtrl.$inject=['$scope','BetaRedirectService'];var componentsModule=require('../');/** * @ngInject */function betaSwitchButtonCtrl($scope,BetaRedirectService){$scope.switchVersion=function(goToBeta){if(goToBeta){BetaRedirectService.goToBeta();}else{BetaRedirectService.goToStable();}};$scope.switchStatus=function(status){window.localStorage.setItem('betaOptIn',status);};$scope.leaveBeta=function(){$scope.switchStatus(false);$scope.switchVersion(false);};$scope.isOnBeta=function(){return window.insightsGlobal.isBeta;};$scope.isOptedIn=function(){return JSON.parse(window.localStorage.getItem('betaOptIn'));};}function betaSwitchButton(){return{templateUrl:'js/components/betaSwitchButton/betaSwitchButton.html',restrict:'E',controller:betaSwitchButtonCtrl,replace:true};}componentsModule.directive('betaSwitchButton',betaSwitchButton);},{"../":83}],43:[function(require,module,exports){'use strict';bullhornCtrl.$inject=['$rootScope','$scope','$state','Announcement','AnnouncementService'];var componentsModule=require('../');/** * @ngInject */function bullhornCtrl($rootScope,$scope,$state,Announcement,AnnouncementService){$scope.loadingAnnouncements=true;$scope.selectAnnouncement=function(announcement){$state.go('app.announcements',{announcementId:announcement.id});};$scope.ack=function(announcement){$scope.loadingAnnouncements=true;AnnouncementService.acknowledge(announcement).then(function(){$scope.selectAnnouncement(announcement);$scope.loadingAnnouncements=false;});};function _getAnnouncements(){$scope.loadingAnnouncements=true;AnnouncementService.init().then(function(){$scope.items=AnnouncementService.announcements.slice(0,AnnouncementService.announcements.length<7?AnnouncementService.announcements.length:6);$scope.unackedCount=AnnouncementService.unseen.count;$scope.loadingAnnouncements=false;});}switch($scope.type){case'announcements':_getAnnouncements();break;}$rootScope.$on('announcement:changed',function(){_getAnnouncements();});}function bullhorn(){return{templateUrl:'js/components/bullhorn/bullhorn.html',restrict:'E',replace:true,scope:{type:'@type',icon:'@icon'},controller:bullhornCtrl};}componentsModule.directive('bullhorn',bullhorn);},{"../":83}],44:[function(require,module,exports){/*global require*/'use strict';CardFooterLink.$inject=['scope','element','attrs','card'];CardContentLink.$inject=['scope','element','attrs','card'];CardHeaderLink.$inject=['scope','element','attrs','card'];CardCtrl.$inject=['$scope','Utils','Events','$q'];var componentsModule=require('../');/** * @ngInject */function CardCtrl($scope,Utils,Events,$q){var priv={};var vars={};priv.toggleContent=function(){$scope.collapsed=!$scope.collapsed;};// collapsed by default unless overriden with init-collapsed $scope.collapsed=!angular.isDefined($scope.initCollapsed)||Boolean($scope.initCollapsed);// do this as soon as all the accessibles are defined (aka define vars, run dis) Utils.generateAccessors($scope,vars);$scope.toggleContent=function(){if($scope.expandable){$q.when($scope.onToggle({ctx:{collapsing:!$scope.collapsed}})).then(priv.toggleContent);}};$scope.$on(Events.cards.expandAll,function(){if($scope.collapsed){$scope.toggleContent();}});$scope.$on(Events.cards.collapseAll,function(){if(!$scope.collapsed){$scope.toggleContent();}});this.api=$scope;}/** * @ngInject */function CardHeaderLink(scope,element,attrs,card){scope.card=card.api;}/** * @ngInject */function CardContentLink(scope,element,attrs,card){scope.card=card.api;}/** * @ngInject */function CardFooterLink(scope,element,attrs,card){scope.card=card.api;}/** * @ngInject */function card(){return{templateUrl:'js/components/card/card.html',restrict:'E',replace:true,transclude:true,controller:CardCtrl,scope:{expandable:'@',initCollapsed:'=',// called when the card is being collapsed / expanded // the actual transition happens after this callback returns // if the callback returns a promise then the transition happens once the // promise completes (if the promise is rejected then the transition is // ignored) onToggle:'&'},/* * Workaround for https://github.com/angular/angular.js/issues/5695 * If both directive declaration and template's root element declare ng-class * then angular merges its contents which results in invalid JSON * Instead of declaring ng-class we handle 'expanded' and * 'content-block-expandable' classes programatically here. */link:function link(scope,element){function bindCssClass($scope,element,expression,cls){$scope.$watch(expression,function(val){if(val){element.addClass(cls);}else{element.removeClass(cls);}});}bindCssClass(scope,element,'!collapsed','expanded');bindCssClass(scope,element,'expandable','content-block-expandable');}};}/** * @ngInject */function cardHeader(){return{templateUrl:'js/components/card/cardHeader.html',restrict:'E',replace:true,transclude:true,link:CardHeaderLink,require:'^card'};}/** * @ngInject */function cardContent(){return{templateUrl:'js/components/card/cardContent.html',restrict:'E',replace:true,transclude:true,link:CardContentLink,require:'^card'};}/** * @ngInject */function cardFooter(){return{templateUrl:'js/components/card/cardFooter.html',restrict:'E',replace:true,transclude:true,link:CardFooterLink,require:'^card'};}componentsModule.directive('card',card);componentsModule.directive('cardHeader',cardHeader);componentsModule.directive('cardContent',cardContent);componentsModule.directive('cardFooter',cardFooter);},{"../":83}],45:[function(require,module,exports){'use strict';systemCardCtrl.$inject=['$scope','$modal','User','Utils','InventoryService','System','Products','SystemsService','$q','AnsibleService','MaintenanceService','InsightsConfig'];var componentsModule=require('../../');var find=require('lodash/find');var findIndex=require('lodash/findIndex');var map=require('lodash/map');var groupBy=require('lodash/groupBy');function systemCardCtrl($scope,$modal,User,Utils,InventoryService,System,Products,SystemsService,$q,AnsibleService,MaintenanceService,InsightsConfig){$scope.getSystemName=Utils.getSystemDisplayName;$scope.hostnameTitle=null;$scope.selected=false;$scope.plans=MaintenanceService.plans.all;$scope.config=InsightsConfig;$scope.systemHostnameHover=function($event){var element=$event.relatedTarget;if(!$scope.hostnameTitle&&element.offsetWidth0){return true;}return false;};$scope.showActions=function(){InventoryService.showSystemModal($scope.system);};$scope.unregister=function(){SystemsService.unregisterSelectedSystems([$scope.system]);};User.asyncCurrent(function(){$scope.isInternal=User.current.is_internal;});}/** * @ngInject */function systemCard(){return{templateUrl:'js/components/card/systemCard/systemCard.html',restrict:'E',replace:true,controller:systemCardCtrl,scope:{rule:'=',system:'=',checkboxes:'=',selectableVal:'=selectable',checkboxTooltipExp:'&checkboxTooltip'},link:function link(scope,element,attrs){var defaultTooltip='Select System';if(attrs.checkboxTooltip){scope.checkboxTooltip=scope.checkboxTooltipExp({defaultTooltip:defaultTooltip});}else{scope.checkboxTooltip=defaultTooltip;}if(angular.isDefined(scope.selectableVal)){scope.selectable=scope.selectableVal;}else{scope.selectable=true;}}};}componentsModule.animation('.slide',['$timeout',function($timeout){var NG_HIDE_CLASS='ng-hide';var NO_BORDER_CLASS='no-border-bottom';return{beforeAddClass:function beforeAddClass(element,className,done){if(className===NG_HIDE_CLASS){jQuery(element).slideUp(done);}else if(className===NO_BORDER_CLASS){jQuery(element).addClass(NO_BORDER_CLASS);}},removeClass:function removeClass(element,className,done){function reallyDone(){jQuery(element).css('overflow','visible');done();}if(className===NG_HIDE_CLASS){jQuery(element).hide().slideDown(reallyDone);}else if(className===NO_BORDER_CLASS){jQuery(element).addClass(NO_BORDER_CLASS);$timeout(function(){jQuery(element).removeClass(NO_BORDER_CLASS);},399);}}};}]);componentsModule.directive('systemCard',systemCard);},{"../../":83,"lodash/find":491,"lodash/findIndex":492,"lodash/groupBy":497,"lodash/map":522}],46:[function(require,module,exports){'use strict';var componentsModule=require('../');function categoryIcon(){return{scope:{category:'='},templateUrl:'js/components/categoryIcon/categoryIcon.html',restrict:'E',replace:true};}componentsModule.directive('categoryIcon',categoryIcon);},{"../":83}],47:[function(require,module,exports){/*global require*/'use strict';CollapsibleFilterCtrl.$inject=['$element','$location','$q','$rootScope','$scope','Events'];var componentsModule=require('../');var priv={};/** * @ngInject */function CollapsibleFilterCtrl($element,$location,$q,$rootScope,$scope,Events){/** * Removes tag from footer and broadcasts the event for reseting the filter */$scope.removeFilter=function(tagId){delete $scope.tags[tagId];$rootScope.$broadcast(Events.filters.removeTag,tagId);};$scope.hasTags=function(){return Object.keys($scope.tags).length>0;};/** * listens for new tags and adds them to the footer as they come in */$scope.$on(Events.filters.tag,function(event,tag,filter){$scope.tags[filter]=tag;if(tag===null){delete $scope.tags[filter];}});/** * exposes an 'open' variable which tracks the state of the collapsible filter so that * classes can be applied to the frontend when the filters are open/closed */priv.toggleTray=function(){$scope.open=!$scope.open;$scope.$digest();};$element.bind('show.bs.collapse',priv.toggleTray);$element.bind('hidden.bs.collapse',priv.toggleTray);function init(){$scope.tags={};}init();}/** * @ngInject */function collapsibleFilter(){return{templateUrl:'js/components/collapsibleFilter/collapsibleFilter.html',restrict:'E',replace:true,transclude:true,controller:CollapsibleFilterCtrl};}/** * @ngInject */function collapsibleFilterContent(){return{templateUrl:'js/components/collapsibleFilter/collapsibleFilterContent.html',restrict:'E',replace:true,transclude:true,require:'^collapsibleFilter'};}componentsModule.directive('collapsibleFilter',collapsibleFilter);componentsModule.directive('collapsibleFilterContent',collapsibleFilterContent);},{"../":83}],48:[function(require,module,exports){/*global window, require*/'use strict';ConfigDevCtrl.$inject=['$scope','User','InsightsConfig'];var componentsModule=require('../../');/** * @ngInject */function ConfigDevCtrl($scope,User,InsightsConfig){$scope.user=User.current;$scope.tmp={apiPrefix:InsightsConfig.apiPrefix,apiVersion:InsightsConfig.apiVersion,entitlements:{}};$scope.presets={apiPrefix:{default:InsightsConfig.defaultApiPrefix,local:'local/'},apiVersions:['v1','v2','v3'],entitlements:{paid:{unlimitedRHEL:false,whitelist:{rhel:false,osp:false},totalRHEL:32,skus:[{skuName:'MCT3292',quantity:2,system_count:1,unlimited:false}],activeSystemCount:1,systemLimitReached:false},paidWithFewSystems:{unlimitedRHEL:false,whitelist:{rhel:false,osp:false},totalRHEL:9,skus:[{skuName:'MCT3292',quantity:2,system_count:1,unlimited:false}],activeSystemCount:1,systemLimitReached:false},unpaid:{activeSystemCount:1,skus:[{skuName:'SER0482',quantity:2,system_count:0,unlimited:false}],systemLimitReached:false,totalRHEL:8,unlimitedRHEL:false,whitelist:{osp:false,rhel:false}},unpaidSystemLimitReached:{activeSystemCount:10,skus:[{skuName:'SER0482',quantity:2,system_count:0,unlimited:false}],systemLimitReached:true,totalRHEL:10,unlimitedRHEL:false,whitelist:{osp:false,rhel:false}},whitelisted:{whitelist:{rhel:true,osp:true}}}};$scope.getPretty=function(obj){return JSON.stringify(obj,null,2);};$scope.setEntitlements=function(){$scope.user.current_entitlements=$scope.presets.entitlements[$scope.tmp.tmpEnt];window.localStorage.setItem('insights:fake:entitlements',JSON.stringify($scope.user.current_entitlements));};$scope.setEntitlementsByKey=function(key){var obj;try{obj=JSON.parse($scope.tmp.entitlements[key]);$scope.user.current_entitlements[key]=obj;}catch(ignore){}};$scope.save=function(){window.localStorage.setItem('insights:apiVersion',$scope.tmp.apiVersion);window.localStorage.setItem('insights:apiPrefix',$scope.tmp.apiPrefix);window.location.reload();};$scope.injectNewKey=function(){$scope.user.current_entitlements[$scope.newKey]={};};function demoModeIsEnabled(){if(window.localStorage.getItem('tapi:demo')==='true'){return true;}else{return false;}}$scope.getDemoModeButtonText=function(){if(demoModeIsEnabled()){return'Disable Demo Mode';}else{return'Enable Demo Mode';}};$scope.demoModeButtonOnClick=function(){if(demoModeIsEnabled()){window.localStorage.setItem('tapi:demo',false);}else{window.localStorage.setItem('tapi:demo',true);}window.location.reload();//$route.reload(); };}function configDev(){return{templateUrl:'js/components/config/dev/dev.html',restrict:'E',scope:false,controller:ConfigDevCtrl};}componentsModule.directive('configDev',configDev);},{"../../":83}],49:[function(require,module,exports){'use strict';configGroupsCtrl.$inject=['$scope','Group'];var componentsModule=require('../../');/** * @ngInject */function configGroupsCtrl($scope,Group){Group.init();$scope.isCreating=false;$scope.groups=Group.groups;}function configGroups(){return{templateUrl:'js/components/config/groups/groups.html',restrict:'EA',scope:false,controller:configGroupsCtrl};}componentsModule.directive('configGroups',configGroups);},{"../../":83}],50:[function(require,module,exports){'use strict';configHiddenCtrl.$inject=['$scope','Ack'];var componentsModule=require('../../');/** * @ngInject */function configHiddenCtrl($scope,Ack){Ack.init();$scope.acks=Ack.acks;$scope.delete=Ack.deleteAck;}function configHidden(){return{templateUrl:'js/components/config/hidden/hidden.html',restrict:'EA',scope:{},controller:configHiddenCtrl};}componentsModule.directive('configHidden',configHidden);},{"../../":83}],51:[function(require,module,exports){'use strict';configMessagingCtrl.$inject=['$scope','Messaging'];var componentsModule=require('../../');/** * @ngInject */function configMessagingCtrl($scope,Messaging){Messaging.getCampaigns().success(function(campaigns){$scope.campaigns=campaigns;});$scope.save=function($form){Messaging.saveCampaigns($scope.campaigns).then(function(){$form.$setPristine();});};}function configMessaging(){return{templateUrl:'js/components/config/messaging/messaging.html',restrict:'EA',scope:{},controller:configMessagingCtrl};}componentsModule.directive('configMessaging',configMessaging);},{"../../":83}],52:[function(require,module,exports){'use strict';configPermissionsCtrl.$inject=['$scope','$q','sweetAlert','UserPermissions','Permission','User'];var componentsModule=require('../../');var groupBy=require('lodash/groupBy');var map=require('lodash/map');var uniq=require('lodash/uniq');/** * @ngInject */function configPermissionsCtrl($scope,$q,sweetAlert,UserPermissions,Permission,User){var priv={};$scope.newPermission={};priv.removeAll=function(data){var dfrds=[];data.forEach(function(p){dfrds.push(UserPermissions.delete({userPermissionId:p.id}).$promise);});$q.all(dfrds).finally(function(){$scope.init();});};$scope.isCreating=false;$scope.addNewPermission=function(){var newUserPermission;$scope.newPermission.permission_id=$scope.tmpPermission.id;newUserPermission=new UserPermissions($scope.newPermission);newUserPermission.$save().finally(function(){$scope.init();});};$scope.removeSingle=function(p){$scope.removeAll([{id:p.id}]);};$scope.removeAll=function(data){var conf={confirmButtonText:'Yes, delete it!'};if(data[0].sso_username===User.current.sso_username){conf.text='Delete yourself?? Remember... no dumb here!';}sweetAlert(conf).then(function(){priv.removeAll(data);});};$scope.init=function(){Permission.query().$promise.then(function(data){$scope.permissions=data;});UserPermissions.query().$promise.then(function(data){$scope.users=uniq(map(data,'sso_username'));$scope.userMap=groupBy(data,function(element){return element.sso_username;});});};$scope.init();}function configPermissions(){return{templateUrl:'js/components/config/permissions/permissions.html',restrict:'EA',scope:false,controller:configPermissionsCtrl};}componentsModule.directive('configPermissions',configPermissions);},{"../../":83,"lodash/groupBy":497,"lodash/map":522,"lodash/uniq":551}],53:[function(require,module,exports){'use strict';configSettingsCtrl.$inject=['$scope','AccountSettings'];var componentsModule=require('../../');/** * @ngInject */function configSettingsCtrl($scope,AccountSettings){$scope.loading=true;AccountSettings.get().success(function(settings){$scope.settings=settings;}).finally(function(){$scope.loading=false;});$scope.save=function(){$scope.loading=true;AccountSettings.update($scope.settings).catch(function(){//TODO: handle the error window.alert('Only an org admin is allowed to change this setting');}).finally(function(){$scope.loading=false;});};}function configSettings(){return{templateUrl:'js/components/config/settings/settings.html',restrict:'EA',scope:{},controller:configSettingsCtrl};}componentsModule.directive('configSettings',configSettings);},{"../../":83}],54:[function(require,module,exports){'use strict';configWebhooksCtrl.$inject=['$q','$scope','gettextCatalog','sweetAlert','Utils','Webhooks'];var componentsModule=require('../../');var some=require('lodash/some');/** * @ngInject */function configWebhooksCtrl($q,$scope,gettextCatalog,sweetAlert,Utils,Webhooks){$scope.loader=new Utils.Loader(false);$scope.init=$scope.loader.bind(function(){return Webhooks.get().then(function(res){$scope.webhooks=res.data;});});$scope.activeChanged=Webhooks.update;$scope.delete=function(webhook){sweetAlert({html:gettextCatalog.getString('Webhook configuration will be lost')}).then(function(){return Webhooks.delete(webhook);}).then($scope.init);};$scope.ping=Webhooks.ping;$scope.isPingable=function(){return some($scope.webhooks,'active');};$scope.init();}function configWebhooks(){return{templateUrl:'js/components/config/webhooks/webhooks.html',restrict:'E',scope:{},controller:configWebhooksCtrl};}componentsModule.directive('configWebhooks',configWebhooks);},{"../../":83,"lodash/some":539}],55:[function(require,module,exports){'use strict';demoAccountSwitcherCtrl.$inject=['$scope','PermissionService'];var componentsModule=require('../');/** * @ngInject */function demoAccountSwitcherCtrl($scope,PermissionService){$scope.demoAccountEnabled=$scope.account_number==='6';$scope.toggleDemoAccount=function(){if(!$scope.demoAccountEnabled){$scope.accountChange('6');}else{$scope.accountChange($scope.user.account_number);}};$scope.showDemoSwitcher=function(){if($scope.user&&($scope.user.is_internal||$scope.demoAccountEnabled)&&!PermissionService.has($scope.user,PermissionService.PERMS.SU)){return true;}return false;};}function demoAccountSwitcher(){return{templateUrl:'js/components/demoAccountSwitcher/demoAccountSwitcher.html',controller:demoAccountSwitcherCtrl};}componentsModule.directive('demoAccountSwitcher',demoAccountSwitcher);},{"../":83}],56:[function(require,module,exports){/*global require*/'use strict';digestGraphController.$inject=['$scope'];var componentsModule=require('../');var Plotly=require('plotly.js/lib/index-basic');var d3=Plotly.d3;var priv={};priv.initTraces=function initTraces($scope){var legendElements=window.jQuery('#metrics g.traces');$scope.traces=[];$scope.digest.data.forEach(function(data,index){var traceObject={index:index,legendElement:window.jQuery(legendElements[index]),name:data.name,enabled:true};$scope.traces.push(traceObject);traceObject.legendElement.click(function(){traceObject.enabled=!traceObject.enabled;$scope.$apply();});});};/** * @ngInject */function digestGraphController($scope){var jQuery=window.jQuery;var graphNode=d3.select('[digest-key='+$scope.digestKey+'] .digest-graph').append('div').style({width:'100%',height:'400px'}).node();Plotly.newPlot(graphNode,$scope.digest.data,$scope.digest.layout,$scope.digest.options);window.addEventListener('resize',function(){Plotly.Plots.resize(graphNode);});jQuery('a[data-toggle="tab"]').on('shown.bs.tab',function(){Plotly.Plots.resize(graphNode);});$scope.toggleTrace=function toggleTrace(trace){trace.enable=!trace.enabled;var val=trace.enabled?true:'legendonly';Plotly.restyle(graphNode,'visible',[val],[trace.index]);};// THIS MUST BE AFTER THE GRAPH IS BUILT priv.initTraces($scope,graphNode);}function digestGraph(){return{templateUrl:'js/components/digest/digestGraph.html',restrict:'E',controller:digestGraphController,replace:true,scope:{dropDown:'@',digest:'=',digestKey:'@',width:'@',chartType:'@'}};}componentsModule.directive('digestGraph',digestGraph);},{"../":83,"plotly.js/lib/index-basic":567}],57:[function(require,module,exports){'use strict';escKey.$inject=['$rootScope','$document'];var componentsModule=require('../');/** * @ngInject */function escKey($rootScope,$document){return{restrict:'AC',link:function link(){$document.on('keydown',function(evt){if(evt.keyCode===27){$rootScope.$broadcast('telemetry:esc');}});}};}componentsModule.directive('escKey',escKey);},{"../":83}],58:[function(require,module,exports){'use strict';expandAllButtonCtrl.$inject=['$scope','Events'];var componentsModule=require('../');function expandAllButtonCtrl($scope,Events){// indicates the action on next click // true for expand, false for collapse $scope.expand=true;$scope.onClick=function(){var event=$scope.expand?Events.cards.expandAll:Events.cards.collapseAll;$scope.$broadcast(event);$scope.expand=!$scope.expand;};}/** * When clicked sends Events.cards.expandAll or Events.cards.collapseAll event to children * scopes. The button can be in two states: 'expand all', 'collapse all'. * * @ngInject */function expandAllButton(){return{templateUrl:'js/components/expandAllButton/expandAllButton.html',restrict:'E',replace:true,controller:expandAllButtonCtrl};}componentsModule.directive('expandAllButton',expandAllButton);},{"../":83}],59:[function(require,module,exports){'use strict';feedbackButtonCtrl.$inject=['$scope','$state','$location','$modal','User'];var componentsModule=require('../');/** * @ngInject */function feedbackButtonCtrl($scope,$state,$location,$modal,User){$scope.openFeedbackPage=function(){User.asyncCurrent(function(user){$modal.open({templateUrl:'js/components/feedback/feedbackModal.html',windowClass:'system-modal ng-animate-enabled',backdropClass:'ng-animate-enabled',controller:'feedbackModalCtrl',resolve:{feedbackInfo:function feedbackInfo(){return{fromPage:$location.url(),fromState:$state.current.name,user:user};}}});});};}function feedbackButton(){return{templateUrl:'js/components/feedback/feedbackButton.html',restrict:'E',controller:feedbackButtonCtrl,replace:true};}componentsModule.directive('feedbackButton',feedbackButton);},{"../":83}],60:[function(require,module,exports){'use strict';feedbackModalCtrl.$inject=['$scope','$modalInstance','feedbackInfo','Feedback'];var componentsModule=require('../');/** * @ngInject */function feedbackModalCtrl($scope,$modalInstance,feedbackInfo,Feedback){$scope.feedbackInfo=feedbackInfo;$scope.isError=false;$scope.setEmailOverride=function(){$scope.emailOverride=true;};$scope.isCommentPopulated=function(){return $scope.comment!==''&&$scope.comment!==undefined;};$scope.submitForm=function(){var formData={comment:$scope.comment,email:$scope.feedbackInfo.user.email,from_page:feedbackInfo.fromPage};Feedback.sendFeedback(formData).success(function(){$scope.close();}).catch(function(){$scope.isError=true;});return;};$scope.close=function(){$modalInstance.dismiss('close');};}componentsModule.controller('feedbackModalCtrl',feedbackModalCtrl);},{"../":83}],61:[function(require,module,exports){'use strict';actionsSelectCtrl.$inject=['$rootScope','$scope','gettextCatalog','Events','MultiButtonService','FilterService'];var componentsModule=require('../../');var find=require('lodash/find');/** * @ngInject */function actionsSelectCtrl($rootScope,$scope,gettextCatalog,Events,MultiButtonService,FilterService){$scope.options=[{id:'all',label:gettextCatalog.getString('All'),withActions:true,withoutActions:true,tag:null},{id:'affected',label:gettextCatalog.getString('Affected'),withActions:true,withoutActions:false,tag:gettextCatalog.getString('Health: Affected')},{id:'healthy',label:gettextCatalog.getString('Healthy'),withActions:false,withoutActions:true,tag:gettextCatalog.getString('Health: Healthy')}];$scope.select=function(option){$scope.selected=option;MultiButtonService.setState('inventoryWithActions',option.withActions);MultiButtonService.setState('inventoryWithoutActions',option.withoutActions);FilterService.doFilter();$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.actionsSelect);};function getTag(){return $scope.selected.tag;}function read(){$scope.selected=find($scope.options,{withActions:MultiButtonService.getState('inventoryWithActions'),withoutActions:MultiButtonService.getState('inventoryWithoutActions')});if(!$scope.selected){throw new Error('Invalid system health selector state: '+MultiButtonService.getState('inventoryWithActions')+':'+MultiButtonService.getState('inventoryWithoutActions'));}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.actionsSelect);}read();$scope.$on(Events.filters.reset,function(){$scope.select(find($scope.options,function(option){return option.id==='all';}));});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.actionsSelect){$scope.select(find($scope.options,function(option){return option.id==='all';}));}});}function actionsSelect(){return{templateUrl:'js/components/filterComponents/actionsSelect/actionsSelect.html',restrict:'E',controller:actionsSelectCtrl,scope:{}};}componentsModule.directive('actionsSelect',actionsSelect);},{"../../":83,"lodash/find":491}],62:[function(require,module,exports){'use strict';ansibleSupportTriStateCtrl.$inject=['$location','$rootScope','$scope','Events','FilterService','AnsibleSupportFilters'];var componentsModule=require('../../');/** * @ngInject */function ansibleSupportTriStateCtrl($location,$rootScope,$scope,Events,FilterService,AnsibleSupportFilters){$scope.AnsibleSupportFilters=AnsibleSupportFilters;$scope.filterAnsibleSupport=function(key){$scope.showAnsibleSupport=key;FilterService.setAnsibleSupport(key);FilterService.doFilter();// If 'All' is selected there is no reason to store the filter if($scope.showAnsibleSupport==='all'){$location.search(Events.filters.ansibleSupport,null);}else{$location.search(Events.filters.ansibleSupport,$scope.showAnsibleSupport);}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.ansibleSupport);$rootScope.$broadcast(Events.filters.ansibleSupport,$scope.showAnsibleSupport);};$scope.$on(Events.filters.reset,function(){resetFilter();});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.ansibleSupport){resetFilter();$rootScope.$broadcast(filter,'all');}});function resetFilter(){$scope.showAnsibleSupport='all';$scope.filterAnsibleSupport($scope.showAnsibleSupport);$location.search(Events.filters.ansibleSupport,null);}function getTag(){var tag=AnsibleSupportFilters[$scope.showAnsibleSupport].tag;return tag;}function init(){$scope.showAnsibleSupport=$location.search()[Events.filters.ansibleSupport]?$location.search()[Events.filters.ansibleSupport]:FilterService.getAnsibleSupport();$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.ansibleSupport);}init();}function ansibleSupportTriState(){return{templateUrl:'js/components/filterComponents/ansibleSupportTriState'+'/ansibleSupportTriState.html',restrict:'E',controller:ansibleSupportTriStateCtrl};}componentsModule.directive('ansibleSupportTriState',ansibleSupportTriState);},{"../../":83}],63:[function(require,module,exports){'use strict';categorySelectCtrl.$inject=['$location','$rootScope','$scope','gettextCatalog','Events','FilterService'];var componentsModule=require('../../');/** * @ngInject */function categorySelectCtrl($location,$rootScope,$scope,gettextCatalog,Events,FilterService){$scope.options={all:{label:gettextCatalog.getString('All'),tag:null},availability:{label:gettextCatalog.getString('Availability'),tag:gettextCatalog.getString('Category: Availability')},stability:{label:gettextCatalog.getString('Stability'),tag:gettextCatalog.getString('Category: Stability')},performance:{label:gettextCatalog.getString('Performance'),tag:gettextCatalog.getString('Category: Performance')},security:{label:gettextCatalog.getString('Security'),tag:gettextCatalog.getString('Category: Security')}};$scope.select=function(option){$scope.selected=$scope.options[option];FilterService.setCategory(option);FilterService.doFilter();$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.categorySelect);};function getTag(){return $scope.selected.tag;}function read(){var category=$location.search().category?$location.search().category:FilterService.getCategory();$scope.selected=$scope.options[category];$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.categorySelect);}read();$scope.$on(Events.filters.reset,function(){$scope.select('all');});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.categorySelect){$scope.select('all');}});}function categorySelect(){return{templateUrl:'js/components/filterComponents/categorySelect/categorySelect.html',restrict:'E',controller:categorySelectCtrl,scope:{}};}componentsModule.directive('categorySelect',categorySelect);},{"../../":83}],64:[function(require,module,exports){'use strict';checkInSelectCtrl.$inject=['$rootScope','$scope','gettextCatalog','Events','FilterService'];var componentsModule=require('../../');var find=require('lodash/find');/** * @ngInject */function checkInSelectCtrl($rootScope,$scope,gettextCatalog,Events,FilterService){$scope.options=[{id:'all',label:gettextCatalog.getString('All'),online:true,offline:true,tag:null},{id:'online',label:gettextCatalog.getString('Checking In'),online:true,offline:false,tag:gettextCatalog.getString('Status: Checking In')},{id:'offline',label:gettextCatalog.getString('Stale'),online:false,offline:true,tag:gettextCatalog.getString('Status: Stale')}];$scope.select=function(option){$scope.selected=option;FilterService.setOnline(option.online);FilterService.setOffline(option.offline);FilterService.doFilter();$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.checkInSelect);};function getTag(){return $scope.selected.tag;}function read(){$scope.selected=find($scope.options,{online:FilterService.getOnline(),offline:FilterService.getOffline()});if(!$scope.selected){throw new Error('Invalid online selector state: '+FilterService.getOnline()+':'+FilterService.getOffline());}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.checkInSelect);}read();$scope.$on(Events.filters.reset,function(){$scope.select(find($scope.options,function(option){return option.id==='all';}));});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.checkInSelect){$scope.select(find($scope.options,function(option){return option.id==='all';}));}});}function checkInSelect(){return{templateUrl:'js/components/filterComponents/checkInSelect/checkInSelect.html',restrict:'E',controller:checkInSelectCtrl,scope:{}};}componentsModule.directive('checkInSelect',checkInSelect);},{"../../":83,"lodash/find":491}],65:[function(require,module,exports){'use strict';filterButtonCtrl.$inject=['$scope','FilterService'];var componentsModule=require('../../');/** * @ngInject */function filterButtonCtrl($scope,FilterService){$scope.getShowFilters=FilterService.getShowFilters;$scope.toggleShowFilters=FilterService.toggleShowFilters;}function filterButton(){return{templateUrl:'js/components/filterComponents/filterButton/filterButton.html',restrict:'E',replace:true,controller:filterButtonCtrl};}componentsModule.directive('filterButton',filterButton);},{"../../":83}],66:[function(require,module,exports){'use strict';var componentsModule=require('../../');/** * @ngInject */function hideIgnoredToggleCtrl(){}function hideIgnoredToggle(){return{templateUrl:'js/components/filterComponents/hideIgnoredToggle/hideIgnoredToggle.html',restrict:'E',controller:hideIgnoredToggleCtrl};}componentsModule.directive('hideIgnoredToggle',hideIgnoredToggle);},{"../../":83}],67:[function(require,module,exports){'use strict';impactSelectCtrl.$inject=['$location','$rootScope','$scope','ImpactFilters','Events','FilterService'];var componentsModule=require('../../');/** * @ngInject */function impactSelectCtrl($location,$rootScope,$scope,ImpactFilters,Events,FilterService){$scope.options=ImpactFilters;$scope.select=function(option){$scope.selected=$scope.options[option];FilterService.setImpact(option);FilterService.doFilter();// no need to store filter when not filtering rules. if(option===0){$location.search(Events.filters.impact,null);}else{$location.search(Events.filters.impact,option);}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.impact);$rootScope.$broadcast(Events.filters.impact);};function getTag(){return $scope.selected.tag;}function init(){var option=$location.search()[Events.filters.impact]?$location.search()[Events.filters.impact]:FilterService.getImpact();$scope.selected=$scope.options[option];$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.impact);}init();$scope.$on(Events.filters.reset,function(){$scope.select(0);});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.impact){$scope.select(0);}});}function impactSelect(){return{templateUrl:'js/components/filterComponents/impactSelect/impactSelect.html',restrict:'E',controller:impactSelectCtrl,scope:{}};}componentsModule.directive('impactSelect',impactSelect);},{"../../":83}],68:[function(require,module,exports){'use strict';incidentsTriStateCtrl.$inject=['$location','$rootScope','$scope','Events','IncidentFilters','FilterService'];var componentsModule=require('../../');/** * @ngInject */function incidentsTriStateCtrl($location,$rootScope,$scope,Events,IncidentFilters,FilterService){$scope.incidentFilters=IncidentFilters;$scope.filterIncidents=function(key){$scope.showIncidents=key;FilterService.setIncidents(key);FilterService.doFilter();// If 'All' is selected there is no reason to store the filter if($scope.showIncidents==='all'){$location.search(Events.filters.incident,null);}else{$location.search(Events.filters.incident,$scope.showIncidents);}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.incident);$rootScope.$broadcast(Events.filters.incident,$scope.showIncidents);};$scope.$on(Events.filters.reset,function(){resetFilter();});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.incident){resetFilter();$rootScope.$broadcast(filter,'all');}});function resetFilter(){$scope.filterIncidents('all');$location.search(Events.filters.incident,null);}function getTag(){var tag=IncidentFilters[$scope.showIncidents].tag;if($scope.showIncidents==='all'){tag=null;}return tag;}function init(){$scope.showIncidents=$location.search()[Events.filters.incident]?$location.search()[Events.filters.incident]:FilterService.getIncidents();$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.incident);}init();}function incidentsTriState(){return{templateUrl:'js/components/filterComponents/incidentsTriState/incidentsTriState.html',restrict:'E',controller:incidentsTriStateCtrl};}componentsModule.directive('incidentsTriState',incidentsTriState);},{"../../":83}],69:[function(require,module,exports){'use strict';likelihoodSelectCtrl.$inject=['$location','$rootScope','$scope','LikelihoodFilters','Events','FilterService'];var componentsModule=require('../../');/** * @ngInject */function likelihoodSelectCtrl($location,$rootScope,$scope,LikelihoodFilters,Events,FilterService){$scope.options=LikelihoodFilters;$scope.select=function(option){$scope.selected=$scope.options[option];FilterService.setLikelihood(option);FilterService.doFilter();// no need to store filter when not filtering rules. if(option===0){$location.search(Events.filters.likelihood,null);}else{$location.search(Events.filters.likelihood,option);}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.likelihood);$rootScope.$broadcast(Events.filters.likelihood);};function getTag(){return $scope.selected.tag;}function init(){var option=$location.search()[Events.filters.likelihood]?$location.search()[Events.filters.likelihood]:FilterService.getLikelihood();$scope.selected=$scope.options[option];$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.likelihood);}init();$scope.$on(Events.filters.reset,function(){$scope.select(0);});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.likelihood){$scope.select(0);}});}function likelihoodSelect(){return{templateUrl:'js/components/filterComponents/likelihoodSelect/'+'likelihoodSelect.html',restrict:'E',controller:likelihoodSelectCtrl,scope:{}};}componentsModule.directive('likelihoodSelect',likelihoodSelect);},{"../../":83}],70:[function(require,module,exports){'use strict';productSelectCtrl.$inject=['$scope','$rootScope','Account','Events','System','SystemsService','Products','FilterService'];var componentsModule=require('../../');/** * @ngInject */function productSelectCtrl($scope,$rootScope,Account,Events,System,SystemsService,Products,FilterService){$scope.products=[];$scope.getSelectedProduct=FilterService.getSelectedProduct;$scope.getRHELOnly=FilterService.getRHELOnly;$scope.getProductLabel=function(product){if(product==='all'){return'All Platforms';}else if(product===Products.rhel.code){return Products.rhel.fullName;}else if(product===Products.osp.code){return Products.osp.fullName;}else if(product===Products.rhev.code){return Products.rhev.fullName;}else if(product===Products.ocp.code){return Products.ocp.fullName;}else if(product===Products.docker.code){return Products.docker.fullName;}};function setDefaultRHEVRoles(){FilterService.setRolesQueryParam('manager','inventoryRHEVManagers');FilterService.setRolesQueryParam('hypervisor','inventoryRHEVHypervisors');}function setDefaultDockerRoles(){FilterService.setRolesQueryParam('host','inventoryDockerHosts');FilterService.setRolesQueryParam('container','inventoryDockerContainers');FilterService.setRolesQueryParam('image','inventoryDockerImages');}function setDefaultOSPRoles(){FilterService.setRolesQueryParam('director','inventoryOSPDirector');FilterService.setRolesQueryParam('cluster','inventoryOSPCluster');FilterService.setRolesQueryParam('compute','inventoryOSPCompute');FilterService.setRolesQueryParam('controller','inventoryOSPController');}function setDefaultOCPRoles(){FilterService.setRolesQueryParam('director','inventoryOCPDirector');FilterService.setRolesQueryParam('master','inventoryOCPMaster');FilterService.setRolesQueryParam('node','inventoryOCPNodes');}$scope.doSelectProduct=function(product){FilterService.setSelectedProduct(product);$rootScope.$broadcast(Events.checkboxes.reset);if(product===Products.rhev.code){FilterService.setParentNode(null);setDefaultRHEVRoles();FilterService.doFilter();}else if(product===Products.docker.code){System.populateDockerHosts().then(function(){setDefaultDockerRoles();FilterService.doFilter();});}else if(product===Products.ocp.code){System.populateOCPDeployments().then(function(){setDefaultOCPRoles();FilterService.doFilter();});}else if(product===Products.osp.code){System.populateOSPDeployments().then(function(){setDefaultOSPRoles();FilterService.doFilter();});}else{FilterService.setParentNode(null);FilterService.doFilter();}};function init(){Account.getProducts().then(function(products){$scope.products=products.data;if(products.data&&products.data.length<=1){FilterService.setRHELOnly(true);}else{FilterService.setRHELOnly(false);$scope.products.splice(0,0,'all');}$rootScope.$broadcast(Events.filters.populatedProducts);});}$rootScope.$on('reload:data',init);init();}function productSelect(){return{templateUrl:'js/components/filterComponents/productSelect/productSelect.html',restrict:'E',controller:productSelectCtrl};}componentsModule.directive('productSelect',productSelect);},{"../../":83}],71:[function(require,module,exports){'use strict';riskOfChangeSelectCtrl.$inject=['$location','$rootScope','$scope','gettextCatalog','Events'];var componentsModule=require('../../');/** * @ngInject */function riskOfChangeSelectCtrl($location,$rootScope,$scope,gettextCatalog,Events){$scope.options={all:{label:gettextCatalog.getString('All'),tag:null},1:{label:gettextCatalog.getString('Very Low'),tag:gettextCatalog.getString('Risk of Change: Very Low')},2:{label:gettextCatalog.getString('Low'),tag:gettextCatalog.getString('Risk of Change: Low')},3:{label:gettextCatalog.getString('Moderate'),tag:gettextCatalog.getString('Risk of Change: Moderate')},4:{label:gettextCatalog.getString('High'),tag:gettextCatalog.getString('Risk of Change: High')}};$scope.select=function(option){$scope.selected=$scope.options[option];$location.search('riskOfChange',option);// If 'All' is selected there is no reason to store the filter if(option==='All'){delete $location.search().riskOfChange;}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.riskOfChangeSelect);$rootScope.$broadcast(Events.filters.riskOfChangeSelect,option);};function getTag(){return $scope.selected.tag;}function read(){var riskOfChange=$location.search().riskOfChange||'all';$scope.selected=$scope.options[riskOfChange];$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.riskOfChangeSelect);}read();$scope.$on(Events.filters.reset,function(){$scope.select('all');});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.riskOfChangeSelect){$scope.select('all');$rootScope.$broadcast(filter,'all');}});}function riskOfChangeSelect(){return{templateUrl:'js/components/filterComponents/riskOfChangeSelect/riskOfChangeSelect.html',restrict:'E',controller:riskOfChangeSelectCtrl,scope:{}};}componentsModule.directive('riskOfChangeSelect',riskOfChangeSelect);},{"../../":83}],72:[function(require,module,exports){'use strict';ruleStatusTriStateCtrl.$inject=['$location','$rootScope','$scope','Events','RuleStatusFilters','FilterService'];var componentsModule=require('../../');/** * @ngInject */function ruleStatusTriStateCtrl($location,$rootScope,$scope,Events,RuleStatusFilters,FilterService){$scope.RuleStatusFilters=RuleStatusFilters;$scope.filterRuleStatus=function(key){$scope.showRuleStatus=key;FilterService.setRuleStatus(key);FilterService.doFilter();// no need to store filter when // not filtering ignored/unignored rules if(key==='all'){$location.search(Events.filters.ruleStatus,null);}else{$location.search(Events.filters.ruleStatus,$scope.showRuleStatus);}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.ruleStatus);$rootScope.$broadcast(Events.filters.ruleStatus,$scope.showRuleStatus);};$scope.$on(Events.filters.reset,function(){resetFilter();});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.ruleStatus){resetFilter();$rootScope.$broadcast(filter,'all');}});function resetFilter(){$scope.filterRuleStatus('all');}function getTag(){var tag=RuleStatusFilters[$scope.showRuleStatus].tag;return tag;}function init(){$scope.showRuleStatus=$location.search()[Events.filters.ruleStatus]?$location.search()[Events.filters.ruleStatus]:FilterService.getRuleStatus();$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.ruleStatus);}init();}function ruleStatusTriState(){return{templateUrl:'js/components/filterComponents/ruleStatusTriState/ruleStatusTriState.html',restrict:'E',controller:ruleStatusTriStateCtrl};}componentsModule.directive('ruleStatusTriState',ruleStatusTriState);},{"../../":83}],73:[function(require,module,exports){'use strict';searchBoxCtrl.$inject=['$scope','gettextCatalog','Events'];var componentsModule=require('../../');var throttle=require('lodash/throttle');var defaultThrottle=500;/** * Reusable search box component. Supports input throttling. * * Extension points: * - placeholder: defaults to 'Search…' - can be overriden with 'placeholder' attribute * - throttle: delay between invocations of on-search callback. Throttling is disabled * when value is set to 0. See https://lodash.com/docs/3.10.1#throttle for details. * - on-search: callback to invoke. It is invoked for every change in the search input * field (throttling applied) and for each enter keypress. An enter keypress cancels * a throttled event (if any) and causes on-search to be called right away. * * @ngInject */function searchBoxCtrl($scope,gettextCatalog,Events){$scope.placeholder=$scope.placeholder||gettextCatalog.getString('Search…');function doOnSearch(){if($scope.onSearch){$scope.onSearch({model:$scope.model||''});}}function parseThrottleDelay(){if($scope.throttle){var delay=parseInt($scope.throttle);if(isNaN(delay)){throw new Error('Unknown throttle value '+$scope.throttle);}return delay;}return defaultThrottle;}// as _.throttle may call this outside of digestion cycle, we need to use // $scope.$apply if needed function safeApply(fn){return function(){var phase=$scope.$root.$$phase;if(phase==='$apply'||phase==='$digest'){fn();}else{$scope.$apply(fn);}};}$scope.searching=function(){if($scope.model&&$scope.model.length>0){return true;}};$scope.resetSearch=function(){$scope.$broadcast(Events.filters.reset);};$scope.keyPressed=function($event){if($event.keyCode===13){if($scope.change.cancel){$scope.change.cancel();// cancel throttled callbacks }doOnSearch();// input submitted - trigger on-search }};var throttleDelay=parseThrottleDelay();if(throttleDelay===0){$scope.change=doOnSearch;}else{$scope.change=throttle(safeApply(doOnSearch),throttleDelay,{leading:false,trailing:true});}$scope.$on(Events.filters.reset,function(){$scope.model='';doOnSearch();});}function searchBox(){return{templateUrl:'js/components/filterComponents/searchBox/searchBox.html',restrict:'E',replace:true,controller:searchBoxCtrl,scope:{placeholder:'@',model:'=?ngModel',onSearch:'&?',throttle:'@',isFullLength:'='}};}componentsModule.directive('searchBox',searchBox);},{"../../":83,"lodash/throttle":546}],74:[function(require,module,exports){'use strict';totalRiskSelectCtrl.$inject=['$location','$rootScope','$scope','gettextCatalog','Events','Severities','FilterService','MultiButtonService'];var componentsModule=require('../../');var find=require('lodash/find');/** * @ngInject */function totalRiskSelectCtrl($location,$rootScope,$scope,gettextCatalog,Events,Severities,FilterService,MultiButtonService){$scope.options=Severities;$scope.label='All';$scope.select=function(option){$scope.selected=option;$scope.label=option.label;setSeverity(option.value);FilterService.doFilter();// If 'All' is selected there is no reason to store the filter if($scope.selected.value==='All'){delete $location.search()[Events.filters.totalRisk];}else{$location.search(Events.filters.totalRisk,$scope.selected.value);}$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.totalRisk);$rootScope.$broadcast(Events.filters.totalRisk,$scope.selected.value);};function getTag(){var severity=Severities.find(function(severity){return severity.value===$location.search()[Events.filters.totalRisk];});if($scope.selected.value==='All'){severity=null;}return severity?severity.tag:null;}function init(){$scope.selected=find($scope.options,function(option){return option.value===$location.search()[Events.filters.totalRisk];});$scope.selected=$scope.selected?$scope.selected:find($scope.options,function(option){return option.value==='All';});$scope.label=$scope.selected.label;setSeverity($scope.selected.value);$rootScope.$broadcast(Events.filters.tag,getTag(),Events.filters.totalRisk);}function setSeverity(risk){Severities.map(function(s){return s.value;}).forEach(function(severity){if(severity===risk){MultiButtonService.setState('severityFilters'+severity,true);}else{MultiButtonService.setState('severityFilters'+severity,false);}});}function resetFilter(){var option=find($scope.options,function(option){return option.value==='All';});$scope.select(option);}$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.totalRisk){resetFilter();$rootScope.$broadcast(filter,'All');}});$scope.$on(Events.filters.reset,function(){resetFilter();});init();}function totalRiskSelect(){return{templateUrl:'js/components/filterComponents/totalRiskSelect/totalRiskSelect.html',restrict:'E',controller:totalRiskSelectCtrl,scope:{}};}componentsModule.directive('totalRiskSelect',totalRiskSelect);},{"../../":83,"lodash/find":491}],75:[function(require,module,exports){/*global require, angular*/'use strict';getSystemDisplayName.$inject=['Utils'];trust_html.$inject=['$sce'];var componentsModule=require('../');var sortBy=require('lodash/sortBy');var isEmpty=require('lodash/isEmpty');var isArray=require('lodash/isArray');var moment=require('moment-timezone');var map=require('lodash/map');var overSome=require('lodash/overSome');var groupBy=require('lodash/groupBy');var memoize=require('lodash/memoize');var values=require('lodash/values');var orderBy=require('lodash/orderBy');var partial=require('lodash/partial');/** * @ngInject */function trust_html($sce){return function(text){return $sce.trustAsHtml(text);};}function titlecase(){function _titleCase(str){return str.toString().toLowerCase().replace(/\b([a-z])/g,function(ch){return ch.toUpperCase();});}return function(value){var newArr=[];value=value===undefined||value===null?'':value;if(Array.isArray(value)){angular.forEach(value,function(v){newArr.push(_titleCase(v));});return newArr;}else{return _titleCase(value);}};}function sortClass(){return function(predicate,match,reverse){if(!predicate){return'';}if(predicate===match&&!reverse){return'sort-asc';}if(predicate===match&&reverse){return'sort-desc';}return'';};}function orderObjectBy(){return function(items,field,reverse){var sorted=sortBy(items,field);if(reverse){return sorted.reverse();}return sorted;};}function offset(){return function(input,start){start=parseInt(start,10);if(input){return input.slice(start);}return[];};}function productShortName(){return function(input){if(input){return input.replace('Red Hat Enterprise Linux','RHEL');}return input;};}/* * Turns a number from [0,1] interval into a width style attribute (e.g. width="85%") * Numbers above or below this interval result in 100% or 0%, respectively. */function toWidth(){return function(value){if(isNaN(value)){value=0;}else{value=Math.max(0,Math.min(1,value));}return{width:Math.round(value*100)+'%'};};}/** * Returns a CSS class for a system based on how long it's been since its last check-in * @ngInject */function checkInStyle(){return function(system){if(!system.isCheckingIn){return'text-danger';}return'text-success';};}function momentFilter(){return function(input,format,timezone){var result=moment(input);if(timezone){result=result.tz(timezone);}return result.format(format);};}function searchMaintenancePlans($filter,plans,searchTerm){if(!plans||!isArray(plans)){return[];}if(!searchTerm){return plans;}function contains(string){if(string&&typeof string!=='string'){string=string.toString();}return string&&string.toLowerCase().indexOf(searchTerm.toLowerCase())>-1;}function matches(prop){return function(plan){return contains(plan[prop]);};}function matchesAny(array,prop){return function(plan){return map(plan[array],prop).some(contains);};}var predicates=[matches('maintenance_id'),matches('name'),function(plan){return plan.name===null&&contains('Unnamed plan');},function(plan){return contains($filter('date')(plan.start,'fullDate'));},matchesAny('systems','hostname'),matchesAny('systems','display_name'),matchesAny('rules','description'),matchesAny('rules','rule_id')];var someMatches=overSome(predicates);return plans.filter(someMatches);}function timeAgo(){return function(time){return moment(time).fromNow();};}function getSystemDisplayName(Utils){return function(system){return Utils.getSystemDisplayName(system);};}function groupPlans(plans){plans.forEach(function(plan){return plan.month=moment(plan.start).format('YYYY-MM');});return orderBy(values(groupBy(plans,'month')),['0.month'],['desc']);}function cached(fn,keyFn,$rootScope){var wrapped=memoize(fn,keyFn);for(var _len3=arguments.length,events=Array(_len3>3?_len3-3:0),_key3=3;_key3<_len3;_key3++){events[_key3-3]=arguments[_key3];}events.forEach(function(event){return $rootScope.$on(event,function(){return wrapped.cache.clear();});});return wrapped;}componentsModule.filter('trust_html',trust_html);componentsModule.filter('titlecase',titlecase);componentsModule.filter('sortClass',sortClass);componentsModule.filter('productShortName',productShortName);componentsModule.filter('orderObjectBy',orderObjectBy);componentsModule.filter('offset',offset);componentsModule.filter('toWidth',toWidth);componentsModule.filter('checkInStyle',checkInStyle);componentsModule.filter('moment',momentFilter);componentsModule.filter('getSystemDisplayName',getSystemDisplayName);componentsModule.filter('isEmpty',function(){return isEmpty;});componentsModule.filter('timeAgo',timeAgo);componentsModule.filter('searchMaintenancePlans',['$filter','$rootScope','Events',function($filter,$rootScope,Events){return cached(partial(searchMaintenancePlans,$filter),function(plans,searchTerm){return searchTerm+'|'+map(plans,'maintenance_id').join(',');},$rootScope,Events.planner.plansLoaded);// flush cache when plans reloaded }]);componentsModule.filter('groupPlans',['$rootScope','Events',function($rootScope,Events){return cached(groupPlans,function(plans){return map(plans,function(plan){return plan.maintenance_id+':'+plan.start;}).join(',');},$rootScope,Events.planner.plansLoaded);// flush cache when plans reloaded }]);},{"../":83,"lodash/groupBy":497,"lodash/isArray":504,"lodash/isEmpty":508,"lodash/map":522,"lodash/memoize":525,"lodash/orderBy":529,"lodash/overSome":530,"lodash/partial":531,"lodash/sortBy":540,"lodash/values":554,"moment-timezone":557}],76:[function(require,module,exports){/*global require*/'use strict';gaugeMarkerController.$inject=['$scope','gettextCatalog'];var componentsModule=require('../');var priv={};/** * @ngInject */function gaugeMarkerController($scope,gettextCatalog){priv.getString=function(str){return str+' '+$scope.difference+' '+gettextCatalog.getString('points');};priv.init=function(){$scope.rotate=($scope.current-250)*0.3;if($scope.difference>0){$scope.diffClass='increase';$scope.tooltip=priv.getString(gettextCatalog.getString('Your score has increased by'));return;}else if($scope.difference<0){$scope.diffClass='decrease';$scope.tooltip=priv.getString(gettextCatalog.getString('Your score has decreased by'));return;}else if($scope.difference===0){$scope.diffClass=null;$scope.tooltip=gettextCatalog.getString('Your score has not changed');return;}};$scope.$watch('current',priv.init);$scope.$watch('difference',priv.init);}function gaugeMarker(){return{templateUrl:'js/components/gaugeMarker/gaugeMarker.html',restrict:'E',controller:gaugeMarkerController,replace:true,scope:{current:'<',difference:'<'}};}componentsModule.directive('gaugeMarker',gaugeMarker);},{"../":83}],77:[function(require,module,exports){'use strict';var componentsModule=require('../');function gettingStartedTabs(){return{templateUrl:'js/components/gettingStartedTabs/gettingStartedTabs.html',restrict:'ECA',replace:true};}componentsModule.directive('gettingStartedTabs',gettingStartedTabs);},{"../":83}],78:[function(require,module,exports){'use strict';groupSelectCtrl.$inject=['$rootScope','$q','$scope','$state','$timeout','gettextCatalog','sweetAlert','Events','Group','GroupService'];var componentsModule=require('../');var isEmpty=require('lodash/isEmpty');var _filter=require('lodash/filter');/** * @ngInject */function groupSelectCtrl($rootScope,$q,$scope,$state,$timeout,gettextCatalog,sweetAlert,Events,Group,GroupService){Group.init();$scope._groups=Group.groups;$scope.groups=$scope._groups;$scope.group=Group.current();$scope.dropdown=false;$scope.searching=false;$scope.resetFilter=function(){$scope.$broadcast(Events.filters.reset);};$scope.hideSelectedGroup=function(group){if($scope.group&&$scope.group.display_name&&$scope.group.display_name===group.display_name){return false;}return true;};$scope.showAllSystems=function(){if($scope.group&&$scope.group.display_name&&!$scope.searching){return true;}return false;};$scope.searchGroups=function(search){$scope.searching=true;if(search){var filter=search.toLowerCase();$scope.groups=_filter($scope._groups,function(group){return group.display_name.toLowerCase().indexOf(filter)>-1;});}else{$scope.groups=$scope._groups;$scope.searching=false;}};$scope.triggerChange=function(group){Group.setCurrent(group);};$rootScope.$on('group:change',function(event,group){$scope.group=group;});$scope.$on('account:change',function(){$scope.triggerChange(null);});$scope.isGroupSelected=function(){return!isEmpty($scope.group);};$scope.$on(Events.filters.reset,function(){$scope.group=Group.current();$scope.groups=$scope._groups;});$scope.createGroup=function(){GroupService.createGroup().then(function(group){var html=gettextCatalog.getString('Use Actions dropdown in the inventory to add systems '+'to the {{name}} group',{name:group.display_name});return sweetAlert({title:gettextCatalog.getString('Group created'),confirmButtonText:gettextCatalog.getString('OK'),type:'info',html:html,showCancelButton:false});}).then(function(){if($state.current.name!=='app.inventory'){$state.go('app.inventory');}});};$scope.toggleDropdown=function(){$scope.dropdown=!$scope.dropdown;};$scope.keypress=function(event){if(event.which===27){$scope.$broadcast(Events.filters.reset);if($scope.dropdown){$scope.dropdown=false;angular.element(document.getElementById('global-filter-dropdown')).removeClass('open');}}};$scope.deleteGroup=GroupService.deleteGroup;}function groupSelect(){return{templateUrl:'js/components/groupSelect/groupSelect.html',restrict:'E',replace:true,controller:groupSelectCtrl,scope:{round:'=',disabled:'<'}};}componentsModule.directive('groupSelect',groupSelect);},{"../":83,"lodash/filter":490,"lodash/isEmpty":508}],79:[function(require,module,exports){/*global require*/'use strict';incidentIconCtrl.$inject=['$rootScope','$scope','gettextCatalog','IncidentsService'];var componentsModule=require('../../');/** * @ngInject */function incidentIconCtrl($rootScope,$scope,gettextCatalog,IncidentsService){$scope.tooltip=gettextCatalog.getString('This is an incident. '+'An incident means that this has already occurred.');IncidentsService.init().then(function(){$scope.isIncident=IncidentsService.isIncident($scope.ruleId)&&$rootScope.isBeta;});}function incidentIcon(){return{templateUrl:'js/components/incident/incidentIcon/incidentIcon.html',restrict:'E',replace:true,controller:incidentIconCtrl,scope:{ruleId:'='}};}componentsModule.directive('incidentIcon',incidentIcon);},{"../../":83}],80:[function(require,module,exports){/*global require*/'use strict';IncidentLiteController.$inject=['$q','$scope','IncidentsService','Stats'];var componentsModule=require('../../');function IncidentLiteController($q,$scope,IncidentsService,Stats){function loadData(){var promises=[];$scope.loading=true;promises.push(loadIncidentNumbers());promises.push(loadStats());$q.all(promises).finally(function(){$scope.loading=false;});}// loads the total number of rule hits that are incidents function loadIncidentNumbers(){return IncidentsService.loadIncidents().then(function(){$scope.incidentCount=IncidentsService.incidentRulesWithHitsCount;});}// loads the total number of rules that have hits function loadStats(){return Stats.getRules({include:'ansible'}).then(function(res){$scope.totalHits=res.data.total;});}loadData();// reload data when system group changes $scope.$on('group:change',function(){loadData();});}function incidentLite(){return{controller:IncidentLiteController,templateUrl:'js/components/incident/incidentLite/incidentLite.html',restrict:'E',replace:false};}componentsModule.directive('incidentLite',incidentLite);},{"../../":83}],81:[function(require,module,exports){'use strict';var componentsModule=require('../../');function incidentSummary(){return{templateUrl:'js/components/incident/incidentSummary/incidentSummary.html',restrict:'E',scope:{incidentCount:'<',incidentSystemCount:'<',ruleHits:'<'}};}componentsModule.directive('incidentSummary',incidentSummary);},{"../../":83}],82:[function(require,module,exports){'use strict';var componentsModule=require('../');/** * @ngInject */function ngIndeterminate(){return{restrict:'A',link:function link(scope,element,attributes){scope.$watch(attributes.ngIndeterminate,function(value){element.prop('indeterminate',!!value);});}};}componentsModule.directive('ngIndeterminate',ngIndeterminate);},{"../":83}],83:[function(require,module,exports){'use strict';module.exports=angular.module('insights.components',[]);(function(){var f=require("./index.js");f["accountSelect"]={"accountSelect.directive":require("./accountSelect/accountSelect.directive.js")};f["actionbar"]={"actionbar.directive":require("./actionbar/actionbar.directive.js")};f["actions"]={"actions.directive":require("./actions/actions.directive.js"),"actionsBack":{"actionsBack.directive":require("./actions/actionsBack/actionsBack.directive.js")},"actionsBreadcrumbs":{"actionsBreadcrumbs.directive":require("./actions/actionsBreadcrumbs/actionsBreadcrumbs.directive.js")},"actionsRule":{"actionsRule.controller":require("./actions/actionsRule/actionsRule.controller.js")},"actionsRuleFilters":{"actionsRuleFilters.controller":require("./actions/actionsRuleFilters/actionsRuleFilters.controller.js")},"severitySummary":{"severitySummary.directive":require("./actions/severitySummary/severitySummary.directive.js")},"systemSummary":{"systemSummary.directive":require("./actions/systemSummary/systemSummary.directive.js")}};f["announcements"]={"announcementsScroll":{"announcementsScroll.directive":require("./announcements/announcementsScroll/announcementsScroll.directive.js")}};f["ansibleIcon"]={"ansibleIcon.directive":require("./ansibleIcon/ansibleIcon.directive.js")};f["betaSwitchButton"]={"betaSwitchButton.directive":require("./betaSwitchButton/betaSwitchButton.directive.js")};f["bullhorn"]={"bullhorn.directive":require("./bullhorn/bullhorn.directive.js")};f["card"]={"card.directive":require("./card/card.directive.js"),"systemCard":{"systemCard.directive":require("./card/systemCard/systemCard.directive.js")}};f["categoryIcon"]={"categoryIcon.directive":require("./categoryIcon/categoryIcon.directive.js")};f["collapsibleFilter"]={"collapsibleFilter.controller":require("./collapsibleFilter/collapsibleFilter.controller.js")};f["config"]={"dev":{"dev.directive":require("./config/dev/dev.directive.js")},"groups":{"groups.directive":require("./config/groups/groups.directive.js")},"hidden":{"hidden.directive":require("./config/hidden/hidden.directive.js")},"messaging":{"messaging.directive":require("./config/messaging/messaging.directive.js")},"permissions":{"permissions.directive":require("./config/permissions/permissions.directive.js")},"settings":{"settings.directive":require("./config/settings/settings.directive.js")},"webhooks":{"webhooks.directive":require("./config/webhooks/webhooks.directive.js")}};f["demoAccountSwitcher"]={"demoAccountSwitcher.directive":require("./demoAccountSwitcher/demoAccountSwitcher.directive.js")};f["digest"]={"digestGraph.directive":require("./digest/digestGraph.directive.js")};f["escKey"]={"escKey.directive":require("./escKey/escKey.directive.js")};f["expandAllButton"]={"expandAllButton.directive":require("./expandAllButton/expandAllButton.directive.js")};f["feedback"]={"feedbackButton.directive":require("./feedback/feedbackButton.directive.js"),"feedbackModal.directive":require("./feedback/feedbackModal.directive.js")};f["filterComponents"]={"actionsSelect":{"actionsSelect.directive":require("./filterComponents/actionsSelect/actionsSelect.directive.js")},"ansibleSupportTriState":{"ansibleSupportTriState.controller":require("./filterComponents/ansibleSupportTriState/ansibleSupportTriState.controller.js")},"categorySelect":{"categorySelect.directive":require("./filterComponents/categorySelect/categorySelect.directive.js")},"checkInSelect":{"checkInSelect.directive":require("./filterComponents/checkInSelect/checkInSelect.directive.js")},"filterButton":{"filterButton.directive":require("./filterComponents/filterButton/filterButton.directive.js")},"hideIgnoredToggle":{"hideIgnoredToggle.directive":require("./filterComponents/hideIgnoredToggle/hideIgnoredToggle.directive.js")},"impactSelect":{"impactSelect.directive":require("./filterComponents/impactSelect/impactSelect.directive.js")},"incidentsTriState":{"incidentsTriState.controller":require("./filterComponents/incidentsTriState/incidentsTriState.controller.js")},"likelihoodSelect":{"likelihoodSelect.directive":require("./filterComponents/likelihoodSelect/likelihoodSelect.directive.js")},"productSelect":{"productSelect.directive":require("./filterComponents/productSelect/productSelect.directive.js")},"riskOfChangeSelect":{"riskOfChangeSelect.directive":require("./filterComponents/riskOfChangeSelect/riskOfChangeSelect.directive.js")},"ruleStatusTriState":{"ruleStatusTriState.controller":require("./filterComponents/ruleStatusTriState/ruleStatusTriState.controller.js")},"searchBox":{"searchBox.directive":require("./filterComponents/searchBox/searchBox.directive.js")},"totalRiskSelect":{"totalRiskSelect.controller":require("./filterComponents/totalRiskSelect/totalRiskSelect.controller.js")}};f["filters"]={"filters":require("./filters/filters.js")};f["gaugeMarker"]={"gaugeMarker.directive":require("./gaugeMarker/gaugeMarker.directive.js")};f["gettingStartedTabs"]={"gettingStartedTabs.directive":require("./gettingStartedTabs/gettingStartedTabs.directive.js")};f["groupSelect"]={"groupSelect.directive":require("./groupSelect/groupSelect.directive.js")};f["incident"]={"incidentIcon":{"incidentIcon.directive":require("./incident/incidentIcon/incidentIcon.directive.js")},"incidentLite":{"incidentLite.directive":require("./incident/incidentLite/incidentLite.directive.js")},"incidentSummary":{"incidentSummary.directive":require("./incident/incidentSummary/incidentSummary.directive.js")}};f["indeterminate"]={"indeterminate.directive":require("./indeterminate/indeterminate.directive.js")};f["index"]=require("./index.js");f["insightsLogo"]={"insightsLogo.directive":require("./insightsLogo/insightsLogo.directive.js")};f["insightsTagline"]={"insightsTagline.directive":require("./insightsTagline/insightsTagline.directive.js")};f["inventory"]={"inventoryActions":{"inventoryActions.directive":require("./inventory/inventoryActions/inventoryActions.directive.js")},"inventoryFilters":{"inventoryFilters.directive":require("./inventory/inventoryFilters/inventoryFilters.directive.js")},"system":{"system.directive":require("./inventory/system/system.directive.js")}};f["linkGroup"]={"linkGroup.directive":require("./linkGroup/linkGroup.directive.js")};f["listType"]={"listType.directive":require("./listType/listType.directive.js")};f["maintenance"]={"expandableSystemList":{"expandableSystemList":require("./maintenance/expandableSystemList/expandableSystemList.js")},"maintenanceCategorySelect":{"maintenanceCategorySelect.directive":require("./maintenance/maintenanceCategorySelect/maintenanceCategorySelect.directive.js")},"maintenanceModal":{"maintenanceModal.directive":require("./maintenance/maintenanceModal/maintenanceModal.directive.js")},"maintenancePlan":{"maintenancePlan.directive":require("./maintenance/maintenancePlan/maintenancePlan.directive.js")},"maintenanceTable":{"maintenanceTable.controller":require("./maintenance/maintenanceTable/maintenanceTable.controller.js"),"maintenanceTable.directive":require("./maintenance/maintenanceTable/maintenanceTable.directive.js"),"maintenanceTableFooter.directive":require("./maintenance/maintenanceTable/maintenanceTableFooter.directive.js")},"planList":{"planList.directive":require("./maintenance/planList/planList.directive.js")},"resolutionModal":{"resolutionModal.controller":require("./maintenance/resolutionModal/resolutionModal.controller.js")}};f["misc"]={"trimmedText":{"trimmedText.directive":require("./misc/trimmedText/trimmedText.directive.js")}};f["mitigationTrend"]={"mitigationTrend.directive":require("./mitigationTrend/mitigationTrend.directive.js")};f["namespace"]={"namespace.directive":require("./namespace/namespace.directive.js")};f["notifications"]={"notifications.directive":require("./notifications/notifications.directive.js")};f["overview"]={"articleModal":{"articleModal":require("./overview/articleModal/articleModal.js")},"widgets":{"goals":{"goalsBar":{"goalsBar.directive":require("./overview/widgets/goals/goalsBar/goalsBar.directive.js")},"goalsGauge":{"goalsGauge.directive":require("./overview/widgets/goals/goalsGauge/goalsGauge.directive.js")},"goalsLite.directive":require("./overview/widgets/goals/goalsLite.directive.js")},"handcraftedContent":{"handcraftedContent.directive":require("./overview/widgets/handcraftedContent/handcraftedContent.directive.js")},"highPrio":{"highPrio.directive":require("./overview/widgets/highPrio/highPrio.directive.js")},"maintenancePlanLite":{"maintenancePlanLite.directive":require("./overview/widgets/maintenancePlanLite/maintenancePlanLite.directive.js"),"maintenancePlanLiteTable":{"maintenancePlanLiteTable.directive":require("./overview/widgets/maintenancePlanLite/maintenancePlanLiteTable/maintenancePlanLiteTable.directive.js")}},"newSystems":{"newSystems.directive":require("./overview/widgets/newSystems/newSystems.directive.js")}}};f["pageHeader"]={"pageHeader.directive":require("./pageHeader/pageHeader.directive.js")};f["platformFilter"]={"platformFilter.directive":require("./platformFilter/platformFilter.directive.js")};f["recommendedKbase"]={"recommendedKbase.directive":require("./recommendedKbase/recommendedKbase.directive.js")};f["relativeHash"]={"relativeHash.directive":require("./relativeHash/relativeHash.directive.js")};f["rhDonut"]={"rhDonut.directive":require("./rhDonut/rhDonut.directive.js")};f["riskOfChange"]={"riskOfChange.directive":require("./riskOfChange/riskOfChange.directive.js")};f["rule"]={"ruleBreadcrumb":{"ruleBreadcrumb.directive":require("./rule/ruleBreadcrumb/ruleBreadcrumb.directive.js")},"ruleFilter":{"ruleFilter.directive":require("./rule/ruleFilter/ruleFilter.directive.js")},"ruleGroupCard":{"ruleGroupCard.directive":require("./rule/ruleGroupCard/ruleGroupCard.directive.js")},"ruleListSimple":{"ruleListSimple.directive":require("./rule/ruleListSimple/ruleListSimple.directive.js")},"ruleReason":{"ruleReason.directive":require("./rule/ruleReason/ruleReason.directive.js")},"ruleResolution":{"ruleResolution.directive":require("./rule/ruleResolution/ruleResolution.directive.js")},"ruleSummaries":{"ruleSummaries.directive":require("./rule/ruleSummaries/ruleSummaries.directive.js")},"ruleSummary":{"ruleSummary.directive":require("./rule/ruleSummary/ruleSummary.directive.js")},"ruleToggle":{"ruleToggle.directive":require("./rule/ruleToggle/ruleToggle.directive.js")}};f["ruleStateIcon"]={"ruleStateIcon.directive":require("./ruleStateIcon/ruleStateIcon.directive.js")};f["severityIcon"]={"allSeverityIcons.directive":require("./severityIcon/allSeverityIcons.directive.js"),"severityIcon.directive":require("./severityIcon/severityIcon.directive.js")};f["sideNav"]={"actionsSideNav":{"actionsSideNav.directive":require("./sideNav/actionsSideNav/actionsSideNav.directive.js")},"sideNav.directive":require("./sideNav/sideNav.directive.js")};f["simpleSelect"]={"simpleSelect.directive":require("./simpleSelect/simpleSelect.directive.js")};f["statusIcon"]={"statusIcon.directive":require("./statusIcon/statusIcon.directive.js")};f["stickyNav"]={"stickyNav.directive":require("./stickyNav/stickyNav.directive.js")};f["system"]={"addSystemModal":{"addSystemModal.controller":require("./system/addSystemModal/addSystemModal.controller.js")},"systemModal":{"systemModal.controller":require("./system/systemModal/systemModal.controller.js")},"systemTable":{"systemTable.directive":require("./system/systemTable/systemTable.directive.js")}};f["systemMetadata"]={"systemMetadata.directive":require("./systemMetadata/systemMetadata.directive.js")};f["targetBlank"]={"targetBlank.directive":require("./targetBlank/targetBlank.directive.js")};f["timeRange"]={"timeRange.directive":require("./timeRange/timeRange.directive.js")};f["topbar"]={"alerts":{"alerts.directive":require("./topbar/alerts/alerts.directive.js")},"topbar.directive":require("./topbar/topbar.directive.js")};f["topics"]={"otherTopic":{"otherTopic.directive":require("./topics/otherTopic/otherTopic.directive.js")},"topicAdminHelpBar":{"topicAdminHelpBar.directive":require("./topics/topicAdminHelpBar/topicAdminHelpBar.directive.js")},"topicDetails":{"topicDetails.directive":require("./topics/topicDetails/topicDetails.directive.js")},"topicPreviewModal":{"topicPreviewModal":require("./topics/topicPreviewModal/topicPreviewModal.js")},"topicRuleFilters":{"topicRuleFilters.controller":require("./topics/topicRuleFilters/topicRuleFilters.controller.js")},"topicRuleList":{"topicRuleList.directive":require("./topics/topicRuleList/topicRuleList.directive.js")}};f["typeIcon"]={"typeIcon.directive":require("./typeIcon/typeIcon.directive.js")};f["ui-bootstrap-custom"]=require("./ui-bootstrap-custom.js");f["userInfo"]={"userInfo.directive":require("./userInfo/userInfo.directive.js")};f["validators"]={"kbaseSolution":{"kbaseSolution.directive":require("./validators/kbaseSolution/kbaseSolution.directive.js")}};f["yesNo"]={"yesNo.directive":require("./yesNo/yesNo.directive.js")};return f;})();},{"./accountSelect/accountSelect.directive.js":31,"./actionbar/actionbar.directive.js":32,"./actions/actions.directive.js":33,"./actions/actionsBack/actionsBack.directive.js":34,"./actions/actionsBreadcrumbs/actionsBreadcrumbs.directive.js":35,"./actions/actionsRule/actionsRule.controller.js":36,"./actions/actionsRuleFilters/actionsRuleFilters.controller.js":37,"./actions/severitySummary/severitySummary.directive.js":38,"./actions/systemSummary/systemSummary.directive.js":39,"./announcements/announcementsScroll/announcementsScroll.directive.js":40,"./ansibleIcon/ansibleIcon.directive.js":41,"./betaSwitchButton/betaSwitchButton.directive.js":42,"./bullhorn/bullhorn.directive.js":43,"./card/card.directive.js":44,"./card/systemCard/systemCard.directive.js":45,"./categoryIcon/categoryIcon.directive.js":46,"./collapsibleFilter/collapsibleFilter.controller.js":47,"./config/dev/dev.directive.js":48,"./config/groups/groups.directive.js":49,"./config/hidden/hidden.directive.js":50,"./config/messaging/messaging.directive.js":51,"./config/permissions/permissions.directive.js":52,"./config/settings/settings.directive.js":53,"./config/webhooks/webhooks.directive.js":54,"./demoAccountSwitcher/demoAccountSwitcher.directive.js":55,"./digest/digestGraph.directive.js":56,"./escKey/escKey.directive.js":57,"./expandAllButton/expandAllButton.directive.js":58,"./feedback/feedbackButton.directive.js":59,"./feedback/feedbackModal.directive.js":60,"./filterComponents/actionsSelect/actionsSelect.directive.js":61,"./filterComponents/ansibleSupportTriState/ansibleSupportTriState.controller.js":62,"./filterComponents/categorySelect/categorySelect.directive.js":63,"./filterComponents/checkInSelect/checkInSelect.directive.js":64,"./filterComponents/filterButton/filterButton.directive.js":65,"./filterComponents/hideIgnoredToggle/hideIgnoredToggle.directive.js":66,"./filterComponents/impactSelect/impactSelect.directive.js":67,"./filterComponents/incidentsTriState/incidentsTriState.controller.js":68,"./filterComponents/likelihoodSelect/likelihoodSelect.directive.js":69,"./filterComponents/productSelect/productSelect.directive.js":70,"./filterComponents/riskOfChangeSelect/riskOfChangeSelect.directive.js":71,"./filterComponents/ruleStatusTriState/ruleStatusTriState.controller.js":72,"./filterComponents/searchBox/searchBox.directive.js":73,"./filterComponents/totalRiskSelect/totalRiskSelect.controller.js":74,"./filters/filters.js":75,"./gaugeMarker/gaugeMarker.directive.js":76,"./gettingStartedTabs/gettingStartedTabs.directive.js":77,"./groupSelect/groupSelect.directive.js":78,"./incident/incidentIcon/incidentIcon.directive.js":79,"./incident/incidentLite/incidentLite.directive.js":80,"./incident/incidentSummary/incidentSummary.directive.js":81,"./indeterminate/indeterminate.directive.js":82,"./index.js":83,"./insightsLogo/insightsLogo.directive.js":84,"./insightsTagline/insightsTagline.directive.js":85,"./inventory/inventoryActions/inventoryActions.directive.js":86,"./inventory/inventoryFilters/inventoryFilters.directive.js":87,"./inventory/system/system.directive.js":88,"./linkGroup/linkGroup.directive.js":89,"./listType/listType.directive.js":90,"./maintenance/expandableSystemList/expandableSystemList.js":91,"./maintenance/maintenanceCategorySelect/maintenanceCategorySelect.directive.js":92,"./maintenance/maintenanceModal/maintenanceModal.directive.js":93,"./maintenance/maintenancePlan/maintenancePlan.directive.js":94,"./maintenance/maintenanceTable/maintenanceTable.controller.js":95,"./maintenance/maintenanceTable/maintenanceTable.directive.js":96,"./maintenance/maintenanceTable/maintenanceTableFooter.directive.js":97,"./maintenance/planList/planList.directive.js":98,"./maintenance/resolutionModal/resolutionModal.controller.js":99,"./misc/trimmedText/trimmedText.directive.js":100,"./mitigationTrend/mitigationTrend.directive.js":101,"./namespace/namespace.directive.js":102,"./notifications/notifications.directive.js":103,"./overview/articleModal/articleModal.js":104,"./overview/widgets/goals/goalsBar/goalsBar.directive.js":105,"./overview/widgets/goals/goalsGauge/goalsGauge.directive.js":106,"./overview/widgets/goals/goalsLite.directive.js":107,"./overview/widgets/handcraftedContent/handcraftedContent.directive.js":108,"./overview/widgets/highPrio/highPrio.directive.js":109,"./overview/widgets/maintenancePlanLite/maintenancePlanLite.directive.js":110,"./overview/widgets/maintenancePlanLite/maintenancePlanLiteTable/maintenancePlanLiteTable.directive.js":111,"./overview/widgets/newSystems/newSystems.directive.js":112,"./pageHeader/pageHeader.directive.js":113,"./platformFilter/platformFilter.directive.js":114,"./recommendedKbase/recommendedKbase.directive.js":115,"./relativeHash/relativeHash.directive.js":116,"./rhDonut/rhDonut.directive.js":117,"./riskOfChange/riskOfChange.directive.js":118,"./rule/ruleBreadcrumb/ruleBreadcrumb.directive.js":119,"./rule/ruleFilter/ruleFilter.directive.js":120,"./rule/ruleGroupCard/ruleGroupCard.directive.js":121,"./rule/ruleListSimple/ruleListSimple.directive.js":122,"./rule/ruleReason/ruleReason.directive.js":123,"./rule/ruleResolution/ruleResolution.directive.js":124,"./rule/ruleSummaries/ruleSummaries.directive.js":125,"./rule/ruleSummary/ruleSummary.directive.js":126,"./rule/ruleToggle/ruleToggle.directive.js":127,"./ruleStateIcon/ruleStateIcon.directive.js":128,"./severityIcon/allSeverityIcons.directive.js":129,"./severityIcon/severityIcon.directive.js":130,"./sideNav/actionsSideNav/actionsSideNav.directive.js":131,"./sideNav/sideNav.directive.js":132,"./simpleSelect/simpleSelect.directive.js":133,"./statusIcon/statusIcon.directive.js":134,"./stickyNav/stickyNav.directive.js":135,"./system/addSystemModal/addSystemModal.controller.js":136,"./system/systemModal/systemModal.controller.js":137,"./system/systemTable/systemTable.directive.js":138,"./systemMetadata/systemMetadata.directive.js":139,"./targetBlank/targetBlank.directive.js":140,"./timeRange/timeRange.directive.js":141,"./topbar/alerts/alerts.directive.js":142,"./topbar/topbar.directive.js":143,"./topics/otherTopic/otherTopic.directive.js":144,"./topics/topicAdminHelpBar/topicAdminHelpBar.directive.js":145,"./topics/topicDetails/topicDetails.directive.js":146,"./topics/topicPreviewModal/topicPreviewModal.js":147,"./topics/topicRuleFilters/topicRuleFilters.controller.js":148,"./topics/topicRuleList/topicRuleList.directive.js":149,"./typeIcon/typeIcon.directive.js":150,"./ui-bootstrap-custom.js":151,"./userInfo/userInfo.directive.js":152,"./validators/kbaseSolution/kbaseSolution.directive.js":153,"./yesNo/yesNo.directive.js":154}],84:[function(require,module,exports){'use strict';var componentsModule=require('../');function insightsLogo(){return{templateUrl:'js/components/insightsLogo/insightsLogo.html',restrict:'EC',replace:true};}componentsModule.directive('insightsLogo',insightsLogo);},{"../":83}],85:[function(require,module,exports){'use strict';var componentsModule=require('../');function insightsTagline(){return{templateUrl:'js/components/insightsTagline/insightsTagline.html',restrict:'EC',replace:true};}componentsModule.directive('insightsTagline',insightsTagline);},{"../":83}],86:[function(require,module,exports){'use strict';inventoryActionsCtrl.$inject=['$scope','$rootScope','sweetAlert','InventoryService','FilterService','SystemsService','MaintenanceService','$timeout','gettextCatalog','TemplateService','Products','ListTypeService','InsightsConfig','Group','GroupService'];var componentsModule=require('../../');var find=require('lodash/find');var partition=require('lodash/partition');var groupBy=require('lodash/groupBy');var map=require('lodash/map');var UNREGISTER_TYPE_CONFICT='js/components/inventory/inventoryActions/unregisterConflict.html';/** * @ngInject */function inventoryActionsCtrl($scope,$rootScope,sweetAlert,InventoryService,FilterService,SystemsService,MaintenanceService,$timeout,gettextCatalog,TemplateService,Products,ListTypeService,InsightsConfig,Group,GroupService){$scope.getTotal=InventoryService.getTotal;$scope.listTypes=ListTypeService.types();$scope.listType=ListTypeService.getType;$scope.config=InsightsConfig;$scope.plans=MaintenanceService.plans;$scope.getSortLabel=function(field){var item=find($scope.sortItems,{field:field});return item.label;};$scope.$watch('checkboxes.items',function(){var selected=$scope.systemsToAction();if(selected.length&&!$scope.isUnregistrableSelected()){$scope.unregisterButtonTooltip=gettextCatalog.getString('Selected systems cannot be unregistered alone. '+'Remove their respective deployment.');}else{$scope.unregisterButtonTooltip=null;}},true);$scope.doUnregisterSelected=function(){var selected=$scope.systemsToAction();var parts=partition(selected,$scope.canUnregisterSystem);function unregister(){SystemsService.unregisterSelectedSystems(parts[0]);}if(!parts[1].length){unregister();}else{// some systems that cannot be unregistered on their own (e.g. docker image) // are selected --> warn user about that var roles=groupBy(parts[1],'system_type_id');roles=map(roles,function(systems){var system=systems[0];return{name:Products[system.product_code].roles[system.role].fullName,count:systems.length};});TemplateService.renderTemplate(UNREGISTER_TYPE_CONFICT,{roles:roles,validCount:parts[0].length}).then(function(html){var opts={title:gettextCatalog.getPlural(parts[1].length,'1 system cannot be unregistered','{{count}} systems cannot be unregistered',{count:parts[1].length}),html:html};return sweetAlert(opts);}).then(unregister);}};$scope.isUnregistrableSelected=function(){return $scope.systemsToAction().filter($scope.canUnregisterSystem).length>0;};$scope.isFixableSelected=function(){return $scope.systemsToAction().some(function(system){return system.report_count;});};$scope.sortItems=[{field:'toString',label:gettextCatalog.getString('Name')},{field:'created_at',label:gettextCatalog.getString('Registration Date')},{field:'last_check_in',label:gettextCatalog.getString('Last Check In')},{field:'report_count',label:gettextCatalog.getString('Number of Actions')},{field:'system_type_id',label:gettextCatalog.getString('Type')}];$scope.getSortDirection=InventoryService.getSortDirection;$scope.getSortField=InventoryService.getSortField;$scope.sortLabel='Hostname';$scope.getSortType=function(){var sortField=InventoryService.getSortField();if(sortField==='created_at'||sortField==='last_check_in'){return'num';}else if(sortField==='report_count'){return'amount';}else{return'alpha';}};$scope.doSort=function(item){InventoryService.setSortField(item.field);$scope.sortLabel=item.label;//default hostname to ascending //default registration date, last check in, # actions to DESC //because that's what I would want most often when //selecting to sort by each if(item.field==='hostname'){InventoryService.setSortDirection('ASC');}else{InventoryService.setSortDirection('DESC');}FilterService.doFilter();};$scope.toggleSortDirection=function(){if(InventoryService.getSortDirection()==='ASC'){InventoryService.setSortDirection('DESC');}else{InventoryService.setSortDirection('ASC');}FilterService.doFilter();};$scope.getSortIconClasses=function(){return{'fa-sort-alpha-asc':$scope.getSortDirection()==='ASC'&&$scope.getSortType()==='alpha','fa-sort-alpha-desc':$scope.getSortDirection()==='DESC'&&$scope.getSortType()==='alpha','fa-sort-numeric-asc':$scope.getSortDirection()==='ASC'&&$scope.getSortType()==='num','fa-sort-numeric-desc':$scope.getSortDirection()==='DESC'&&$scope.getSortType()==='num','fa-sort-amount-asc':$scope.getSortDirection()==='ASC'&&$scope.getSortType()==='amount','fa-sort-amount-desc':$scope.getSortDirection()==='DESC'&&$scope.getSortType()==='amount'};};$scope.addToPlan=function(existingPlan){var systems=$scope.systemsToAction();if(!systems.length){return;}MaintenanceService.showMaintenanceModal(systems,null,existingPlan);};$scope.systemsToAction=function(){// assume true if the system is not shown in the view if($scope.reallyAllSelected){return $scope.allSystems.filter(function(system){return!$scope.checkboxes.items.hasOwnProperty(system.system_id)||$scope.checkboxes.items.hasOwnProperty(system.system_id)&&$scope.checkboxes.items[system.system_id]===true;});}else{return $scope.checkboxes.getSelected($scope.systems);}};$scope.removeFromGroup=function(){Group.removeSystems(Group.current(),map($scope.systemsToAction(),'system_id')).then($scope.reloadInventory);};$scope.groupSystems=GroupService.groupSystems;}function inventoryActions(){return{templateUrl:'js/components/inventory/inventoryActions/inventoryActions.html',restrict:'E',replace:true,controller:inventoryActionsCtrl};}componentsModule.directive('inventoryActions',inventoryActions);},{"../../":83,"lodash/find":491,"lodash/groupBy":497,"lodash/map":522,"lodash/partition":532}],87:[function(require,module,exports){'use strict';var componentsModule=require('../../');function inventoryFilters(){return{templateUrl:'js/components/inventory/inventoryFilters/inventoryFilters.html',restrict:'E',replace:true};}componentsModule.directive('inventoryFilters',inventoryFilters);},{"../../":83}],88:[function(require,module,exports){'use strict';var componentsModule=require('../../');/** * @ngInject */function systemCtrl(){}function system(){return{templateUrl:'js/components/inventory/system/system.html',restrict:'E',replace:true,controller:systemCtrl};}componentsModule.directive('system',system);},{"../../":83}],89:[function(require,module,exports){'use strict';linkGroupCtrl.$inject=['$scope','$state','InventoryService','SystemsService'];var componentsModule=require('../');/** * @ngInject */function linkGroupCtrl($scope,$state,InventoryService,SystemsService){$scope.collapsed=true;$scope.toggleCollapsed=function(){$scope.collapsed=!$scope.collapsed;};$scope.hasReports=function(){var hasReports=false;$scope.group.forEach(function(system){if(system.report_count>0&&!hasReports){hasReports=true;}});return hasReports;};SystemsService.getSystemTypeAsync($scope.typeid).then(function(type){var text='';if(type){text=$scope.group.length+' '+type.displayName;if($scope.group.length!==1){text+='s';}}$scope.linkGroupText=text;});$scope.getLinkReportCount=function(link){var report_count=link.report_count;var response='';if(report_count!==undefined&&report_count!==null){response=report_count+' Action';if(report_count!==1){response=response+'s';}}return response;};$scope.getLinkStatus=function(link){if(link.report_count>0){return'["fa-exclamation-circle", "fail"]';}else{return'["fa-check-circle", "success"]';}};$scope.getLinkName=function(link){if(link.display_name!==undefined&&link.display_name!==null&&link.display_name!==''){return link.display_name;}else{return link.hostname;}};$scope.goToSystem=function(system){InventoryService.showSystemModal(system);};}function linkGroup(){return{templateUrl:'js/components/linkGroup/linkGroup.html',restrict:'E',controller:linkGroupCtrl,scope:{group:'=',typeid:'='}};}componentsModule.directive('linkGroup',linkGroup);},{"../":83}],90:[function(require,module,exports){/*global require*/'use strict';ListTypeCtrl.$inject=['$scope','ListTypeService'];var componentsModule=require('../');/** * @ngInject */function ListTypeCtrl($scope,ListTypeService){$scope.setListType=ListTypeService.setType;$scope.getListType=ListTypeService.getType;$scope.listTypes=ListTypeService.types();}/** * @ngInject */function listType(){return{templateUrl:'js/components/listType/listType.html',restrict:'E',replace:true,transclude:true,controller:ListTypeCtrl};}componentsModule.directive('listType',listType);},{"../":83}],91:[function(require,module,exports){'use strict';expandableSystemListCtrl.$inject=['$scope','gettextCatalog','InventoryService'];var componentsModule=require('../../');/** * @ngInject */function expandableSystemListCtrl($scope,gettextCatalog,InventoryService){$scope.toggle=function(){$scope.limit=$scope.limit?undefined:3;};$scope.toggle();$scope.InventoryService=InventoryService;}function expandableSystemList(){return{templateUrl:'js/components/maintenance/expandableSystemList/expandableSystemList.html',restrict:'E',controller:expandableSystemListCtrl,replace:true,scope:{systems:'<'}};}componentsModule.directive('expandableSystemList',expandableSystemList);},{"../../":83}],92:[function(require,module,exports){'use strict';maintenanceCategorySelectCtrl.$inject=['$rootScope','$scope','gettextCatalog','Events','MaintenanceService'];var componentsModule=require('../../');var find=require('lodash/find');/** * @ngInject */function maintenanceCategorySelectCtrl($rootScope,$scope,gettextCatalog,Events,MaintenanceService){var defaultCategory='all';$scope.plans=MaintenanceService.plans;$scope.options=[{id:'all',label:gettextCatalog.getString('All'),tag:null},{id:'suggested',label:gettextCatalog.getString('Suggestions'),tag:gettextCatalog.getString('Maintenance: Suggestions')},{id:'past',label:gettextCatalog.getString('Past plans'),tag:gettextCatalog.getString('Maintenance: Past plans')},{id:'unscheduled',label:gettextCatalog.getString('Not scheduled plans'),tag:gettextCatalog.getString('Maintenance: Not scheduled plans')},{id:'future',label:gettextCatalog.getString('Future plans'),tag:gettextCatalog.getString('Maintenance: Future plans')}];$scope.select=function(option){$scope.selected=option;$scope.onSelect({category:option.id});$rootScope.$broadcast(Events.filters.tag,getTag(option),Events.filters.maintenanceCategorySelect);};function getTag(option){return option.tag;}$scope.$watch('category',function(category){$scope.selected=find($scope.options,{id:category});$rootScope.$broadcast(Events.filters.tag,getTag($scope.selected),Events.filters.maintenanceCategorySelect);});$scope.$on(Events.filters.reset,function(){$scope.select(find($scope.options,{id:defaultCategory}));});$scope.$on(Events.filters.removeTag,function(event,filter){if(filter===Events.filters.maintenanceCategorySelect){$scope.select(find($scope.options,{id:defaultCategory}));}});$rootScope.$broadcast(Events.filters.tag,getTag({id:defaultCategory}),Events.filters.maintenanceCategorySelect);}function maintenanceCategorySelect(){return{templateUrl:'js/components/maintenance/maintenanceCategorySelect/'+'maintenanceCategorySelect.html',restrict:'E',controller:maintenanceCategorySelectCtrl,replace:true,scope:{category:'=',onSelect:'&'}};}componentsModule.directive('maintenanceCategorySelect',maintenanceCategorySelect);},{"../../":83,"lodash/find":491}],93:[function(require,module,exports){/*global require*/'use strict';maintenanceModalCtrl.$inject=['$scope','$rootScope','$modalInstance','rule','systems','MaintenanceService','System','Utils','$state','gettextCatalog','ModalUtils','$q','DataUtils','existingPlan','Subset','Rule','Group','Events','InsightsConfig'];var componentsModule=require('../../');var find=require('lodash/find');var indexBy=require('lodash/keyBy');var map=require('lodash/map');var flatMap=require('lodash/flatMap');var constant=require('lodash/constant');var sortBy=require('lodash/sortBy');var MODES={rule:'rule',system:'system',multi:'multi'};/** * @ngInject */function maintenanceModalCtrl($scope,$rootScope,$modalInstance,rule,systems,MaintenanceService,System,Utils,$state,gettextCatalog,ModalUtils,$q,DataUtils,existingPlan,Subset,Rule,Group,Events,InsightsConfig){$scope.MODES=MODES;$scope.tableEdit=true;$scope.selected={};$scope.plans=MaintenanceService.plans;$scope.loader=new Utils.Loader();$scope.loadingSystems=false;$scope.rule=rule;$scope.systems=systems;$scope.Group=Group;$scope.config=InsightsConfig;if(angular.isObject(existingPlan)){$scope.newPlan=false;MaintenanceService.plans.load().then(function(){$scope.selected.plan=find(MaintenanceService.plans.all,{maintenance_id:existingPlan.maintenance_id});});}else if(existingPlan===true){$scope.newPlan=false;}else{$scope.newPlan=true;}if(systems&&systems.length===1){$scope.selected.system=systems[0];}// normalize - if multiselect contains only one system treat as simple case if(!rule&&systems){if(systems.length===1){systems=false;$scope.mode=MODES.system;$scope.systemSelection='system';}else{$scope.mode=MODES.multi;$scope.systemSelection='preselected';}}else if(rule&&systems){$scope.mode=MODES.rule;$scope.systemSelection='preselected';}else if(!rule&&!systems){$scope.mode=MODES.multi;$scope.systemSelection='all';}else{throw new Error('Invalid parameters '+rule+', '+systems);}if(Group.groups.length&&InsightsConfig.isGroupsEnabled){if(Group.current().id){$scope.systemSelection='group';$scope.selected.group=Group.current();}else{$scope.selected.group=sortBy(Group.groups,['display_name','id'])[0];}}$scope.getPlan=function(){if(!$scope.newPlan&&$scope.selected.plan){return $scope.selected.plan;}return{};};function rebuildTable(){$scope.plan=$scope.newPlan?{}:$scope.selected.plan;if($scope.mode===MODES.multi){$scope.tableParams=buildMultiTableParams();}else{$scope.tableParams=buildTableParams();}$scope.$broadcast(Events.planner.reloadTable);}$scope.$watchGroup(['selected.plan','newPlan','selected.group','selected.system'],rebuildTable);$scope.systemSelectionChanged=function(selection){$scope.systemSelection=selection;if(selection==='system'){$scope.mode=MODES.system;$scope.tableParams=null;if($scope.selected.system){rebuildTable();}return;}$scope.mode=MODES.multi;rebuildTable();};function preselectAvailableActions(params,fn){var overriden=params.getAvailableActions;params.getAvailableActions=function(){return overriden.call(params).then(function(available){available.forEach(function(action){action.selected=fn(action);});return available;});};}function buildTableParams(){$scope.noActions=false;var params=null;if($scope.mode===MODES.system){// if the system is part of the plan already, use that object var item=$scope.selected.system;if(!$scope.newPlan&&$scope.selected.plan&&$scope.selected.plan.systems){item=find($scope.selected.plan.systems,{system_id:item.system_id})||item;}params=MaintenanceService.actionTableParams(item,$scope.getPlan(),item.actions,$scope.loader);if(rule){// preselect the given rule if any preselectAvailableActions(params,function(action){return action.rule.rule_id===rule.rule_id;});}}else if($scope.mode===MODES.rule){// if the rule is part of the plan already, use that object var _item=rule;if(!$scope.newPlan&&$scope.selected.plan&&$scope.selected.plan.rules){_item=find($scope.selected.plan.rules,{id:rule.rule_id})||_item;}params=MaintenanceService.systemTableParams(_item,$scope.getPlan(),_item.actions,$scope.loader);// index selected systems by their ID var selectedSystemsById=indexBy(systems,'system_id');// only display available systems selected on the previous screen // TODO: we can eliminate this altogether once we switch to creating plan // actions from (system_id, rule_id) as opposed to report.id var overriden=params.getAvailableActions;params.getAvailableActions=function(){return overriden.call(params).then(function(available){return available.filter(function(action){return action.system.system_id in selectedSystemsById;});});};// and preselect them all preselectAvailableActions(params,constant(true));// suppress actions already in plan params.getActions=constant([]);}// if this is a new plan then we need to create it on-save // instead of updating existing plan var update=params.update;params.update=function(toAdd){if($scope.selected.plan&&!$scope.newPlan){return MaintenanceService.promptResolutionIfNeeded(function(){return update.call(params,toAdd);},$scope.selected.plan);}return MaintenanceService.promptResolutionIfNeeded(function(){return MaintenanceService.plans.create({name:$scope.newPlanAlias,add:toAdd}).then(function(plan){$scope.selected.plan=plan;return plan;});});};return params;}function buildMultiTableParams(){var availableActions=getAvailableActionsForMultiTable();return{getActions:function getActions(){return[];},getAvailableActions:function getAvailableActions(){return availableActions;},implicitOrder:{predicate:'rule.severityNum',reverse:true},save:function save(toAdd){// cartesian product of selected systems and rules // the API is clever enough to filter out combinations with no reports var add=flatMap(toAdd,function(rule){if(rule.systemIds){return map(rule.systemIds,function(system_id){return{system_id:system_id,rule_id:rule.rule.rule_id};});}else if(rule.groupId){return[{group_id:rule.groupId,rule_id:rule.rule.rule_id}];}else{return[{rule_id:rule.rule.rule_id}];}});var payload={name:$scope.newPlanAlias,add:add};if(!$scope.newPlan&&$scope.selected.plan){return MaintenanceService.promptResolutionIfNeeded(function(){return MaintenanceService.plans.update($scope.selected.plan,payload);},$scope.selected.plan);}return MaintenanceService.promptResolutionIfNeeded(function(){return MaintenanceService.plans.create(payload).then(function(plan){$scope.selected.plan=plan;return plan;});});}};}function getAvailableActionsForMultiTable(){if($scope.systemSelection==='preselected'){$scope.fixableSystems=$scope.systems.filter(function(system){return system.report_count>0;});var systemIds=map($scope.fixableSystems,'system_id');// if this is ever used in Satellite it should send the real branch_id instead // of null here return getActionsForSystems(systemIds);}else if($scope.systemSelection==='all'){return Rule.getRulesWithHits().then(function(res){$scope.noActions=res.data.resources.length===0;return map(res.data.resources,ruleToActionMapper());});}else if($scope.systemSelection==='group'){if(!$scope.selected.group||!$scope.selected.group.id){throw new Error('No group selected');// should never happen }return Rule.getRulesWithHits($scope.selected.group.id).then(function(res){$scope.noActions=res.data.resources.length===0;return map(res.data.resources,ruleToActionMapper(null,$scope.selected.group.id));});}else{throw new Error('Unexpected systemSelection value: '+$scope.systemSelection);}}$scope.close=function(){$modalInstance.dismiss('close');};$scope.postSave=function(){$modalInstance.close();$state.go('app.maintenance',{maintenance_id:$scope.selected.plan.maintenance_id});};$scope.$watch('plans.all',function(){if(MaintenanceService.plans.future){$scope.availablePlans=MaintenanceService.plans.future.concat(MaintenanceService.plans.unscheduled);if(!$scope.selected.plan&&$scope.availablePlans.length){$scope.selected.plan=$scope.availablePlans[0];}}});$scope.planGroupFn=function(plan){if(plan.start){return gettextCatalog.getString('Future plans');}else{return gettextCatalog.getString('Not scheduled plans');}};$scope.planOrderFn=function(groups){return sortBy(groups,['items.length']);};$scope.getPlanName=function(plan){if(plan.name&&plan.name.length){return plan.name;}return gettextCatalog.getString('Unnamed plan');};function ruleToActionMapper(systemIds,groupId){return function(rule){var value={id:rule.rule_id,display:rule.description,mid:rule.rule_id,rule:rule,done:false};if(systemIds){value.systemIds=systemIds;}if(groupId){value.groupId=groupId;}return value;};}function getActionsForSystems(systemIds){if(systemIds.length===0){$scope.noActions=true;return $q.resolve([]);}return Subset.create(null,systemIds).then(function(res){return Subset.getRulesWithHits(res.data.hash).then(function(res){$scope.noActions=res.data.resources.length===0;return map(res.data.resources,ruleToActionMapper(systemIds));});});}$scope.searchSystems=function(value){var params={page_size:30,page:0,report_count:'gt0'};if(value&&value.length){params.search_term=value;}var key=String(value);$scope.loadingSystems=key;return System.getSystemsLatest(params).then(function(res){$scope.availableSystems=res.data.resources;}).finally(function(){if($scope.loadingSystems===key){$scope.loadingSystems=false;}});};$scope.searchSystems().then(function(){if($scope.availableSystems.length&&!$scope.selected.system){$scope.selected.system=sortBy($scope.availableSystems,['toString','system_id'])[0];}});MaintenanceService.plans.load(false);ModalUtils.suppressEscNavigation($modalInstance);}function maintenanceModal(){return{templateUrl:'js/components/maintenance/'+'maintenanceModal/maintenanceModal.html',restrict:'E',controller:maintenanceModalCtrl,replace:true};}componentsModule.directive('maintenanceModal',maintenanceModal);componentsModule.controller('maintenanceModalCtrl',maintenanceModalCtrl);},{"../../":83,"lodash/constant":483,"lodash/find":491,"lodash/flatMap":493,"lodash/keyBy":518,"lodash/map":522,"lodash/sortBy":540}],94:[function(require,module,exports){'use strict';maintenancePlan.$inject=['$document'];maintenancePlanCtrl.$inject=['$location','$modal','$rootScope','$scope','$state','$stateParams','$timeout','$document','gettextCatalog','sweetAlert','DataUtils','Events','Group','InsightsConfig','Maintenance','MaintenanceService','SystemsService','Utils'];var componentsModule=require('../../');var assign=require('lodash/assign');var map=require('lodash/map');var find=require('lodash/find');var FileSaver=require('file-saver');var parseHeader=require('parse-http-header');var some=require('lodash/some');var get=require('lodash/get');var sortBy=require('lodash/sortBy');var swal=require('sweetalert2');var filter=require('lodash/filter');var flatMap=require('lodash/flatMap');var uniqBy=require('lodash/uniqBy');var moment=require('moment-timezone');var TAB_MAPPING={undefined:0,systems:1,playbook:2};//This handles the "basic" edit mode of a plan. Activated by clicking the hidden //'Click to edit this plan' button next to the plan name. function BasicEditHandler(plan,Maintenance,Utils,cb){this.active=false;this.plan=plan;this.Maintenance=Maintenance;this.Utils=Utils;this.cb=cb;}BasicEditHandler.prototype.init=function(){if(this.plan){this.name=this.plan.name;this.description=this.plan.description;if(this.plan.start){this.start=moment(this.plan.start);var d=this.start;// we need to convert to Date (which possibly uses a different timezone) // so that we can bind the input to it this.time=new Date(d.year(),d.month(),d.day(),d.hour(),d.minute());this.duration=Math.round((this.plan.end-this.plan.start)/(60*1000));}else{this.start=null;this.time=null;this.duration=null;}}else{this.name='';this.description='';this.start=moment().startOf('day');this.dateChanged(this.start);}};BasicEditHandler.prototype.dateChanged=function(value){if(value&&!this.time){this.time=new Date(this.start.year(),this.start.month(),this.start.day(),22,0);this.duration=60;this.sync();}};BasicEditHandler.prototype.sync=function(){if(this.start&&this.time){this.start.hours(this.time.getHours());this.start.minutes(this.time.getMinutes());}};BasicEditHandler.prototype.toggle=function(){if(!this.active){this.init();}this.active=!this.active;};BasicEditHandler.prototype.getStart=function(){if(this.start){return this.start.clone().toDate();}return null;};BasicEditHandler.prototype.getEnd=function(){if(this.start){return this.start.clone().add(Math.max(this.duration,1),'m').toDate();}return null;};BasicEditHandler.prototype.save=function(){this.sync();if(this.cb){return this.cb(this.name,this.description,this.getStart(),this.getEnd(),this);}};/** * @ngInject */function maintenancePlanCtrl($location,$modal,$rootScope,$scope,$state,$stateParams,$timeout,$document,gettextCatalog,sweetAlert,DataUtils,Events,Group,InsightsConfig,Maintenance,MaintenanceService,SystemsService,Utils){$scope.pager=new Utils.Pager(10);$scope.loader=new Utils.Loader();$scope.exportPlan=Maintenance.exportPlan;$scope.error=null;$scope.MaintenanceService=MaintenanceService;$scope.ansibleRunner=InsightsConfig.ansibleRunner;$scope.editDateTooltip=gettextCatalog.getString('Date, time and duration of a maintenance window can be defined here. '+'If a maintenance window for this plan is set then Insights will verify that '+'all the actions in this plan are resolved once the window ends. Insights will '+'warn you this plan is not fully resolved when the maintenance window ends.');var CURRENT_GROUP_PREFIX=gettextCatalog.getString('Current Group');$scope.editBasic=new BasicEditHandler($scope.plan,Maintenance,Utils,$scope.loader.bind(function(name,description,start,end,handler){$scope.plan.description=description;return Maintenance.updatePlan($scope.plan.maintenance_id,{name:name,description:description,start:start,end:end}).then(function(){handler.active=false;if(!Utils.datesEqual($scope.plan.end,end)||!Utils.datesEqual($scope.plan.start,start)){$scope.loadPlans(true).then(function(){$scope.scrollToPlan($scope.plan.maintenance_id);});}else{$scope.plan.name=name;handler.plan=$scope.plan;}});}));$scope.removeActions=$scope.loader.bind(function(actions){return MaintenanceService.plans.update($scope.plan,{delete:map(actions,'id')});});$scope.silence=$scope.loader.bind(function(){$scope.plan.silenced=true;Maintenance.silence($scope.plan).then(function(){if($scope.edit.isActive($scope.plan.maintenance_id)){$scope.edit.toggle($scope.plan.maintenance_id);}});});$scope.delete=function(){sweetAlert({text:gettextCatalog.getString('You will not be able to recover this maintenance plan')}).then($scope.loader.bind(function(){$scope.edit.deactivate($scope.plan.maintenance_id);return Maintenance.deletePlan($scope.plan);}));};$scope.showSystemModal=MaintenanceService.showSystemModal;$scope.addSystem=new AddActionSelectionHandler($scope);$scope.addRule=new AddActionSelectionHandler($scope);$scope.systemTableParams=MaintenanceService.systemTableParams;$scope.actionTableParams=MaintenanceService.actionTableParams;function tryFindItemInPlan(item,systems){if(systems){return find($scope.plan.rules,{id:item.rule_id});}else{return find($scope.plan.systems,{system_id:item.system_id});}}$scope.tableParams=function(item,systems,newTable){var plannedItem=tryFindItemInPlan(item,systems);var actions=plannedItem?plannedItem.actions:[];var loader=newTable?$scope.loader:undefined;if(systems){return MaintenanceService.systemTableParams(plannedItem||item,$scope.plan,actions,loader);}else{return MaintenanceService.actionTableParams(plannedItem||item,$scope.plan,actions,loader);}};$scope.minimize=function(){// don't minimize right away because that messes up with the global click handler $timeout(function(){$scope.error=null;$scope.edit.deactivate($scope.plan.maintenance_id);});};$scope.export=function(){Maintenance.exportPlan($scope.plan.maintenance_id);};$scope.downloadPlaybook=function(){if(!$scope.ansibleSupport){return;}if($scope.selectTab){$scope.selectTab('playbook');}Maintenance.downloadPlaybook($scope.plan.maintenance_id).then(function(response){if(response.status===200){var disposition=response.headers('content-disposition');var filename=parseHeader(disposition).filename.replace(/"/g,'');var blob=new Blob([response.data],{type:response.headers('content-type')});FileSaver.saveAs(blob,filename);}});};$scope.runPlaybook=function(button){if(!$scope.ansibleRunner){return;}return $scope.ansibleRunner($location,$scope.plan.maintenance_id,button);};$scope.dateChanged=function(unused,value,explicit){if(explicit){$scope.editBasic.dateChanged(value);}};$scope.minimize=function(){if($scope.editBasic.active){$scope.editBasic.toggle();}$scope.error=null;$scope.edit.deactivate($scope.plan.maintenance_id);};$scope.$on('telemetry:esc',function($event){if($event.defaultPrevented){return;}if(swal.isVisible()){return;}if($scope.edit.isActive($scope.plan.maintenance_id)&&!$scope.editBasic.active){$scope.error=null;$scope.edit.deactivate($scope.plan.maintenance_id);}});$scope.update=$scope.loader.bind(function(data){$scope.error=null;return Maintenance.updatePlan($scope.plan.maintenance_id,data);});$scope.hidden=function(value){function cb(){var data={hidden:value};if($scope.plan.suggestion===Maintenance.SUGGESTION.REJECTED){data.suggestion=Maintenance.SUGGESTION.PROPOSED;}$scope.update(data).then(function(){assign($scope.plan,data);$rootScope.$broadcast(Events.planner.planChanged,$scope.plan.maintenance_id);});}if(!value){sweetAlert({text:gettextCatalog.getString('You are about to send a plan suggestion to the customer.')}).then(cb);}else{cb();}};$scope.accept=function(){var data={suggestion:Maintenance.SUGGESTION.ACCEPTED};$scope.update(data).then(function(){assign($scope.plan,data);$rootScope.$broadcast(Events.planner.planChanged,$scope.plan.maintenance_id);$scope.scrollToPlan($scope.plan.maintenance_id);});};$scope.reject=function(){var data={suggestion:Maintenance.SUGGESTION.REJECTED};$scope.update(data).then(function(){assign($scope.plan,data);$scope.plan.hidden=true;$rootScope.$broadcast(Events.planner.planChanged,$scope.plan.maintenance_id);if(!MaintenanceService.plans.suggested.length){$scope.setCategory('unscheduled');}});};$scope.isReadOnly=function(){return $scope.plan.isReadOnly()&&!$scope.isInternal;};$scope.registerGroupChangeListener=function(uiSelect){var unregister=$rootScope.$on('group:change',function(){if(uiSelect.refreshItems){uiSelect.refreshItems();}});$scope.$on('$destroy',unregister);};$scope.groupBySystemType=function(system){var group=Group.current();if(group.display_name!==undefined){if(find(group.systems,{system_id:system.system_id})){return CURRENT_GROUP_PREFIX+': '+group.display_name;}}// this is safe as system select won't be shown before system types are loaded return get(SystemsService.getSystemTypeUnsafe(system.system_type_id),'displayName');};$scope.groupFilter=function(groups){return sortBy(groups,[function(group){return!group.name.startsWith(CURRENT_GROUP_PREFIX);},'name']);};function checkAnsibleSupport(){$scope.error=null;$scope.ansibleSupport=some($scope.plan.actions,'rule.ansible');}$scope.$watch('plan.actions',checkAnsibleSupport);$scope.playbookTabLoader=new Utils.Loader(false);$scope.prepareAnsibleTab=$scope.playbookTabLoader.bind(function(){return SystemsService.getSystemTypesAsync().then(function(){return Maintenance.getPlayMetadata($scope.plan.maintenance_id);}).then(function(res){$scope.plays=res.data;$scope.plays.forEach(function(play){play.systemType=SystemsService.getSystemTypeUnsafe(play.system_type_id);play.systems=map(filter($scope.plan.actions,function(action){return action.rule.rule_id===play.rule.rule_id&&action.system.system_type_id===play.system_type_id;}),'system');});$scope.systemsToReboot=uniqBy(flatMap(filter($scope.plays,'ansible_resolutions[0].needs_reboot'),'systems'),'system_id');});});$scope.setupTabActivator=function(value){value.$watch('tabs',function(tabs){if(tabs){$scope.selectTab=function(name,ignoreUrl){var tab=TAB_MAPPING[name];if(tab!==undefined){tabs[tab].select();if(!ignoreUrl){$scope.tabSelected(name);}}};}});};$scope.tabSelected=function(){// this double-assignment is a workaround for how ui-bootstrap handles selection $scope.tabSelected=function(name){$state.transitionTo($state.current,{maintenance_id:$stateParams.maintenance_id,tab:name},{notify:false,reload:false,location:'replace'});};if($stateParams.maintenance_id&&$stateParams.tab&&$scope.selectTab){$scope.selectTab($stateParams.tab,true);}};$scope.resolutionModal=function(play){return MaintenanceService.resolutionModal($scope.plan,play,0,0).then($scope.prepareAnsibleTab);};$scope.addActions=function(){return MaintenanceService.showMaintenanceModal(null,null,$scope.plan).then($scope.prepareAnsibleTab);};$scope.allowRebootChanged=function(){return Maintenance.updatePlan($scope.plan.maintenance_id,{allow_reboot:$scope.plan.allow_reboot});};function deleteHandler(event){if($scope.edit.isActive($scope.plan.maintenance_id)&&event.keyCode===46){$scope.delete();}}$document.on('keydown',deleteHandler);$scope.$on('$destroy',function(){$document.off('keydown',deleteHandler);});}function AddActionSelectionHandler($scope){var self=this;self.reset();$scope.$watch('edit.isActive(plan.maintenance_id)',function(){self.reset();});}AddActionSelectionHandler.prototype.reset=function(){this.selected=null;};function maintenancePlan($document){function isContainedBy($event,className){var elements=$document[0].getElementsByClassName(className);for(var i=0;i$scope.length){$scope.trimmedText=$scope.text.slice(0,$scope.length)+'…';$scope.showTooltip=true;}else{$scope.trimmedText=$scope.text;}}function trimmedText(){return{templateUrl:'js/components/misc/trimmedText/trimmedText.html',restrict:'E',replace:true,transclude:true,controller:TrimmedTextCtrl,scope:{length:'@',text:'@'}};}componentsModule.directive('trimmedText',trimmedText);},{"../../":83}],101:[function(require,module,exports){'use strict';var componentsModule=require('../');var c3=require('c3');/** * @ngInject */function mitigationTrendController(){// puke chart to page c3.generate({bindto:'.mitigation-chart',size:{width:400,height:300},data:{columns:[['Security',30,200,100,400,150,250],['Availability',50,20,10,40,15,25],['Stability',130,150,200,300,200,100],['Performance',230,190,300,500,300,400]],type:'spline'}});}function mitigationTrend(){return{templateUrl:'js/components/mitigationTrend/mitigationTrend.html',restrict:'E',controller:mitigationTrendController,replace:true};}componentsModule.directive('mitigationTrend',mitigationTrend);},{"../":83,"c3":262}],102:[function(require,module,exports){'use strict';insightsNamespacerCtrl.$inject=['$scope','$state'];var componentsModule=require('../');/** * @ngInject */function insightsNamespacerCtrl($scope,$state){var routeClass=this;//jshint ignore:line $scope.$on('$stateChangeSuccess',function(){routeClass.current='insights-'+$state.current.name.replace(/\./g,'-');});}function insightsNamespacer(){return{restrict:'A',controller:insightsNamespacerCtrl,controllerAs:'insightsNamespacer'};}componentsModule.directive('insightsNamespacer',insightsNamespacer);},{"../":83}],103:[function(require,module,exports){'use strict';rhaNotificationsCtrl.$inject=['$scope','$state','AlertService','RhaTelemetryActionsService'];var componentsModule=require('../');/** * @ngInject */function rhaNotificationsCtrl($scope,$state,AlertService,RhaTelemetryActionsService){$scope.alerts=AlertService.alerts;$scope.dismiss=function(index,type){$scope.alerts.splice(index,1);if(type==='http'&&$state.current.actions){RhaTelemetryActionsService.reload();}else if(type==='http'){$state.reload();}};}function rhaNotifications(){return{templateUrl:'js/components/notifications/notifications.html',restrict:'C',replace:true,controller:rhaNotificationsCtrl};}componentsModule.directive('rhaNotifications',rhaNotifications);},{"../":83}],104:[function(require,module,exports){/*global require*/'use strict';ArticleModalCtrl.$inject=['$scope','OverviewService','$modalInstance','Article','InsightsConfig','ModalUtils'];var componentsModule=require('../../');var pick=require('lodash/pick');var find=require('lodash/find');/** * @ngInject */function ArticleModalCtrl($scope,OverviewService,$modalInstance,Article,InsightsConfig,ModalUtils){$scope.selected={};$scope.close=function(){$modalInstance.dismiss('close');};$scope.save=function(){OverviewService.update($scope.selected.article.id,$scope.article).then($scope.close);};$scope.preview=function(){Article.preview($scope.article).then(function(res){$scope.previewData=res.data;});};$scope.load=function(id){Article.get(id).then(function(res){$scope.article=pick(res.data,['title','content']);});};Article.getArticles().then(function(res){var current={id:InsightsConfig.overviewKey};$scope.articles=res.data;if(!find($scope.articles,current)){$scope.articles.push(current);$scope.selected.article=current;}});$scope.$watch('selected.article',function(value){if(value&&value.id){$scope.load(value.id);$scope.previewData=null;}});$scope.selected.article=OverviewService.article;ModalUtils.suppressEscNavigation($modalInstance);}componentsModule.controller('ArticleModalCtrl',ArticleModalCtrl);},{"../../":83,"lodash/find":491,"lodash/pick":533}],105:[function(require,module,exports){'use strict';generateBarCtrl.$inject=['$scope','$element'];var componentsModule=require('../../../../');var c3=require('c3');/** * @ngInject */function generateBarCtrl($scope,$element){$scope.buildBar=function(options){$scope.chartMax=options.chartMax;$scope.goalsBar=c3.generate({bindto:$element[0].getElementsByClassName('bar-chart-wrapper')[0],size:{height:200,width:100},bar:{width:{ratio:0.5},expand:true},data:{columns:[['maxSystems',options.chartMax],['systemsRegistered',options.systemCount<=options.chartMax?options.systemCount:options.chartMax]],type:'bar'},axis:{x:{show:false},y:{show:false}},legend:{show:false},grid:{x:{show:true},y:{show:true}},tooltip:{format:{/*...*/},contents:function contents(d){var text;text='
'+''+'Register more systems to complete the goal'+''+'
';return d[1].value!=d[0].value?text:'';//jshint ignore:line }}});$scope.$watch('systemCount',function(newValue){if(newValue!==undefined){$scope.goalsBar.load({columns:[['maxSystems',$scope.chartMax],['systemsRegistered',newValue<=$scope.chartMax?newValue:$scope.chartMax]]});}});};}/** * @ngInject */function goalsBar(){return{templateUrl:'js/components/goals/goalsBar/goalsBar.html',restrict:'E',replace:true,controller:generateBarCtrl};}componentsModule.directive('goalsBar',goalsBar);},{"../../../../":83,"c3":262}],106:[function(require,module,exports){'use strict';generateChartCtrl.$inject=['$scope','$element'];var componentsModule=require('../../../../');var c3=require('c3');var priv={};priv.putGaugeOnPage=function(element,number,max){return c3.generate({bindto:element[0].getElementsByClassName('chart-wrapper')[0],size:{height:80,width:80},gauge:{width:10,max:max,expand:true,label:{format:function format(value){return value+'/'+max;},show:false},fullCircle:true,startingAngle:-Math.PI/2},data:{columns:[['systems',number]],type:'gauge'},color:{pattern:['#FF0000','#F97600','#F6C600','#0095DB'],threshold:{values:[max/4,max*2/4,max*3/4,max]}}});};priv.updateScope=function($scope,num,max){$scope.gaugeNumber=num;$scope.gaugeMax=max;};/** * @ngInject */function generateChartCtrl($scope,$element){$scope.buildGauge=function(options){// init options.getGaugeNumber().then(function(data){$scope.goalsGauge=priv.putGaugeOnPage($element,data,options.getGaugeMax());priv.updateScope($scope,data,options.getGaugeMax());});// when the account changes $scope.$on('account:change',function(){$scope.gaugeMax=options.getGaugeMax();options.getGaugeNumber().then(function(data){$scope.goalsGauge.load({columns:[['systems',data]]});priv.updateScope($scope,data,options.getGaugeMax());});});};}/** * @ngInject */function goalsGauge(){return{templateUrl:'js/components/overview/widgets/goals/goalsGauge/goalsGauge.html',restrict:'E',replace:true,scope:true,controller:generateChartCtrl};}componentsModule.directive('goalsGauge',goalsGauge);},{"../../../../":83,"c3":262}],107:[function(require,module,exports){'use strict';GoalsLiteDirectiveCtrl.$inject=['$q','$scope','System','Evaluation','HttpHeaders','InsightsConfig','Report','Stats'];var componentsModule=require('../../../');var reduce=require('lodash/reduce');/** * @ngInject */function GoalsLiteDirectiveCtrl($q,$scope,System,Evaluation,HttpHeaders,InsightsConfig,Report,Stats){var priv={};// Gauge requires a function $scope.getMaxFreeSystems=function(){return $scope.maxFreeSystems;};$scope.gettingStartedLink=InsightsConfig.gettingStartedLink;priv.getSevCount=function(sev,data){return reduce(data,function(sum,obj){if(obj.severity===sev){return sum+1;}return 0;},0);};priv.getActionsData=function(){$scope.loading=true;var promises=[];var statsRulesPromise=Stats.getRules().then(function(stats){$scope.securityErrors=stats.data.security;$scope.stabilityErrors=stats.data.stability;});promises.push(statsRulesPromise);var statsSystemsPromise=Stats.getSystems().then(function(stats){return stats.data.total;});var evalPromise=Evaluation.getEvaluationStatus().then(function(status){return statsSystemsPromise.then(function(systemTotal){var currentEval=status.current;$scope.systemCount=systemTotal;$scope.maxFreeSystems=currentEval?currentEval.systems:0;$scope.systemLimitReached=systemTotal>=$scope.maxFreeSystems;if(currentEval){var activationDate=new Date(Date.parse(currentEval.created_at));activationDate.setDate(activationDate.getDate()+currentEval.duration);$scope.expiration=activationDate.toDateString();}});});promises.push(evalPromise);$q.all(promises).finally(function promisesFinally(){$scope.loading=false;});$scope.getSystemCount=function(){return statsSystemsPromise;};};$scope.$on('account:change',function(){priv.getActionsData();});priv.getActionsData();}function goalsLite(){return{templateUrl:'js/components/overview/widgets/goals/goalsLite.html',restrict:'E',controller:GoalsLiteDirectiveCtrl,replace:true};}componentsModule.directive('goalsLite',goalsLite);},{"../../../":83,"lodash/reduce":536}],108:[function(require,module,exports){'use strict';HandcraftedContentDirectiveCtrl.$inject=['$scope','AccountService','OverviewService'];var componentsModule=require('../../../');/** * @ngInject */function HandcraftedContentDirectiveCtrl($scope,AccountService,OverviewService){$scope.accountNumber=AccountService.number;$scope.overview=OverviewService;}function handcraftedContent(){return{templateUrl:'js/components/overview/widgets/'+'handcraftedContent/handcraftedContent.html',restrict:'E',replace:true,scope:{},controller:HandcraftedContentDirectiveCtrl};}componentsModule.directive('handcraftedContent',handcraftedContent);},{"../../../":83}],109:[function(require,module,exports){'use strict';HighPrioCtrl.$inject=['$scope','System'];var componentsModule=require('../../../');/** * @ngInject */function HighPrioCtrl($scope,System){$scope.loading=true;$scope.refresh=function(){$scope.loading=true;$scope.items={};System.getSystemStatus(true).then(function(res){if(res.data&&res.data.stale){$scope.items.staleSystems=res.data.stale;}$scope.loading=false;});};$scope.itemCount=function(){return Object.keys($scope.items).length;};$scope.$on('account:change',$scope.refresh);$scope.refresh();}function HighPrio(){return{templateUrl:'js/components/overview/widgets/highPrio/highPrio.html',restrict:'E',replace:true,scope:{},controller:HighPrioCtrl};}componentsModule.directive('highPrio',HighPrio);},{"../../../":83}],110:[function(require,module,exports){'use strict';MaintenancePlanLiteCtrl.$inject=['$scope','Maintenance','MaintenanceService','$state'];var componentsModule=require('../../../');var moment=require('moment-timezone');var find=require('lodash/find');/** * @ngInject */function MaintenancePlanLiteCtrl($scope,Maintenance,MaintenanceService,$state){$scope.MaintenanceService=MaintenanceService;$scope.plans=MaintenanceService.plans;$scope.duration=function(start,end){return moment.duration(moment(end).diff(moment(start))).asMinutes();};$scope.silence=function(plan){plan.silenced=true;return Maintenance.silence(plan);};$scope.getFirstSuggestion=function(){return find(MaintenanceService.plans.suggested,{hidden:false});};$scope.openPlan=function(plan){$state.go('app.maintenance',{maintenance_id:plan.maintenance_id});};}function MaintenancePlanLite(){return{templateUrl:'js/components/overview/widgets/'+'maintenancePlanLite/maintenancePlanLite.html',restrict:'E',replace:true,scope:true,controller:MaintenancePlanLiteCtrl};}componentsModule.directive('maintenancePlanLite',MaintenancePlanLite);},{"../../../":83,"lodash/find":491,"moment-timezone":557}],111:[function(require,module,exports){'use strict';var componentsModule=require('../../../../');function MaintenancePlanLiteTable(){return{templateUrl:'js/components/overview/widgets/maintenancePlanLite/'+'maintenancePlanLiteTable/maintenancePlanLiteTable.html',restrict:'E',replace:true,scope:true,link:function link(scope,element,attr){scope.plans=scope.$parent.$eval(attr.plans);if(angular.isDefined(attr.overdue)){scope.overdue=scope.$parent.$eval(attr.overdue);}}};}componentsModule.directive('maintenancePlanLiteTable',MaintenancePlanLiteTable);},{"../../../../":83}],112:[function(require,module,exports){'use strict';NewSystemsCtrl.$inject=['$scope','SystemsService','Utils','InventoryService'];var componentsModule=require('../../../');/** * @ngInject */function NewSystemsCtrl($scope,SystemsService,Utils,InventoryService){$scope.loader=new Utils.Loader(false);$scope.showSystemModal=InventoryService.showSystemModal;var load=$scope.loader.bind(function(){// currently the product selection is not reflected within the newest systems // as there is on notion of product on the overview page yet return SystemsService.populateNewestSystems().then(function(){$scope.systems=SystemsService.getNewestSystems();});});$scope.$on('account:change',load);$scope.$on('group:change',load);load();}function newSystems(){return{templateUrl:'js/components/overview/widgets/newSystems/newSystems.html',restrict:'E',replace:true,scope:{},controller:NewSystemsCtrl};}componentsModule.directive('newSystems',newSystems);},{"../../../":83}],113:[function(require,module,exports){/*global require*/'use strict';pageHeaderCtrl.$inject=['$scope','gettextCatalog'];var componentsModule=require('../');/** * @ngInject */function pageHeaderCtrl($scope,gettextCatalog){function init(){$scope.titleTranslated=gettextCatalog.getString($scope.title);}init();}function pageHeader(){return{transclude:true,templateUrl:'js/components/pageHeader/pageHeader.html',restrict:'E',replace:true,controller:pageHeaderCtrl,scope:{title:'@',icon:'@'}};}componentsModule.directive('pageHeader',pageHeader);},{"../":83}],114:[function(require,module,exports){'use strict';platformFilterCtrl.$inject=['$scope','FilterService','MultiButtonService','Products','Categories','Report','System','HttpHeaders','QuickFilters','$q','PlatformService'];var componentsModule=require('../');/** * @ngInject */function platformFilterCtrl($scope,FilterService,MultiButtonService,Products,Categories,Report,System,HttpHeaders,QuickFilters,$q,PlatformService){$scope.summaryTypes=QuickFilters.summaryTypes;$scope.loadingSummary=true;$scope.summaryCounts={};$scope.$on('group:change',reload);$scope.$on('account:change',reload);$scope.getSummaryItems=function(productCode){var items=[];if($scope.summaryType===QuickFilters.summaryTypes.systems){for(var role in Products[productCode].roles){var roleObj=Products[productCode].roles[role];items.push($scope.summaryCounts[productCode][roleObj.code]+' '+roleObj.shortName+'s');}}else if($scope.summaryType===QuickFilters.summaryTypes.categories){items.push($scope.summaryCounts[productCode].stability+' Stability');items.push($scope.summaryCounts[productCode].performance+' Performance');items.push($scope.summaryCounts[productCode].security+' Security');items.push($scope.summaryCounts[productCode].availability+' Availability');}return items;};//TODO: refactor MultiButtonService and this to by DRY function inventoryPlatforms(){return[{product:Products.rhel,// link to the Product to get more info in the Views // should deprecate next line?? displayName:Products.rhel.fullName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.rhel.code);}},{product:Products.docker,// link to the Product to get more info in the Views //should deprecate next line?? displayName:Products.docker.fullName,subFilters:[{displayName:Products.docker.roles.host.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.docker.code);MultiButtonService.setState('inventoryDockerContainers',false);MultiButtonService.setState('inventoryDockerImages',false);MultiButtonService.setState('inventoryDockerHosts',true);}},{displayName:Products.docker.roles.container.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.docker.code);MultiButtonService.setState('inventoryDockerContainers',true);MultiButtonService.setState('inventoryDockerImages',false);MultiButtonService.setState('inventoryDockerHosts',false);}},{displayName:Products.docker.roles.image.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.docker.code);MultiButtonService.setState('inventoryDockerContainers',false);MultiButtonService.setState('inventoryDockerImages',true);MultiButtonService.setState('inventoryDockerHosts',false);}}],doFilter:function doFilter(){FilterService.setSelectedProduct(Products.docker.code);MultiButtonService.setState('inventoryDockerContainers',true);MultiButtonService.setState('inventoryDockerImages',true);MultiButtonService.setState('inventoryDockerHosts',true);}},{product:Products.ocp,displayName:Products.ocp.fullName,subFilters:[{displayName:Products.ocp.roles.cluster.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.ocp.code);MultiButtonService.setState('inventoryOCPMasters',false);MultiButtonService.setState('inventoryOCPNodes',false);MultiButtonService.setState('inventoryOCPClusters',true);}},{displayName:Products.ocp.roles.master.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.ocp.code);MultiButtonService.setState('inventoryOCPMasters',true);MultiButtonService.setState('inventoryOCPNodes',false);MultiButtonService.setState('inventoryOCPClusters',false);}},{displayName:Products.ocp.roles.node.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.ocp.code);MultiButtonService.setState('inventoryOCPMasters',false);MultiButtonService.setState('inventoryOCPNodes',true);MultiButtonService.setState('inventoryOCPClusters',false);}}],doFilter:function doFilter(){FilterService.setSelectedProduct(Products.ocp.code);MultiButtonService.setState('inventoryOCPMasters',true);MultiButtonService.setState('inventoryOCPNodes',true);MultiButtonService.setState('inventoryOCPClusters',true);}},{product:Products.rhev,// link to the Product to get more info in the Views // should deprecate next line?? displayName:Products.rhev.fullName,subFilters:[{displayName:Products.rhev.roles.cluster.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.rhev.code);MultiButtonService.setState('inventoryRHEVManagers',false);MultiButtonService.setState('inventoryRHEVHypervisors',false);MultiButtonService.setState('inventoryRHEVClusters',true);}},{displayName:Products.rhev.roles.manager.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.rhev.code);MultiButtonService.setState('inventoryRHEVManagers',true);MultiButtonService.setState('inventoryRHEVHypervisors',false);MultiButtonService.setState('inventoryRHEVClusters',false);}},{displayName:Products.rhev.roles.hypervisor.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.rhev.code);MultiButtonService.setState('inventoryRHEVManagers',false);MultiButtonService.setState('inventoryRHEVHypervisors',true);MultiButtonService.setState('inventoryRHEVClusters',false);}}],doFilter:function doFilter(){FilterService.setSelectedProduct(Products.rhev.code);MultiButtonService.setState('inventoryRHEVManagers',true);MultiButtonService.setState('inventoryRHEVHypervisors',true);MultiButtonService.setState('inventoryRHEVClusters',true);}},{product:Products.osp,// link to the Product to get more info in the Views //should deprecate next line?? displayName:Products.osp.fullName,subFilters:[{displayName:Products.osp.roles.cluster.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.osp.code);MultiButtonService.setState('inventoryOSPCluster',true);MultiButtonService.setState('inventoryOSPDirector',false);MultiButtonService.setState('inventoryOSPCompute',false);MultiButtonService.setState('inventoryOSPController',false);}},{displayName:Products.osp.roles.director.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.osp.code);MultiButtonService.setState('inventoryOSPCluster',false);MultiButtonService.setState('inventoryOSPDirector',true);MultiButtonService.setState('inventoryOSPCompute',false);MultiButtonService.setState('inventoryOSPController',false);}},{displayName:Products.osp.roles.compute.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.osp.code);MultiButtonService.setState('inventoryOSPCluster',false);MultiButtonService.setState('inventoryOSPDirector',false);MultiButtonService.setState('inventoryOSPCompute',true);MultiButtonService.setState('inventoryOSPController',false);}},{displayName:Products.osp.roles.controller.shortName,doFilter:function doFilter(){FilterService.setSelectedProduct(Products.osp.code);MultiButtonService.setState('inventoryOSPCluster',false);MultiButtonService.setState('inventoryOSPDirector',false);MultiButtonService.setState('inventoryOSPCompute',false);MultiButtonService.setState('inventoryOSPController',true);}}],doFilter:function doFilter(){FilterService.setSelectedProduct(Products.osp.code);MultiButtonService.setState('inventoryOSPCluster',true);MultiButtonService.setState('inventoryOSPDirector',true);MultiButtonService.setState('inventoryOSPCompute',true);MultiButtonService.setState('inventoryOSPController',true);}}];}function getCategoriesSummary(platforms){var promises=[];function responseHandler(product_code,category){return function(response){$scope.summaryCounts[product_code][category]=response.headers(HttpHeaders.resourceCount);};}for(var prod in Products){if(!platforms.includes(prod)){continue;}for(var i=0;i=0.07){return _value2;}var format=d3.format('0.3p');return _value2+' ('+format(ratio)+')';}}}});}/** * @ngInject */function rhaTelemetryDonut($state,$timeout,RhaTelemetryActionsService,Stats,FilterService,Categories){return{restrict:'C',replace:true,link:function link($scope){RhaTelemetryActionsService.setDonutChart(generateChart(RhaTelemetryActionsService.mapName));var cats=Categories.filter(function(cat){return cat!=='all';});var refreshDonut=function refreshDonut(rules){if(!rules){return;}var donutChart=RhaTelemetryActionsService.getDonutChart();function arcSelector(arcTitle){var classyTitle=arcTitle.replace(/[|._]/g,'-');return'.c3-arc-'+classyTitle;}function loadData(cols,unload){donutChart.load({columns:cols,unload:unload,done:function done(){angular.forEach(cols,function(col){var category=col[0];var selector=arcSelector(category);var elem=document.querySelector(selector);angular.element(elem).on('touchend click',function(){$state.go('app.topic',{id:category.toLowerCase(),product:FilterService.getSelectedProduct()});});});}});}var cols=cats.map(function(cat){return[capitalize(cat),rules[cat]];}).filter(function(cat){return cat[1]>0;});loadData(cols,cats.map(capitalize));};$scope.$on('filterService:doFilter',refreshDonut);$scope.$watch('stats.rules',refreshDonut,true);}};}componentsModule.directive('rhaTelemetryDonut',rhaTelemetryDonut);},{"../":83,"c3":262,"d3":263,"lodash/capitalize":480}],118:[function(require,module,exports){/*global require*/'use strict';var componentsModule=require('../');function riskOfChange(){return{scope:{changeRisk:'<',hideLabel:'<'},templateUrl:'js/components/riskOfChange/riskOfChange.html',restrict:'E',replace:true};}componentsModule.directive('riskOfChange',riskOfChange);},{"../":83}],119:[function(require,module,exports){'use strict';var componentsModule=require('../../');function ruleBreadcrumb(){return{templateUrl:'js/components/rule/ruleBreadcrumb/ruleBreadcrumb.html',restrict:'ECA'};}componentsModule.directive('ruleBreadcrumb',ruleBreadcrumb);},{"../../":83}],120:[function(require,module,exports){'use strict';var componentsModule=require('../../');function ruleFilter(){return{templateUrl:'js/components/rule/ruleFilter/ruleFilter.html',restrict:'E'};}componentsModule.directive('ruleFilter',ruleFilter);},{"../../":83}],121:[function(require,module,exports){'use strict';ruleGroupCardCtrl.$inject=['$scope','$timeout','$document','InsightsConfig'];var componentsModule=require('../../');var LEFT_ARROW_KEY=37;var RIGHT_ARROW_KEY=39;function ruleGroupCardCtrl($scope,$timeout,$document,InsightsConfig){var active=false;$scope.config=InsightsConfig;$scope.setActive=function(val){active=val;};function keydownHandler($event){if(active){if($event.keyCode===LEFT_ARROW_KEY){$scope.previous();}if($event.keyCode===RIGHT_ARROW_KEY){$scope.next();}}}$document.on('keydown',keydownHandler);$scope.$on('$destroy',function(){$document.off('keydown',keydownHandler);});// triggers CSS effect function doEffect(){$scope.swapping=true;$timeout(function(){$scope.swapping=false;},0);}$scope.index=0;$scope.$watch('plugin',function(value){if(value){$scope.rule=value.rules[$scope.index];}});$scope.hasNext=function(){return $scope.index+1<$scope.plugin.rules.length;};$scope.next=function(){if($scope.hasNext()){$scope.index++;$scope.rule=$scope.plugin.rules[$scope.index];doEffect();}};$scope.hasPrevious=function(){return $scope.index>0;};$scope.previous=function(){if($scope.hasPrevious()){$scope.index--;$scope.rule=$scope.plugin.rules[$scope.index];doEffect();}};}/** * @ngInject */function ruleGroupCard(){return{templateUrl:'js/components/rule/ruleGroupCard/ruleGroupCard.html',restrict:'E',scope:{plugin:'='},controller:ruleGroupCardCtrl};}componentsModule.directive('ruleGroupCard',ruleGroupCard);},{"../../":83}],122:[function(require,module,exports){'use strict';var componentsModule=require('../../');/** * @ngInject */function ruleListSimple(){return{templateUrl:'js/components/rule/ruleListSimple/ruleListSimple.html',restrict:'E',scope:{rules:'=',onDelete:'&',onSelect:'&',order:'='},link:function link(scope,element,attrs){scope.readOnly=!attrs.onDelete;scope.$watch('rules',function(){scope.limit=10;});}};}componentsModule.directive('ruleListSimple',ruleListSimple);},{"../../":83}],123:[function(require,module,exports){'use strict';var componentsModule=require('../../');/** * @ngInject */function ruleReason(){return{templateUrl:'js/components/rule/ruleReason/ruleReason.html',restrict:'EC'};}componentsModule.directive('ruleReason',ruleReason);},{"../../":83}],124:[function(require,module,exports){'use strict';ruleResolutionCtrl.$inject=['RhaTelemetryActionsService','$scope'];var componentsModule=require('../../');function ruleResolutionCtrl(RhaTelemetryActionsService,$scope){$scope.getSystemNameFromId=function(systemId){var systemNames=RhaTelemetryActionsService.getSystemNames();if(systemNames&&systemNames.hasOwnProperty(systemId)){return systemNames[systemId];}else{return systemId;}};}/** * @ngInject */function ruleResolution(){return{templateUrl:'js/components/rule/ruleResolution/ruleResolution.html',restrict:'EC',replace:true,controller:ruleResolutionCtrl};}componentsModule.directive('ruleResolution',ruleResolution);},{"../../":83}],125:[function(require,module,exports){'use strict';RuleSummariesCtrl.$inject=['$scope','$stateParams','$location','System','Report','InsightsConfig'];var componentsModule=require('../../');/** * @ngInject */function RuleSummariesCtrl($scope,$stateParams,$location,System,Report,InsightsConfig){$scope.getLoadingMessage=function(){var response='Loading report(s)';if($scope.system.hostname){response=response+' for '+$scope.system.hostname;}return response+'…';};function getSystemReports(){$scope.loading.isLoading=true;System.getSystemReports($scope.machineId).success(function(system){angular.extend($scope.system,system);if($scope.rule_id){$scope.system.reports.sort(function(a,b){if(a.rule_id===$scope.rule_id){return-1;}else if(b.rule_id===$scope.rule_id){return 1;}return 0;});}$scope.loading.isLoading=false;}).error(function(data,status,headers,config){if(InsightsConfig.getReportsErrorCallback){$scope.errorMessage=InsightsConfig.getReportsErrorCallback(data,status,headers,config);}$scope.loading.isLoading=false;});}// if the rule attribute is set it has precedence. Otherwise use the state param. if($scope.rule){$scope.rule_id=$scope.rule;}else{$scope.rule_id=$stateParams.rule;}if($scope.machineId&&!$scope.system.__fake){if($scope.rule_id){$scope.ruleFilter=true;}if($scope.system){$scope.system.reports=[];getSystemReports();}}}function ruleSummaries(){return{controller:RuleSummariesCtrl,templateUrl:'js/components/rule/ruleSummaries/ruleSummaries.html',restrict:'EC',scope:{system:'=',machineId:'=',rule:'=',ruleFilter:'=',loading:'='}};}componentsModule.directive('ruleSummaries',ruleSummaries);},{"../../":83}],126:[function(require,module,exports){'use strict';RuleSummaryCtrl.$inject=['$scope','InsightsConfig'];var componentsModule=require('../../');/** * @ngInject */function RuleSummaryCtrl($scope,InsightsConfig){$scope.config=InsightsConfig;// this needs to be an object so that it can be accessed from the transcluded scope $scope.show={moreInfo:false};$scope.initCollapsed=false;if($scope.ruleFilter&&$scope.ruleId&&$scope.ruleId!==$scope.report.rule_id){$scope.initCollapsed=true;}$scope.resetShowMore=function(ctx){if(ctx.collapsing){$scope.show.moreInfo=false;}};}function ruleSummary(){return{controller:RuleSummaryCtrl,templateUrl:'js/components/rule/ruleSummary/ruleSummary.html',restrict:'EC',scope:{report:'=',rule:'=',ruleId:'=',ruleFilter:'=',loading:'='}};}componentsModule.directive('ruleSummary',ruleSummary);},{"../../":83}],127:[function(require,module,exports){'use strict';ruleToggleCtrl.$inject=['$scope','Ack','InsightsConfig','gettextCatalog'];var componentsModule=require('../../');/** * @ngInject */function ruleToggleCtrl($scope,Ack,InsightsConfig,gettextCatalog){function init(){$scope.text=gettextCatalog.getString('Ignore Rule');if($scope.rule&&$scope.rule.ack_id){$scope.text=gettextCatalog.getString('Unignore Rule');}}$scope.ackAction=function(){if($scope.rule&&$scope.rule.ack_id){Ack.deleteAck({id:$scope.rule.ack_id});$scope.rule.ack_id=null;}else{Ack.createAck($scope.rule).then(function(ack){$scope.rule.ack_id=ack.id;});}};init();$scope.$watch('rule.ack_id',init);}function ruleToggle(){return{templateUrl:'js/components/rule/ruleToggle/ruleToggle.html',restrict:'E',scope:{rule:'='},controller:ruleToggleCtrl};}componentsModule.directive('ruleToggle',ruleToggle);},{"../../":83}],128:[function(require,module,exports){'use strict';var componentsModule=require('../');function ruleStateIcon(){return{scope:{state:'='},templateUrl:'js/components/ruleStateIcon/ruleStateIcon.html',restrict:'E',replace:true};}componentsModule.directive('ruleStateIcon',ruleStateIcon);},{"../":83}],129:[function(require,module,exports){/*global require*/'use strict';var componentsModule=require('../');function allSeverityIcon(){return{scope:{rule:'<'},templateUrl:'js/components/severityIcon/allSeverityIcons.html',restrict:'E',replace:true};}componentsModule.directive('allSeverityIcons',allSeverityIcon);},{"../":83}],130:[function(require,module,exports){/*global require*/'use strict';severityIconCtrl.$inject=['$scope','gettextCatalog'];var componentsModule=require('../');var has=require('lodash/has');/** * @ngInject */function severityIconCtrl($scope,gettextCatalog){var priv={};priv.iconClass=function iconClass(level){switch(level){case 1:return'low';case 2:return'med';case 3:return'high';case 4:return'critical';default:return'unknown';}};priv.checkTypes=function(){// check to ensure a valid type is given! if($scope.type&&$scope.type){if(has(priv.typeMap,$scope.type)){return;}throw new Error('Invalid severity-icon type selected! '+$scope.type);}throw new Error('No severity-icon type selected! '+$scope.type);};priv.typeMap={impact:gettextCatalog.getString('Impact'),likelihood:gettextCatalog.getString('Likelihood'),severity:gettextCatalog.getString('Total Risk')};// pre compute this so the digest cycles dont re-run iconClass // we really only use this once since the refactor // it is nice an delcaritive though, leaving it in for now priv.sevClassMap={// for now handle the stringed severity... this will go away once sevs are 1 - 4 INFO:priv.iconClass(1),WARN:priv.iconClass(2),ERROR:priv.iconClass(3),CRITICAL:priv.iconClass(4),UNKNOWN:priv.iconClass(-1),1:priv.iconClass(1),2:priv.iconClass(2),3:priv.iconClass(3),4:priv.iconClass(4)};priv.numberToStringMap={1:gettextCatalog.getString('Low'),2:gettextCatalog.getString('Medium'),3:gettextCatalog.getString('High'),4:gettextCatalog.getString('Critical'),INFO:gettextCatalog.getString('Low'),WARN:gettextCatalog.getString('Medium'),ERROR:gettextCatalog.getString('High'),CRITICAL:gettextCatalog.getString('Critical'),UNKNOWN:gettextCatalog.getString('Unknown')};$scope.getClass=function(type){if(type==='severity'){return'total-risk';}return type;};// init the var on the scope // it should not change and there is no point in re calculating it $scope.init=function init(){priv.checkTypes();if(!$scope.severity){$scope.severity='UNKNOWN';}// if a type is specified and the label is not overridden // set the label per the typeMap if($scope.type&&!$scope.label){$scope.label=priv.typeMap[$scope.type];}$scope.tooltip=priv.typeMap[$scope.type]+': '+(''+priv.numberToStringMap[$scope.severity]);$scope.sevClass=priv.sevClassMap[$scope.severity];};}function severityIcon(){return{scope:{type:'@',label:'@',severity:'<'},templateUrl:'js/components/severityIcon/severityIcon.html',restrict:'E',replace:true,controller:severityIconCtrl};}componentsModule.directive('severityIcon',severityIcon);},{"../":83,"lodash/has":498}],131:[function(require,module,exports){/*global require*/'use strict';rhaTelemetryActionsSideNavCtrl.$inject=['$scope','$state','$stateParams','Stats','Categories','FilterService'];var componentsModule=require('../../');/** * @ngInject */function rhaTelemetryActionsSideNavCtrl($scope,$state,$stateParams,Stats,Categories,FilterService){$scope.product=FilterService.getSelectedProduct();if($scope.product==='all'){$scope.product=undefined;}$scope.categoryCounts={};function init(){Stats.getRules({product:$scope.product}).then(function(res){$scope.categoryCounts=res.data;});}$scope.categories=Categories;$scope.actions=false;$scope.isActive=function(category){return $scope.actions&&$stateParams.id===category;};$scope.$watch(function(){return $state.current&&$state.current.actions;},function(actions){$scope.actions=actions;});$scope.$on('group:change',init);init();}function rhaTelemetryActionsSideNav(){return{templateUrl:'js/components/sideNav/actionsSideNav/actionsSideNav.html',restrict:'E',replace:true,controller:rhaTelemetryActionsSideNavCtrl};}componentsModule.directive('rhaTelemetryActionsSideNav',rhaTelemetryActionsSideNav);},{"../../":83}],132:[function(require,module,exports){'use strict';sideNavCtrl.$inject=['$scope','Utils','$state','InsightsConfig'];var componentsModule=require('../');var includes=require('lodash/includes');/** * @ngInject */function sideNavCtrl($scope,Utils,$state,InsightsConfig){$scope.isHidden=false;$scope.toggleNav=function(){$scope.isHidden=!$scope.isHidden;};$scope.utils=Utils;$scope.state=$state;$scope.includes=includes;$scope.config=InsightsConfig;$scope.states={rules:['app.rules','app.admin-rules','app.admin-rule-tags','app.create-rule','app.show-rule','app.edit-rule'],actions:['app.actions','app.actions-rule','app.topic'],config:['app.config','app.config-webhook-edit']};}function sideNav(){return{templateUrl:'js/components/sideNav/sideNav.html',restrict:'E',replace:true,controller:sideNavCtrl};}componentsModule.directive('sideNav',sideNav);},{"../":83,"lodash/includes":501}],133:[function(require,module,exports){'use strict';simpleSelectCtrl.$inject=['$scope'];var componentsModule=require('../');var find=require('lodash/find');/** * @ngInject */function simpleSelectCtrl($scope){function handleDefault(){if($scope.model===undefined&&$scope.options&&$scope.options.length){$scope.selected=find($scope.options,{id:null});}}$scope.$watch('options',handleDefault);$scope.$watch('model',function(model){$scope.selected=find($scope.options,function(option){return String(option.id).toLowerCase()===String(model).toLowerCase();});handleDefault();});$scope.select=function(selected){$scope.selected=selected;$scope.model=selected.id;};}function simpleSelect(){return{scope:{model:'=',options:'=',disabled:'='},templateUrl:'js/components/simpleSelect/simpleSelect.html',restrict:'E',replace:true,controller:simpleSelectCtrl};}componentsModule.directive('simpleSelect',simpleSelect);},{"../":83,"lodash/find":491}],134:[function(require,module,exports){'use strict';statusIconCtrl.$inject=['$scope'];var componentsModule=require('../');/** * @ngInject */function statusIconCtrl($scope){$scope.statusClass='';if($scope.status){$scope.statusClass='fa-check-circle-o';}else{$scope.statusClass='fa-circle-o';}}function statusIcon(){return{scope:{status:'='},templateUrl:'js/components/statusIcon/statusIcon.html',restrict:'E',replace:true,controller:statusIconCtrl};}componentsModule.directive('statusIcon',statusIcon);},{"../":83}],135:[function(require,module,exports){'use strict';stickyNavLink.$inject=['$scope'];var componentsModule=require('../');function _offsetSticky($child,parent,sibling,offset){var scroll=document.body.scrollTop;var siblingRect=sibling.getBoundingClientRect();var topPosElem=siblingRect.top+document.body.scrollTop+offset;var parentRect=parent.getBoundingClientRect();var containerBottom=parentRect.top+scroll+parentRect.height;var bottomOfPage=scroll+document.body.offsetHeight;var toAdd='';var toRemove='';if(scrollcontainerBottom){toAdd='absolute-bottom';toRemove='absolute-top fixed-bottom';}else{toAdd='fixed-bottom';toRemove='absolute-top absolute-bottom';}$child.addClass(toAdd).removeClass(toRemove);}function _topSticky($child){var stuckClass='stuck';var scroll=document.body.scrollTop;var elm=$child[0];var topOffset;if(typeof $child.originalOffset==='undefined'){$child.removeClass(stuckClass);topOffset=elm.getBoundingClientRect().top;$child.originalOffset=topOffset+scroll;}else{topOffset=elm.getBoundingClientRect().top;}if(scroll>=$child.originalOffset){$child.addClass(stuckClass);}else{$child.removeClass(stuckClass);}}function _sticky($child,parent,sibling,offset){if($child&&parent&&sibling){_offsetSticky($child,parent,sibling,offset);}else if($child){_topSticky($child);}}/** * @ngInject */function stickyNavLink($scope){var $child;var parent;var sibling;var offset;var $window;if(!$scope.child){console.warn('Missing parameter');return;}// Get refs to elements $child=angular.element(document.querySelector($scope.child));parent=document.querySelector($scope.parent);sibling=document.querySelector($scope.sibling);// normalize offset var offset=$scope.offset;if(typeof offset==='undefined'){offset=0;}offset=parseInt(offset,10);function sticky(e){if(e&&e.type==='resize'){delete $child.originalOffset;}return _sticky($child,parent,sibling,offset);}// bind scroll and resize events $window=angular.element(window);$window.on('scroll',sticky);$window.on('resize',sticky);$scope.$on('$destroy',function(){$window.off('scroll',sticky);$window.off('resize',sticky);});}function stickyNav(){return{restrict:'CA',scope:{// Look at this happy family parent:'@',child:'@',sibling:'@',offset:'@'},link:stickyNavLink};}componentsModule.directive('stickyNav',stickyNav);},{"../":83}],136:[function(require,module,exports){'use strict';AddSystemModalCtrl.$inject=['$scope','$modalInstance','InsightsConfig'];var componentsModule=require('../../');/** * @ngInject */function AddSystemModalCtrl($scope,$modalInstance,InsightsConfig){$scope.close=function(){$modalInstance.dismiss('close');};$scope.gettingStartedLink=InsightsConfig.gettingStartedLink;}componentsModule.controller('AddSystemModalCtrl',AddSystemModalCtrl);},{"../../":83}],137:[function(require,module,exports){/*global require*/'use strict';SystemModalCtrl.$inject=['$scope','$rootScope','$location','$timeout','$modalInstance','system','rule','AnalyticsService','Utils','FilterService'];var componentsModule=require('../../');/** * @ngInject */function SystemModalCtrl($scope,$rootScope,$location,$timeout,$modalInstance,system,rule,AnalyticsService,Utils,FilterService){$scope.report={};function close(){$modalInstance.dismiss('close');}AnalyticsService.triggerEvent('InsightsCompletion');$scope.system=system;$scope.rule=rule;$scope.loading={isLoading:true};if(system===false){$scope.system={hostname:'this.is.a.fake.system',machine_id:'fakemachineidfakemachineidfakemachineid',__fake:true};}$scope.close=close;FilterService.setMachine($scope.system.system_id);var stateChangeUnreg=$rootScope.$on('$stateChangeStart',close);var escUnreg=$rootScope.$on('telemetry:esc',function($event){$event.preventDefault();return false;});$modalInstance.result.then(angular.noop,function(){// defer unregistering of the esc suppressor $timeout(function(){escUnreg();stateChangeUnreg();});});$scope.$on('modal.closing',function(){FilterService.setMachine(null);escUnreg();});$scope.getUUID=function(){if($scope.system.machine_id){return $scope.system.machine_id;// for legacy }return $scope.system.system_id;};}componentsModule.controller('SystemModalCtrl',SystemModalCtrl);},{"../../":83}],138:[function(require,module,exports){'use strict';systemTableCtrl.$inject=['$scope','System','Group','Utils','$filter','gettext'];var componentsModule=require('../../');var indexBy=require('lodash/keyBy');var reject=require('lodash/reject');/** * @ngInject */function systemTableCtrl($scope,System,Group,Utils,$filter,gettext){var _systems;var lastClicked;var type=$scope.type;$scope._filteredSystems=[];$scope.indeterminate=false;$scope.items=[10,25,50,100];$scope.itemsPerPage=10;$scope.title=type==='in'?gettext('Systems in This Group'):gettext('Available Systems');$scope.currentPage=1;$scope.total=0;$scope.maxPages=4;$scope.systemFilter='';$scope.checkboxes={checked:false,items:{}};$scope.filterSystems=function(){$scope.filteredSystems=$filter('filter')($scope.systems,{hostname:$scope.systemsFilter});$scope.total=$scope.filteredSystems.length;};var resetChecked=Utils.resetChecked($scope.checkboxes);$scope.pageChanged=resetChecked;$scope.addSystems=function(){var systemsToAdd=[];var _systems=indexBy($scope._filteredSystems,'system_id');for(var system_id in $scope.checkboxes.items){if($scope.checkboxes.items[system_id]&&_systems[system_id]){systemsToAdd.push(_systems[system_id]);}}Group.addSystems($scope.group,systemsToAdd);resetChecked();};$scope.removeSystems=function(){var systemsToRemove=[];for(var system_id in $scope.checkboxes.items){if($scope.checkboxes.items[system_id]){systemsToRemove.push(system_id);}}Group.removeSystems($scope.group,systemsToRemove);resetChecked();};$scope.rowClick=function($event){if($event.shiftKey&&$event.type==='mousedown'){// Prevents selection of rows on shift+click $event.preventDefault();return false;}var target=$event.currentTarget;if(!lastClicked){lastClicked=target;return;}if($event.shiftKey){Utils.selectBetween(target,lastClicked,$scope.checkboxes.items);}lastClicked=target;};function filterGroupSystems(){if(!_systems||!_systems.length){return;}var groupMachines=indexBy($scope.group.systems,'system_id');$scope.systems=reject(_systems,function(s){return!!groupMachines[s.system_id];});groupMachines=null;$scope.filterSystems();}if(type==='in'){$scope.systems=$scope.group.systems;$scope.filterSystems();$scope.$watchCollection('group.systems',function(){$scope.filterSystems();});}else{System.getSystems(false).success(function(systems){_systems=systems.resources;filterGroupSystems();});$scope.$watchCollection('group.systems',filterGroupSystems);}$scope.$watch('checkboxes.checked',function(value){Utils.checkboxChecked(value,$scope._filteredSystems,$scope.checkboxes.items);});function itemsChanged(){var result=Utils.itemsChanged($scope._filteredSystems,$scope.checkboxes.items);$scope.totalChecked=result.totalChecked;$scope.indeterminate=result.indeterminate;if(angular.isDefined(result.checked)){$scope.checkboxes.checked=result.checked;}}$scope.$watchCollection('checkboxes.items',itemsChanged);}function systemTable(){return{templateUrl:'js/components/system/systemTable/systemTable.html',restrict:'E',scope:{group:'=',type:'@'},controller:systemTableCtrl};}componentsModule.directive('systemTable',systemTable);},{"../../":83,"lodash/keyBy":518,"lodash/reject":537}],139:[function(require,module,exports){'use strict';systemMetadataCtrl.$inject=['$scope','System','SystemsService'];var componentsModule=require('../');/** * @ngInject */function systemMetadataCtrl($scope,System,SystemsService){$scope.loading=false;if($scope.system&&$scope.system.system_id){$scope.loading=true;System.getSystemMetadata($scope.system.system_id).then(function(metadata){$scope.systemFacts=SystemsService.getSystemFacts($scope.system,metadata.data);$scope.loading=false;});}}function systemMetadata(){return{templateUrl:'js/components/systemMetadata/systemMetadata.html',restrict:'E',controller:systemMetadataCtrl,scope:{system:'='}};}componentsModule.directive('systemMetadata',systemMetadata);},{"../":83}],140:[function(require,module,exports){'use strict';targetBlank.$inject=['$timeout'];var componentsModule=require('../');/** * @ngInject */function targetBlank($timeout){function addTargetBlank(element){var anchors=element.querySelectorAll('a');// loop through anchors and add target _blank for(var i=0;i0){TopbarAlertsService.push({type:'stale',msg:gettextCatalog.getPlural(res.data.stale-res.data.ackedStale,'1 new system not checking in','{{count}} new systems not checking in',{count:res.data.stale-res.data.ackedStale}),acked:false,icon:'fa-server',onAck:ack,onSelect:select});}}}).finally(function(){$scope.loading=false;});}function ack(){return System.ackStaleSystems().then(loadSystemAlerts);}function goToInventory(){FilterService.setOnline(false);FilterService.setOffline(true);InventoryService.setSort({field:'last_check_in',direction:'DESC'});$state.go('app.inventory',{offline:true});}function select(item){if(item.acked===false){ack().then(goToInventory);}else{goToInventory();}}loadSystemAlerts();MaintenanceService.plans.load();}function alerts(){return{templateUrl:'js/components/topbar/alerts/alerts.html',restrict:'E',replace:true,scope:{icon:'@icon'},controller:alertsCtrl};}componentsModule.directive('alerts',alerts);},{"../../":83}],143:[function(require,module,exports){/*global require*/'use strict';TopbarCtrl.$inject=['$scope','InsightsConfig','$state'];var componentsModule=require('../');var includes=require('lodash/includes');// global filters are disabled in these states var DISABLED_STATES=['app.config','app.digests','app.rules'];/** * @ngInject */function TopbarCtrl($scope,InsightsConfig,$state){$scope.gettingStartedLink=InsightsConfig.gettingStartedLink;$scope.isPortal=InsightsConfig.isPortal;$scope.isGroupsEnabled=InsightsConfig.isGroupsEnabled;function checkState(){$scope.disabled=includes(DISABLED_STATES,$state.current.name);}$scope.$on('$stateChangeSuccess',checkState);checkState();}/** * @ngInject */function topbar(){return{templateUrl:'js/components/topbar/topbar.html',restrict:'E',controller:TopbarCtrl,replace:true};}componentsModule.directive('topbar',topbar);},{"../":83,"lodash/includes":501}],144:[function(require,module,exports){'use strict';otherTopic.$inject=['FilterService'];var componentsModule=require('../../');/** * @ngInject */function otherTopic(FilterService){return{templateUrl:'js/components/topics/otherTopic/otherTopic.html',restrict:'E',replace:true,scope:{topic:'='},link:function link(scope){scope.getSelectedProduct=FilterService.getSelectedProduct;}};}componentsModule.directive('otherTopic',otherTopic);},{"../../":83}],145:[function(require,module,exports){'use strict';topicAdminHelpBar.$inject=['$document'];var componentsModule=require('../../');/** * @ngInject */function topicAdminHelpBar($document){return{scope:true,templateUrl:'js/components/topics/topicAdminHelpBar/topicAdminHelpBar.html',restrict:'E',replace:true,link:function link($scope,element,attrs){var textArea;angular.forEach($document.find('textarea'),function(element){if(element.id===attrs.textArea){textArea=element;}});if(!textArea){throw new Error('Unable to find text area with id: '+attrs.textArea);}function appendToContent(value){var position=textArea.selectionStart;var text=textArea.value;if(!text){text='';}var prefix=text.substring(0,position);var suffix=text.substring(position);textArea.value=prefix+value+suffix;textArea.selectionStart=textArea.selectionEnd=position+value.length;$scope.topic.content=textArea.value;}$scope.help={show:false};$scope.help.appendInterpolation=function($event){var text='topic.actions.totalCount';if($event){text=$event.target.text.trim();}appendToContent('{{= '+text+' }}');};$scope.help.appendConditional=function(){appendToContent('\n{{? topic.actions.totalCount}}\nActions found!'+'\n{{??}}\nNo actions found!\n{{?}}\n');};$scope.help.interpolations=[];$scope.help.createInterpolations=function(){if(!$scope.topic||!$scope.topic.rules){return;}var statics=[{key:'systems.totalCount',value:'Number of systems with a given product '+'(or all systems combined if no product selected)'},{key:'systems.affectedCount',value:'Number of systems affected by this topic'},{key:'product.code',value:'Product code (rhel, rhev, osp or docker)'},{key:'topic.rules.totalCount',value:'Number of rules that belong to this topic'},{key:'topic.rules.ackedCount',value:'Number of rules that belong to this topic '+'and are ignored (acked) by the customer'},{key:'topic.actions.totalCount',value:'Number of actions (rule violations on systems) for this topic'}];var single=[{key:'topic.rule.actionCount',value:'Number of actions for the given rule'},{key:'topic.rule.description',value:'Name of the rule'},{key:'topic.rule.link',value:'Link to a page for the given rule'},{key:'topic.rule.category',value:'Rule category (Security, Performance, '+'Availability or Stability)'},{key:'topic.rule.severity',value:'Rule severity (ERROR, WARN, INFO)'},{key:'topic.rule.acked',value:'Indicated whether this rule has '+'been ignored (acked) by the user'}];if($scope.ruleBinding==='explicit'&&$scope.topic.rules.length===1){$scope.help.interpolations=statics.concat(single);}else{var ruleId=$scope.selectedRule;$scope.help.interpolations=statics.concat([{key:'topic.rules.byId[\''+ruleId+'\'].actionCount',value:single[0].value},{key:'topic.rules.byId[\''+ruleId+'\'].description',value:single[1].value},{key:'topic.rules.byId[\''+ruleId+'\'].link',value:single[2].value},{key:'topic.rules.byId[\''+ruleId+'\'].category',value:single[3].value},{key:'topic.rules.byId[\''+ruleId+'\'].severity',value:single[4].value},{key:'topic.rules.byId[\''+ruleId+'\'].acked',value:single[5].value}]);}};$scope.$watchCollection('topic.rules',$scope.help.createInterpolations);$scope.$watch('selectedRule',$scope.help.createInterpolations);}};}componentsModule.directive('topicAdminHelpBar',topicAdminHelpBar);},{"../../":83}],146:[function(require,module,exports){'use strict';topicDetails.$inject=['$rootScope'];var componentsModule=require('../../');/** * @ngInject */function topicDetails($rootScope){return{templateUrl:'js/components/topics/topicDetails/topicDetails.html',restrict:'E',replace:true,scope:{topic:'='},link:function link(scope){$rootScope.$watch('isContentManager',function(value){scope.isContentManager=value;});}};}componentsModule.directive('topicDetails',topicDetails);},{"../../":83}],147:[function(require,module,exports){/*global require*/'use strict';TopicPreviewModalCtrl.$inject=['$scope','topic','Topic','summary','details','DataUtils','Utils'];var componentsModule=require('../../');var cloneDeep=require('lodash/cloneDeep');var pick=require('lodash/pick');/** * @ngInject */function TopicPreviewModalCtrl($scope,topic,Topic,summary,details,DataUtils,Utils){$scope.summary=summary;$scope.details=details;$scope.editData=false;$scope.loader=new Utils.Loader();$scope.topic={};var loadPreview=$scope.loader.bind(function(topic){return Topic.preview(topic).then(function(res){$scope.topic=res.data;$scope.topic.rules.forEach(DataUtils.readRule);$scope.sampleData={rules:cloneDeep($scope.topic.rules),systems:{affectedCount:$scope.topic.affectedSystemCount}};},function(res){if(res.status===404){$scope.topic=null;}});});$scope.updateSampleData=function(){var data=pick(topic,['title','content','summary','alwaysShow','tag','category','rules']);data.overrides={rules:$scope.sampleData.rules.map(function(rule){return{rule_id:rule.rule_id,hitCount:rule.hitCount,acked:rule.acked};}),systems:$scope.sampleData.systems};loadPreview(data);};loadPreview(topic);}componentsModule.controller('TopicPreviewModalCtrl',TopicPreviewModalCtrl);},{"../../":83,"lodash/cloneDeep":482,"lodash/pick":533}],148:[function(require,module,exports){'use strict';var componentsModule=require('../../');function topicRuleFilters(){return{templateUrl:'js/components/topics/topicRuleFilters/topicRuleFilters.html',restrict:'E',replace:true};}componentsModule.directive('topicRuleFilters',topicRuleFilters);},{"../../":83}],149:[function(require,module,exports){'use strict';topicRuleListCtrl.$inject=['$filter','$location','$rootScope','$scope','$state','$stateParams','Events','IncidentsService','InsightsConfig','ListTypeService','RuleService','Utils'];var componentsModule=require('../../');var partition=require('lodash/partition');var _filter=require('lodash/filter');function topicRuleListCtrl($filter,$location,$rootScope,$scope,$state,$stateParams,Events,IncidentsService,InsightsConfig,ListTypeService,RuleService,Utils){$scope.config=InsightsConfig;$scope.listTypes=ListTypeService.types();$scope.getListType=ListTypeService.getType;$scope.sorter=new Utils.Sorter({predicate:['acked','hitCount === 0','-severityNum','-hitCount'],reverse:false},order);$scope.filterZero=function filterZero(rule){if($scope.showRulesWithNoHits||rule.hitCount>0&&!rule.acked){return true;}return false;};function init(){$scope.showRulesWithNoHits=false;$scope.hiddenCount=0;if($stateParams[Events.filters.totalRisk]&&$stateParams[Events.filters.totalRisk]!=='All'){$location.search(Events.filters.totalRisk,$stateParams[Events.filters.totalRisk]);}$scope.filterIncidents=$location.search()[Events.filters.incident]||'all';$scope.totalRisk=$location.search()[Events.filters.totalRisk]||'All';$scope.riskOfChange=$location.search().riskOfChange||'all';}function updateList(topic){if(!topic){return;}$scope.filteredRules=applyFilters($scope.topic.rules);updateCards($scope.filteredRules);}$rootScope.$on('account:change',init);$scope.$watch('topic',function(topic){$scope.showRulesWithNoHits=topic&&topic.hitCount===0;updateList(topic);});// Listens for change in incidents filter $scope.$on(Events.filters.incident,function(event,value){$scope.filterIncidents=value;updateList($scope.topic);});// Listens for change in total risk filter $scope.$on(Events.filters.totalRisk,function(event,value){$scope.totalRisk=value;updateList($scope.topic);});// Listens for change in risk of change filter $scope.$on(Events.filters.riskOfChangeSelect,function(event,value){$scope.riskOfChange=value;updateList($scope.topic);});// Listens for Reset filters $scope.$on(Events.filters.reset,function(){$scope.filterIncidents='all';$scope.totalRisk='All';updateList($scope.topic);});function applyFilters(rules){// Filter based on incidents value if($scope.filterIncidents==='incidents'){rules=_filter(rules,function(rule){return IncidentsService.isIncident(rule.rule_id);});}else if($scope.filterIncidents==='nonIncidents'){rules=_filter(rules,function(rule){return!IncidentsService.isIncident(rule.rule_id);});}// Filter based on total risk if($scope.totalRisk!=='All'){rules=_filter(rules,function(rule){return rule.severity===$scope.totalRisk;});}// Filter based on riskOfChange if($scope.riskOfChange!=='all'){rules=_filter(rules,function(rule){return rule.resolution_risk===parseInt($scope.riskOfChange);});}return rules;}function updateCards(rules){if(!$scope.showRulesWithNoHits){var parts=partition(rules,function(rule){return rule.hitCount&&!rule.acked;});$scope.plugins=RuleService.groupRulesByPlugin(parts[0]);$scope.hiddenCount=parts[1].length;}else{$scope.plugins=RuleService.groupRulesByPlugin(rules);}}// show even rules with no hits $scope.showAll=function(){$scope.showRulesWithNoHits=true;updateList($scope.topic);};$scope.viewImpactedSystems=function(category,ruleId){$state.go('app.actions-rule',{category:category,rule:ruleId});};$scope.checkIncident=function(rule_id){return IncidentsService.isIncident(rule_id)&&$rootScope.isBeta;};function order(){$scope.topic.rules=$filter('orderBy')($scope.topic.rules,['_type',$scope.sorter.reverse?'-'+$scope.sorter.predicate:$scope.sorter.predicate]);$scope.filteredRules=applyFilters($scope.topic.rules);updateCards($scope.filteredRules);}init();}/** * @ngInject */function topicRuleList(){return{templateUrl:'js/components/topics/topicRuleList/topicRuleList.html',restrict:'E',replace:true,controller:topicRuleListCtrl,scope:{topic:'='}};}componentsModule.directive('topicRuleList',topicRuleList);},{"../../":83,"lodash/filter":490,"lodash/partition":532}],150:[function(require,module,exports){'use strict';typeIconCtrl.$inject=['$scope','SystemsService'];var componentsModule=require('../');/** * @ngInject */function typeIconCtrl($scope,SystemsService){$scope.systemTypeIcon='';$scope.systemTypeDisplayName='';$scope.systemTypeDisplayNameShort='';SystemsService.getSystemTypeAsync($scope.typeId).then(function(systemType){if(systemType){$scope.systemTypeIcon=systemType.imageClass;$scope.systemTypeDisplayName=systemType.displayName;$scope.systemTypeDisplayNameShort=systemType.displayNameShort;}});}function typeIcon(){return{scope:{typeId:'=',includeText:'='},templateUrl:'js/components/typeIcon/typeIcon.html',restrict:'EC',replace:true,controller:typeIconCtrl};}componentsModule.directive('typeIcon',typeIcon);},{"../":83}],151:[function(require,module,exports){/* * angular-ui-bootstrap * http://angular-ui.github.io/bootstrap/ * Version: 0.13.2 - 2015-08-02 * License: MIT */angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.collapse","ui.bootstrap.alert","ui.bootstrap.dropdown","ui.bootstrap.position","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.bindHtml","ui.bootstrap.tabs"]);angular.module("ui.bootstrap.tpls",["template/alert/alert.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-popup.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/tooltip/tooltip-template-popup.html","template/tabs/tab.html","template/tabs/tabset.html"]);angular.module('ui.bootstrap.collapse',[]).directive('collapse',['$animate',function($animate){return{link:function link(scope,element,attrs){function expand(){element.removeClass('collapse').addClass('collapsing').attr('aria-expanded',true).attr('aria-hidden',false);$animate.addClass(element,'in',{to:{height:element[0].scrollHeight+'px'}}).then(expandDone);}function expandDone(){element.removeClass('collapsing');element.css({height:'auto'});}function collapse(){if(!element.hasClass('collapse')&&!element.hasClass('in')){return collapseDone();}element// IMPORTANT: The height must be set before adding "collapsing" class. // Otherwise, the browser attempts to animate from height 0 (in // collapsing class) to the given height here. .css({height:element[0].scrollHeight+'px'})// initially all panel collapse have the collapse class, this removal // prevents the animation from jumping to collapsed state .removeClass('collapse').addClass('collapsing').attr('aria-expanded',false).attr('aria-hidden',true);$animate.removeClass(element,'in',{to:{height:'0'}}).then(collapseDone);}function collapseDone(){element.css({height:'0'});// Required so that collapse works when animation is disabled element.removeClass('collapsing');element.addClass('collapse');}scope.$watch(attrs.collapse,function(shouldCollapse){if(shouldCollapse){collapse();}else{expand();}});}};}]);angular.module('ui.bootstrap.alert',[]).controller('AlertController',['$scope','$attrs',function($scope,$attrs){$scope.closeable=!!$attrs.close;this.close=$scope.close;}]).directive('alert',function(){return{restrict:'EA',controller:'AlertController',templateUrl:'template/alert/alert.html',transclude:true,replace:true,scope:{type:'@',close:'&'}};}).directive('dismissOnTimeout',['$timeout',function($timeout){return{require:'alert',link:function link(scope,element,attrs,alertCtrl){$timeout(function(){alertCtrl.close();},parseInt(attrs.dismissOnTimeout,10));}};}]);angular.module('ui.bootstrap.dropdown',['ui.bootstrap.position']).constant('dropdownConfig',{openClass:'open'}).service('dropdownService',['$document','$rootScope',function($document,$rootScope){var openScope=null;this.open=function(dropdownScope){if(!openScope){$document.bind('click',closeDropdown);$document.bind('keydown',keybindFilter);}if(openScope&&openScope!==dropdownScope){openScope.isOpen=false;}openScope=dropdownScope;};this.close=function(dropdownScope){if(openScope===dropdownScope){openScope=null;$document.unbind('click',closeDropdown);$document.unbind('keydown',keybindFilter);}};var closeDropdown=function closeDropdown(evt){// This method may still be called during the same mouse event that // unbound this event handler. So check openScope before proceeding. if(!openScope){return;}if(evt&&openScope.getAutoClose()==='disabled'){return;}var toggleElement=openScope.getToggleElement();if(evt&&toggleElement&&toggleElement[0].contains(evt.target)){return;}var dropdownElement=openScope.getDropdownElement();if(evt&&openScope.getAutoClose()==='outsideClick'&&dropdownElement&&dropdownElement[0].contains(evt.target)){return;}openScope.isOpen=false;if(!$rootScope.$$phase){openScope.$apply();}};var keybindFilter=function keybindFilter(evt){if(evt.which===27){openScope.focusToggleElement();closeDropdown();}else if(openScope.isKeynavEnabled()&&/(38|40)/.test(evt.which)&&openScope.isOpen){evt.preventDefault();evt.stopPropagation();openScope.focusDropdownEntry(evt.which);}};}]).controller('DropdownController',['$scope','$attrs','$parse','dropdownConfig','dropdownService','$animate','$position','$document','$compile','$templateRequest',function($scope,$attrs,$parse,dropdownConfig,dropdownService,$animate,$position,$document,$compile,$templateRequest){var self=this,scope=$scope.$new(),// create a child scope so we are not polluting original one templateScope,openClass=dropdownConfig.openClass,getIsOpen,setIsOpen=angular.noop,toggleInvoker=$attrs.onToggle?$parse($attrs.onToggle):angular.noop,appendToBody=false,keynavEnabled=false,selectedOption=null;this.init=function(element){self.$element=element;if($attrs.isOpen){getIsOpen=$parse($attrs.isOpen);setIsOpen=getIsOpen.assign;$scope.$watch(getIsOpen,function(value){scope.isOpen=!!value;});}appendToBody=angular.isDefined($attrs.dropdownAppendToBody);keynavEnabled=angular.isDefined($attrs.keyboardNav);if(appendToBody&&self.dropdownMenu){$document.find('body').append(self.dropdownMenu);element.on('$destroy',function handleDestroyEvent(){self.dropdownMenu.remove();});}};this.toggle=function(open){return scope.isOpen=arguments.length?!!open:!scope.isOpen;};// Allow other directives to watch status this.isOpen=function(){return scope.isOpen;};scope.getToggleElement=function(){return self.toggleElement;};scope.getAutoClose=function(){return $attrs.autoClose||'always';//or 'outsideClick' or 'disabled' };scope.getElement=function(){return self.$element;};scope.isKeynavEnabled=function(){return keynavEnabled;};scope.focusDropdownEntry=function(keyCode){var elems=self.dropdownMenu?//If append to body is used. angular.element(self.dropdownMenu).find('a'):angular.element(self.$element).find('ul').eq(0).find('a');switch(keyCode){case 40:{if(!angular.isNumber(self.selectedOption)){self.selectedOption=0;}else{self.selectedOption=self.selectedOption===elems.length-1?self.selectedOption:self.selectedOption+1;}break;}case 38:{if(!angular.isNumber(self.selectedOption)){return;}else{self.selectedOption=self.selectedOption===0?0:self.selectedOption-1;}break;}}elems[self.selectedOption].focus();};scope.getDropdownElement=function(){return self.dropdownMenu;};scope.focusToggleElement=function(){if(self.toggleElement){self.toggleElement[0].focus();}};scope.$watch('isOpen',function(isOpen,wasOpen){if(appendToBody&&self.dropdownMenu){var pos=$position.positionElements(self.$element,self.dropdownMenu,'bottom-left',true);var css={top:pos.top+'px',display:isOpen?'block':'none'};var rightalign=self.dropdownMenu.hasClass('dropdown-menu-right');if(!rightalign){css.left=pos.left+'px';css.right='auto';}else{css.left='auto';css.right=window.innerWidth-(pos.left+self.$element.prop('offsetWidth'))+'px';}self.dropdownMenu.css(css);}$animate[isOpen?'addClass':'removeClass'](self.$element,openClass).then(function(){if(angular.isDefined(isOpen)&&isOpen!==wasOpen){toggleInvoker($scope,{open:!!isOpen});}});if(isOpen){if(self.dropdownMenuTemplateUrl){$templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent){templateScope=scope.$new();$compile(tplContent.trim())(templateScope,function(dropdownElement){var newEl=dropdownElement;self.dropdownMenu.replaceWith(newEl);self.dropdownMenu=newEl;});});}scope.focusToggleElement();dropdownService.open(scope);}else{if(self.dropdownMenuTemplateUrl){if(templateScope){templateScope.$destroy();}var newEl=angular.element('');self.dropdownMenu.replaceWith(newEl);self.dropdownMenu=newEl;}dropdownService.close(scope);self.selectedOption=null;}if(angular.isFunction(setIsOpen)){setIsOpen($scope,isOpen);}});$scope.$on('$locationChangeSuccess',function(){if(scope.getAutoClose()!=='disabled'){scope.isOpen=false;}});$scope.$on('$destroy',function(){scope.$destroy();});}]).directive('dropdown',function(){return{controller:'DropdownController',link:function link(scope,element,attrs,dropdownCtrl){dropdownCtrl.init(element);element.addClass('dropdown');}};}).directive('dropdownMenu',function(){return{restrict:'AC',require:'?^dropdown',link:function link(scope,element,attrs,dropdownCtrl){if(!dropdownCtrl){return;}var tplUrl=attrs.templateUrl;if(tplUrl){dropdownCtrl.dropdownMenuTemplateUrl=tplUrl;}if(!dropdownCtrl.dropdownMenu){dropdownCtrl.dropdownMenu=element;}}};}).directive('keyboardNav',function(){return{restrict:'A',require:'?^dropdown',link:function link(scope,element,attrs,dropdownCtrl){element.bind('keydown',function(e){if([38,40].indexOf(e.which)!==-1){e.preventDefault();e.stopPropagation();var elems=angular.element(element).find('a');switch(e.keyCode){case 40:{// Down if(!angular.isNumber(dropdownCtrl.selectedOption)){dropdownCtrl.selectedOption=0;}else{dropdownCtrl.selectedOption=dropdownCtrl.selectedOption===elems.length-1?dropdownCtrl.selectedOption:dropdownCtrl.selectedOption+1;}}break;case 38:{// Up dropdownCtrl.selectedOption=dropdownCtrl.selectedOption===0?0:dropdownCtrl.selectedOption-1;}break;}elems[dropdownCtrl.selectedOption].focus();}});}};}).directive('dropdownToggle',function(){return{require:'?^dropdown',link:function link(scope,element,attrs,dropdownCtrl){if(!dropdownCtrl){return;}element.addClass('dropdown-toggle');dropdownCtrl.toggleElement=element;var toggleDropdown=function toggleDropdown(event){event.preventDefault();if(!element.hasClass('disabled')&&!attrs.disabled){scope.$apply(function(){dropdownCtrl.toggle();});}};element.bind('click',toggleDropdown);// WAI-ARIA element.attr({'aria-haspopup':true,'aria-expanded':false});scope.$watch(dropdownCtrl.isOpen,function(isOpen){element.attr('aria-expanded',!!isOpen);});scope.$on('$destroy',function(){element.unbind('click',toggleDropdown);});}};});angular.module('ui.bootstrap.position',[])/** * A set of utility methods that can be use to retrieve position of DOM elements. * It is meant to be used where we need to absolute-position DOM elements in * relation to other, existing elements (this is the case for tooltips, popovers, * typeahead suggestions etc.). */.factory('$position',['$document','$window',function($document,$window){function getStyle(el,cssprop){if(el.currentStyle){//IE return el.currentStyle[cssprop];}else if($window.getComputedStyle){return $window.getComputedStyle(el)[cssprop];}// finally try and get inline style return el.style[cssprop];}/** * Checks if a given element is statically positioned * @param element - raw DOM element */function isStaticPositioned(element){return(getStyle(element,'position')||'static')==='static';}/** * returns the closest, non-statically positioned parentOffset of a given element * @param element */var parentOffsetEl=function parentOffsetEl(element){var docDomEl=$document[0];var offsetParent=element.offsetParent||docDomEl;while(offsetParent&&offsetParent!==docDomEl&&isStaticPositioned(offsetParent)){offsetParent=offsetParent.offsetParent;}return offsetParent||docDomEl;};return{/** * Provides read-only equivalent of jQuery's position function: * http://api.jquery.com/position/ */position:function position(element){var elBCR=this.offset(element);var offsetParentBCR={top:0,left:0};var offsetParentEl=parentOffsetEl(element[0]);if(offsetParentEl!=$document[0]){offsetParentBCR=this.offset(angular.element(offsetParentEl));offsetParentBCR.top+=offsetParentEl.clientTop-offsetParentEl.scrollTop;offsetParentBCR.left+=offsetParentEl.clientLeft-offsetParentEl.scrollLeft;}var boundingClientRect=element[0].getBoundingClientRect();return{width:boundingClientRect.width||element.prop('offsetWidth'),height:boundingClientRect.height||element.prop('offsetHeight'),top:elBCR.top-offsetParentBCR.top,left:elBCR.left-offsetParentBCR.left};},/** * Provides read-only equivalent of jQuery's offset function: * http://api.jquery.com/offset/ */offset:function offset(element){var boundingClientRect=element[0].getBoundingClientRect();return{width:boundingClientRect.width||element.prop('offsetWidth'),height:boundingClientRect.height||element.prop('offsetHeight'),top:boundingClientRect.top+($window.pageYOffset||$document[0].documentElement.scrollTop),left:boundingClientRect.left+($window.pageXOffset||$document[0].documentElement.scrollLeft)};},/** * Provides coordinates for the targetEl in relation to hostEl */positionElements:function positionElements(hostEl,targetEl,positionStr,appendToBody){var positionStrParts=positionStr.split('-');var pos0=positionStrParts[0],pos1=positionStrParts[1]||'center';var hostElPos,targetElWidth,targetElHeight,targetElPos;hostElPos=appendToBody?this.offset(hostEl):this.position(hostEl);targetElWidth=targetEl.prop('offsetWidth');targetElHeight=targetEl.prop('offsetHeight');var shiftWidth={center:function center(){return hostElPos.left+hostElPos.width/2-targetElWidth/2;},left:function left(){return hostElPos.left;},right:function right(){return hostElPos.left+hostElPos.width;}};var shiftHeight={center:function center(){return hostElPos.top+hostElPos.height/2-targetElHeight/2;},top:function top(){return hostElPos.top;},bottom:function bottom(){return hostElPos.top+hostElPos.height;}};switch(pos0){case'right':targetElPos={top:shiftHeight[pos1](),left:shiftWidth[pos0]()};break;case'left':targetElPos={top:shiftHeight[pos1](),left:hostElPos.left-targetElWidth};break;case'bottom':targetElPos={top:shiftHeight[pos0](),left:shiftWidth[pos1]()};break;default:targetElPos={top:hostElPos.top-targetElHeight,left:shiftWidth[pos1]()};break;}return targetElPos;}};}]);angular.module('ui.bootstrap.modal',[])/** * A helper, internal data structure that acts as a map but also allows getting / removing * elements in the LIFO order */.factory('$$stackedMap',function(){return{createNew:function createNew(){var stack=[];return{add:function add(key,value){stack.push({key:key,value:value});},get:function get(key){for(var i=0;i0);});checkRemoveBackdrop();//move focus to specified element if available, or else to body if(elementToReceiveFocus&&elementToReceiveFocus.focus){elementToReceiveFocus.focus();}else{body.focus();}}function checkRemoveBackdrop(){//remove backdrop if no longer needed if(backdropDomEl&&backdropIndex()==-1){var backdropScopeRef=backdropScope;removeAfterAnimate(backdropDomEl,backdropScope,function(){backdropScopeRef=null;});backdropDomEl=undefined;backdropScope=undefined;}}function removeAfterAnimate(domEl,scope,done){var asyncDeferred;var asyncPromise=null;var setIsAsync=function setIsAsync(){if(!asyncDeferred){asyncDeferred=$q.defer();asyncPromise=asyncDeferred.promise;}return function asyncDone(){asyncDeferred.resolve();};};scope.$broadcast($modalStack.NOW_CLOSING_EVENT,setIsAsync);// Note that it's intentional that asyncPromise might be null. // That's when setIsAsync has not been called during the // NOW_CLOSING_EVENT broadcast. return $q.when(asyncPromise).then(afterAnimating);function afterAnimating(){if(afterAnimating.done){return;}afterAnimating.done=true;$animate.leave(domEl);scope.$destroy();if(done){done();}}}$document.bind('keydown',function(evt){var modal=openedWindows.top();if(modal&&modal.value.keyboard){switch(evt.which){case 27:{evt.preventDefault();$rootScope.$apply(function(){$modalStack.dismiss(modal.key,'escape key press');});break;}case 9:{$modalStack.loadFocusElementList(modal);var focusChanged=false;if(evt.shiftKey){if($modalStack.isFocusInFirstItem(evt)){focusChanged=$modalStack.focusLastFocusableElement();}}else{if($modalStack.isFocusInLastItem(evt)){focusChanged=$modalStack.focusFirstFocusableElement();}}if(focusChanged){evt.preventDefault();evt.stopPropagation();}break;}}}});$modalStack.open=function(modalInstance,modal){var modalOpener=$document[0].activeElement;openedWindows.add(modalInstance,{deferred:modal.deferred,renderDeferred:modal.renderDeferred,modalScope:modal.scope,backdrop:modal.backdrop,keyboard:modal.keyboard});var body=$document.find('body').eq(0),currBackdropIndex=backdropIndex();if(currBackdropIndex>=0&&!backdropDomEl){backdropScope=$rootScope.$new(true);backdropScope.index=currBackdropIndex;var angularBackgroundDomEl=angular.element('
');angularBackgroundDomEl.attr('backdrop-class',modal.backdropClass);if(modal.animation){angularBackgroundDomEl.attr('modal-animation','true');}backdropDomEl=$compile(angularBackgroundDomEl)(backdropScope);body.append(backdropDomEl);}var angularDomEl=angular.element('
');angularDomEl.attr({'template-url':modal.windowTemplateUrl,'window-class':modal.windowClass,'size':modal.size,'index':openedWindows.length()-1,'animate':'animate'}).html(modal.content);if(modal.animation){angularDomEl.attr('modal-animation','true');}var modalDomEl=$compile(angularDomEl)(modal.scope);openedWindows.top().value.modalDomEl=modalDomEl;openedWindows.top().value.modalOpener=modalOpener;body.append(modalDomEl);body.addClass(OPENED_MODAL_CLASS);$modalStack.clearFocusListCache();};function broadcastClosing(modalWindow,resultOrReason,closing){return!modalWindow.value.modalScope.$broadcast('modal.closing',resultOrReason,closing).defaultPrevented;}$modalStack.close=function(modalInstance,result){var modalWindow=openedWindows.get(modalInstance);if(modalWindow&&broadcastClosing(modalWindow,result,true)){modalWindow.value.deferred.resolve(result);removeModalWindow(modalInstance,modalWindow.value.modalOpener);return true;}return!modalWindow;};$modalStack.dismiss=function(modalInstance,reason){var modalWindow=openedWindows.get(modalInstance);if(modalWindow&&broadcastClosing(modalWindow,reason,false)){modalWindow.value.deferred.reject(reason);removeModalWindow(modalInstance,modalWindow.value.modalOpener);return true;}return!modalWindow;};$modalStack.dismissAll=function(reason){var topModal=this.getTop();while(topModal&&this.dismiss(topModal.key,reason)){topModal=this.getTop();}};$modalStack.getTop=function(){return openedWindows.top();};$modalStack.modalRendered=function(modalInstance){var modalWindow=openedWindows.get(modalInstance);if(modalWindow){modalWindow.value.renderDeferred.resolve();}};$modalStack.focusFirstFocusableElement=function(){if(focusableElementList.length>0){focusableElementList[0].focus();return true;}return false;};$modalStack.focusLastFocusableElement=function(){if(focusableElementList.length>0){focusableElementList[focusableElementList.length-1].focus();return true;}return false;};$modalStack.isFocusInFirstItem=function(evt){if(focusableElementList.length>0){return(evt.target||evt.srcElement)==focusableElementList[0];}return false;};$modalStack.isFocusInLastItem=function(evt){if(focusableElementList.length>0){return(evt.target||evt.srcElement)==focusableElementList[focusableElementList.length-1];}return false;};$modalStack.clearFocusListCache=function(){focusableElementList=[];focusIndex=0;};$modalStack.loadFocusElementList=function(modalWindow){if(focusableElementList===undefined||!focusableElementList.length0){if(modalWindow){var modalDomE1=modalWindow.value.modalDomEl;if(modalDomE1&&modalDomE1.length){focusableElementList=modalDomE1[0].querySelectorAll(tababbleSelector);}}}};return $modalStack;}]).provider('$modal',function(){var $modalProvider={options:{animation:true,backdrop:true,//can also be false or 'static' keyboard:true},$get:['$injector','$rootScope','$q','$templateRequest','$controller','$modalStack',function($injector,$rootScope,$q,$templateRequest,$controller,$modalStack){var $modal={};function getTemplatePromise(options){return options.template?$q.when(options.template):$templateRequest(angular.isFunction(options.templateUrl)?options.templateUrl():options.templateUrl);}function getResolvePromises(resolves){var promisesArr=[];angular.forEach(resolves,function(value){if(angular.isFunction(value)||angular.isArray(value)){promisesArr.push($q.when($injector.invoke(value)));}});return promisesArr;}$modal.open=function(modalOptions){var modalResultDeferred=$q.defer();var modalOpenedDeferred=$q.defer();var modalRenderDeferred=$q.defer();//prepare an instance of a modal to be injected into controllers and returned to a caller var modalInstance={result:modalResultDeferred.promise,opened:modalOpenedDeferred.promise,rendered:modalRenderDeferred.promise,close:function close(result){return $modalStack.close(modalInstance,result);},dismiss:function dismiss(reason){return $modalStack.dismiss(modalInstance,reason);}};//merge and clean up options modalOptions=angular.extend({},$modalProvider.options,modalOptions);modalOptions.resolve=modalOptions.resolve||{};//verify options if(!modalOptions.template&&!modalOptions.templateUrl){throw new Error('One of template or templateUrl options is required.');}var templateAndResolvePromise=$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));templateAndResolvePromise.then(function resolveSuccess(tplAndVars){var modalScope=(modalOptions.scope||$rootScope).$new();modalScope.$close=modalInstance.close;modalScope.$dismiss=modalInstance.dismiss;var ctrlInstance,ctrlLocals={};var resolveIter=1;//controllers if(modalOptions.controller){ctrlLocals.$scope=modalScope;ctrlLocals.$modalInstance=modalInstance;angular.forEach(modalOptions.resolve,function(value,key){ctrlLocals[key]=tplAndVars[resolveIter++];});ctrlInstance=$controller(modalOptions.controller,ctrlLocals);if(modalOptions.controllerAs){if(modalOptions.bindToController){angular.extend(ctrlInstance,modalScope);}modalScope[modalOptions.controllerAs]=ctrlInstance;}}$modalStack.open(modalInstance,{scope:modalScope,deferred:modalResultDeferred,renderDeferred:modalRenderDeferred,content:tplAndVars[0],animation:modalOptions.animation,backdrop:modalOptions.backdrop,keyboard:modalOptions.keyboard,backdropClass:modalOptions.backdropClass,windowClass:modalOptions.windowClass,windowTemplateUrl:modalOptions.windowTemplateUrl,size:modalOptions.size});},function resolveError(reason){modalResultDeferred.reject(reason);});templateAndResolvePromise.then(function(){modalOpenedDeferred.resolve(true);},function(reason){modalOpenedDeferred.reject(reason);});return modalInstance;};return $modal;}]};return $modalProvider;});angular.module('ui.bootstrap.pagination',[]).controller('PaginationController',['$scope','$attrs','$parse',function($scope,$attrs,$parse){var self=this,ngModelCtrl={$setViewValue:angular.noop},// nullModelCtrl setNumPages=$attrs.numPages?$parse($attrs.numPages).assign:angular.noop;this.init=function(ngModelCtrl_,config){ngModelCtrl=ngModelCtrl_;this.config=config;ngModelCtrl.$render=function(){self.render();};if($attrs.itemsPerPage){$scope.$parent.$watch($parse($attrs.itemsPerPage),function(value){self.itemsPerPage=parseInt(value,10);$scope.totalPages=self.calculateTotalPages();});}else{this.itemsPerPage=config.itemsPerPage;}$scope.$watch('totalItems',function(){$scope.totalPages=self.calculateTotalPages();});$scope.$watch('totalPages',function(value){setNumPages($scope.$parent,value);// Readonly variable if($scope.page>value){$scope.selectPage(value);}else{ngModelCtrl.$render();}});};this.calculateTotalPages=function(){var totalPages=this.itemsPerPage<1?1:Math.ceil($scope.totalItems/this.itemsPerPage);return Math.max(totalPages||0,1);};this.render=function(){$scope.page=parseInt(ngModelCtrl.$viewValue,10)||1;};$scope.selectPage=function(page,evt){var clickAllowed=!$scope.ngDisabled||!evt;if(clickAllowed&&$scope.page!==page&&page>0&&page<=$scope.totalPages){if(evt&&evt.target){evt.target.blur();}ngModelCtrl.$setViewValue(page);ngModelCtrl.$render();}};$scope.getText=function(key){return $scope[key+'Text']||self.config[key+'Text'];};$scope.noPrevious=function(){return $scope.page===1;};$scope.noNext=function(){return $scope.page===$scope.totalPages;};}]).constant('paginationConfig',{itemsPerPage:10,boundaryLinks:false,directionLinks:true,firstText:'First',previousText:'Previous',nextText:'Next',lastText:'Last',rotate:true}).directive('pagination',['$parse','paginationConfig',function($parse,paginationConfig){return{restrict:'EA',scope:{totalItems:'=',firstText:'@',previousText:'@',nextText:'@',lastText:'@',ngDisabled:'='},require:['pagination','?ngModel'],controller:'PaginationController',templateUrl:'template/pagination/pagination.html',replace:true,link:function link(scope,element,attrs,ctrls){var paginationCtrl=ctrls[0],ngModelCtrl=ctrls[1];if(!ngModelCtrl){return;// do nothing if no ng-model }// Setup configuration parameters var maxSize=angular.isDefined(attrs.maxSize)?scope.$parent.$eval(attrs.maxSize):paginationConfig.maxSize,rotate=angular.isDefined(attrs.rotate)?scope.$parent.$eval(attrs.rotate):paginationConfig.rotate;scope.boundaryLinks=angular.isDefined(attrs.boundaryLinks)?scope.$parent.$eval(attrs.boundaryLinks):paginationConfig.boundaryLinks;scope.directionLinks=angular.isDefined(attrs.directionLinks)?scope.$parent.$eval(attrs.directionLinks):paginationConfig.directionLinks;paginationCtrl.init(ngModelCtrl,paginationConfig);if(attrs.maxSize){scope.$parent.$watch($parse(attrs.maxSize),function(value){maxSize=parseInt(value,10);paginationCtrl.render();});}// Create page object used in template function makePage(number,text,isActive){return{number:number,text:text,active:isActive};}function getPages(currentPage,totalPages){var pages=[];// Default page limits var startPage=1,endPage=totalPages;var isMaxSized=angular.isDefined(maxSize)&&maxSizetotalPages){endPage=totalPages;startPage=endPage-maxSize+1;}}else{// Visible pages are paginated with maxSize startPage=(Math.ceil(currentPage/maxSize)-1)*maxSize+1;// Adjust last page if limit is exceeded endPage=Math.min(startPage+maxSize-1,totalPages);}}// Add page number links for(var number=startPage;number<=endPage;number++){var page=makePage(number,number,number===currentPage);pages.push(page);}// Add links to move between page sets if(isMaxSized&&!rotate){if(startPage>1){var previousPageSet=makePage(startPage-1,'...',false);pages.unshift(previousPageSet);}if(endPage0&&scope.page<=scope.totalPages){scope.pages=getPages(scope.page,scope.totalPages);}};}};}]).constant('pagerConfig',{itemsPerPage:10,previousText:'« Previous',nextText:'Next »',align:true}).directive('pager',['pagerConfig',function(pagerConfig){return{restrict:'EA',scope:{totalItems:'=',previousText:'@',nextText:'@'},require:['pager','?ngModel'],controller:'PaginationController',templateUrl:'template/pagination/pager.html',replace:true,link:function link(scope,element,attrs,ctrls){var paginationCtrl=ctrls[0],ngModelCtrl=ctrls[1];if(!ngModelCtrl){return;// do nothing if no ng-model }scope.align=angular.isDefined(attrs.align)?scope.$parent.$eval(attrs.align):pagerConfig.align;paginationCtrl.init(ngModelCtrl,pagerConfig);}};}]);/** * The following features are still outstanding: animation as a * function, placement as a function, inside, support for more triggers than * just mouse enter/leave, html tooltips, and selector delegation. */angular.module('ui.bootstrap.tooltip',['ui.bootstrap.position','ui.bootstrap.bindHtml'])/** * The $tooltip service creates tooltip- and popover-like directives as well as * houses global options for them. */.provider('$tooltip',function(){// The default options tooltip and popover. var defaultOptions={placement:'top',animation:true,popupDelay:0,useContentExp:false};// Default hide triggers for each show trigger var triggerMap={'mouseenter':'mouseleave','click':'click','focus':'blur'};// The options specified to the provider globally. var globalOptions={};/** * `options({})` allows global configuration of all tooltips in the * application. * * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { * // place tooltips left instead of top by default * $tooltipProvider.options( { placement: 'left' } ); * }); */this.options=function(value){angular.extend(globalOptions,value);};/** * This allows you to extend the set of trigger mappings available. E.g.: * * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); */this.setTriggers=function setTriggers(triggers){angular.extend(triggerMap,triggers);};/** * This is a helper function for translating camel-case to snake-case. */function snake_case(name){var regexp=/[A-Z]/g;var separator='-';return name.replace(regexp,function(letter,pos){return(pos?separator:'')+letter.toLowerCase();});}/** * Returns the actual instance of the $tooltip service. * TODO support multiple triggers */this.$get=['$window','$compile','$timeout','$document','$position','$interpolate',function($window,$compile,$timeout,$document,$position,$interpolate){return function $tooltip(type,prefix,defaultTriggerShow,options){options=angular.extend({},defaultOptions,globalOptions,options);/** * Returns an object of show and hide triggers. * * If a trigger is supplied, * it is used to show the tooltip; otherwise, it will use the `trigger` * option passed to the `$tooltipProvider.options` method; else it will * default to the trigger supplied to this directive factory. * * The hide trigger is based on the show trigger. If the `trigger` option * was passed to the `$tooltipProvider.options` method, it will use the * mapped trigger from `triggerMap` or the passed trigger if the map is * undefined; otherwise, it uses the `triggerMap` value of the show * trigger; else it will just use the show trigger. */function getTriggers(trigger){var show=trigger||options.trigger||defaultTriggerShow;var hide=triggerMap[show]||show;return{show:show,hide:hide};}var directiveName=snake_case(type);var startSym=$interpolate.startSymbol();var endSym=$interpolate.endSymbol();var template='
'+'
';return{restrict:'EA',compile:function compile(tElem,tAttrs){var tooltipLinker=$compile(template);return function link(scope,element,attrs,tooltipCtrl){var tooltip;var tooltipLinkedScope;var transitionTimeout;var popupTimeout;var appendToBody=angular.isDefined(options.appendToBody)?options.appendToBody:false;var triggers=getTriggers(undefined);var hasEnableExp=angular.isDefined(attrs[prefix+'Enable']);var ttScope=scope.$new(true);var positionTooltip=function positionTooltip(){if(!tooltip){return;}var ttPosition=$position.positionElements(element,tooltip,ttScope.placement,appendToBody);ttPosition.top+='px';ttPosition.left+='px';// Now set the calculated positioning. tooltip.css(ttPosition);};var positionTooltipAsync=function positionTooltipAsync(){$timeout(positionTooltip,0,false);};// Set up the correct scope to allow transclusion later ttScope.origScope=scope;// By default, the tooltip is not open. // TODO add ability to start tooltip opened ttScope.isOpen=false;function toggleTooltipBind(){if(!ttScope.isOpen){showTooltipBind();}else{hideTooltipBind();}}// Show the tooltip with delay if specified, otherwise show it immediately function showTooltipBind(){if(hasEnableExp&&!scope.$eval(attrs[prefix+'Enable'])){return;}prepareTooltip();if(ttScope.popupDelay){// Do nothing if the tooltip was already scheduled to pop-up. // This happens if show is triggered multiple times before any hide is triggered. if(!popupTimeout){popupTimeout=$timeout(show,ttScope.popupDelay,false);popupTimeout.then(function(reposition){reposition();});}}else{show()();}}function hideTooltipBind(){scope.$apply(function(){hide();});}// Show the tooltip popup element. function show(){popupTimeout=null;// If there is a pending remove transition, we must cancel it, lest the // tooltip be mysteriously removed. if(transitionTimeout){$timeout.cancel(transitionTimeout);transitionTimeout=null;}// Don't show empty tooltips. if(!(options.useContentExp?ttScope.contentExp():ttScope.content)){return angular.noop;}createTooltip();// Set the initial positioning. tooltip.css({top:0,left:0,display:'block'});ttScope.$digest();positionTooltip();// And show the tooltip. ttScope.isOpen=true;ttScope.$apply();// digest required as $apply is not called // Return positioning function as promise callback for correct // positioning after draw. return positionTooltip;}// Hide the tooltip popup element. function hide(){// First things first: we don't show it anymore. ttScope.isOpen=false;//if tooltip is going to be shown after delay, we must cancel this $timeout.cancel(popupTimeout);popupTimeout=null;// And now we remove it from the DOM. However, if we have animation, we // need to wait for it to expire beforehand. // FIXME: this is a placeholder for a port of the transitions library. if(ttScope.animation){if(!transitionTimeout){transitionTimeout=$timeout(removeTooltip,500);}}else{removeTooltip();}}function createTooltip(){// There can only be one tooltip element per directive shown at once. if(tooltip){removeTooltip();}tooltipLinkedScope=ttScope.$new();tooltip=tooltipLinker(tooltipLinkedScope,function(tooltip){if(appendToBody){$document.find('body').append(tooltip);}else{element.after(tooltip);}});if(options.useContentExp){tooltipLinkedScope.$watch('contentExp()',function(val){positionTooltipAsync();if(!val&&ttScope.isOpen){hide();}});}}function removeTooltip(){transitionTimeout=null;if(tooltip){tooltip.remove();tooltip=null;}if(tooltipLinkedScope){tooltipLinkedScope.$destroy();tooltipLinkedScope=null;}}function prepareTooltip(){prepPopupClass();prepPlacement();prepPopupDelay();}ttScope.contentExp=function(){return scope.$eval(attrs[type]);};/** * Observe the relevant attributes. */if(!options.useContentExp){attrs.$observe(type,function(val){ttScope.content=val;positionTooltipAsync();if(!val&&ttScope.isOpen){hide();}});}attrs.$observe('disabled',function(val){if(val&&ttScope.isOpen){hide();}});attrs.$observe(prefix+'Title',function(val){ttScope.title=val;positionTooltipAsync();});attrs.$observe(prefix+'Placement',function(){if(ttScope.isOpen){$timeout(function(){prepPlacement();show()();},0,false);}});function prepPopupClass(){ttScope.popupClass=attrs[prefix+'Class'];}function prepPlacement(){var val=attrs[prefix+'Placement'];ttScope.placement=angular.isDefined(val)?val:options.placement;}function prepPopupDelay(){var val=attrs[prefix+'PopupDelay'];var delay=parseInt(val,10);ttScope.popupDelay=!isNaN(delay)?delay:options.popupDelay;}var unregisterTriggers=function unregisterTriggers(){element.unbind(triggers.show,showTooltipBind);element.unbind(triggers.hide,hideTooltipBind);};function prepTriggers(){var val=attrs[prefix+'Trigger'];unregisterTriggers();triggers=getTriggers(val);if(triggers.show===triggers.hide){element.bind(triggers.show,toggleTooltipBind);}else{element.bind(triggers.show,showTooltipBind);element.bind(triggers.hide,hideTooltipBind);}}prepTriggers();var animation=scope.$eval(attrs[prefix+'Animation']);ttScope.animation=angular.isDefined(animation)?!!animation:options.animation;var appendToBodyVal=scope.$eval(attrs[prefix+'AppendToBody']);appendToBody=angular.isDefined(appendToBodyVal)?appendToBodyVal:appendToBody;// if a tooltip is attached to we need to remove it on // location change as its parent scope will probably not be destroyed // by the change. if(appendToBody){scope.$on('$locationChangeSuccess',function closeTooltipOnLocationChangeSuccess(){if(ttScope.isOpen){hide();}});}// Make sure tooltip is destroyed and removed. scope.$on('$destroy',function onDestroyTooltip(){$timeout.cancel(transitionTimeout);$timeout.cancel(popupTimeout);unregisterTriggers();removeTooltip();ttScope=null;});};}};};}];})// This is mostly ngInclude code but with a custom scope .directive('tooltipTemplateTransclude',['$animate','$sce','$compile','$templateRequest',function($animate,$sce,$compile,$templateRequest){return{link:function link(scope,elem,attrs){var origScope=scope.$eval(attrs.tooltipTemplateTranscludeScope);var changeCounter=0,currentScope,previousElement,currentElement;var cleanupLastIncludeContent=function cleanupLastIncludeContent(){if(previousElement){previousElement.remove();previousElement=null;}if(currentScope){currentScope.$destroy();currentScope=null;}if(currentElement){$animate.leave(currentElement).then(function(){previousElement=null;});previousElement=currentElement;currentElement=null;}};scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude),function(src){var thisChangeId=++changeCounter;if(src){//set the 2nd param to true to ignore the template request error so that the inner //contents and scope can be cleaned up. $templateRequest(src,true).then(function(response){if(thisChangeId!==changeCounter){return;}var newScope=origScope.$new();var template=response;var clone=$compile(template)(newScope,function(clone){cleanupLastIncludeContent();$animate.enter(clone,elem);});currentScope=newScope;currentElement=clone;currentScope.$emit('$includeContentLoaded',src);},function(){if(thisChangeId===changeCounter){cleanupLastIncludeContent();scope.$emit('$includeContentError',src);}});scope.$emit('$includeContentRequested',src);}else{cleanupLastIncludeContent();}});scope.$on('$destroy',cleanupLastIncludeContent);}};}])/** * Note that it's intentional that these classes are *not* applied through $animate. * They must not be animated as they're expected to be present on the tooltip on * initialization. */.directive('tooltipClasses',function(){return{restrict:'A',link:function link(scope,element,attrs){if(scope.placement){element.addClass(scope.placement);}if(scope.popupClass){element.addClass(scope.popupClass);}if(scope.animation()){element.addClass(attrs.tooltipAnimationClass);}}};}).directive('tooltipPopup',function(){return{restrict:'EA',replace:true,scope:{content:'@',placement:'@',popupClass:'@',animation:'&',isOpen:'&'},templateUrl:'template/tooltip/tooltip-popup.html'};}).directive('tooltip',['$tooltip',function($tooltip){return $tooltip('tooltip','tooltip','mouseenter');}]).directive('tooltipTemplatePopup',function(){return{restrict:'EA',replace:true,scope:{contentExp:'&',placement:'@',popupClass:'@',animation:'&',isOpen:'&',originScope:'&'},templateUrl:'template/tooltip/tooltip-template-popup.html'};}).directive('tooltipTemplate',['$tooltip',function($tooltip){return $tooltip('tooltipTemplate','tooltip','mouseenter',{useContentExp:true});}]).directive('tooltipHtmlPopup',function(){return{restrict:'EA',replace:true,scope:{contentExp:'&',placement:'@',popupClass:'@',animation:'&',isOpen:'&'},templateUrl:'template/tooltip/tooltip-html-popup.html'};}).directive('tooltipHtml',['$tooltip',function($tooltip){return $tooltip('tooltipHtml','tooltip','mouseenter',{useContentExp:true});}])/* Deprecated */.directive('tooltipHtmlUnsafePopup',function(){return{restrict:'EA',replace:true,scope:{content:'@',placement:'@',popupClass:'@',animation:'&',isOpen:'&'},templateUrl:'template/tooltip/tooltip-html-unsafe-popup.html'};}).value('tooltipHtmlUnsafeSuppressDeprecated',false).directive('tooltipHtmlUnsafe',['$tooltip','tooltipHtmlUnsafeSuppressDeprecated','$log',function($tooltip,tooltipHtmlUnsafeSuppressDeprecated,$log){if(!tooltipHtmlUnsafeSuppressDeprecated){$log.warn('tooltip-html-unsafe is now deprecated. Use tooltip-html or tooltip-template instead.');}return $tooltip('tooltipHtmlUnsafe','tooltip','mouseenter');}]);angular.module('ui.bootstrap.bindHtml',[]).value('$bindHtmlUnsafeSuppressDeprecated',false).directive('bindHtmlUnsafe',['$log','$bindHtmlUnsafeSuppressDeprecated',function($log,$bindHtmlUnsafeSuppressDeprecated){return function(scope,element,attr){if(!$bindHtmlUnsafeSuppressDeprecated){$log.warn('bindHtmlUnsafe is now deprecated. Use ngBindHtml instead');}element.addClass('ng-binding').data('$binding',attr.bindHtmlUnsafe);scope.$watch(attr.bindHtmlUnsafe,function bindHtmlUnsafeWatchAction(value){element.html(value||'');});};}]);/** * @ngdoc actions * @name ui.bootstrap.tabs * * @description * AngularJS version of the tabs directive. */angular.module('ui.bootstrap.tabs',[]).controller('TabsetController',['$scope',function TabsetCtrl($scope){var ctrl=this,tabs=ctrl.tabs=$scope.tabs=[];ctrl.select=function(selectedTab){angular.forEach(tabs,function(tab){if(tab.active&&tab!==selectedTab){tab.active=false;tab.onDeselect();}});selectedTab.active=true;selectedTab.onSelect();};ctrl.addTab=function addTab(tab){tabs.push(tab);// we can't run the select function on the first tab // since that would select it twice if(tabs.length===1&&tab.active!==false){tab.active=true;}else if(tab.active){ctrl.select(tab);}else{tab.active=false;}};ctrl.removeTab=function removeTab(tab){var index=tabs.indexOf(tab);//Select a new tab if the tab to be removed is selected and not destroyed if(tab.active&&tabs.length>1&&!destroyed){//If this is the last tab, select the previous tab. else, the next tab. var newActiveIndex=index==tabs.length-1?index-1:index+1;ctrl.select(tabs[newActiveIndex]);}tabs.splice(index,1);};var destroyed;$scope.$on('$destroy',function(){destroyed=true;});}])/** * @ngdoc directive * @name ui.bootstrap.tabs.directive:tabset * @restrict EA * * @description * Tabset is the outer container for the tabs directive * * @param {boolean=} vertical Whether or not to use vertical styling for the tabs. * @param {boolean=} justified Whether or not to use justified styling for the tabs. * * @example First Content! Second Content!
First Vertical Content! Second Vertical Content! First Justified Content! Second Justified Content!
*/.directive('tabset',function(){return{restrict:'EA',transclude:true,replace:true,scope:{type:'@'},controller:'TabsetController',templateUrl:'template/tabs/tabset.html',link:function link(scope,element,attrs){scope.vertical=angular.isDefined(attrs.vertical)?scope.$parent.$eval(attrs.vertical):false;scope.justified=angular.isDefined(attrs.justified)?scope.$parent.$eval(attrs.justified):false;}};})/** * @ngdoc directive * @name ui.bootstrap.tabs.directive:tab * @restrict EA * * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}. * @param {string=} select An expression to evaluate when the tab is selected. * @param {boolean=} active A binding, telling whether or not this tab is selected. * @param {boolean=} disabled A binding, telling whether or not this tab is disabled. * * @description * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}. * * @example

First Tab Alert me! Second Tab, with alert callback and html heading! {{item.content}}
function TabsDemoCtrl($scope) { $scope.items = [ { title:"Dynamic Title 1", content:"Dynamic Item 0" }, { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true } ]; $scope.alertMe = function() { setTimeout(function() { alert("You've selected the alert tab!"); }); }; };
*//** * @ngdoc directive * @name ui.bootstrap.tabs.directive:tabHeading * @restrict EA * * @description * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element. * * @example HTML in my titles?! And some content, too! Icon heading?!? That's right. */.directive('tab',['$parse','$log',function($parse,$log){return{require:'^tabset',restrict:'EA',replace:true,templateUrl:'template/tabs/tab.html',transclude:true,scope:{active:'=?',heading:'@',onSelect:'&select',//This callback is called in contentHeadingTransclude //once it inserts the tab's content into the dom onDeselect:'&deselect'},controller:function controller(){//Empty controller so other directives can require being 'under' a tab },link:function link(scope,elm,attrs,tabsetCtrl,transclude){scope.$watch('active',function(active){if(active){tabsetCtrl.select(scope);}});scope.disabled=false;if(attrs.disable){scope.$parent.$watch($parse(attrs.disable),function(value){scope.disabled=!!value;});}// Deprecation support of "disabled" parameter // fix(tab): IE9 disabled attr renders grey text on enabled tab #2677 // This code is duplicated from the lines above to make it easy to remove once // the feature has been completely deprecated if(attrs.disabled){$log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');scope.$parent.$watch($parse(attrs.disabled),function(value){scope.disabled=!!value;});}scope.select=function(){if(!scope.disabled){scope.active=true;}};tabsetCtrl.addTab(scope);scope.$on('$destroy',function(){tabsetCtrl.removeTab(scope);});//We need to transclude later, once the content container is ready. //when this link happens, we're inside a tab heading. scope.$transcludeFn=transclude;}};}]).directive('tabHeadingTransclude',[function(){return{restrict:'A',require:'^tab',link:function link(scope,elm,attrs,tabCtrl){scope.$watch('headingElement',function updateHeadingElement(heading){if(heading){elm.html('');elm.append(heading);}});}};}]).directive('tabContentTransclude',function(){return{restrict:'A',require:'^tabset',link:function link(scope,elm,attrs){var tab=scope.$eval(attrs.tabContentTransclude);//Now our tab is ready to be transcluded: both the tab heading area //and the tab content area are loaded. Transclude 'em both. tab.$transcludeFn(tab.$parent,function(contents){angular.forEach(contents,function(node){if(isTabHeading(node)){//Let tabHeadingTransclude know. tab.headingElement=node;}else{elm.append(node);}});});}};function isTabHeading(node){return node.tagName&&(node.hasAttribute('tab-heading')||node.hasAttribute('data-tab-heading')||node.tagName.toLowerCase()==='tab-heading'||node.tagName.toLowerCase()==='data-tab-heading');}});angular.module("template/alert/alert.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/alert/alert.html","
\n"+" \n"+"
\n"+"
\n"+"");}]);angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/modal/backdrop.html","
\n"+"");}]);angular.module("template/modal/window.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/modal/window.html","
\n"+"
\n"+"
\n"+"");}]);angular.module("template/pagination/pager.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/pagination/pager.html","");}]);angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/pagination/pagination.html","\n"+"");}]);angular.module("template/tooltip/tooltip-html-popup.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/tooltip/tooltip-html-popup.html","
\n"+"
\n"+"
\n"+"
\n"+"");}]);angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html","
\n"+"
\n"+"
\n"+"
\n"+"");}]);angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/tooltip/tooltip-popup.html","
\n"+"
\n"+"
\n"+"
\n"+"");}]);angular.module("template/tooltip/tooltip-template-popup.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/tooltip/tooltip-template-popup.html","
\n"+"
\n"+"
\n"+"
\n"+"");}]);angular.module("template/tabs/tab.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/tabs/tab.html","
  • \n"+" {{heading}}\n"+"
  • \n"+"");}]);angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/tabs/tabset.html","
    \n"+"
      \n"+"
      \n"+"
      \n"+"
      \n"+"
      \n"+"
      \n"+"");}]);},{}],152:[function(require,module,exports){'use strict';UserInfoDirectiveCtrl.$inject=['$scope','AccountService','AnnouncementService','User'];var componentsModule=require('../');var filter=require('lodash/filter');/** * @ngInject */function UserInfoDirectiveCtrl($scope,AccountService,AnnouncementService,User){AnnouncementService.getAnnouncements({showAll:true}).then(function(res){$scope.accountNumber=res.data;$scope.alerts={unseen:filter(res.data,{seen:false})};});User.asyncCurrent(function(user){$scope.user=user;});}function userInfo(){return{templateUrl:'js/components/userInfo/userInfo.html',restrict:'E',replace:true,scope:{},controller:UserInfoDirectiveCtrl};}componentsModule.directive('userInfo',userInfo);},{"../":83,"lodash/filter":490}],153:[function(require,module,exports){'use strict';var componentsModule=require('../../');componentsModule.directive('kbaseSolution',['$q','$http','StrataService',function($q,$http,StrataService){function validator(property,value){return function(results){if(results.length!==1||results[0][property].trim()!==value.trim()){$q.reject('no match');}};}function validateId(id){return StrataService.searchById(id).then(validator('display_id',id));}function validateUrl(url){return StrataService.searchByUri(url).then(validator('view_uri',url));}return{require:'ngModel',link:function link(scope,element,attrs,ctrl){ctrl.$asyncValidators.kbaseSolution=function(model){if(ctrl.$isEmpty(model)){return $q.when();// empty model considered valid }if(/^\d+$/.test(model)){return validateId(model);// numeric kbase id - let's verify it }if(model.indexOf('http')>-1){return validateUrl(model);// kbase URL - let's verify it }return $q.reject();};}};}]);},{"../../":83}],154:[function(require,module,exports){'use strict';var componentsModule=require('../');function yesNo(){return{scope:{yes:'=',no:'=',text:'@'},templateUrl:'js/components/yesNo/yesNo.html',restrict:'EC'};}componentsModule.directive('yesNo',yesNo);},{"../":83}],155:[function(require,module,exports){'use strict';Config.$inject=['$stateProvider','$urlRouterProvider','$urlMatcherFactoryProvider','$locationProvider','$httpProvider','$compileProvider','$anchorScrollProvider','$animateProvider','$tooltipProvider','paginationConfig'];var AuthInterceptor=require('./interceptors/auth');var XomitInterceptor=require('./interceptors/xomit');var ErrorInterceptor=require('./interceptors/error');/** * @ngInject */function Config($stateProvider,$urlRouterProvider,$urlMatcherFactoryProvider,$locationProvider,$httpProvider,$compileProvider,$anchorScrollProvider,$animateProvider,$tooltipProvider,paginationConfig){// strict mode requires a trailing slash $urlMatcherFactoryProvider.strictMode(false);// no hash bang $locationProvider.html5Mode(true).hashPrefix('!');// Default route $urlRouterProvider.otherwise('/');// Route redirects $urlRouterProvider.when('/error_infos','/rules/admin/');$urlRouterProvider.when('/info/security','/security/');//https://github.com/angular-ui/ui-router/wiki/ //Frequently-Asked-Questions#how-to-make-a-trailing-slash-optional-for-all-routes $urlRouterProvider.rule(function($injector,$location){var path=$location.url();// check to see if the path already has a slash where it should be if(path[path.length-1]==='/'||path.indexOf('/?')>-1){return;}if(path.indexOf('?')>-1){return path.replace('?','/?');}return path+'/';});// We are doing our own anchor scrolling, so disable angulars $anchorScrollProvider.disableAutoScrolling();// Prevents Basic Auth Popup $httpProvider.interceptors.push(XomitInterceptor);// Bounces to login if 401 is caught $httpProvider.interceptors.push(AuthInterceptor);// Adds a notification about http errors $httpProvider.interceptors.push(ErrorInterceptor);$animateProvider.classNameFilter(/ng-animate-enabled/);if("production"==='production'){$compileProvider.debugInfoEnabled(false);}$tooltipProvider.options({appendToBody:true,trigger:'mouseenter',placement:'top'});paginationConfig.maxSize=5;paginationConfig.previousText='«';paginationConfig.nextText='»';}module.exports=Config;},{"./interceptors/auth":165,"./interceptors/error":166,"./interceptors/xomit":167}],156:[function(require,module,exports){'use strict';__Categories.$inject=['gettext'];var constantsModule=require('./');constantsModule.constant('Categories',['all','availability','stability','performance','security']);/** * @ngInject */function __Categories(gettext){gettext('all');gettext('availability');gettext('stability');gettext('performance');gettext('security');}// This controller is not actually used, just needed to extract the category strings constantsModule.controller('__Categories',__Categories);},{"./":160}],157:[function(require,module,exports){'use strict';/** * This is a dumping ground for constants that don't need a specific module. * Check the other modules in the constants directory before adding a constant here. */var constantsModule=require('./');constantsModule.constant('GettingStartedUrl','https://access.redhat.com/products/red-hat-insights');constantsModule.constant('InviteUrl','https://home.corp.redhat.com/webform/evaluation-request-form');constantsModule.constant('QuickFilters',{summaryTypes:{systems:'systems',categories:'categories'}});// entitlements constants constantsModule.constant('TrialSku','SER0482');// tableToCards breakpoint constantsModule.constant('TableToCardsBreakpoint','650px');constantsModule.constant('HttpHeaders',{resourceCount:'X-Resource-Count'});// These are avalible via a service, but the service is not a production service. // For speed and reliability storing them here for now // We can move them to InsightsAPI if we want, // and have that API endpoint cache the data from Klink // https://klink.gsslab.rdu2.redhat.com/telemetry/accounts/internal constantsModule.constant('IgnoreAccountList',['939082','1070555','5361051','636204','5526886','730731','5506478','1298305','1337999','5463389','5341931','5557007','5586766','1494526','1460290','1568253','5491806','631105','1191423','5387712','5301816','5606428','5348764','901578','5364511','5597433','633200','5345665','971738','5496022','5582724','5496024','1455657','5538252','5273074','5457785','5301467','5254297','5535221','5274410','5597794','5385776','5596826','5243891','5673127','5594202','5375112','5463401','477931','5582531','5445856','720046','1061991','939054','5351378','761015','895158','5471870','5582336','6','1640157','5357088','1495773','901532','5258694','5505446','940527','5530698','853019','1292438','1212729','5453171','5645132','972614','1034029','958959','1262852','5644938','1626050','1191884','5574082','5618348','5436601','5305464','941133','1456379','5547202','1546454','5309654','540155','1650204','5591454','5440919','1446047','000006','1469411','5524039','5632300','5455085','5513381']);},{"./":160}],158:[function(require,module,exports){'use strict';var constantsModule=require('./');function buildEventsForRoot(root){var eventObj={};eventObj={};root.events.forEach(function(event){eventObj[event]=root.name+':'+event;});return eventObj;}function buildEventsForRootNoName(root){var eventObj={};eventObj={};root.events.forEach(function(event){eventObj[event]=event;});return eventObj;}function buildEvents(eventRoots){var eventsObj={};eventRoots.forEach(function(root){if(root.name==='filters'){eventsObj[root.name]=buildEventsForRootNoName(root);}else{eventsObj[root.name]=buildEventsForRoot(root);}});return eventsObj;}var _EVENT_ROOTS=[{name:'cards',events:['expandAll','collapseAll']},{name:'filters',events:['actionsSelect','categorySelect','checkInSelect','incident','maintenanceCategorySelect','populatedProducts','reset','riskOfChangeSelect','totalRisk','tag','removeTag','ansibleSupport','ruleStatus','likelihood','impact']},{name:'checkboxes',events:['reset']},{name:'planner',events:['planChanged','planDeleted','reloadTable','plansLoaded','openPlan']}];constantsModule.constant('Events',buildEvents(_EVENT_ROOTS));},{"./":160}],159:[function(require,module,exports){'use strict';var constantsModule=require('./');constantsModule.constant('IncidentFilters',{all:{title:'All',tag:null},incidents:{title:'Incident',tag:'Incidents: Incident'},nonIncidents:{title:'No Incident',tag:'Incidents: No Incident'}});constantsModule.constant('AnsibleSupportFilters',{all:{title:'All',tag:null},supported:{title:'Supported',tag:'Ansible Support: Supported'},notSupported:{title:'Not Supported',tag:'Ansible Support: Not Supported'}});constantsModule.constant('RuleStatusFilters',{all:{title:'All',tag:null},active:{title:'Enabled',tag:'Rule Status: Enabled'},ignored:{title:'Disabled',tag:'Rule Status: Disabled'}});constantsModule.constant('LikelihoodFilters',[{title:'All',tag:null},{title:'Low',tag:'Likelihood: Low'},{title:'Medium',tag:'Likelihood: Medium'},{title:'High',tag:'Likelihood: High'},{title:'Critical',tag:'Likelihood: Critical'}]);constantsModule.constant('ImpactFilters',[{title:'All',tag:null},{title:'Low',tag:'Impact: Low'},{title:'Medium',tag:'Impact: Medium'},{title:'High',tag:'Impact: High'},{title:'Critical',tag:'Impact: Critical'}]);},{"./":160}],160:[function(require,module,exports){'use strict';module.exports=angular.module('insights.constants',[]);(function(){var f=require("./index.js");f["categories"]=require("./categories.js");f["constants"]=require("./constants.js");f["events"]=require("./events.js");f["filters"]=require("./filters.js");f["index"]=require("./index.js");f["products"]=require("./products.js");f["ruleStates"]=require("./ruleStates.js");f["severites"]=require("./severites.js");return f;})();},{"./categories.js":156,"./constants.js":157,"./events.js":158,"./filters.js":159,"./index.js":160,"./products.js":161,"./ruleStates.js":162,"./severites.js":163}],161:[function(require,module,exports){'use strict';var constantsModule=require('./');constantsModule.constant('Products',{rhel:{shortName:'RHEL Server',fullName:'Red Hat Enterprise Linux',code:'rhel',icon:'fa-linux',roles:{host:{code:'host',icon:'fa-linux',fullName:'Red Hat Enterprise Linux System',shortName:'System'}}},rhev:{shortName:'RHEV',fullName:'Red Hat Enterprise Virtualization',code:'rhev',icon:'',roles:{cluster:{code:'cluster',fullName:'Red Hat Enterprise Virtualization Deployment',shortName:'Deployment',icon:'fa-object-group'},manager:{code:'manager',fullName:'Red Hat Enterprise Virtualization Manager',shortName:'Manager',icon:'fa-building'},hypervisor:{code:'hypervisor',fullName:'Red Hat Enterprise Virtualization Hypervisor',shortName:'Hypervisor',icon:'fa-server'}}},ocp:{shortName:'OCP',fullName:'Openshift Compute Platform',code:'ocp',icon:'',roles:{cluster:{code:'cluster',fullName:'Red Hat Openshift Compute Platform',shortName:'Deployment',icon:'fa-cubes'},master:{code:'master',fullName:'Red Hat Openshift Compute Platform Master',shortName:'Master',icon:'fa-home'},node:{code:'node',fullName:'Red Hat Openshift Compute Platform Node',shortName:'Node',icon:'fa-cogs'}}},osp:{shortName:'OSP',fullName:'Red Hat OpenStack Platform',code:'osp',icon:'',roles:{cluster:{code:'cluster',fullName:'Red Hat OpenStack Platform Deployment',shortName:'Deployment',icon:'fa-cubes'},director:{code:'director',fullName:'Red Hat OpenStack Platform Director',shortName:'Director',icon:'fa-home'},compute:{code:'compute',fullName:'Red Hat OpenStack Platform Compute Node',shortName:'Compute',icon:'fa-cogs'},controller:{code:'controller',fullName:'Red Hat OpenStack Platform Controller Node',shortName:'Controller',icon:'fa-wrench'}}},docker:{shortName2:'Container',shortName:'Containers',fullName:'Red Hat Containers',code:'docker',icon:'',roles:{host:{code:'host',fullName:'Red Hat Container Host',shortName:'Host',icon:'fa-ship'},image:{code:'image',fullName:'Red Hat Container Image',shortName:'Image',icon:'fa-archive'},container:{code:'container',fullName:'Red Hat Container',shortName:'Container',icon:'fa-cube'}}}});},{"./":160}],162:[function(require,module,exports){'use strict';var constantsModule=require('./');constantsModule.constant('RuleStates',['all','active','needs content','inactive','retired']);},{"./":160}],163:[function(require,module,exports){'use strict';var constantsModule=require('./');constantsModule.constant('Severities',[{label:'All',value:'All',icon:'all',tag:'Total Risk: All'},{label:'Low',value:'INFO',icon:'low',tag:'Total Risk: Low'},{label:'Medium',value:'WARN',icon:'med',tag:'Total Risk: Medium'},{label:'High',value:'ERROR',icon:'high',tag:'Total Risk: High'},{label:'Critical',value:'CRITICAL',icon:'critical',tag:'Total Risk: Critical'}]);},{"./":160}],164:[function(require,module,exports){/*global require, angular*/'use strict';var isPortal=typeof window.chrometwo_ready!=='undefined';if(typeof window.angular==='undefined'){// No angular found on window, pull it in. require('angular');}// angular modules require('angular-resource');require('angular-ui-router');require('angular-sanitize');require('angular-scroll');require('angular-gettext');require('angular-cookies');require('ui-select');require('angular-gravatar');require('ng-table');require('angular-datepicker');require('ng-infinite-scroll');require('angular-aria');require('angular-animate');require('angular-material');require('./components/ui-bootstrap-custom');// app modules require('./api');require('./services');require('./constants');require('./providers');require('./states');require('./components');require('./templates');var requires=['insights.api','insights.services','insights.constants','insights.providers','insights.components','insights.states','insights.templates','ui.router','ui.bootstrap','ngResource','gettext','ngTable','duScroll','ngCookies','ui.select','ngSanitize','ui.gravatar','datePicker','infinite-scroll','ngAnimate','ngMaterial'];if(isPortal){require('angular-loading-bar');requires.push('angular-loading-bar');}angular.module('insights',requires);angular.module('insights').value('duScrollBottomSpy',true);function bootstrap(){angular.bootstrap(document,['insights']);}function megaMenuHacks(){var anchors=document.querySelectorAll('.tools-menu .col-sm-4:first-child a');[].forEach.call(anchors,function(anchor){anchor.target='_self';});}if(isPortal){angular.module('insights').run(require('./portal/boot'));angular.module('insights').factory('Email',require('./portal/emailOptIn'));angular.module('insights').config(require('./portal/config'));angular.module('insights').config(require('./portal/base_routes'));angular.module('insights').config(require('./portal/routes'));window.chrometwo_ready(function(){bootstrap();megaMenuHacks();});}else{angular.module('insights').config(require('./base_routes'));}// Common routes angular.module('insights').config(require('./routes'));angular.module('insights').config(require('./config'));// workaround for https://github.com/angular-ui/ui-select/issues/1560 angular.module('ui.select').run(['$animate',function($animate){var overridden=$animate.enabled;$animate.enabled=function(elem){if(elem.hasOwnProperty('length')&&elem.length===1){elem=elem[0];}if(elem&&elem.className.includes('ui-select-choices')){return false;}return overridden(elem);};}]);// Angular Material Theme angular.module('ngMaterial').config(['$mdThemingProvider',function($mdThemingProvider){$mdThemingProvider.theme('default').primaryPalette('blue').accentPalette('grey').warnPalette('red');}]);// Insights Material Theme angular.module('ngMaterial').config(['$mdThemingProvider',function($mdThemingProvider){$mdThemingProvider.definePalette('insightsPrimary',{50:'86e0fe',100:'6ddafd',200:'54d3fd',300:'3acdfd',400:'21c6fc',500:'08c0fc',600:'03afe8',700:'039cce',800:'0289b5',900:'02769c',A100:'9fe7fe',A200:'b8edfe',A400:'d2f3fe',A700:'026383',contrastDefaultColor:'light',contrastDarkColors:['50','100','200','300','400','A100'],contrastLightColors:undefined});$mdThemingProvider.definePalette('insightsAccent',{50:'8c8c8c',100:'999999',200:'a6a6a6',300:'b3b3b3',400:'bfbfbf',500:'cccccc',600:'e6e6e6',700:'f2f2f2',800:'ffffff',900:'ffffff',A100:'e6e6e6',A200:'d9d9d9',A400:'cccccc',A700:'ffffff',contrastDefaultColor:'light',contrastDarkColors:['50','100','200','300','400','A100'],contrastLightColors:undefined});$mdThemingProvider.definePalette('insightsWarn',{50:'ff4d4d',100:'ff3333',200:'ff1a1a',300:'ff0000',400:'e60000',500:'cc0000',600:'b30000',700:'990000',800:'800000',900:'660000',A100:'ff6666',A200:'ff8080',A400:'ff9999',A700:'4d0000',contrastDefaultColor:'light',contrastDarkColors:['50','100','200','300','400','A100'],contrastLightColors:undefined});$mdThemingProvider.theme('default').primaryPalette('insightsPrimary').accentPalette('insightsAccent').warnPalette('insightsWarn');}]);},{"./api":15,"./base_routes":30,"./components":83,"./components/ui-bootstrap-custom":151,"./config":155,"./constants":160,"./portal/base_routes":168,"./portal/boot":169,"./portal/config":170,"./portal/emailOptIn":171,"./portal/routes":172,"./providers":174,"./routes":175,"./services":193,"./states":227,"./templates":241,"angular":261,"angular-animate":243,"angular-aria":245,"angular-cookies":247,"angular-datepicker":248,"angular-gettext":249,"angular-gravatar":250,"angular-loading-bar":251,"angular-material":253,"angular-resource":255,"angular-sanitize":257,"angular-scroll":258,"angular-ui-router":259,"ng-infinite-scroll":560,"ng-table":561,"ui-select":823}],165:[function(require,module,exports){'use strict';/** * @ngInject */AuthInterceptor.$inject=['$q','$injector','BounceService'];function AuthInterceptor($q,$injector,BounceService){return{responseError:function responseError(response){if(response.status===401){BounceService.bounce();}else if(response.status===402){var $state=$injector.get('$state');if($state){$state.go('app.paymentrequired');}}return $q.reject(response);}};}module.exports=AuthInterceptor;},{}],166:[function(require,module,exports){'use strict';ErrorInterceptor.$inject=['$q','AlertService'];function isError(code){return code>=500&&code<600;}/** * @ngInject */function ErrorInterceptor($q,AlertService){return{responseError:function responseError(response){// These seems a bit of overkill... However, // the docs on responseError are a bit sketchy. // It says this gets called whenever a deferred is rejected, but doesn't // explicitly say on a 5xx error. So I am double checking it here. if(isError(response.status)){AlertService.setHttpError(response);}return $q.reject(response);}};}module.exports=ErrorInterceptor;},{}],167:[function(require,module,exports){'use strict';function XomitInterceptor(){return{request:function request(config){// stomp on basic auth requests if(config&&config.headers){config.headers['X-Omit']='WWW-Authenticate';}return config;}};}module.exports=XomitInterceptor;},{}],168:[function(require,module,exports){'use strict';/** * @ngInject */Routes.$inject=['$stateProvider'];function Routes($stateProvider){// Base States $stateProvider.state('app',{templateUrl:'js/states/base/app.html',abstract:true,controller:'AppCtrl'});$stateProvider.state('info',{templateUrl:'js/states/base/info.html',abstract:true});$stateProvider.state('login',{url:'/login/',controller:'LoginCtrl'});}module.exports=Routes;},{}],169:[function(require,module,exports){'use strict';OnRun.$inject=['$location','$timeout','$rootScope','$state','$urlRouter','InsightsConfig','User','$document','$anchorScroll','PermissionService','AnalyticsService','TitleService','Email'];function scrollTop(){window.scrollTo(0,0);}function stateScroll(from,next){if(typeof next.scrollTop==='function'){next.scrollTop(from,next,function(shouldScroll){if(shouldScroll){scrollTop();}});}else if(next.scrollTop){scrollTop();}}/** * @ngInject */function OnRun($location,$timeout,$rootScope,$state,$urlRouter,InsightsConfig,User,$document,$anchorScroll,PermissionService,AnalyticsService,TitleService,Email){// make sure the view can access the global isBeta $rootScope.isBeta=window.insightsGlobal.isBeta;// We want to tigger analytics on location changes, not state changes // with state changes we miss things // like the System Modal click when look at actions :( $rootScope.$on('$locationChangeSuccess',function(){AnalyticsService.pageLoad();});$rootScope.$on('$stateChangeSuccess',function(event,next,nextParams,from){if(next&&next.triggerComplete){AnalyticsService.triggerEvent('InsightsCompletion');}TitleService.set(next.title);stateScroll(from,next);});AnalyticsService.triggerEvent('InsightsBegin');if(!InsightsConfig.authenticate){return;}var firstStateChange=true;$rootScope.$on('$stateChangeStart',function(event,next,nextParams){var nextState=next.name;if(nextState==='evaluation'){firstStateChange=false;}if(firstStateChange&&nextState.startsWith('app.')){firstStateChange=false;event.preventDefault();$state.go('evaluation',{originalPath:window.location.toString()});}var hash=$location.hash();if(hash){nextParams['#']=hash;}if(!next.unauthenticated&&InsightsConfig.authenticate){User.init();Email.optUserIntoCampaigns();}var user=User.current;if(next.permission&&!user.loaded){event.preventDefault();User.asyncCurrent(function(user){if(PermissionService.has(user,PermissionService.PERMS[next.permission])){$urlRouter.sync();}else{$state.go('app.forbidden');}});}else if(next.permission&&!PermissionService.has(user,PermissionService.PERMS[next.permission])){$state.go('app.forbidden');}});$timeout(function(){$anchorScroll($location.hash());},1);}module.exports=OnRun;},{}],170:[function(require,module,exports){/*global window*/'use strict';/** * @ngInject */Config.$inject=['cfpLoadingBarProvider','InsightsConfigProvider'];function Config(cfpLoadingBarProvider,InsightsConfigProvider){cfpLoadingBarProvider.includeSpinner=false;cfpLoadingBarProvider.loadingBarTemplate='
      \n
      \n
      \n
      \n
      ';var apiPrefix=window.localStorage.getItem('insights:apiPrefix');if(apiPrefix){InsightsConfigProvider.setApiPrefix(apiPrefix);}var apiVersion=window.localStorage.getItem('insights:apiVersion');if(apiVersion){InsightsConfigProvider.setApiVersion(apiVersion);}InsightsConfigProvider.setAuthenticate(true);InsightsConfigProvider.setAllowExport(true);InsightsConfigProvider.setDoPaf(true);InsightsConfigProvider.setFetchRelatedSolution(true);var anHour=60*60*1000;InsightsConfigProvider.setAutoRefresh(anHour);InsightsConfigProvider.setPortal(true);InsightsConfigProvider.setGettingStartedLink('https://access.redhat.com/products/red-hat-insights#getstarted');/* Used for Satellite integration InsightsConfigProvider.setAnsibleRunner(function ($location, planId, button) { $location.url(`/runPlaybook?planId=${planId}&customize=${customize}`); }); */}module.exports=Config;},{}],171:[function(require,module,exports){'use strict';/** * @ngInject */emailUtils.$inject=['User','System','Messaging','Blog'];function emailUtils(User,System,Messaging,Blog){var optedIn=false;function optUserIntoCampaigns(){if(optedIn){//This function is only allowed to be called once return;}optedIn=true;User.init().then(function(user){if(user.autoOptedIn===false){System.getSystemStatus().success(function checkSystemCount(status){if(status.count>0){return optUserIn();}});}});}function optUserIn(){Blog.subscribe();return Messaging.getCampaigns().success(function(campaigns){campaigns.forEach(function(campaign){campaign.enrolled=true;});return Messaging.saveCampaigns(campaigns).then(function(){return Messaging.saveOptedIn();});});}return{optUserIntoCampaigns:optUserIntoCampaigns};}module.exports=emailUtils;},{}],172:[function(require,module,exports){'use strict';/** * @ngInject */Routes.$inject=['$stateProvider','$locationProvider','$urlRouterProvider','GettingStartedUrl','InviteUrl'];function Routes($stateProvider,$locationProvider,$urlRouterProvider,GettingStartedUrl,InviteUrl){// App Routes // Rule routes $stateProvider.state('app.admin-rules',{url:'/rules/admin/?tags',templateUrl:'js/states/rules/views/admin-rules.html',controller:'AdminRuleCtrl',title:'Rules Admin',hideGroup:true}).state('app.admin-rule-tags',{url:'/rules/admin/tags',templateUrl:'js/states/rules/views/admin-rule-tags.html',controller:'AdminRuleTagCtrl',title:'Rule Tag Admin',hideGroup:true}).state('app.create-rule',{url:'/rules/new/',templateUrl:'js/states/rules/views/create-rules.html',controller:'NewRuleCtrl',scrollTop:true,hideGroup:true}).state('app.show-rule',{url:'/rules/:id/',templateUrl:'js/states/rules/views/show-rules.html',controller:'ShowRuleCtrl',scrollTop:true,hideGroup:true})/* .state('app.edit-rule', { url: '/rules/:id/edit/', templateUrl: 'js/states/rules/views/edit-rules.html', controller: 'EditRuleCtrl', scrollTop: true, hideGroup: true, params: { newRule: false } }) */;// Page routes $stateProvider.state('app.forbidden',{url:'/403/',templateUrl:'js/states/pages/views/403.html'});$stateProvider.state('app.paymentrequired',{url:'/402/',templateUrl:'js/states/pages/views/402.html'});// Config routes $stateProvider.state('app.config-webhook-edit',{url:'/config/webhooks/:id',templateUrl:'js/states/config/views/webhook-edit.html',controller:'WebhookEditCtrl',title:'Edit Webhook'}).state('app.config',{url:'/config/:tab',templateUrl:'js/states/config/views/config.html',controller:'ConfigCtrl',params:{tab:null},title:'Configuration',hideGroup:true});// Announcement routes $stateProvider.state('app.new-announcement',{url:'/announcements/new/',templateUrl:'js/states/announcements/new-announcement.html',controller:'NewAnnouncementCtrl',permission:'CREATE_ANNOUNCEMENT'}).state('app.edit-announcement',{url:'/announcements/:id/edit/',templateUrl:'js/states/announcements/edit-announcement.html',controller:'EditAnnouncementCtrl',permission:'CREATE_ANNOUNCEMENT'});// Analytics routes $stateProvider.state('app.analytics',{url:'/__analytics',templateUrl:'js/states/analytics/analytics.html',controller:'AnalyticsCtrl'});// Info routes $stateProvider.state('info.root',{url:'/',templateUrl:'js/states/splash/splash.html',controller:'SplashCtrl',bounceLoggedin:true,scrollTop:true,unauthenticated:true});$stateProvider.state('info.splash',{url:'/splash/',templateUrl:'js/states/splash/splash.html',controller:'SplashCtrl',scrollTop:true,unauthenticated:true});function redirectGetStarted(from,to){$urlRouterProvider.when(from,function(){window.location=GettingStartedUrl+to;});}redirectGetStarted('/getting-started/cloudforms/','#cloudforms');redirectGetStarted('/getting-started/containers/','#containers');redirectGetStarted('/getting-started/direct/','#direct');redirectGetStarted('/getting-started/openshift/','#openshift');redirectGetStarted('/getting-started/osp/','#osp');redirectGetStarted('/getting-started/rhev/','#rhv');redirectGetStarted('/getting-started/rhv/','#rhv');redirectGetStarted('/getting-started/satellite/6/','#satellite6');redirectGetStarted('/getting-started/satellite/5/','#satellite5');redirectGetStarted('/getting-started','#getstarted');redirectGetStarted('/getting-started/','#getstarted');$urlRouterProvider.when('/invite',function(){window.location=InviteUrl;});$stateProvider.state('info.info',{url:'/info/',templateUrl:'js/states/splash/splash.html',controller:'SplashCtrl',title:'Learn More',scrollTop:true,unauthenticated:true}).state('info.security',{url:'/security/',templateUrl:'js/states/security/security.html',controller:'SecurityCtrl',title:'Security',scrollTop:true,unauthenticated:true});$stateProvider.state('app.edit-topic',{url:'/topics/:id/edit/',templateUrl:'js/states/topics/views/edit-topics.html',controller:'EditTopicCtrl',hideGroup:true});$stateProvider.state('app.admin-topic',{url:'/topics/admin',templateUrl:'js/states/topics/views/topic-admin.html',controller:'TopicAdminCtrl',hideGroup:true});$stateProvider.state('invites',{url:'/invite',templateUrl:'js/states/invites/invites.html',controller:'InvitesCtrl'});$urlRouterProvider.when('/invite/','/invite');}module.exports=Routes;},{}],173:[function(require,module,exports){'use strict';var providersModule=require('./');/** * @ngInject */function InsightsConfig(){var title='Red Hat Insights';var autoRefresh=0;var authenticate=false;var allowExport=false;var doPaf=false;var getReportsErrorCallback=null;// function that is called when a system is clicked while // viewing actions var actionsShowSystem=null;// function that is called when a system is clicked while // viewing systems var systemShowSystem=null;var getSystemStatus=true;var fetchRelatedSolution=false;var gettingStartedLink=null;var apiPrefix='/r/insights/';var apiVersion='v3';var apiRoot=concatRoot();var defaultApiPrefix=apiPrefix;var acctKey='telemetry:account_number';// Functions that will be called to determine if users // have access to certain functions. Functions will be passed // a callback that should be called with either true or false var canUnregisterSystems=null;var canIgnoreRules=null;var isPortal=false;// are we running inside customer portal or not? var systemAlerts={'other_linux_system|OTHER_LINUX_SYSTEM':1,'other_linux_system|OTHER_LINUX_SYSTEM_DETECTED':1,'insights_heartbeat|INSIGHTS_HEARTBEAT':1};var overviewKey=null;// // Example configuration setting the property 'permission' to // 'admin' for the route app.actions // { // 'app.actions': { // customConfig: { // permission: 'admin' // } // } // }; // var routeStateConfig={};function concatRoot(){return apiPrefix+apiVersion+'/';}var isPlannerEnabled=true;var isGroupsEnabled=true;var ansibleRunner=false;return{setTitle:function setTitle(value){title=value;},setAutoRefresh:function setAutoRefresh(value){autoRefresh=value;},setAuthenticate:function setAuthenticate(value){authenticate=value;},setAllowExport:function setAllowExport(value){allowExport=value;},setDoPaf:function setDoPaf(value){doPaf=value;},setGetReportsErrorCallback:function setGetReportsErrorCallback(value){getReportsErrorCallback=value;},setFetchRelatedSolution:function setFetchRelatedSolution(value){fetchRelatedSolution=value;},setActionsShowSystem:function setActionsShowSystem(value){actionsShowSystem=value;},setSystemShowSystem:function setSystemShowSystem(value){systemShowSystem=value;},setGetSystemStatus:function setGetSystemStatus(value){getSystemStatus=value;},setGettingStartedLink:function setGettingStartedLink(value){gettingStartedLink=value;},setApiPrefix:function setApiPrefix(value){apiPrefix=value;apiRoot=concatRoot();},setApiVersion:function setApiVersion(value){apiVersion=value;apiRoot=concatRoot();},setCanUnregisterSystems:function setCanUnregisterSystems(value){canUnregisterSystems=value;},setCanIgnoreRules:function setCanIgnoreRules(value){canIgnoreRules=value;},setPortal:function setPortal(value){isPortal=value;},setRouteStateConfig:function setRouteStateConfig(value){routeStateConfig=value;},setOverviewKey:function setOverviewKey(value){overviewKey=value;},setPlannerEnabled:function setPlannerEnabled(value){isPlannerEnabled=value;},setGroupsEnabled:function setGroupsEnabled(value){isGroupsEnabled=value;},setAnsibleRunner:function setAnsibleRunner(value){ansibleRunner=value;},$get:['Utils',function $get(Utils){overviewKey=overviewKey||(Utils.isBeta()?'overview-beta':'overview-stable');return{title:title,autoRefresh:autoRefresh,authenticate:authenticate,allowExport:allowExport,doPaf:doPaf,getReportsErrorCallback:getReportsErrorCallback,fetchRelatedSolution:fetchRelatedSolution,actionsShowSystem:actionsShowSystem,systemShowSystem:systemShowSystem,getSystemStatus:getSystemStatus,gettingStartedLink:gettingStartedLink,apiRoot:apiRoot,apiPrefix:apiPrefix,apiVersion:apiVersion,defaultApiPrefix:defaultApiPrefix,acctKey:acctKey,systemAlerts:systemAlerts,canUnregisterSystems:canUnregisterSystems,canIgnoreRules:canIgnoreRules,isPortal:isPortal,routeStateConfig:routeStateConfig,overviewKey:overviewKey,isPlannerEnabled:isPlannerEnabled,isGroupsEnabled:isGroupsEnabled,ansibleRunner:ansibleRunner};}]};}providersModule.provider('InsightsConfig',InsightsConfig);},{"./":174}],174:[function(require,module,exports){'use strict';module.exports=angular.module('insights.providers',[]);(function(){var f=require("./index.js");f["config.provider"]=require("./config.provider.js");f["index"]=require("./index.js");return f;})();},{"./config.provider.js":173,"./index.js":174}],175:[function(require,module,exports){'use strict';/** * @ngInject */Routes.$inject=['$stateProvider'];function Routes($stateProvider){/* * This is the very first state into which the app gets when loaded. * It's not a real state (has no view). * Its controller decides whether to load the Actions or the System view * and changes the state accordingly. See INSIGHTS-1005 for details. */$stateProvider.state('app.initial',{template:'',controller:'InitialCtrl'}).state('evaluation',{url:'/evaluation',templateUrl:'js/states/evaluation/evaluation.html',controller:'EvaluationCtrl',title:'evaluation',params:{originalPath:null}});// Shared Routes // Actions routes $stateProvider.state('app.actions',{url:'/actions',templateUrl:'js/states/actions/actions.html',controller:'ActionsCtrl',params:{category:{value:null,squash:true}},title:'Actions',actions:true}).state('app.actions-rule',{url:'/actions/:category/:rule',templateUrl:'js/components/actions/actionsRule/actionsRule.html',controller:'ActionsRuleCtrl',actions:true,title:'Actions',triggerComplete:true}).state('app.topic',{url:'/actions/:id?product',templateUrl:'js/states/topics/views/topic-list.html',controller:'TopicRuleListCtrl',title:'Actions',actions:true,params:{'filters:totalRisk':'All'}});$stateProvider.state('app.overview',{url:'/overview/',templateUrl:'js/states/overview/overview.html',controller:'OverviewCtrl',title:'Overview'});$stateProvider.state('app.components',{url:'/hidden/components/',templateUrl:'js/states/hidden/components/components.html',controller:'ComponentsCtrl',title:'Components'});// Digest routes $stateProvider.state('app.digests',{url:'/reports/executive/',templateUrl:'js/states/digests/digests.html',controller:'DigestsCtrl',title:'Executive Reports'});// System routes $stateProvider.state('app.inventory',{url:'/inventory?product&roles&osp_deployment'+'&docker_host&search_term&sort_field&sort_dir&'+'offline&online&machine&page&pageSize',templateUrl:'js/states/inventory/inventory.html',controller:'InventoryCtrl',title:'Inventory',reloadOnSearch:false});// Rule routes $stateProvider.state('app.rules',{url:'/rules?product&roles&osp_deployment&docker_host&category'+'&ansibleSupport&incident&ruleStatus&impact&likelihood&totalRisk',templateUrl:'js/states/rules/list-rules.html',controller:'ListRuleCtrl',title:'Rules',hideGroup:true,reloadOnSearch:false});// Planner routes $stateProvider.state('app.maintenance',{// the parameter is optional and allows the maintenance view to be bookmarked // with "quick-edit" of a specific plan open url:'/planner/{maintenance_id}?tab',templateUrl:'js/states/maintenance/maintenance.html',controller:'MaintenanceCtrl',title:'Planner',params:{newPlan:false}});// Common announcements routes $stateProvider.state('app.announcements',{url:'/announcements/',templateUrl:'js/states/announcements/announcements.html',controller:'ListAnnouncementCtrl',params:{announcementId:null}}).state('app.view-announcement',{url:'/announcements/:slug/',templateUrl:'js/states/announcements/view-announcement.html',controller:'ViewAnnouncementCtrl'});}module.exports=Routes;},{}],176:[function(require,module,exports){'use strict';AccountService.$inject=['$rootScope','User','InsightsConfig'];var servicesModule=require('./');/** * @ngInject */function AccountService($rootScope,User,InsightsConfig){var user=User.current;var account=user.account_number;var storage_account=window.sessionStorage.getItem(InsightsConfig.acctKey);if(storage_account){account=storage_account;}$rootScope.$on('account:change',function(evt,acct){// Double equal on purpose if(account!=acct){// jshint ignore:line account=acct;$rootScope.$broadcast('reload:data');}});return{current:function current(separator){var accountStr;if(account){accountStr=separator?separator:'?';accountStr+='account_number='+account;return accountStr;}return'';},number:function number(){return account;},queryParam:function queryParam(){return{account_number:account};}};}servicesModule.service('AccountService',AccountService);},{"./":193}],177:[function(require,module,exports){'use strict';ActionbarService.$inject=['gettextCatalog'];var servicesModule=require('./');/** * @ngInject */function ActionbarService(gettextCatalog){var _actions=[];var result={actions:_actions,add:function add(action){_actions.push(action);},set:function set(actions){_actions.length=0;Array.prototype.push.apply(_actions,actions);},clear:function clear(){_actions.length=0;}};result.addExportAction=function(callback){result.add({icon:'fa-download',title:gettextCatalog.getString('Export CSV'),fn:callback});};return result;}servicesModule.factory('ActionbarService',ActionbarService);},{"./":193}],178:[function(require,module,exports){/*global require, angular, window*/'use strict';ActionsService.$inject=['$filter','$location','$q','$rootScope','$state','Report','Rule','System','Ack','InsightsConfig','Severities','ReloadService','FilterService','Utils'];var servicesModule=require('./');var reject=require('lodash/reject');var filter=require('lodash/filter');var map=require('lodash/map');var reduce=require('lodash/reduce');var indexOf=require('lodash/indexOf');var groupBy=require('lodash/groupBy');var indexBy=require('lodash/keyBy');/** * @ngInject */function ActionsService($filter,$location,$q,$rootScope,$state,Report,Rule,System,Ack,InsightsConfig,Severities,ReloadService,FilterService,Utils){var priv={};var vars={};var pub={};pub.priv=priv;// just so we can unit test em pub.vars=vars;// just so we can unit test em // window.pub = pub; // just for debugging, commentme vars.allSystems=null;vars.category=null;vars.counts={};vars.data={};vars.dataLoaded=false;vars.donutChart=null;vars.initialSeverity=null;vars.loading=false;vars.oldCols=[];vars.page=0;vars.pageSize=10;vars.ruleSystems=null;vars.rules=[];vars.rule=null;vars.ruleDetails=null;vars.severities={};vars.severityNames=Severities.map(function(severity){return severity.value;});vars.systemNames=null;vars.total=0;vars.totalRuleSystems=0;// do this as soon as all the accessibles are defined (aka define vars, run dis) Utils.generateAccessors(pub,vars);angular.forEach(vars.severityNames,function(s){vars.severities[s]={selected:true,count:0};});priv.severityFilter=function(report){return vars.severities[report.severity].selected;};pub.isActions=function(){if(pub.getCategory()){return false;}return true;};priv.ackFilter=function(report){try{return Ack.ackMap[report.rule_id];}catch(ignore){}};pub.mapName=function(rule_id){if(vars.data[rule_id]&&vars.data[rule_id].name){return vars.data[rule_id].name;}return rule_id;};priv.processRules=function(){var filteredRules=reject(vars.rules,priv.ackFilter);//TODO: this won't wor; var tmpData=null;var total=0;priv.updateCounts(filteredRules);priv.updateSeverityCounts(filteredRules);filteredRules=filter(filteredRules,priv.severityFilter);if(pub.isActions()){tmpData=groupBy(filteredRules,function(r){return r.category;});tmpData=map(tmpData,function(g,category){var groupTotal=reduce(g,function(sum,n){return sum+n.report_count;},0);return{id:category,name:category,value:groupTotal,category:true};});tmpData=indexBy(tmpData,'id');}else{tmpData=filter(filteredRules,function(r){return r.category.toLowerCase()===vars.category.toLowerCase();});tmpData=map(tmpData,function(r){return{id:r.rule_id,name:r.description,severityNum:indexOf(vars.severityNames,r.severity),severity:r.severity,category:r.category,value:r.report_count,color:''};});tmpData=indexBy(tmpData,'id');}total=reduce(tmpData,function(sum,n){return sum+n.value;},0);pub.setData(tmpData);pub.setTotal(total);$rootScope.$broadcast('rha-telemetry-refreshdonut');};pub.populateData=function(){var rulesQuery;var rulesDeferred;var ackDeferred;var deferred;if(!vars.dataLoaded){//pub.setLoading(true); rulesQuery=FilterService.buildRequestQueryParams([],['role']);rulesQuery.report_count='gt0';rulesDeferred=Rule.getRulesLatest(rulesQuery);ackDeferred=Ack.reload();ackDeferred.then(rulesDeferred);rulesDeferred.success(function(response){pub.setRules(response.resources);priv.processRules();});rulesDeferred.finally(function(){pub.setLoading(false);vars.dataLoaded=true;});return rulesDeferred;}//dataLoaded already deferred=$q(function(resolve){resolve('norefresh');});deferred.then(function(){priv.processRules();});return deferred;};priv.updateCounts=function(rules){var _count=0;var tempCounts={security:0,stability:0,performance:0};rules=groupBy(rules,function(r){return r.category;});reduce(rules,function(result,n,key){var k=key.toLowerCase();result[k]=reduce(n,function(sum,n){return sum+n.report_count;},0);_count+=result[k];return result;},tempCounts);pub.setCounts(tempCounts);};priv.updateSeverityCounts=function(rules){var rulesBySeverity;var total=0;if(!pub.isActions()){rules=filter(rules,function(r){return r.category.toLowerCase()===vars.category.toLowerCase();});}rulesBySeverity=groupBy(rules,'severity');angular.forEach(vars.severityNames,function(s){if(angular.isDefined(rulesBySeverity[s])){vars.severities[s].count=reduce(rulesBySeverity[s],function(sum,n){return sum+n.report_count;},0);total+=vars.severities[s].count;}else{vars.severities[s].count=0;}});vars.severities.All.count=total;};pub.arcClick=function(arc){if(arc.category===true){return $state.go('app.actions',{category:arc.id.toLowerCase()});}$state.go('app.actions-rule',{category:arc.category.toLowerCase(),rule:arc.id});};pub.ackAction=function(rule){Ack.createAck(rule).then(priv.processRules);};/** * Builds systemsQuery for retrieving the systems affected by the current action * * @param paginate if false retrieves all systems affected by the current action * @param pager contains the current page and page size values */priv.buildSystemsQuery=function(paginate,pager){var systemsQuery=FilterService.buildRequestQueryParams(null,['role']);systemsQuery.rule=encodeURIComponent(pub.getRule());if(paginate){// UI paging starts at 1, programmatic page starts at 0 systemsQuery.page=pager.currentPage-1;systemsQuery.page_size=pager.perPage;}// offline / systems not checking in if(FilterService.getOffline()!==FilterService.getOnline()){systemsQuery.offline=FilterService.getOffline().toString();}systemsQuery.sort_by=$location.search().sort_field;systemsQuery.sort_dir=$location.search().sort_direction;return systemsQuery;};/** * Sets ruleSystems to the list of systems on the current page * * @param pager contains the current page and page size values * @param paginate if false sets allSystems to all of the systems affected by the * current action * @param systems the systems returned by the query */priv.setRuleSystems=function(pager,paginate,systems){var offset=(pager.currentPage-1)*pager.perPage;if(!paginate){vars.allSystems=systems.resources;vars.ruleSystems=vars.allSystems.slice(offset,priv.getPageEnd(vars.allSystems,pager.currentPage-1,pager.perPage));}else{vars.ruleSystems=systems.resources;}vars.totalRuleSystems=systems.total;};/** * Returns the index of the last item in the current page */priv.getPageEnd=function(source,page,pageSize){if(source===null){return 0;}var offset=page*pageSize;return offset+pageSize0){affectedHosts=[];for(var i in results[0].data){affectedHosts.push(results[0].data[i].system_id);}}// If affected hosts were found, then retrieve their system names if(affectedHosts){for(var j in affectedHosts){namePromises.push(System.getSingleSystem(affectedHosts[j]).success(function(systemResult){return systemResult;}));}return $q.all(namePromises).then(function(nameResults){if(nameResults){vars.systemNames={};vars.systems={};for(var theSystemKey in nameResults){var theSystem=nameResults[theSystemKey].data;var theSystemId=theSystem.system_id;var theSystemName=theSystem.toString;vars.systems[theSystemId]=theSystem;vars.systemNames[theSystemId]=theSystemName;}}});}};/** * Builds the query for getting the next page for actions page 3 * * @param paginate if false pulls all systems affected by this action * @param pager contains the current page and page size of the page being pulled */pub.buildSystemsDeferred=function(paginate,pager){var systemDeferred=void 0;var systemsQuery=priv.buildSystemsQuery(paginate,pager);if(FilterService.getParentNode()){systemsQuery.includeSelf=true;systemDeferred=System.getSystemLinks(FilterService.getParentNode(),systemsQuery).success(function(systems){priv.setRuleSystems(pager,paginate,systems);});}else{systemDeferred=System.getSystemsLatest(systemsQuery).success(function(response){priv.setRuleSystems(pager,paginate,response);});}return systemDeferred;};/** * Populates the data needed for actions page 3 * * Sets ruleSystems to the first page of systems * * @param paginate if false pulls all systems affected by this action * @param pager contains the current page and page size of the page being pulled */pub.initActionsRule=function(pager){var systemDeferred=pub.buildSystemsDeferred(true,pager);var ruleDeferred=Rule.byId(pub.getRule()).success(function(rule){vars.ruleDetails=rule;});return $q.all([systemDeferred,ruleDeferred]).then(function(results){return priv.populateAffectedHosts(results);});};/** * Gets the current page for the actionsRule page (actions page 3) * * @param paginate if false pulls all systems affected by this action * @param pager contains the current page and page size of the page being pulled */pub.getActionsRulePage=function(paginate,pager){var systemDeferred=void 0;// if we already have all of the systems affected use vars.allSystems if(vars.allSystems===null){systemDeferred=pub.buildSystemsDeferred(paginate,pager).then(function(results){return priv.populateAffectedHosts(results);});}else{systemDeferred=$q.resolve(true);vars.ruleSystems=vars.allSystems.slice((pager.currentPage-1)*pager.perPage,priv.getPageEnd(vars.allSystems,pager.currentPage-1,pager.perPage));}return systemDeferred;};/** * Sort actionsRule page (actions page 3) * * @param pager contains the page size of the page being pulled */pub.sortActionsRulePage=function(pager,predicate,reverse){// if we already have all of the systems affected sort allSystems if(vars.allSystems!==null){vars.allSystems=$filter('orderBy')(vars.allSystems,reverse?'-'+predicate:predicate);}return pub.getActionsRulePage(true,pager);};pub.reload=function(){if(!vars.dataLoaded){// we don't even have data yet - no need to reload return;}pub.setDataLoaded(false);pub.populateData().then(function(){pub.setLoading(false);});};pub.setInitialSeverity=function(sev){if(sev){angular.forEach(vars.severityNames,function(s){if(sev===s||sev.toLowerCase()==='all'){vars.severities[s].selected=true;}else{vars.severities[s].selected=false;}});}};if(InsightsConfig.autoRefresh&&!isNaN(InsightsConfig.autoRefresh)){window.setInterval(pub.reload,parseInt(InsightsConfig.autoRefresh,10));}return pub;}servicesModule.factory('RhaTelemetryActionsService',ActionsService);},{"./":193,"lodash/filter":490,"lodash/groupBy":497,"lodash/indexOf":502,"lodash/keyBy":518,"lodash/map":522,"lodash/reduce":536,"lodash/reject":537}],179:[function(require,module,exports){'use strict';var servicesModule=require('./');/** * @ngInject */function ActionsBreadcrumbs(){var _crumbs=[];function set(crumbs){_crumbs=angular.copy(crumbs);return _crumbs;}function setCrumb(crumb,index){if(_crumbs.length';}}clearType(_alerts,'http');_alerts.push({type:'http',msg:message,level:'danger'});},clear:function clear(){_alerts.length=0;}};}function clearType(messages,type){var i=0;var len=messages.length;for(;i2){tmp=input.slice(0,2);if(supportedLangsMap.hasOwnProperty(tmp)){return supportedLangsMap[tmp];}}// I give up return'en';}};}servicesModule.service('CoercionService',CoercionService);},{"./":193}],187:[function(require,module,exports){/*global require*/'use strict';DataUtils.$inject=['Utils','Severities'];var servicesModule=require('./');var clone=require('lodash/clone');var some=require('lodash/some');function DataUtils(Utils,Severities){var service={};service.readArray=function(fn){return function(array){return array.map(function(element){return fn(element);});};};/* * True if either the end date is in the future or it is in the past * but there are actions that has not been finished yet. */function isPending(plan){if(plan.overdue){return true;}if(new Date(plan.end)>new Date()){return true;}return false;}function countDone(object){if('actions'in object){object.actionsDone=object.actions.filter(function(action){return action.done;}).length;}}function readDate(date){var parsed;if(date){parsed=new Date(date);if(parsed.getTime()){// temp. workaround for an API glitch return parsed;}}return null;}service.readPlan=function(plan){plan.actions=plan.actions.filter(function(action){return action.system!==null&&action.rule!==null;});plan.actions.forEach(function(action){action.system=service.readSystem(action.system);action.rule.rule_id=action.rule.id;service.readRule(action.rule);});plan.rules=Utils.groupByObject(plan.actions,'rule.id','rule','actions').map(clone);plan.systems=Utils.groupByObject(plan.actions,'system.system_id','system','actions');countDone(plan);plan.systems.forEach(countDone);plan.rules.forEach(countDone);plan.rules.forEach(function(rule){rule.ansible=some(rule.actions,'rule.ansible');});plan.start=readDate(plan.start);plan.end=readDate(plan.end);if(!plan.end){plan.overdue=false;// temp. workaround for an API glitch }plan.pending=isPending(plan);if(plan.description){plan.description=plan.description.trim();}plan.isReadOnly=function(){return plan.suggestion==='proposed';};return plan;};service.readSystem=function(system){system._name=system.display_name||system.hostname;return system;};service.readReport=function(report){report.system=service.readSystem(report.system);};service.readRule=function(rule){rule.severityNum=Severities.map(function(severity){return severity.value;}).indexOf(rule.severity);};function getRuleState(rule){if(rule.active){return'active';}else if(rule.needs_content){return'needs-content';}else if(rule.retired){return'retired';}else{return'inactive';}}service.readRuleState=function(rule){rule.state=getRuleState(rule);};return service;}servicesModule.service('DataUtils',DataUtils);},{"./":193,"lodash/clone":481,"lodash/some":539}],188:[function(require,module,exports){'use strict';DigestService.$inject=['Digest'];var servicesModule=require('./');/** * @ngInject */function DigestService(Digest){return{get:function get(digest_id){return Digest.getDigest(digest_id);},digestsByType:function digestsByType(digest_type_id){return Digest.getDigestsByType(digest_type_id);}};}servicesModule.service('DigestService',DigestService);},{"./":193}],189:[function(require,module,exports){'use strict';FilterService.$inject=['MultiButtonService','Group','$location','$rootScope','Severities','Categories','Events'];var servicesModule=require('./');function FilterService(MultiButtonService,Group,$location,$rootScope,Severities,Categories,Events){var recSeveritiesMap={1:'INFO',2:'WARN',3:'ERROR',4:'CRITICAL'};var filterService={};var _category=$location.search().category||'all';var _incidents=$location.search()[Events.filters.incident]||'all';var _ansibleSupport=$location.search()[Events.filters.ansibleSupport]||'all';var _ruleStatus=$location.search()[Events.filters.ruleStatus]||'all';var _likelihood=$location.search()[Events.filters.likelihood]||0;var _impact=$location.search()[Events.filters.impact]||0;var _selectedProduct='all';var _parentNode=null;var _dockerHosts=[];var _ospDeployments=[];var _ocpDeployments=[];var _selectedDockerHost={};var _selectedOSPDeployment={};var _selectedOCPDeployment={};var _searchTerm=null;var _showFilters=false;var _rhelOnly=false;var _offline=true;var _online=true;var _machine=null;filterService.setMachine=function(machine){_machine=machine;filterService.setQueryParam('machine',machine);};filterService.getMachine=function(){return _machine;};filterService.setRHELOnly=function(rhelOnly){_rhelOnly=rhelOnly;};filterService.getRHELOnly=function(){return _rhelOnly;};filterService.toggleShowFilters=function(){_showFilters=!_showFilters;};filterService.getShowFilters=function(){return _showFilters;};filterService.setShowFilters=function(showFilters){_showFilters=showFilters;};filterService.setRolesQueryParam=function(role,roleStateKey){var index;var roles=$location.search().roles;if(!roles){roles=[];}else{roles=roles.split(',');}if(MultiButtonService.getState(roleStateKey)){roles.push(role);}else{index=roles.indexOf(role);roles.splice(index,1);}filterService.setQueryParam('roles',roles.join(','));};filterService.setOffline=function(offline){_offline=offline;var param=offline.toString()==='all'?null:offline.toString();filterService.setQueryParam('offline',param);};filterService.setOnline=function(online){_online=online;filterService.setQueryParam('online',online.toString());};filterService.getOffline=function(){return _offline;};filterService.getOnline=function(){return _online;};filterService.deleteQueryParam=function(name){var obj=$location.search();obj[name]=null;$location.search(obj);};filterService.setQueryParam=function(name,value){var obj=$location.search();obj[name]=value;$location.search(obj);};filterService.getSearchTerm=function(){return _searchTerm;};filterService.setSearchTerm=function(searchTerm){_searchTerm=searchTerm;filterService.setQueryParam('search_term',searchTerm);};filterService.setCategory=function(category){_category=category;filterService.setQueryParam('category',category);};filterService.getCategory=function(){return _category;};filterService.setIncidents=function(incidents){_incidents=incidents;};filterService.getIncidents=function(){return _incidents;};filterService.setAnsibleSupport=function(ansibleSupport){_ansibleSupport=ansibleSupport;};filterService.getAnsibleSupport=function(){return _ansibleSupport;};filterService.setRuleStatus=function(ruleStatus){_ruleStatus=ruleStatus;};filterService.getRuleStatus=function(){return _ruleStatus;};filterService.setLikelihood=function(likelihood){_likelihood=likelihood;};filterService.getLikelihood=function(){return _likelihood;};filterService.setImpact=function(impact){_impact=impact;};filterService.getImpact=function(){return _impact;};/** * resets all filters and broadcasts doFilter */filterService.clearAll=function(){var resetParent={system_id:'all'};filterService.setSearchTerm('');filterService.setSelectedDockerHost(resetParent);filterService.setSelectedOSPDeployment(resetParent);filterService.setSelectedProduct('all');filterService.setOnline(true);filterService.setOffline(true);MultiButtonService.setState('inventoryWithActions',true);MultiButtonService.setState('inventoryWithoutActions',true);filterService.doFilter();};filterService.doFilter=function(){$rootScope.$broadcast('filterService:doFilter');};filterService.getSelectedProduct=function(){return _selectedProduct;};filterService.setSelectedProduct=function(selectedProduct){_selectedProduct=selectedProduct;filterService.setQueryParam('product',selectedProduct);};filterService.setParentNode=function(parentNode){_parentNode=parentNode;};filterService.getParentNode=function(){return _parentNode;};filterService.setDockerHosts=function(dockerHosts){_dockerHosts=dockerHosts;};filterService.getDockerHosts=function(){return _dockerHosts;};filterService.setSelectedDockerHost=function(selectedDockerHost){_selectedDockerHost=selectedDockerHost;if(filterService.getSelectedDockerHost().system_id==='all'){filterService.setParentNode(null);}else{filterService.setParentNode(selectedDockerHost.system_id);}filterService.setQueryParam('docker_host',selectedDockerHost.system_id);};filterService.getSelectedDockerHost=function(){return _selectedDockerHost;};//ocp filterService.setOCPDeployments=function(ocpDeployments){_ocpDeployments=ocpDeployments;};filterService.getSelectedOCPDeployment=function(){return _selectedOCPDeployment;};filterService.setSelectedOCPDeployment=function(selectedOCPDeployment){_selectedOCPDeployment=selectedOCPDeployment;if(filterService.getSelectedOCPDeployment().system_id==='all'){filterService.setParentNode(null);}else{filterService.setParentNode(selectedOCPDeployment.system_id);}filterService.setQueryParam('ocp_deployment',selectedOCPDeployment.system_id);};//osp filterService.setOSPDeployments=function(ospDeployments){_ospDeployments=ospDeployments;};filterService.getOSPDeployments=function(){return _ospDeployments;};filterService.setSelectedOSPDeployment=function(selectedOSPDeployment){_selectedOSPDeployment=selectedOSPDeployment;if(filterService.getSelectedOSPDeployment().system_id==='all'){filterService.setParentNode(null);}else{filterService.setParentNode(selectedOSPDeployment.system_id);}filterService.setQueryParam('osp_deployment',selectedOSPDeployment.system_id);};filterService.getSelectedOSPDeployment=function(){return _selectedOSPDeployment;};filterService.buildOSPRolesString=function(){var roles=[];if(MultiButtonService.getState('inventoryOSPCluster')){roles.push('cluster');}if(MultiButtonService.getState('inventoryOSPDirector')){roles.push('director');}if(MultiButtonService.getState('inventoryOSPCompute')){roles.push('compute');}if(MultiButtonService.getState('inventoryOSPController')){roles.push('controller');}return roles.toString();};filterService.buildDockerRolesString=function(){var roles=[];if(MultiButtonService.getState('inventoryDockerHosts')){roles.push('host');}if(MultiButtonService.getState('inventoryDockerImages')){roles.push('image');}if(MultiButtonService.getState('inventoryDockerContainers')){roles.push('container');}return roles.toString();};filterService.updateParams=function(url_params){if(!url_params.search_term&&filterService.getSearchTerm()){url_params.search_term=filterService.getSearchTerm();}if(!url_params.product&&filterService.getSelectedProduct()){url_params.product=filterService.getSelectedProduct();}if(!url_params.role&&filterService.getSelectedProduct()){if(filterService.getSelectedProduct()==='rhev'){url_params.role=filterService.buildRHEVRolesString();}else if(filterService.getSelectedProduct()==='docker'){url_params.role=filterService.buildDockerRolesString();}else if(filterService.getSelectedProduct()==='osp'){url_params.role=filterService.buildOSPRolesString();}}if(!url_params.osp_deployment&&filterService.getSelectedOSPDeployment()){if(filterService.getSelectedOSPDeployment().system_id){url_params.osp_deployment=filterService.getSelectedOSPDeployment().system_id;}}if(!url_params.docker_host&&filterService.getSelectedDockerHost()){if(filterService.getSelectedDockerHost().system_id){url_params.docker_host=filterService.getSelectedDockerHost().system_id;}}if(!url_params.offline){url_params.offline=filterService.getOffline();}if(!url_params.online){url_params.online=filterService.getOnline();}return url_params;};filterService.parseBrowserQueryParams=function(){var roles;var params=$location.search();if(params.product){filterService.setSelectedProduct(params.product);}if(params.actions){if(params.actions==='all'){MultiButtonService.setState('inventoryWithoutActions',true);MultiButtonService.setState('inventoryWithActions',true);}else if(params.actions==='with'){MultiButtonService.setState('inventoryWithoutActions',false);MultiButtonService.setState('inventoryWithActions',true);}else if(params.actions==='without'){MultiButtonService.setState('inventoryWithoutActions',true);MultiButtonService.setState('inventoryWithActions',false);}}if(params.search_term){filterService.setSearchTerm(params.search_term);}if(params.osp_deployment){filterService.setSelectedOSPDeployment({system_id:params.osp_deployment});}if(params.docker_host){filterService.setSelectedDockerHost({system_id:params.docker_host});}if(params.roles){roles=params.roles.split(',');roles.forEach(function(role){if(role==='cluster'){MultiButtonService.setState('inventoryOSPCluster',true);}else if(role==='compute'){MultiButtonService.setState('inventoryOSPCompute',true);}else if(role==='director'){MultiButtonService.setState('inventoryOSPDirector',true);}else if(role==='controller'){MultiButtonService.setState('inventoryOSPController',true);}else if(role==='host'){MultiButtonService.setState('inventoryDockerHosts',true);}else if(role==='container'){MultiButtonService.setState('inventoryDockerContainers',true);}else if(role==='image'){MultiButtonService.setState('inventoryDockerImages',true);}else if(role==='hypervisor'){MultiButtonService.setState('inventoryRHEVHypervisors',true);}else if(role==='manager'){MultiButtonService.setState('inventoryRHEVManagers',true);}});}else{//default to show all roles MultiButtonService.setState('inventoryOSPCluster',true);MultiButtonService.setState('inventoryOSPCompute',true);MultiButtonService.setState('inventoryOSPDirector',true);MultiButtonService.setState('inventoryOSPController',true);MultiButtonService.setState('inventoryDockerHosts',true);MultiButtonService.setState('inventoryDockerContainers',true);MultiButtonService.setState('inventoryDockerImages',true);MultiButtonService.setState('inventoryOCPCluster',true);MultiButtonService.setState('inventoryOCPMaster',true);MultiButtonService.setState('inventoryOCPNodes',true);MultiButtonService.setState('inventoryRHEVHypervisors',true);MultiButtonService.setState('inventoryRHEVManagers',true);}if(params.severity){Severities.map(function(s){return s.value;}).forEach(function(severity){if(severity===params.severity){MultiButtonService.setState('severityFilters'+severity,true);}else{MultiButtonService.setState('severityFilters'+severity,false);}});}if(params.category){Categories.forEach(function(category){if(category===params.category){MultiButtonService.setState('categoryFilters'+category,true);}else{MultiButtonService.setState('categoryFilters'+category,false);}});}//this is invalid, revert both back to true if(params.offline==='false'&¶ms.online==='false'){filterService.setOffline(true);filterService.setOnline(true);}else{if(params.offline){var offline=params.offline==='true';filterService.setOffline(offline);}if(params.online){var online=params.online==='true';filterService.setOnline(online);}}if(params.machine){filterService.setMachine(params.machine);}};filterService.buildRHEVRolesString=function(){var roles=[];if(MultiButtonService.getState('inventoryRHEVManagers')){roles.push('manager');}if(MultiButtonService.getState('inventoryRHEVHypervisors')){roles.push('hypervisor');}if(MultiButtonService.getState('inventoryRHEVDeployments')){roles.push('cluster');}return roles.toString();};/** * whitelist > blacklist * * Each is an array of query params. If a param is on the whitelist it will be * included, if it's on the blacklist it won't be included. Priority is given * to the whitelist. If a param is on neither list it will be included. */filterService.buildRequestQueryParams=function(whitelist,blacklist){var query={};var osp_deployment=filterService.getSelectedOSPDeployment().system_id;var docker_host=filterService.getSelectedDockerHost().system_id;if(!whitelist){whitelist=[];}if(!blacklist){blacklist=[];}function isOnList(param,list){var response=false;if(list.indexOf(param)!==-1){response=true;}return response;}function includeParam(param){var response=true;if(isOnList(param,blacklist)&&!isOnList(param,whitelist)){response=false;}return response;}//search term if(filterService.getSearchTerm()&&includeParam('search_term')){query.search_term=filterService.getSearchTerm();}//with or without actions if(includeParam('report_count')){if(MultiButtonService.getState('inventoryWithActions')&&!MultiButtonService.getState('inventoryWithoutActions')){query.report_count='gt0';}else if(!MultiButtonService.getState('inventoryWithActions')&&MultiButtonService.getState('inventoryWithoutActions')){query.report_count='lt1';}}//product if(filterService.getSelectedProduct()==='rhel'&&includeParam('product_code')){query.product_code='rhel';}else if(filterService.getSelectedProduct()==='osp'){if(includeParam('product_code')){query.product_code='osp';}if(includeParam('role')){query.role=filterService.buildOSPRolesString();}}else if(filterService.getSelectedProduct()==='rhev'){if(includeParam('product_code')){query.product_code='rhev';}if(includeParam('role')){query.role=filterService.buildRHEVRolesString();}}else if(filterService.getSelectedProduct()==='docker'){if(includeParam('product_code')){query.product_code='docker';}if(includeParam('role')){query.role=filterService.buildDockerRolesString();}}else if(filterService.getSelectedProduct()==='ocp'){if(includeParam('product_code')){query.product_code='ocp';}}//root if(osp_deployment&&osp_deployment!=='all'&&includeParam('root')){query.root=osp_deployment;}if(docker_host&&docker_host!=='all'&&includeParam('root')){query.root=docker_host;}//group if(Group.current().id&&includeParam('group')){query.group=Group.current().id;}//severity if(includeParam('severity')){Severities.map(function(s){return s.value;}).forEach(function(severity){if(MultiButtonService.getState('severityFilters'+severity)&&severity!=='All'){query.severity=severity;}});}//category if(includeParam('category')&&_category!=='all'){query.category=_category;}//impact if(includeParam('rec_impact')&&recSeveritiesMap[_impact]){query.rec_impact=recSeveritiesMap[_impact];}//likelihood if(includeParam('rec_likelihood')&&recSeveritiesMap[_likelihood]){query.rec_likelihood=recSeveritiesMap[_likelihood];}//ansible support if(includeParam('ansible')&&_ansibleSupport!=='all'){if(_ansibleSupport==='supported'){query.ansible=true;}else if(_ansibleSupport==='notSupported'){query.ansible=false;}}//ignored/active rule if(includeParam('ignoredRules')&&_ruleStatus!=='all'){query.ignoredRules=_ruleStatus;}//incidents if(includeParam('incidents')&&_incidents!=='all'){if(_incidents==='incidents'){query.hasIncidents=true;}else if(_incidents==='nonIncidents'){query.hasIncidents=false;}}return query;};return filterService;}servicesModule.factory('FilterService',FilterService);},{"./":193}],190:[function(require,module,exports){'use strict';var servicesModule=require('./');function GettingStarted(){var _sections=[];function set(sections){_sections.length=0;Array.prototype.push.apply(_sections,sections);return _sections;}function add(crumb){_sections.push(crumb);}return{sections:function sections(){return _sections;},setSections:set,addSection:add};}servicesModule.factory('GettingStarted',GettingStarted);},{"./":193}],191:[function(require,module,exports){'use strict';GroupService.$inject=['$q','gettextCatalog','sweetAlert','Group'];var servicesModule=require('./');var map=require('lodash/map');var sortBy=require('lodash/sortBy');var pick=require('lodash/pick');var some=require('lodash/some');/** * @ngInject */function GroupService($q,gettextCatalog,sweetAlert,Group){function groupNameValidator(name){if(!name||typeof name!=='string'||!name.length){return $q.reject(gettextCatalog.getString('Please specify a system group name'));}if(some(Group.groups,function(group){return group.display_name===name;})){return $q.reject(gettextCatalog.getString('Name already used'));}return $q.resolve();}function groupSystems(selectedSystems){function assignSystemsToGroup(group){var systems=map(selectedSystems,function(s){return pick(s,'system_id');});return Group.addSystems(group,systems).then(function(){return group;});}// no groups - skip directly to creating new group if(!Group.groups.length){return createGroup().then(assignSystemsToGroup).then(Group.setCurrent);}var groups=sortBy(Group.groups,'display_name');groups.unshift({display_name:gettextCatalog.getString('Create new System Group')});var options=map(groups,'display_name');return sweetAlert({title:gettextCatalog.getString('Group Systems'),type:undefined,input:'select',inputOptions:options,confirmButtonText:gettextCatalog.getString('OK')}).then(function(param){var index=parseInt(param);// creating new group if(index===0){return createGroup().then(assignSystemsToGroup).then(Group.setCurrent).catch(function(){return groupSystems(selectedSystems);});}// adding to existing group var group=groups[index];return assignSystemsToGroup(group).then(Group.setCurrent);});}function createGroup(){return sweetAlert({title:gettextCatalog.getString('Create new System Group'),input:'text',type:undefined,confirmButtonText:gettextCatalog.getString('Create'),inputPlaceholder:gettextCatalog.getString('Group name'),inputValidator:groupNameValidator}).then(function(name){return Group.createGroup({display_name:name});});}function deleteGroup(group){var html=gettextCatalog.getString('You will not be able to recover {{name}}',{name:group.display_name});sweetAlert({html:html}).then(function(){return Group.deleteGroup(group);}).then(function(){if(group.id===Group.current().id){Group.setCurrent();}});}return{groupSystems:groupSystems,createGroup:createGroup,deleteGroup:deleteGroup};}servicesModule.service('GroupService',GroupService);},{"./":193,"lodash/map":522,"lodash/pick":533,"lodash/some":539,"lodash/sortBy":540}],192:[function(require,module,exports){'use strict';IncidentsService.$inject=['$q','Topic'];var servicesModule=require('./');/** * @ngInject */function IncidentsService($q,Topic){var service={};var incidentRules=[];service.incidentRulesWithHitsCount=0;service.affectedSystemCount=0;/** * Populates incidentRules and returns a promise */service.init=function(){if(incidentRules.length===0){return service.loadIncidents();}else{return $q.resolve(true);}};/** * Allows user to force reload incidentRules */service.loadIncidents=function(){return Topic.get('incidents').success(function(topic){incidentRules=topic.rules;setRulesWithHitsCount(topic.rules);service.affectedSystemCount=topic.affectedSystemCount;});};/** * Determines if rule of given ruleId is an incident */service.isIncident=function(ruleId){var isIncident=incidentRules.find(function(incident){return incident.rule_id===ruleId;});return isIncident!==undefined;};function setRulesWithHitsCount(rules){var rulesWithHits=0;rules.forEach(function(rule){if(rule.hitCount>0&&!rule.acked){rulesWithHits++;}});service.incidentRulesWithHitsCount=rulesWithHits;}return service;}servicesModule.factory('IncidentsService',IncidentsService);},{"./":193}],193:[function(require,module,exports){'use strict';module.exports=angular.module('insights.services',[]);(function(){var f=require("./index.js");f["account.service"]=require("./account.service.js");f["actionbar.service"]=require("./actionbar.service.js");f["actions.service"]=require("./actions.service.js");f["actionsBreadcrumbs.service"]=require("./actionsBreadcrumbs.service.js");f["alert.service"]=require("./alert.service.js");f["analytics.service"]=require("./analytics.service.js");f["announcement.service"]=require("./announcement.service.js");f["ansible.service"]=require("./ansible.service.js");f["betaRedirect.service"]=require("./betaRedirect.service.js");f["bounce.service"]=require("./bounce.service.js");f["coercion.service"]=require("./coercion.service.js");f["dataUtils.service"]=require("./dataUtils.service.js");f["digest.service"]=require("./digest.service.js");f["filter.service"]=require("./filter.service.js");f["gettingStarted.service"]=require("./gettingStarted.service.js");f["group.service"]=require("./group.service.js");f["incidents.service"]=require("./incidents.service.js");f["index"]=require("./index.js");f["inventory.service"]=require("./inventory.service.js");f["listType.service"]=require("./listType.service.js");f["maintenance.service"]=require("./maintenance.service.js");f["modalUtils"]=require("./modalUtils.js");f["multibutton.service"]=require("./multibutton.service.js");f["overview.service"]=require("./overview.service.js");f["permalink.service"]=require("./permalink.service.js");f["permission.service"]=require("./permission.service.js");f["platform.service"]=require("./platform.service.js");f["preference.service"]=require("./preference.service.js");f["reload.service"]=require("./reload.service.js");f["rule.service"]=require("./rule.service.js");f["ruleAdmin.service"]=require("./ruleAdmin.service.js");f["rulePreview.service"]=require("./rulePreview.service.js");f["strata.service"]=require("./strata.service.js");f["sweetalert.service"]=require("./sweetalert.service.js");f["systems.service"]=require("./systems.service.js");f["template.service"]=require("./template.service.js");f["title.service"]=require("./title.service.js");f["topbarAlerts.service"]=require("./topbarAlerts.service.js");f["topic.service"]=require("./topic.service.js");f["utils.service"]=require("./utils.service.js");return f;})();},{"./account.service.js":176,"./actionbar.service.js":177,"./actions.service.js":178,"./actionsBreadcrumbs.service.js":179,"./alert.service.js":180,"./analytics.service.js":181,"./announcement.service.js":182,"./ansible.service.js":183,"./betaRedirect.service.js":184,"./bounce.service.js":185,"./coercion.service.js":186,"./dataUtils.service.js":187,"./digest.service.js":188,"./filter.service.js":189,"./gettingStarted.service.js":190,"./group.service.js":191,"./incidents.service.js":192,"./index.js":193,"./inventory.service.js":194,"./listType.service.js":195,"./maintenance.service.js":196,"./modalUtils.js":197,"./multibutton.service.js":198,"./overview.service.js":199,"./permalink.service.js":200,"./permission.service.js":201,"./platform.service.js":202,"./preference.service.js":203,"./reload.service.js":204,"./rule.service.js":205,"./ruleAdmin.service.js":206,"./rulePreview.service.js":207,"./strata.service.js":208,"./sweetalert.service.js":209,"./systems.service.js":210,"./template.service.js":211,"./title.service.js":212,"./topbarAlerts.service.js":213,"./topic.service.js":214,"./utils.service.js":215}],194:[function(require,module,exports){'use strict';InventoryService.$inject=['$modal','FilterService','System','InsightsConfig'];var servicesModule=require('./');function InventoryService($modal,FilterService,System,InsightsConfig){var inventoryService={};var _sort={field:'toString',direction:'ASC'};var _total=0;inventoryService.loading=true;inventoryService.allExpanded=false;inventoryService.getSortField=function(){return _sort.field;};inventoryService.setSortField=function(field){_sort.field=field;FilterService.setQueryParam('sort_field',field);};inventoryService.getSortDirection=function(){return _sort.direction;};inventoryService.setSortDirection=function(direction){_sort.direction=direction;FilterService.setQueryParam('sort_dir',direction);};inventoryService.toggleSortDirection=function(){_sort.direction=_sort.direction==='ASC'?'DESC':'ASC';FilterService.setQueryParam('sort_dir',_sort.direction);};inventoryService.getSort=function(){return _sort;};inventoryService.setSort=function(sort){_sort=sort;FilterService.setQueryParam('sort_dir',sort.direction);FilterService.setQueryParam('sort_field',sort.field);};inventoryService.setTotal=function(total){_total=total;};inventoryService.getTotal=function(){return _total;};inventoryService._systemModal=null;inventoryService.showSystemModal=function(system,loadSystem){function displayModal(_system){if(typeof InsightsConfig.systemShowSystem==='function'){InsightsConfig.systemShowSystem(_system);return;}function openModal(opts){if(inventoryService._systemModal){return;// Only one modal at a time please }inventoryService._systemModal=$modal.open(opts);inventoryService._systemModal.result.finally(function(){inventoryService._systemModal=null;});}openModal({templateUrl:'js/components/system/systemModal/systemModal.html',windowClass:'system-modal ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'SystemModalCtrl',resolve:{system:function system(){return _system;},rule:function rule(){return false;}}});}if(loadSystem){System.getSingleSystem(system.system_id).then(function(system){displayModal(system.data);});}else{displayModal(system);}};return inventoryService;}servicesModule.factory('InventoryService',InventoryService);},{"./":193}],195:[function(require,module,exports){/*global require*/'use strict';ListTypeService.$inject=['TableToCardsBreakpoint'];var servicesModule=require('./');/** * @ngInject */function ListTypeService(TableToCardsBreakpoint){var _types=Object.freeze({card:'card',table:'table'});var type=_types.card;var decideType=function decideType(event){if(event.matches){type=_types.table;}else{type=_types.card;}};var mediaQuery=window.matchMedia('(min-width: '+TableToCardsBreakpoint+')');// on construction set type based on window decideType(mediaQuery);// listen to all further changes // the trello card does not really ask for this... mediaQuery.addListener(decideType);return{types:function types(){return _types;},getType:function getType(){return type;},setType:function setType(input){return type=_types[input];}};}servicesModule.service('ListTypeService',ListTypeService);},{"./":193}],196:[function(require,module,exports){'use strict';MaintenanceService.$inject=['Utils','Severities','Report','$rootScope','Rule','System','InsightsConfig','$modal','Maintenance','User','$q','DataUtils','TopbarAlertsService','$state','gettextCatalog','Events'];var servicesModule=require('./');var filter=require('lodash/filter');var map=require('lodash/map');var flatMap=require('lodash/flatMap');var indexBy=require('lodash/keyBy');var reject=require('lodash/reject');var remove=require('lodash/remove');var some=require('lodash/some');var assign=require('lodash/assign');var pick=require('lodash/pick');var diffWith=require('lodash/differenceWith');/** * @ngInject */function MaintenanceService(Utils,Severities,Report,$rootScope,Rule,System,InsightsConfig,$modal,Maintenance,User,$q,DataUtils,TopbarAlertsService,$state,gettextCatalog,Events){var service={};var plansDfd=false;var MAINTENANCE_ACTION_TYPE=Object.freeze({PLANNED:0,AVAILABLE:1,PLANNED_ELSEWHERE:2});service.MAINTENANCE_ACTION_TYPE=MAINTENANCE_ACTION_TYPE;function getPlannedActionsReportIds(){var now=new Date();var plans=filter(service.plans.all,function(plan){// only show planning conflict with real plans, not suggestions and past plans return(plan.suggestion===null||plan.suggestion===Maintenance.SUGGESTION.ACCEPTED)&&(!plan.end||plan.end>now);});return indexBy(reject(flatMap(plans,'actions'),'done'),'current_report.id');}service.showSystemModal=function(s,_rule){if(_rule){_rule=_rule.rule_id;}System.getSystemReports(s.system_id).success(function(_system2){if(typeof InsightsConfig.actionsShowSystem==='function'){return InsightsConfig.actionsShowSystem(_system2,_rule);}$modal.open({templateUrl:'js/components/system/systemModal/systemModal.html',windowClass:'system-modal ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'SystemModalCtrl',resolve:{system:function system(){return _system2;},rule:function rule(){return _rule;}}});});};service.showMaintenanceModal=function(_systems2,_rule2,_existingPlan){return $modal.open({templateUrl:'js/components/maintenance/'+'maintenanceModal/maintenanceModal.html',windowClass:'maintenance-modal modal-wizard ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'maintenanceModalCtrl',resolve:{systems:function systems(){return _systems2||false;},rule:function rule(){return _rule2||false;},existingPlan:function existingPlan(){return _existingPlan;}}}).result;};function TableParams(plan,item,savedActions,loader){this.plan=plan;this.item=item;this.savedActions=savedActions||[];this.loader=loader;this.implicitOrder={predicate:'display',reverse:false};}TableParams.prototype.getActions=function(){return this.savedActions.map(this.actionMapper);};TableParams.prototype.update=function(toAdd,toRemove){return service.plans.update(this.plan,{add:toAdd,delete:toRemove});};TableParams.prototype.save=function(toAdd,toRemove){return this.update(map(toAdd,function(item){return pick(item,['rule_id','system_id']);}),map(toRemove,'mid'));};service.systemTableParams=function(rule,plan,actions,loader){var params=new TableParams(plan,rule,actions,loader);params.actionMapper=function(action){return{id:action.system.system_id,display:action.system._name,mid:action.id,system:action.system,done:action.done,systemTypeIconId:action.system.system_type_id};};params.getAvailableActions=function(){var query={rule:rule.rule_id,expand:'system'};return Report.getReportsLatest(query).then(function(response){var plannedActions=getPlannedActionsReportIds();var reports=response.data.resources;var planItems=[];reports.forEach(function forEachReport(report){report.system=DataUtils.readSystem(report.system);planItems.push({id:report.system.system_id,display:report.system._name,system:report.system,done:false,system_id:report.system.system_id,rule_id:rule.rule_id,_type:report.id in plannedActions?MAINTENANCE_ACTION_TYPE.PLANNED_ELSEWHERE:MAINTENANCE_ACTION_TYPE.AVAILABLE,systemTypeIconId:report.system.system_type_id});});return planItems;});};return params;};service.actionTableParams=function(system,plan,actions,loader){var params=new TableParams(plan,system,actions,loader);params.actionMapper=function(action){return{id:action.rule.rule_id,display:action.rule.description,mid:action.id,rule:action.rule,done:action.done};};params.getAvailableActions=function(){return System.getSystemReports(system.system_id).then(function(response){var plannedActions=getPlannedActionsReportIds();var reports=response.data.reports;var planItems=[];reports.forEach(function forEachReport(report){planItems.push({id:report.rule.rule_id,display:report.rule.description,rule:report.rule,done:false,system_id:system.system_id,rule_id:report.rule.rule_id,_type:report.id in plannedActions?MAINTENANCE_ACTION_TYPE.PLANNED_ELSEWHERE:MAINTENANCE_ACTION_TYPE.AVAILABLE});});return planItems;});};params.implicitOrder={predicate:'rule.severityNum',reverse:true};return params;};service.plans={all:[]};service.plans.load=function(force){if(force||!plansDfd){plansDfd=Maintenance.getMaintenancePlans().then(function(plans){service.plans.all=plans;return service.plans.process().then(function(){$rootScope.$broadcast(Events.planner.plansLoaded);});});}return plansDfd;};service.plans.process=function(){var now=new Date();return User.init().then(function(user){var isInternal=user.is_internal;service.plans.future=[];service.plans.past=[];service.plans.overdue=[];service.plans.unscheduled=[];service.plans.suggested=[];service.plans.scheduled=[];service.plans.all.forEach(function(plan){// filter out hidden plans // TODO: this is what backend is supposed to do if(plan.hidden&&!isInternal){return;}if(plan.suggestion===Maintenance.SUGGESTION.PROPOSED||plan.suggestion===Maintenance.SUGGESTION.REJECTED){service.plans.suggested.push(plan);}else if(plan.end){if(plan.end>now){service.plans.future.push(plan);}else{service.plans.past.push(plan);if(plan.overdue&&!plan.silenced){service.plans.overdue.push(plan);}}service.plans.scheduled.push(plan);}else{service.plans.unscheduled.push(plan);}});// update topbar alerts TopbarAlertsService.removeAll('maintenance');if(service.plans.overdue.length){TopbarAlertsService.push({type:'maintenance',msg:gettextCatalog.getPlural(service.plans.overdue.length,'A plan was not completed on time','{{$count}} plans were not completed on time',{}),acked:false,icon:'fa-calendar-times-o red',onSelect:function onSelect(){$state.go('app.maintenance',{maintenance_id:service.plans.overdue[0].maintenance_id});},onAck:function onAck(){var pending=service.plans.overdue.map(function(plan){return Maintenance.silence(plan).then(function(){plan.silenced=true;});});$q.all(pending).then(function(){service.plans.process();});}});}var suggested=service.plans.suggested.filter(function(plan){return!plan.hidden;});if(suggested.length){TopbarAlertsService.push({type:'maintenance',msg:gettextCatalog.getPlural(suggested.length,'You have a new plan suggestion','You have {{$count}} new plan suggestions',{}),acked:false,icon:'fa-wrench',onSelect:function onSelect(){$state.go('app.maintenance',{maintenance_id:suggested[0].maintenance_id});}});}});};service.plans.findCategory=function(id){var options=['future','unscheduled','past','suggested'];function someCallback(item){return item.maintenance_id==id;// jshint ignore:line }for(var i=0;i1;});}).catch(function(res){if(res.status===404){// the endpoint returns 404 if there are no ansible-enabled rules return[];}});}function doPrompt(plan,toPrompt){return toPrompt.reduce(function(acc,play,index){return acc.then(function(){return service.resolutionModal(plan,play,toPrompt.length,index);});},$q.resolve()).catch(function(){// empty catch handler that prevents the promise from being rejected // if "use defaults" is clicked on resolution modal });}function playComparator(one,two){return one.system_type_id===two.system_type_id&&one.rule.rule_id===two.rule.rule_id;}/** * Compares the list of multi-resolution plays before and after the change. * The user is prompted to choose a resolution for the new ones. */service.promptResolutionIfNeeded=function(fn,plan){var dfd=$q.resolve([]);if(plan){dfd=dfd.then(function(){return getMultiResolutionRules(plan);});}return dfd.then(function(before){return fn().then(function(plan){return getMultiResolutionRules(plan).then(function(after){var diff=diffWith(after,before,playComparator);return doPrompt(plan,diff);});});});};return service;}servicesModule.service('MaintenanceService',MaintenanceService);},{"./":193,"lodash/assign":479,"lodash/differenceWith":487,"lodash/filter":490,"lodash/flatMap":493,"lodash/keyBy":518,"lodash/map":522,"lodash/pick":533,"lodash/reject":537,"lodash/remove":538,"lodash/some":539}],197:[function(require,module,exports){'use strict';ModalUtils.$inject=['$rootScope','$timeout'];var servicesModule=require('./');/** * @ngInject */function ModalUtils($rootScope,$timeout){function suppressEscNavigation($modalInstance){var escUnreg=$rootScope.$on('telemetry:esc',function($event){$event.preventDefault();return false;});$modalInstance.result.then(angular.noop,function(){// defer unregistering of the esc suppressor $timeout(function(){escUnreg();});});}return{suppressEscNavigation:suppressEscNavigation};}servicesModule.service('ModalUtils',ModalUtils);},{"./":193}],198:[function(require,module,exports){'use strict';MultiButton.$inject=['Categories','Severities'];var servicesModule=require('./');//service to store multi button directive states, //this allows other directives to easily read button state function MultiButton(Categories,Severities){var multiButton={};multiButton.RULE_ADMIN_CAT_FILTER_KEY='rule-admin-category';multiButton.inventoryWithActions={state:true};multiButton.inventoryWithoutActions={state:true};multiButton.inventoryInfo={state:true};multiButton.inventoryWarn={state:true};multiButton.inventoryError={state:true};multiButton.inventoryAvailability={state:true};multiButton.inventoryStability={state:true};multiButton.inventoryPerformance={state:true};multiButton.inventorySecurity={state:true};multiButton.inventoryStandalone={state:true};multiButton.inventoryRHEVClusters={state:true};multiButton.inventoryRHEVManagers={state:true};multiButton.inventoryRHEVHypervisors={state:true};multiButton.inventoryDockerHosts={state:true};multiButton.inventoryDockerContainers={state:true};multiButton.inventoryDockerImages={state:true};multiButton.inventoryOSPCluster={state:true};multiButton.inventoryOSPDirector={state:true};multiButton.inventoryOSPCompute={state:true};multiButton.inventoryOSPController={state:true};Severities.map(function(s){return s.value;}).forEach(function(severity){var key='severityFilters'+severity;var state=false;if(severity==='all'){state=true;}multiButton[key]={state:state};});Categories.forEach(function(category){var key='categoryFilters'+category;var state=false;if(category==='all'){state=true;}multiButton[key]={state:state};});// init rule admin category filter // unlike other filters, this one is persisted using localStorage Categories.forEach(function(category){multiButton[multiButton.RULE_ADMIN_CAT_FILTER_KEY+category]={state:false};});var activeKey=window.localStorage.getItem('insights:'+multiButton.RULE_ADMIN_CAT_FILTER_KEY)||'all';multiButton[multiButton.RULE_ADMIN_CAT_FILTER_KEY+activeKey].state=true;multiButton.getState=function(key){if(multiButton[key]){return multiButton[key].state;}else{console.error('MultiButtonService state "'+key+'" not found. Available states: '+JSON.stringify(Object.keys(multiButton)));return false;}};multiButton.setState=function(key,state){if(multiButton[key]){multiButton[key].state=state;}};multiButton.initState=function(key){multiButton[key]={state:false};};return multiButton;}servicesModule.service('MultiButtonService',MultiButton);},{"./":193}],199:[function(require,module,exports){/*global require*/'use strict';OverviewService.$inject=['Article'];var servicesModule=require('./');/** * @ngInject */function OverviewService(Article){var pub={};pub.loading=false;function load(){Article.get().then(function(res){pub.article=res.data;});}pub.update=function(id,data){return Article.update(id,data).then(load);};load();return pub;}servicesModule.factory('OverviewService',OverviewService);},{"./":193}],200:[function(require,module,exports){'use strict';PermalinkService.$inject=['$document','$location','$timeout'];var servicesModule=require('./');/** * @ngInject */function PermalinkService($document,$location,$timeout){var DEFAULTS={offset:10,speed:300};function scrollTo(id,offset,speed){if(typeof offset==='undefined'){offset=DEFAULTS.offset;}if(typeof speed==='undefined'){speed=DEFAULTS.speed;}$document.scrollTo(document.getElementById(id),offset,speed);}return{make:function make(id,scroll,offset,speed){id=id.replace('#','');$location.hash(id);if(scroll===true){scrollTo(id,offset,speed);}},scroll:function scroll(id,offset,speed){var hash=$location.hash();if(!id){id=hash;}if(!id){return;}// Yes, you are reading this right. A timeout in a timeout. // Greasy AF but fought with this so much. and this caused the most // reliable results. $timeout(function(){$timeout(function(){scrollTo(id,offset,speed);},0);},0);}};}servicesModule.service('PermalinkService',PermalinkService);},{"./":193}],201:[function(require,module,exports){'use strict';var servicesModule=require('./');var PERMS={SU:'SU',INTERNAL:'INTERNAL',CREATE_ANNOUNCEMENT:'CREATE_ANNOUNCEMENT',ACCOUNT_SWITCHER:'ACCOUNT_SWITCHER',CONTENT_MANAGER:'CONTENT_MANAGER'};function PermissionService(){function has(user,permission){if(!user||!user.permissions){return false;}if(!user.is_internal){return false;}if(user.permissions[PERMS.SU]){return true;}if(user.permissions[permission]){return true;}return false;}return{has:has,PERMS:PERMS};}servicesModule.service('PermissionService',PermissionService);},{"./":193}],202:[function(require,module,exports){'use strict';PlatformService.$inject=['$rootScope','Account'];var servicesModule=require('./');/** * @ngInject */function PlatformService($rootScope,Account){var platforms;function load(){platforms=Account.getProducts().then(function(res){return res.data;});}$rootScope.$on('account:change',load);load();return{getPlatforms:function getPlatforms(){return platforms;}};}servicesModule.service('PlatformService',PlatformService);},{"./":193}],203:[function(require,module,exports){'use strict';PreferenceService.$inject=['UserSettings','$rootScope','Utils'];var servicesModule=require('./');/** * @ngInject */function PreferenceService(UserSettings,$rootScope,Utils){var prefs={};return{//must explicitely say do not persist to backend, set:function set(key,value,updateBackend){prefs[key]=value;if(updateBackend||updateBackend===undefined){UserSettings.update(prefs);}},get:function get(key){return prefs[key];},appendProductToUrl:function appendProductToUrl(url,type){var prependChar=Utils.getNextQueryPrependChar(url);var product=this.get('dashboard_mode');var environments=null;var theType=type?type:'machine';if(product){url+=prependChar+'product='+product;prependChar=Utils.getNextQueryPrependChar(url);}url+=prependChar+'type='+theType;prependChar=Utils.getNextQueryPrependChar(url);if(product==='osp'){environments=this.get('osp_deployment');if(environments){url+=prependChar+'environments='+environments;}}return url;}};}servicesModule.service('PreferenceService',PreferenceService);},{"./":193}],204:[function(require,module,exports){'use strict';ReloadService.$inject=['$http'];var servicesModule=require('./');var headerName='x-insights-autoreload';/** * @ngInject */function ReloadService($http){var pub={};pub.begin=function(){$http.defaults.headers.common[headerName]=true;};pub.end=function(){delete $http.defaults.headers.common[headerName];};pub.wrap=function(cb){pub.begin();cb(function(){pub.end();});};return pub;}servicesModule.service('ReloadService',ReloadService);},{"./":193}],205:[function(require,module,exports){'use strict';RuleService.$inject=['$filter'];var servicesModule=require('./');var groupBy=require('lodash/groupBy');var maxBy=require('lodash/maxBy');var sumBy=require('lodash/sumBy');var every=require('lodash/every');var uniq=require('lodash/uniq');var map=require('lodash/map');// this is a temporary cheat until this data is available in the API var PLUGIN_NAMES={CVE_2016_0800_openssl_drown:'DROWN',CVE_2016_0728_kernel:'Kernel keychain vulnerability (CVE-2016-0728)',CVEs_samba_badlock:'Badlock (Samba)',CVE_2016_2315_24_git:'CVE-2016-2315, CVE-2016-2324 (Git)',heartbleed:'Heartbleed',httpd_poodle:'POODLE (Httpd)',vsftpd_local_enable:'vsftpd',CVE_2016_3714_imagemagick:'ImageMagick',httpd_weak_ciphers:'Weak Ciphers (Apache)',network_skb_panic_after_add_grhead:'Networking caused kernel panic',sanity_check_fstab:'/etc/fstab misconfigurations',docker_journald_stop:'Docker crashes after journald is stopped',cifs_share_lost:'Interruption in CIFS shares',rhev_qla2xxx_firmware_failed:'RHEV Qla2xxx firmware failed',docker_mount_dev:'Docker mounting',unsupported_journal_mode:'Unsupported journal mode',panic_anon_vma_degree:'unlink_anon_vmas() kernel panic',sosreport_load_bridge_module:'sosreport loads the bridge kernel module unintentionally',rpm_database_problems:'RPM database problems',xinetd_per_source_limit:'xinetd per_source_limit messages',crashkernel_with_3w_modules:'Kernel crash with 3w-sas or 3w-9xxx modules loaded',fs_resize2fs_bug:'resize2fs utility issue on ext4 file system',multipathd_start_failed:'Multipathd service failing to start at boot time',rport_delete_crash:'Kernel panic on SCSI host removal',tg3_driver_issue:'tg3 driver bug causes network instability',ilo:'HP iLO memory corruption',missing_boot_files:'Grub configuration: missing files',nproc_common_entries:'Common entries in both limits.conf and 90-nproc.conf',tsc_reboot_uptime:'Clock overflow panic after 200+ days uptime',rsyslogd_shutdown_crash:'Rsyslogd crashing during shutdown',rx_crc_errors:'Interface RX CRC errors',vm_swappiness_oom:'vm.swappiness value at risk of OOM termination condition'};/** * @ngInject */function RuleService($filter){var service={};var orderBy=$filter('orderBy');service.groupRulesByPlugin=function(rules){var plugins=groupBy(rules,'plugin');return map(plugins,function(rules,key){rules=orderBy(rules,['acked','hitCount === 0','-severityNum','-hitCount']);var result={rules:rules,plugin_id:key};if(result.rules.length===1){result.rule=result.rules[0];// shortcut for easier access result.type='rule';}else{result.type='group';}result.severityNum=maxBy(result.rules,'severityNum').severityNum;result.categories=uniq(map(result.rules,'category'));// sum up hitCounts for the plugin result.hitCount=sumBy(result.rules,'hitCount');// a plugin is acked as long as every single rule is acked result.acked=every(result.rules,'acked');result.display_name=result.rules[0].plugin_name;if(result.display_name in PLUGIN_NAMES){result.display_name=PLUGIN_NAMES[result.display_name];}return result;});};return service;}servicesModule.service('RuleService',RuleService);},{"./":193,"lodash/every":489,"lodash/groupBy":497,"lodash/map":522,"lodash/maxBy":524,"lodash/sumBy":544,"lodash/uniq":551}],206:[function(require,module,exports){'use strict';RuleAdminService.$inject=['Report'];var servicesModule=require('./');/** * @ngInject */function RuleAdminService(Report){var globalCache={};globalCache.get=function(rule){if(!globalCache.contains(rule)){globalCache[rule]=Report.getReportsLatest({rule:rule,expand:'system',account_number:'*'}).then(function(res){return res.data.resources;}).catch(function(){delete globalCache[rule];});}return globalCache[rule];};globalCache.contains=function(rule){return rule in globalCache;};return{globalCache:globalCache};}servicesModule.service('RuleAdminService',RuleAdminService);},{"./":193}],207:[function(require,module,exports){'use strict';RulePreview.$inject=['$http','$modal'];var servicesModule=require('./');/** * @ngInject */function RulePreview($http,$modal){return{preview:function preview(_rule3){$modal.open({templateUrl:'js/components/system/systemModal/systemModal.html',windowClass:'system-modal ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'SystemModalCtrl',resolve:{rule:function rule(){return _rule3;},system:function system(){return false;}}});}};}servicesModule.service('RulePreview',RulePreview);},{"./":193}],208:[function(require,module,exports){'use strict';StrataService.$inject=['$http','$q'];var servicesModule=require('./');var urijs=require('urijs');var SEARCH_URL='https://access.redhat.com/rs/search';var OPTS=Object.freeze({headers:{'Content-Type':'application/json',Accept:'application/json','X-Omit':'WWW-Authenticate'}});/** * @ngInject */function StrataService($http,$q){function search(field,value){var uri=urijs(SEARCH_URL).addSearch('keyword',field+':'+value);return $http.get(uri.toString(),OPTS).then(function(res){if(res.data&&res.data.search_result){return res.data.search_result;}return $q.reject('no results');});}return{searchById:function searchById(query){return search('id',query);},searchByUri:function searchByUri(query){return search('view_uri',query);}};}servicesModule.service('StrataService',StrataService);},{"./":193,"urijs":826}],209:[function(require,module,exports){'use strict';sweetAlert.$inject=['$rootScope','$q','gettextCatalog'];var servicesModule=require('./');var swal=require('sweetalert2');/** * @ngInject */function sweetAlert($rootScope,$q,gettextCatalog){var DEFAULTS={title:gettextCatalog.getString('Are you sure?'),type:'warning',confirmButtonColor:'#08C0FC',confirmButtonText:gettextCatalog.getString('Yes'),showCancelButton:true,cancelButtonText:gettextCatalog.getString('Cancel')};return function(options){var opts=angular.extend({},DEFAULTS,options);var defer=$q.defer();swal(opts).then(defer.resolve).catch(defer.reject);return defer.promise;};}servicesModule.factory('sweetAlert',sweetAlert);},{"./":193,"sweetalert2":820}],210:[function(require,module,exports){'use strict';SystemsService.$inject=['$filter','$rootScope','$q','gettextCatalog','sweetAlert','Group','Products','System'];var servicesModule=require('./');var get=require('lodash/get');var arrayRemove=require('lodash/remove');var find=require('lodash/find');var assign=require('lodash/assign');function SystemsService($filter,$rootScope,$q,gettextCatalog,sweetAlert,Group,Products,System){var systemsService={};var _newestSystems=null;var _UNREGISTER_TEXT_SINGLE='
      This will unregister the selected system from Red Hat Insights.'+'

      To undo this action redhat-access-insights --register '+'must be run from the unregistered system.

      ';var _UNREGISTER_TEXT_MULTI='
      This will unregister the {{count}} selected systems from '+'Red Hat Insights.

      To undo this action '+'redhat-access-insights --register '+'must be run from each unregistered system.

      ';var systemTypes=[];// system type indication is ubiquitous - let's eagerly load this var systemTypesDfd=System.getSystemTypes().then(function(response){systemTypes=response.data;return response.data;});// unsafe because there is no guarantee system types have been loaded systemsService.getSystemTypeUnsafe=function(system_type_id){return find(systemTypes,{id:parseInt(system_type_id)});};systemsService.getSystemTypesAsync=function(){return systemTypesDfd;};systemsService.getSystemTypeAsync=function(system_type_id){return systemsService.getSystemTypesAsync().then(function(){return systemsService.getSystemTypeUnsafe(system_type_id);});};function unregisterSystems(systems){var count=systems.length;var promises=[];var systemDelete;var html=gettextCatalog.getPlural(count,_UNREGISTER_TEXT_SINGLE,_UNREGISTER_TEXT_MULTI,{count:count});return sweetAlert({html:html}).then(function(){for(var i=0;i-1;});// split array into two columns for displaying rows evenly in view return[systemData.slice(0,Math.ceil(systemData.length/2)),systemData.slice(Math.ceil(systemData.length/2),systemData.length)];};function getHardwarePlatform(metadata){var systemInfoProperties=['system_information.product_name','system_information.version','system_information.family','system_information.manufacturer'];var i=0;var value=get(metadata,systemInfoProperties[systemInfoProperties.length-1]);// intentionally skips over last element // since the value is set to it by default // if none of the other elements have whitespace for(i=0;i=0){value=get(metadata,systemInfoProperties[i]);break;}}return value;}systemsService.populateNewestSystems=function(product){var parameters={page_size:5,page:0,sort_by:'created_at',sort_dir:'DESC'};if(product&&product!=='all'){parameters.product_code=product;}assign(parameters,Group.queryParam());return System.getSystemsLatest(parameters).success(function(response){_newestSystems=response.resources;});};systemsService.getNewestSystems=function(){return _newestSystems;};return systemsService;}servicesModule.factory('SystemsService',SystemsService);},{"./":193,"lodash/assign":479,"lodash/find":491,"lodash/get":496,"lodash/remove":538}],211:[function(require,module,exports){'use strict';TemplateService.$inject=['$compile','$templateCache','$q','$timeout','$rootScope'];var servicesModule=require('./');var assign=require('lodash/assign');/** * Utilities for using angular for rendering / interpolating templates. * * @ngInject */function TemplateService($compile,$templateCache,$q,$timeout,$rootScope){var service={};service.renderTemplate=function(id,ctx){var scope=$rootScope.$new(true);assign(scope,ctx);var compiler=$compile($templateCache.get(id));var element=compiler(scope);var def=$q.defer();// rendering is not done until the end of the current digest cycle $timeout(function(){def.resolve(element.html());},0);return def.promise;};return service;}servicesModule.service('TemplateService',TemplateService);},{"./":193,"lodash/assign":479}],212:[function(require,module,exports){'use strict';TitleService.$inject=['InsightsConfig'];var servicesModule=require('./');/** * @ngInject */function TitleService(InsightsConfig){return{set:function set(newTitle){var defaultTitle=InsightsConfig.title;if(newTitle){document.title=newTitle+' | '+defaultTitle;}else{document.title=defaultTitle;}}};}servicesModule.service('TitleService',TitleService);},{"./":193}],213:[function(require,module,exports){'use strict';var servicesModule=require('./');var sumBy=require('lodash/sumBy');var remove=require('lodash/remove');/** * @ngInject */function TopbarAlertsService(){var service={items:[],unackedCount:0};function updateCounts(){service.unackedCount=sumBy(service.items,function(i){return i.acked?0:1;});}service.push=function(item){service.items.push(item);updateCounts();};service.removeAll=function(type){remove(service.items,{type:type});updateCounts();};return service;}servicesModule.service('TopbarAlertsService',TopbarAlertsService);},{"./":193,"lodash/remove":538,"lodash/sumBy":544}],214:[function(require,module,exports){'use strict';TopicService.$inject=['Topic'];var servicesModule=require('./');/** * @ngInject */function TopicService(Topic){var service={};var limit=14;service.topics=[];service.reload=function(product){var p=product&&product!=='all'?{product:product}:null;return Topic.getAll(p,limit).then(function(resp){service.topics=resp.data;});};service.init=function(){return service.reload();};return service;}servicesModule.service('TopicService',TopicService);},{"./":193}],215:[function(require,module,exports){/*global window, document, angular, require*/'use strict';Utils.$inject=['$filter','$rootScope','Events'];var servicesModule=require('./');var forOwn=require('lodash/forOwn');var upperFirst=require('lodash/upperFirst');var groupBy=require('lodash/groupBy');var values=require('lodash/values');var get=require('lodash/get');function Pager(perPage){this.perPage=perPage||15;this.reset();}Pager.prototype.update=function(){this.offset=(this.currentPage-1)*this.perPage;};Pager.prototype.reset=function(){this.currentPage=1;this.update();};function Utils($filter,$rootScope,Events){var utils={};utils.generateAccessors=function(pub,vars){// console.time('generateAccessors'); forOwn(vars,function(ignore,key){var cap=upperFirst(key);var getter='get'+cap;var setter='set'+cap;pub[getter]=function(){// for debugging dont delete // console.log({ func: getter, ret: vars[key] }); return vars[key];};pub[setter]=function(input){// for debugging dont delete // console.log({ func: setter, arg: input }); vars[key]=input;};// console.timeEnd('generateAccessors'); });};utils.selectBetween=function(target,source,items){function getDataId(element){return element.getAttribute('data-id');}target=utils.findFirstAncestor(target,getDataId);source=utils.findFirstAncestor(source,getDataId);var parent=target.parentElement;var children=Array.prototype.slice.call(angular.element(parent).children());var start=children.indexOf(target);var end=children.indexOf(source);angular.forEach(children.slice(Math.min(start,end),Math.max(start,end)+1),function(e){var id=e.getAttribute('data-id');items[id]=true;});};utils.escapeHtml=function(str){var div=document.createElement('div');div.appendChild(document.createTextNode(str));return div.innerHTML;};utils.resetChecked=function(checkboxes){return function(){checkboxes.checked=false;checkboxes.items={};};};utils.checkboxChecked=function(value,data,items,idProperty){idProperty=idProperty||'system_id';angular.forEach(data,function(element){if(angular.isDefined(element[idProperty])){items[element[idProperty]]=value;}});};utils.itemsChanged=function(data,items,idProperty){idProperty=idProperty||'system_id';if(!data||!data.length){return{checked:false,totalChecked:0,indeterminate:false};}var checked=0;var unchecked=0;var total=data.length;angular.forEach(data,function(element){checked+=items[element[idProperty]]||0;unchecked+=!items[element[idProperty]]||0;});var result={totalChecked:checked,indeterminate:checked!==0&&unchecked!==0};if(unchecked===0||checked===0){result.checked=checked===total;}return result;};utils.rowClick=function(checkboxes){return function($event,id){var target=$event.target||$event.srcElement;if(target.tagName==='A'){return;}if($event.shiftKey&&$event.type==='mousedown'){// Prevents selection of rows on shift+click $event.preventDefault();return false;}if((target.tagName==='TD'||target.tagName==='SPAN')&&$event.type==='click'){checkboxes.items[id]=!checkboxes.items[id];}target=$event.currentTarget;if(!checkboxes.lastClicked){checkboxes.lastClicked=target;return;}if($event.shiftKey){utils.selectBetween(target,checkboxes.lastClicked,checkboxes.items);}checkboxes.lastClicked=target;};};utils.findFirstAncestor=function(element,predicate){while(!predicate(element)&&element){element=element.parentElement;}return element;};utils.isBeta=function(){return window.insightsGlobal&&window.insightsGlobal.isBeta;};function wrap(array,replaceWith,wrapAs){var item;if(!array.length||!(replaceWith in array[0])||array[0][replaceWith]===null){return{};}item=array[0][replaceWith];item[wrapAs]=array;return item;}utils.groupByObject=function(array,iteratee,object,wrapAs){//groupBy(array, iteratee): creates a map of system_id to it's reports // - this could be done with GET /systems?report_count=gt0 //values(groupBy(array, iteratee)): creates an array of the reports //wrap(subArray, object, wrapAs): creates a system object with a list of actions. // This could be done with GET /systems/:id/reports return values(groupBy(array,iteratee)).map(function(subArray){return wrap(subArray,object,wrapAs);});};/* * Assign the value of the source property of an object to the target property. */utils.alias=function(source,target){return function(item){if(angular.isObject(item)&&source in item){item[target]=item[source];}};};utils.watchOnce=function(scope,expression,fn){var handle=scope.$watch(expression,function(){fn.apply(null,arguments);handle();});};utils.Pager=Pager;utils.Checkboxes=function(idProperty){var self=this;this.idProperty=idProperty||'id';this.reset=utils.resetChecked(this);this.reset();this.rowClick=utils.rowClick(this);$rootScope.$on(Events.checkboxes.reset,function(){self.reset();});};utils.Checkboxes.prototype.checkboxChecked=function(value,data){utils.checkboxChecked(value,data,this.items,this.idProperty);};utils.Checkboxes.prototype.update=function(data){var result=utils.itemsChanged(data,this.items,this.idProperty);this.totalChecked=result.totalChecked;this.indeterminate=result.indeterminate;if(angular.isDefined(result.checked)){this.checked=result.checked;}};utils.Checkboxes.prototype.isChecked=function(id){return id in this.items&&this.items[id];};utils.Checkboxes.prototype.getSelected=function(data){var self=this;return data.filter(function(item){return self.isChecked(item[self.idProperty]);});};utils.Sorter=function(implicitOrder,cb){if(implicitOrder){this.predicate=implicitOrder.predicate;this.reverse=implicitOrder.reverse;}else{this.predicate='';this.reverse=false;}this.cb=cb;};utils.Sorter.prototype.sort=function(name){this.predicate=name;this.reverse=!this.reverse;if(this.cb){this.cb();}};utils.Sorter.prototype.getSortClass=function(name){return $filter('sortClass')(this.predicate,name,this.reverse);};/* * Holds state for a "loading" spinner */utils.Loader=function(deflectWhenLoading){this.loading=false;if(angular.isDefined(deflectWhenLoading)){this.deflectWhenLoading=deflectWhenLoading;}else{this.deflectWhenLoading=true;}};utils.Loader.prototype.bind=function(fn,thisArg){var self=this;var value;function done(value){self.loading=false;return value;}return function(){if(self.deflectWhenLoading&&self.loading){return;}self.loading=true;value=fn.apply(thisArg,arguments);if(angular.isDefined(value)&&angular.isDefined(value.then)){return value.then(done,done);}done();return value;};};utils.getNextQueryPrependChar=function(url){var prependChar='?';if(url.indexOf('?')>-1){prependChar='&';}return prependChar;};utils.addQueryToUrl=function(url,query){var key;var prependChar;for(key in query){prependChar=this.getNextQueryPrependChar(url);url+=prependChar+key+'='+query[key];}return url;};utils.getSystemDisplayName=function(system){var name=false;if(system){if(system.display_name){name=system.display_name;}else if(system.hostname){name=system.hostname;}else{name='UUID: '+system.system_id;}}// stash the display name for filtration! system.displayName=name;return name;};/* * Lodash' get() that also defaults on null */utils.get=function(object,propName,defaultValue){var value=get(object,propName,defaultValue);if(value===null&&angular.isDefined(defaultValue)){return defaultValue;}return value;};utils.datesEqual=function(d1,d2){if(d1===d2){return true;}return new Date(d1).getTime()===new Date(d2).getTime();};return utils;}servicesModule.service('Utils',Utils);},{"./":193,"lodash/forOwn":495,"lodash/get":496,"lodash/groupBy":497,"lodash/upperFirst":553,"lodash/values":554}],216:[function(require,module,exports){'use strict';ActionsCtrl.$inject=['$q','$rootScope','$scope','$stateParams','$state','ActionbarService','ActionsBreadcrumbs','Categories','Export','FilterService','IncidentsService','InsightsConfig','PreferenceService','QuickFilters','RhaTelemetryActionsService','Stats','System','SystemsService','Topic','TopicService'];var statesModule=require('../');/** * @ngInject */function ActionsCtrl($q,$rootScope,$scope,$stateParams,$state,ActionbarService,ActionsBreadcrumbs,Categories,Export,FilterService,IncidentsService,InsightsConfig,PreferenceService,QuickFilters,RhaTelemetryActionsService,Stats,System,SystemsService,Topic,TopicService){var params=$state.params;$scope.loading=true;$scope.stats={};$scope.show={extraTopics:false};FilterService.parseBrowserQueryParams();FilterService.setShowFilters(false);FilterService.setSearchTerm('');RhaTelemetryActionsService.setDataLoaded(false);$state.transitionTo('app.actions',FilterService.updateParams(params),{notify:false});$scope.QuickFilters=QuickFilters;$scope.getSelectedProduct=FilterService.getSelectedProduct;var reload=function reload(){var promises=[];$scope.loading=true;RhaTelemetryActionsService.reload();TopicService.reload(FilterService.getSelectedProduct()).then(function(){var topics=TopicService.topics.filter(function(topic){return topic.ruleBinding!=='implicit';});$scope.featuredTopics=topics.slice(0,3);$scope.extraTopics=topics.slice(3);var product=void 0;if(FilterService.getSelectedProduct()!=='all'){product=FilterService.getSelectedProduct();}promises.push(loadStats());promises.push(IncidentsService.loadIncidents());$q.all(promises).finally(function(){$scope.incidentCount=IncidentsService.incidentRulesWithHitsCount;$scope.incidentSystemCount=IncidentsService.affectedSystemCount;$scope.loading=false;});});};$scope.$on('filterService:doFilter',reload);$scope.$on('group:change',reload);RhaTelemetryActionsService.setInitialSeverity($stateParams.initialSeverity);RhaTelemetryActionsService.setCategory($stateParams.category);RhaTelemetryActionsService.setRule(null);ActionsBreadcrumbs.init($stateParams);var getData=function getData(){System.getProductSpecificData().then(function(){RhaTelemetryActionsService.populateData();});reload();};if(InsightsConfig.authenticate&&!PreferenceService.get('loaded')){$rootScope.$on('user:loaded',getData);}else{getData();}function loadStats(){var product=FilterService.getSelectedProduct();var promises=[];if(product==='all'){product=undefined;}promises.push(Stats.getRules({product:product}).then(function(res){$scope.stats.rules=res.data;setCategoryTopics(res.data);}));promises.push(Stats.getSystems({product:product,minSeverity:'CRITICAL'}).then(function(res){$scope.stats.systems=res.data;}));return $q.all(promises);}/** * adds non-empty categories to $scope.categoryTopics */function setCategoryTopics(rules){var categoryCount=void 0;$scope.categoryTopics=[];Categories.forEach(function(category){categoryCount=rules[category];if(categoryCount!==undefined&&categoryCount>0&&category!=='all'){$scope.categoryTopics.push({id:category,count:categoryCount});}});}if(InsightsConfig.allowExport){ActionbarService.addExportAction(function(){Export.getReports();});}}statesModule.controller('ActionsCtrl',ActionsCtrl);},{"../":227}],217:[function(require,module,exports){'use strict';AnalyticsCtrl.$inject=['Analytic'];var statesModule=require('../');var c3=require('c3');/** * @ngInject */function AnalyticsCtrl(Analytic){var chart=c3.generate({data:{x:'x',xFormat:'%Y-%m-%dT%H:%M:%S.%LZ',columns:[]},axis:{x:{type:'timeseries',tick:{format:'%Y-%m-%d'}}}});Analytic.reports().success(function(data){var dates=data.map(function(d){return d.date;});var totals=data.map(function(d){return d.total;});totals.unshift('Totals');dates.unshift('x');chart.load({columns:[dates,totals]});});}statesModule.controller('AnalyticsCtrl',AnalyticsCtrl);},{"../":227,"c3":262}],218:[function(require,module,exports){'use strict';NewAnnouncementCtrl.$inject=['$scope','$rootScope','$state','Announcement'];EditAnnouncementCtrl.$inject=['$scope','$rootScope','$state','$stateParams','Announcement','AnnouncementService'];ViewAnnouncementCtrl.$inject=['$scope','$stateParams','Announcement'];ListAnnouncementCtrl.$inject=['$scope','$location','$state','$stateParams','$timeout','Announcement','AnnouncementService','User','PermalinkService','PermissionService'];var statesModule=require('../');var params={showAll:true};/** * @ngInject */function ListAnnouncementCtrl($scope,$location,$state,$stateParams,$timeout,Announcement,AnnouncementService,User,PermalinkService,PermissionService){$scope.params=params;AnnouncementService.init($scope.params);$scope.announcements=AnnouncementService.announcements;$scope.canCreate=false;$scope.permalink=PermalinkService.make;if($stateParams.announcementId){$scope.announcementId=$stateParams.announcementId.toString();}$scope.reload=function(){AnnouncementService.reload($scope.params);};User.asyncCurrent(function(user){$scope.canCreate=user&&PermissionService.has(user,PermissionService.PERMS.CREATE_ANNOUNCEMENT);});$scope.$on('reload:data',$scope.reload);}/** * @ngInject */function ViewAnnouncementCtrl($scope,$stateParams,Announcement){$scope.loadingAnnouncement=true;Announcement.bySlug($stateParams.slug).then(function(announcement){$scope.announcement=announcement.data;$scope.loadingAnnouncement=false;});}/** * @ngInject */function EditAnnouncementCtrl($scope,$rootScope,$state,$stateParams,Announcement,AnnouncementService){$scope.loadingAnnouncement=true;AnnouncementService.init();Announcement.byId($stateParams.id).then(function(announcement){$scope.announcement=announcement.data;$scope.loadingAnnouncement=false;});$scope.save=function(){AnnouncementService.updateAnnouncement($scope.announcement).success(function(){$rootScope.$broadcast('announcement:changed');$state.go('app.announcements');}).error(function(data){$scope.announcementError=data;});};}/** * @ngInject */function NewAnnouncementCtrl($scope,$rootScope,$state,Announcement){$scope.announcement={hidden:false,start:new Date().toISOString()};$scope.create=function(){Announcement.createAnnouncement($scope.announcement).success(function(){$rootScope.$broadcast('announcement:changed');$state.go('app.announcements');}).error(function(data){$scope.announcementError=data;});};}statesModule.controller('ListAnnouncementCtrl',ListAnnouncementCtrl);statesModule.controller('ViewAnnouncementCtrl',ViewAnnouncementCtrl);statesModule.controller('EditAnnouncementCtrl',EditAnnouncementCtrl);statesModule.controller('NewAnnouncementCtrl',NewAnnouncementCtrl);},{"../":227}],219:[function(require,module,exports){'use strict';AppCtrl.$inject=['$scope','$rootScope','User','PermissionService'];var statesModule=require('../');/** * @ngInject */function AppCtrl($scope,$rootScope,User,PermissionService){User.asyncCurrent(function(user){$rootScope.isContentManager=PermissionService.has(user,PermissionService.PERMS.CONTENT_MANAGER);});$scope.toggleFullScreen=function(){if(window&&window.jQuery){window.jQuery('html').toggleClass('fullscreen');}};}statesModule.controller('AppCtrl',AppCtrl);},{"../":227}],220:[function(require,module,exports){/*global require*/'use strict';ConfigCtrl.$inject=['$scope','$state','$stateParams','User','PermissionService','$rootScope'];var statesModule=require('../');/** * @ngInject */function ConfigCtrl($scope,$state,$stateParams,User,PermissionService,$rootScope){$scope.current={hidden:true};$scope.isBeta=$rootScope.isBeta;User.asyncCurrent(function(user){$scope.user=user;});if($stateParams.tab){$scope.current.hidden=false;$scope.current[$stateParams.tab]=true;}$scope.tabClick=function(tab){$state.go('app.config',{tab:tab},{notify:false});};$scope.perms=PermissionService;}statesModule.controller('ConfigCtrl',ConfigCtrl);},{"../":227}],221:[function(require,module,exports){'use strict';WebhookEditCtrl.$inject=['$scope','$state','$stateParams','gettextCatalog','Severities','Utils','Webhooks'];var statesModule=require('../');var pickBy=require('lodash/pickBy');var keyBy=require('lodash/keyBy');var map=require('lodash/map');var mapValues=require('lodash/mapValues');var get=require('lodash/get');var isEmpty=require('lodash/isEmpty');var find=require('lodash/find');var moment=require('moment-timezone');var REPORT_NEW='report:new';var REPORT_RESOLVED='report:resolved';/** * @ngInject */function WebhookEditCtrl($scope,$state,$stateParams,gettextCatalog,Severities,Utils,Webhooks){$scope.severities=Severities.filter(function(item){return item.value!=='All';}).reverse();$scope.eventTypes=[{name:REPORT_NEW,description:gettextCatalog.getString('New report identified')},{name:REPORT_RESOLVED,description:gettextCatalog.getString('Report resolved')},{name:'system:registered',description:gettextCatalog.getString('New system registered')},{name:'system:unregistered',description:gettextCatalog.getString('System unregistered')}];$scope.loader=new Utils.Loader(false);$scope.selected={events:{},severityFilters:{},certificateExpanded:false};$scope.errors={};$scope.certificateExpanded=false;function isNew(){return $stateParams.id==='new';}// these are not general-purpose util functions but rather controller-specific // utils used repeatedly in this form function arrayToObject(array,iteratee){return mapValues(keyBy(array,iteratee),function(){return true;});}function objectToArray(object,fn){return map(pickBy(object),fn);}function exportEventTypes(){if($scope.webhook.firehose){return[];}return objectToArray($scope.selected.events,function(value,name){var filters=null;if((name===REPORT_NEW||name===REPORT_RESOLVED)&&!isEmpty($scope.selected.severityFilters)){var severity=objectToArray($scope.selected.severityFilters,function(value,severity){return severity;});if(severity.length){filters={severity:severity};}}return{name:name,filters:filters};});}function importEventTypes(webhook){$scope.selected.events=arrayToObject(webhook.event_types||[],'name');var reportNew=find(webhook.event_types,function(type){return type.name===REPORT_NEW||type.name===REPORT_RESOLVED;});if(reportNew&&reportNew.filters&&reportNew.filters.severity){$scope.selected.severityFilters=arrayToObject(reportNew.filters.severity);}else{$scope.selected.severityFilters=arrayToObject($scope.severities,'value');}}$scope.save=function(){$scope.webhook.event_types=exportEventTypes();if($scope.webhook.certificate&&$scope.webhook.certificate.trim().length){$scope.webhook.certificate=$scope.webhook.certificate.trim();}else{$scope.webhook.certificate=null;}var fn=isNew()?'create':'update';return Webhooks[fn]($scope.webhook).success(function(){return $state.go('app.config',{tab:'webhooks'});}).catch(function(e){if(get(e,'status')===400){var errorKey=get(e,'data.error.key');switch(errorKey){case'INVALID_WEBHOOK_URL':$scope.errors.url=true;break;case'INVALID_WEBHOOK_CERT':$scope.errors.certificate=true;break;}}});};$scope.loader.bind(function(){if(isNew()){$scope.webhook={active:true,firehose:true};$scope.selected.severityFilters=arrayToObject($scope.severities,'value');$scope.selected.events=arrayToObject($scope.eventTypes,'name');}else{return Webhooks.get($stateParams.id).success(function(webhook){$scope.webhook=webhook;importEventTypes(webhook);}).catch(function(){return $state.go('app.config',{tab:'webhooks'});});}})();$scope.certDateClass=function(date){return moment().diff(date)<0?'green':'red';};}statesModule.controller('WebhookEditCtrl',WebhookEditCtrl);},{"../":227,"lodash/find":491,"lodash/get":496,"lodash/isEmpty":508,"lodash/keyBy":518,"lodash/map":522,"lodash/mapValues":523,"lodash/pickBy":534,"moment-timezone":557}],222:[function(require,module,exports){/*global angular, require*/'use strict';DigestsCtrl.$inject=['$scope','$http','DigestService','System','Rule','AccountService','InventoryService','Severities','InsightsConfig','User'];var statesModule=require('../');var takeRight=require('lodash/takeRight');var last=require('lodash/last');var sortBy=require('lodash/sortBy');var filter=require('lodash/filter');var sum=require('lodash/sum');var URI=require('urijs');var TIME_PERIOD=30;/** * @ngInject */function DigestsCtrl($scope,$http,DigestService,System,Rule,AccountService,InventoryService,Severities,InsightsConfig,User){var digestPromise=DigestService.digestsByType('eval');var systemPromise=System.getSystems();var rulePromise=Rule.getRulesLatest();function getDirection(data){var direction=data[1]-data[0];if(direction>0){return 1;}if(direction<0){return-1;}return 0;}function justLineGraph(digestBase,dataType,name,color){return{data:[{x:takeRight(digestBase.timeseries,TIME_PERIOD),y:takeRight(digestBase[dataType],TIME_PERIOD),mode:'lines',name:name,line:{width:3,color:color}}],layout:{margin:{t:10,l:40,r:10,b:40},showlegend:true,legend:{orientation:'h'}},options:{displayModeBar:false}};}function getHitsPerCat(label,lineBase,icon){return{current:last(lineBase),direction:getDirection(takeRight(lineBase,2)),icon:icon,label:label};}function getTenWorst(items){items=takeRight(sortBy(items,function(i){return i.report_count;}),10);// throw out 0 hit items items=filter(items,function(i){return i.report_count>0;});items.reverse();return items;}function ruleAppendixSorting(rules){rules=sortBy(rules,function(r){// this is gross but sortBy seems to sort the numbers // alphabetically instead of numerically when dual-sorting, // so pad with zeroes // of course, this breaks if a rule has 10 billion hits var paddedcount='0000000000'+r.report_count;paddedcount=paddedcount.substr((''+r.report_count).length);return[Severities.map(function(severity){return severity.value;}).indexOf(r.severity),paddedcount];}).filter(function(r){return r.report_count>0;});rules.reverse();return rules;}$scope.checkboxValues={availability:true,security:true,stability:true,performance:true};function calculateResolvedIssues(digest){return sum(takeRight(digest.resolved,TIME_PERIOD));}$scope.timePeriod=function(){return TIME_PERIOD;};$scope.dateFormat=function(dateString){var date=new Date(dateString);return date.getMonth()+'-'+date.getDay()+'-'+date.getFullYear();};$scope.loading=true;$scope.showSystem=InventoryService.showSystemModal;Promise.all([digestPromise,systemPromise,rulePromise]).then(function(responses){var res=responses[0];var sysres=responses[1];var ruleres=responses[2];var digestBase=res.data.resources[0].data.data;function getDigestMetricsLine(category,lineBase,color){// warning this function needs to have access to digestBase.timeseries return{x:takeRight(digestBase.timeseries,TIME_PERIOD),y:takeRight(lineBase,TIME_PERIOD),type:'scatter',name:category,marker:{color:color}};}$scope.downloading=false;$scope.downloadPdf=function(){var digestId=res.data.resources[0].digest_id;var uri=URI(InsightsConfig.apiRoot).segment('digests').segment(digestId.toString());if(User.current.account_number!==AccountService.number()){uri.query(AccountService.current());}$scope.downloading=true;$http.get(uri.toString(),{responseType:'arraybuffer',headers:{Accept:'application/pdf'}}).then(function(response){var file=new Blob([response.data],{type:'application/pdf'});var url=window.URL||window.webkitURL;var isChrome=!!window.chrome&&!!window.chrome.webstore;if(isChrome){var downloadLink=angular.element('');downloadLink.attr('href',url.createObjectURL(file));downloadLink.attr('target','_self');downloadLink.attr('download','insights-executive-report.pdf');downloadLink[0].click();}else{var fileURL=URL.createObjectURL(file);window.open(fileURL);}$scope.downloading=false;},function(err){$scope.downloading=false;$scope.digestError='PDF Download Failed. '+err.status;});};$scope.latest_score=takeRight(digestBase.scores,1)[0];window.scope=$scope;$scope.score_difference=$scope.latest_score-digestBase.scores[digestBase.scores.length-2];// max score is 850, min is 250. Levels calculated by // separating the 600 range into 4 segments and dividing // score by segment length (150) var level=Math.ceil(($scope.latest_score-250)/150)||1;var scoreDiv=angular.element(document.querySelector('.gauge.gauge-circle.score'));scoreDiv.addClass('score_lvl'+level);// current counts by category $scope.digest_hits_per_cat=[getHitsPerCat('Security',digestBase.security,'fa-shield'),getHitsPerCat('Availability',digestBase.availability,'fa-hand-paper-o'),getHitsPerCat('Stability',digestBase.stability,'fa-cubes'),getHitsPerCat('Performance',digestBase.performance,'fa-tachometer')];// metrics // big graphic // systems not checking in // total system actions $scope.digest_metrics_data=[{x:takeRight(digestBase.timeseries,TIME_PERIOD),y:takeRight(digestBase.distinct_rules,TIME_PERIOD),type:'bar',name:'Total Actions',marker:{color:'#c7e7f6'}},getDigestMetricsLine('Security',digestBase.security,'#e77baf'),getDigestMetricsLine('Availability',digestBase.availability,'#f3923e'),getDigestMetricsLine('Stability',digestBase.stability,'#3badde'),getDigestMetricsLine('Performance',digestBase.performance,'#a5d786')];$scope.digest_metrics={data:angular.copy($scope.digest_metrics_data),layout:{margin:{t:10,l:40,r:10,b:40},showlegend:true,legend:{orientation:'h'}},options:{displayModeBar:false}};$scope.digest_registered=justLineGraph(digestBase,'checkins_per_day','Active Systems','#97cde6');$scope.digest_score=justLineGraph(digestBase,'scores','Score','#3083FB');$scope.systemsTilTen=10-sysres.data.resources.length;$scope.topTenWorstSystems=getTenWorst(sysres.data.resources);$scope.topTenRules=getTenWorst(ruleres.data.resources);$scope.allRuleHits=ruleAppendixSorting(ruleres.data.resources);$scope.resolvedIssues=calculateResolvedIssues(digestBase);$scope.loading=false;});}statesModule.controller('DigestsCtrl',DigestsCtrl);},{"../":227,"lodash/filter":490,"lodash/last":521,"lodash/sortBy":540,"lodash/sum":543,"lodash/takeRight":545,"urijs":826}],223:[function(require,module,exports){'use strict';EvaluationCtrl.$inject=['$scope','$location','$stateParams','Evaluation'];var statesModule=require('../');var URI=require('urijs');/** * @ngInject */function EvaluationCtrl($scope,$location,$stateParams,Evaluation){$scope.loading=true;$scope.successfulActivation=false;$scope.cancel=cancel;$scope.getExpiration=getExpiration;$scope.getNonLegacyEval=getNonLegacyEval;$scope.getLegacyEval=getLegacyEval;$scope.activateLegacy=activateLegacy;$scope.activateNonLegacy=activateNonLegacy;init();function init(){Evaluation.getEvaluationStatus().then(function(status){var originalDestination=$stateParams.originalPath;if(originalDestination){//If we were coerced onto this state if(status.purchased||status.current){var uri=URI(originalDestination);uri.segment(uri.segment().slice(1));$location.url(uri.pathname()+uri.search());return;}}$scope.loading=false;$scope.status=status;}).catch(function(err){if(err.status===401){//The auth interceptor will handle the redirect return;}$scope.loading=false;$scope.error='Could not load evaluation status.';});}function activateNonLegacy(){activate(getNonLegacyEval().type);}function activateLegacy(){activate(getLegacyEval().type);}function activate(type){$scope.loading=true;Evaluation.activate(type).then(function(){return Evaluation.getEvaluationStatus().then(function(status){$scope.loading=false;$scope.successfulActivation=true;$scope.status=status;});}).catch(function(){$scope.loading=false;$scope.error='Could not activate Insights.';});}function cancel(){window.location='https://access.redhat.com/';}function getNonLegacyEval(){var salesEval=$scope.status.available.find(function(evaluation){return evaluation.type.startsWith('sales');});if(salesEval){return salesEval;}return $scope.status.available.find(function(evaluation){return evaluation.type==='self';});}function getLegacyEval(){return $scope.status.available.find(function(evaluation){return evaluation.type==='legacy';});}function getExpiration(){var activatedEvaluation=$scope.status.current;var activationDate=new Date(Date.parse(activatedEvaluation.created_at));activationDate.setDate(activationDate.getDate()+activatedEvaluation.duration);return activationDate.toDateString();}}statesModule.controller('EvaluationCtrl',EvaluationCtrl);},{"../":227,"urijs":826}],224:[function(require,module,exports){'use strict';GettingStartedCtrl.$inject=['$scope','GettingStarted','sections'];var statesModule=require('../');/** * @ngInject */function GettingStartedCtrl($scope,GettingStarted,sections){GettingStarted.setSections(sections);}statesModule.controller('GettingStartedCtrl',GettingStartedCtrl);},{"../":227}],225:[function(require,module,exports){'use strict';GettingStartedRootCtrl.$inject=['$scope','PermalinkService','GettingStarted'];var statesModule=require('../');/** * @ngInject */function GettingStartedRootCtrl($scope,PermalinkService,GettingStarted){$scope.sections=GettingStarted.sections;PermalinkService.scroll();}statesModule.controller('GettingStartedRootCtrl',GettingStartedRootCtrl);},{"../":227}],226:[function(require,module,exports){/*global require*/'use strict';ComponentsCtrl.$inject=['$scope','$mdDialog','$mdToast'];var statesModule=require('../../');function ComponentsCtrl($scope,$mdDialog,$mdToast){$scope.status=' ';$scope.customFullscreen=false;$scope.showToast=function(h,v,state){$mdToast.show($mdToast.simple().content('Toast Content!').hideDelay('3000').position(h+' '+v).toastClass('stickyToast'+' '+state));};$scope.showAlert=function(ev){$mdDialog.show($mdDialog.alert().parent(angular.element(document.querySelector('#popupContainer'))).clickOutsideToClose(true).title('This is the title').textContent('This is the content').ariaLabel('Alert Dialog Demo').ok('Got it!').targetEvent(ev));};$scope.showConfirm=function(ev){$mdDialog.show($mdDialog.confirm().parent(angular.element(document.querySelector('#popupContainer'))).clickOutsideToClose(true).title('This is a title').textContent('This is the content').ariaLabel('Comfirm Dialog Demo').ok('Confirm').cancel('Cancel').targetEvent(ev));};$scope.onSwipeUp=function(){window.alert('You swiped up!!');};}statesModule.controller('ComponentsCtrl',ComponentsCtrl);},{"../../":227}],227:[function(require,module,exports){'use strict';module.exports=angular.module('insights.states',[]);(function(){var f=require("./index.js");f["actions"]={"actions.controller":require("./actions/actions.controller.js")};f["analytics"]={"analytics.controller":require("./analytics/analytics.controller.js")};f["announcements"]={"announcements.controller":require("./announcements/announcements.controller.js")};f["base"]={"app.controller":require("./base/app.controller.js")};f["config"]={"config.controller":require("./config/config.controller.js"),"webhook-edit.controller":require("./config/webhook-edit.controller.js")};f["digests"]={"digests.controller":require("./digests/digests.controller.js")};f["evaluation"]={"evaluation.controller":require("./evaluation/evaluation.controller.js")};f["getting-started"]={"getting-started.controller":require("./getting-started/getting-started.controller.js"),"getting-started.root.controller":require("./getting-started/getting-started.root.controller.js")};f["hidden"]={"components":{"components.controller":require("./hidden/components/components.controller.js")}};f["index"]=require("./index.js");f["info"]={"info.controller":require("./info/info.controller.js")};f["initial"]={"initial.controller":require("./initial/initial.controller.js")};f["inventory"]={"inventory.controller":require("./inventory/inventory.controller.js")};f["invites"]={"invites.controller":require("./invites/invites.controller.js")};f["login"]={"login.controller":require("./login/login.controller.js")};f["maintenance"]={"maintenance.controller":require("./maintenance/maintenance.controller.js")};f["overview"]={"overview.controller":require("./overview/overview.controller.js")};f["rules"]={"list.rules.controller":require("./rules/list.rules.controller.js")};f["security"]={"security.controller":require("./security/security.controller.js")};f["splash"]={"splash.controller":require("./splash/splash.controller.js")};f["topics"]={"controllers":{"edit.topics.controller":require("./topics/controllers/edit.topics.controller.js"),"list.topics.controller":require("./topics/controllers/list.topics.controller.js"),"topic.admin.controller":require("./topics/controllers/topic.admin.controller.js")}};return f;})();},{"./actions/actions.controller.js":216,"./analytics/analytics.controller.js":217,"./announcements/announcements.controller.js":218,"./base/app.controller.js":219,"./config/config.controller.js":220,"./config/webhook-edit.controller.js":221,"./digests/digests.controller.js":222,"./evaluation/evaluation.controller.js":223,"./getting-started/getting-started.controller.js":224,"./getting-started/getting-started.root.controller.js":225,"./hidden/components/components.controller.js":226,"./index.js":227,"./info/info.controller.js":228,"./initial/initial.controller.js":229,"./inventory/inventory.controller.js":230,"./invites/invites.controller.js":231,"./login/login.controller.js":232,"./maintenance/maintenance.controller.js":233,"./overview/overview.controller.js":234,"./rules/list.rules.controller.js":235,"./security/security.controller.js":236,"./splash/splash.controller.js":237,"./topics/controllers/edit.topics.controller.js":238,"./topics/controllers/list.topics.controller.js":239,"./topics/controllers/topic.admin.controller.js":240}],228:[function(require,module,exports){'use strict';InfoCtrl.$inject=['$scope','InsightsConfig','PermalinkService'];var statesModule=require('../');/** * @ngInject */function InfoCtrl($scope,InsightsConfig,PermalinkService){$scope.gettingStartedLink=InsightsConfig.gettingStartedLink;PermalinkService.scroll();}statesModule.controller('InfoCtrl',InfoCtrl);},{"../":227}],229:[function(require,module,exports){/*global*/'use strict';InitialCtrl.$inject=['Report','$state'];var statesModule=require('../');var priv={};priv.doOverview=function($state){$state.go('app.overview',{},{location:'replace'});};//DEPRECATED priv.doLegacy=function(Report,$state,InsightsConfig,HttpHeaders){var state='app.actions';//var accountNumber = window.sessionStorage.getItem(InsightsConfig.acctKey); // intentionally not waiting for User and AccountService here to speed up // loading of the initial page // worst case the backend will reject the request // if the user tries to mess with the account number Report.headReports().success(function(response){var count=response.headers(HttpHeaders.resourceCount);if(count<=0){// if there are no reports, redirect to systems view instead of actions state='app.inventory';}}).finally(function(){$state.go(state,{},{location:'replace'});});};/** * @ngInject */function InitialCtrl(Report,$state){// if (window.insightsGlobal && window.insightsGlobal.isBeta) { return priv.doOverview($state);// } // return priv.doLegacy(Report, $state, InsightsConfig); }statesModule.controller('InitialCtrl',InitialCtrl);},{"../":227}],230:[function(require,module,exports){/*global require*/'use strict';InventoryCtrl.$inject=['$filter','$location','$modal','$rootScope','$scope','$state','System','InventoryService','MultiButtonService','SystemsService','InsightsConfig','ReloadService','Account','FilterService','QuickFilters','PreferenceService','User','Utils','ListTypeService','ActionbarService','Export','Group'];var statesModule=require('../');var find=require('lodash/find');var pick=require('lodash/pick');var diff=require('lodash/difference');/** * @ngInject */function InventoryCtrl($filter,$location,$modal,$rootScope,$scope,$state,System,InventoryService,MultiButtonService,SystemsService,InsightsConfig,ReloadService,Account,FilterService,QuickFilters,PreferenceService,User,Utils,ListTypeService,ActionbarService,Export,Group){var DEFAULT_PAGE_SIZE=15;$scope.Group=Group;$scope.filter=FilterService;function updateParams(params){params=FilterService.updateParams(params);if(!params.sort_field){params.sort_field=InventoryService.getSortField();}if(!params.sort_dir){params.sort_dir=InventoryService.getSortDirection();}return params;}// remove this when we re-factor table view $scope.getSystemName=Utils.getSystemDisplayName;$scope.showActions=InventoryService.showSystemModal;$scope.listTypes=ListTypeService.types();FilterService.setSearchTerm('');FilterService.setShowFilters(false);var params=$state.params;$state.transitionTo('app.inventory',updateParams(params),{notify:false});$scope.QuickFilters=QuickFilters;$scope.systems=[];$scope.allSystems=null;$scope.errored=false;$scope.actionFilter=null;$scope.loading=InventoryService.loading;$scope.reloading=false;$scope.sorter=new Utils.Sorter(false,order);$scope.sorter.predicate='toString';$scope.allSelected=false;$scope.reallyAllSelected=false;$scope.$on('reload:data',function(){cleanTheScope();initInventory(false);});$scope.$on('group:change',function(){cleanTheScope();getData(false);});$scope.$on('filterService:doFilter',function(){cleanTheScope();getData(false);});$scope.$on('systems:unregistered',function(){cleanTheScope();getData();});var systemModal=null;$scope.isPortal=InsightsConfig.isPortal;function cleanTheScope(){$scope.pager=new Utils.Pager(DEFAULT_PAGE_SIZE);$scope.checkboxes.reset();}function initInventory(){$scope.loading=true;//initialize pager and grab paging params from url $scope.pager=new Utils.Pager(DEFAULT_PAGE_SIZE);$scope.pager.currentPage=$location.search().page?$location.search().page:$scope.pager.currentPage;$scope.pager.perPage=$location.search().pageSize?$location.search().pageSize:$scope.pager.perPage;//get system types SystemsService.getSystemTypesAsync().then(function(){getData(false);});}function buildRequestQueryParams(paginate){var query=FilterService.buildRequestQueryParams();// offline / systems not checking in if(FilterService.getOffline()!==FilterService.getOnline()){query.offline=FilterService.getOffline().toString();}//search term if(FilterService.getSearchTerm()){query.search_term=FilterService.getSearchTerm();}//sort field if(InventoryService.getSortField()){query.sort_by=InventoryService.getSortField();}//sort direction if(InventoryService.getSortDirection()){query.sort_dir=InventoryService.getSortDirection();}//pagination if(paginate){query.page_size=$scope.pager.perPage;// programmatic page starts at 0 while ui page starts at 1 query.page=$scope.pager.currentPage-1;}return query;}function getData(paginate){$scope.loading=true;if(!paginate){$scope.reloading=true;}function getSystemsResponseHandler(response){var systems=[];if(FilterService.getParentNode()){systems=response;}else{systems=response.resources;}$scope.systems=response.resources;InventoryService.setTotal(response.total);SystemsService.systems=$scope.systems;}var query=buildRequestQueryParams(true);var promise=null;if(FilterService.getParentNode()!==null){query.includeSelf=true;promise=System.getSystemLinks(FilterService.getParentNode(),query);}else{promise=System.getSystemsLatest(query);}promise.success(getSystemsResponseHandler).error(function(){$scope.errored=true;}).finally(function(){InventoryService.loading=$scope.loading=$scope.reloading=false;});}$scope.applyActionFilter=function(filter){$scope.actionFilter=filter;};$scope.doScroll=function(){$location.search('page',$scope.pager.currentPage);$location.search('pageSize',$scope.pager.perPage);getData(true);$scope.pager.update();$scope.checkboxes.reset();};function parseBrowserQueryParams(){FilterService.parseBrowserQueryParams();if(params.sort_field){InventoryService.setSortField(params.sort_field);if(params.sort_dir){InventoryService.setSortDirection(params.sort_dir);}}}parseBrowserQueryParams();System.getProductSpecificData();if(InsightsConfig.authenticate&&!PreferenceService.get('loaded')){$rootScope.$on('user:loaded',function(){initInventory();});}else{initInventory();}$scope.getSelectableSystems=function(){return $scope.systems;};$scope.checkboxes=new Utils.Checkboxes('system_id');setTotalSystemsSelected();function updateCheckboxes(){$scope.checkboxes.update($scope.getSelectableSystems());if(visiblySelectedSystemsCount()===$scope.systems.length&&!$scope.loading){$scope.allSelected=true;}else{$scope.allSelected=false;$scope.reallyAllSelected=false;}setTotalSystemsSelected();}function addSystems(newSys,oldSys){if($scope.reallyAllSelected){$scope.checkboxes.checkboxChecked(true,diff(newSys,oldSys));}}$scope.$watchCollection('checkboxes.items',updateCheckboxes);$scope.$watchCollection('systems',addSystems);$scope.canUnregisterSystem=function(system){var canSelect=true;var invalidTypes=[{product_code:'osp',role:'compute'},{product_code:'osp',role:'controller'},{product_code:'osp',role:'director'},{product_code:'docker',role:'image'},{product_code:'docker',role:'container'},{product_code:'rhev',role:'manager'},{product_code:'rhev',role:'hypervisor'}];var systemType=pick(// this is safe as system types are awaited within initInventory() SystemsService.getSystemTypeUnsafe(system.system_type_id),'product_code','role');if(find(invalidTypes,systemType)){canSelect=false;}return canSelect;};function order(){$scope.systems=$filter('orderBy')($scope.systems,['_type',$scope.sorter.reverse?'-'+$scope.sorter.predicate:$scope.sorter.predicate]);}$scope.sort=function(column){$scope.loading=true;$scope.predicate=column;// just changing the sort direction if(column===InventoryService.getSortField()){InventoryService.toggleSortDirection();$scope.reverse=!$scope.reverse;}else{InventoryService.setSortField(column);InventoryService.setSortDirection('ASC');$scope.reverse=false;// special case where we are sorting by timestamp but visually // showing timeago if(column==='last_check_in'){InventoryService.setSortDirection('DESC');}}// if we have the full inventory list then use local sorting if($scope.systems.length===InventoryService.getTotal()){$scope.sorter.predicate=column;$scope.sorter.reverse=$scope.reverse;order();$scope.loading=false;}else{// reset dataset cleanTheScope();getData(false);}};$scope.selectAll=function(){$scope.allSelected=!$scope.allSelected;// disabling the select all checkbox will also deselect everything unseen if($scope.reallyAllSelected){$scope.reallyAllSelected=false;}};$scope.deselectAll=function(){$scope.reallyAllSelected=false;$scope.allSelected=false;$scope.checkboxes.reset();};$scope.reallySelectAll=function(){function getAllSystems(){// For when ALL are selected, not just all visible // condensed version of 'getData()' var query=buildRequestQueryParams(false);var promise=null;if(FilterService.getParentNode()!==null){query.includeSelf=true;promise=System.getSystemLinks(FilterService.getParentNode(),query);}else{promise=System.getSystemsLatest(query);}return promise;}// select ALL, not just all visible if($scope.allSystems===null){// first load of all systems $scope.loading=true;getAllSystems().then(function(res){$scope.allSystems=res.data.resources;$scope.reallyAllSelected=true;$scope.totalSystemsSelected=$scope.allSystems.length;$scope.loading=false;});}else{// already loaded, just set the flag $scope.reallyAllSelected=true;$scope.totalSystemsSelected=$scope.allSystems.length;}};$scope.totalSystems=function(){return InventoryService.getTotal();};function setTotalSystemsSelected(){if($scope.reallyAllSelected){$scope.totalSystemsSelected=InventoryService.getTotal();// remove systems in view that are not checked $scope.totalSystemsSelected-=Object.keys($scope.checkboxes.items).length-visiblySelectedSystemsCount();}else{$scope.totalSystemsSelected=visiblySelectedSystemsCount();}}function visiblySelectedSystemsCount(){var value=$scope.systems.filter(function(system){return $scope.checkboxes.items.hasOwnProperty(system.system_id)&&$scope.checkboxes.items[system.system_id]===true;}).length;return value;}$scope.allSystemsShown=function(){// don't show the 'select all __' prompt // if all systems are already shown return InventoryService.getTotal()===$scope.systems.length;};/** * calls register new system modal */$scope.registerSystem=function(){var systemLimitReached=false;User.asyncCurrent(function(user){systemLimitReached=user.current_entitlements?user.current_entitlements.systemLimitReached:!user.is_internal;if(user.current_entitlements&&user.current_entitlements.unlimitedRHEL){systemLimitReached=false;}});openModal({templateUrl:'js/components/system/addSystemModal/'+(systemLimitReached?'upgradeSubscription.html':'addSystemModal.html'),windowClass:'system-modal ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'AddSystemModalCtrl'});};/** * opens modal if a modal isn't currently opened */function openModal(opts){if(systemModal){return;// Only one modal at a time please }systemModal=$modal.open(opts);systemModal.result.finally(function(){systemModal=null;});}if(InsightsConfig.allowExport){ActionbarService.addExportAction(function(){var stale=void 0;if(FilterService.getOnline()&&!FilterService.getOffline()){stale=false;}if(!FilterService.getOnline()&&FilterService.getOffline()){stale=true;}Export.getSystems(Group.current().id,stale,FilterService.getSearchTerm());});}if(params.machine){var system={system_id:params.machine};$scope.showActions(system,true);}$scope.reloadInventory=function(){cleanTheScope();getData(false);};}statesModule.controller('InventoryCtrl',InventoryCtrl);},{"../":227,"lodash/difference":486,"lodash/find":491,"lodash/pick":533}],231:[function(require,module,exports){'use strict';/*global alert*/InvitesCtrl.$inject=['$scope','EvaluationInvite','User'];var statesModule=require('../');/** * @ngInject */function InvitesCtrl($scope,EvaluationInvite,User){var evaluations=[{type:'sales100',systems:100},{type:'sales250',systems:250},{type:'sales500',systems:500},{type:'sales750',systems:750},{type:'sales1000',systems:1000}];$scope.data={};$scope.evals=evaluations;$scope.data.newInvite=evaluations[0];$scope.loading=true;User.asyncCurrent(function(user){$scope.forbidden=!user.is_internal;$scope.loading=false;});$scope.reset=reset;$scope.createNewInvite=function(){$scope.successfulInvite=null;if(!$scope.data.account){alert('Account Number cannot be blank');return;}var invite={account_number:$scope.data.account,email:$scope.data.email,type:$scope.data.newInvite.type};$scope.loading=true;EvaluationInvite.create(invite).then(function(){$scope.successfulInvite=invite;$scope.data.account=null;$scope.data.email=null;}).catch(function(err){$scope.failedInvite=invite;$scope.error=err;}).finally(function(){$scope.loading=false;});};function reset(){$scope.failedInvite=null;$scope.successfulInvite=null;}}statesModule.controller('InvitesCtrl',InvitesCtrl);},{"../":227}],232:[function(require,module,exports){/*global window*/'use strict';LoginCtrl.$inject=['$state'];var statesModule=require('../');/** * @ngInject */function LoginCtrl($state){if(window.LOGGED_IN){return $state.go('app.initial');}window.location='https://www.redhat.com/wapps/sso/login.html?redirect='+window.encodeURIComponent(window.location.toString());}statesModule.controller('LoginCtrl',LoginCtrl);},{"../":227}],233:[function(require,module,exports){'use strict';MaintenanceCtrl.$inject=['$scope','$timeout','Maintenance','Utils','$stateParams','MaintenanceService','$filter','PreferenceService','PermalinkService','$state','$rootScope','SystemsService','Events'];var statesModule=require('../');var groupBy=require('lodash/groupBy');var sortBy=require('lodash/sortBy');var values=require('lodash/values');var moment=require('moment-timezone');var CATEGORY_PREFERENCE_KEY='maintenance_plan_category';var DEFAULT_CATEGORY='all';// tracks which plans are expanded into the edit mode // This handles the mode activated by the first click on a plan. function EditToggleHandler($state,$stateParams){this.items={};this.$state=$state;this.$stateParams=$stateParams;}EditToggleHandler.prototype.isActive=function(id){return id in this.items;};EditToggleHandler.prototype.toggle=function(id){if(this.isActive(id)){this.deactivate(id);}else{this.activate(id);}};EditToggleHandler.prototype.reset=function(){this.items={};this.transition();};EditToggleHandler.prototype.activate=function(id){this.items={};this.items[id]=true;this.updateUrl();};EditToggleHandler.prototype.deactivate=function(id){delete this.items[id];this.updateUrl();};EditToggleHandler.prototype.updateUrl=function(){var keys=Object.keys(this.items);// if a single plan is open, make it bookmarkable if(keys.length===1){this.transition(keys[0]);}else{this.transition();}};EditToggleHandler.prototype.transition=function(id){var tab=id?this.$stateParams.tab:undefined;this.$state.transitionTo(this.$state.current,{maintenance_id:id,tab:tab},{notify:false,reload:false,location:'replace'});};/** * @ngInject */function MaintenanceCtrl($scope,$timeout,Maintenance,Utils,$stateParams,MaintenanceService,$filter,PreferenceService,PermalinkService,$state,$rootScope,SystemsService,Events){$scope.isDefined=angular.isDefined;$scope.loader=new Utils.Loader();$scope.MaintenanceService=MaintenanceService;// used in calendars for highlighting days on which maintenance is already scheduled $scope.calendarDates=[];$scope.refreshCalendar=function(){$scope.calendarDates=MaintenanceService.plans.all.filter(function(plan){return angular.isDefined(plan.start);}).map(function(plan){return moment(plan.start);});};$scope.plans=MaintenanceService.plans;$scope.edit=new EditToggleHandler($state,$stateParams);$scope.loadPlans=$scope.loader.bind(function(force){return MaintenanceService.plans.load(force).then(function(){if(Object.keys($scope.edit.items).length===1){var id=Object.keys($scope.edit.items)[0];$scope.scrollToPlan(id);}});});$scope.scrollToPlan=function(id){if($scope.category!=='all'){$scope.setCategory('all');}$timeout(function(){$scope.edit.activate(id);$rootScope.$broadcast(Events.planner.openPlan,id);PermalinkService.scroll('maintenance-plan-'+id,50);});};$scope.redrawPlans=function(){var category=$scope.category||DEFAULT_CATEGORY;var plans=MaintenanceService.plans[category];if($scope.category==='future'||$scope.category==='past'){// group by year/month then transform into array of arrays for ordering plans=sortBy(plans,'start');var groupedPlans=values(groupBy(plans,function(plan){var start=moment(plan.start);return start.year()+'-'+start.month();}));if(category==='past'){// show past plans in chronologically descending order groupedPlans.reverse();}$scope.shownPlansByMonth=groupedPlans;}else{plans=sortBy(plans,'maintenance_id');}$scope.shownPlans=plans;};$scope.category=PreferenceService.get(CATEGORY_PREFERENCE_KEY)||DEFAULT_CATEGORY;if($stateParams.maintenance_id){var id=parseInt($stateParams.maintenance_id);if(!isNaN(id)){$scope.edit.activate(id);}}$scope.setCategory=function(value,suppressEditReset){if(value){$scope.category=value;PreferenceService.set(CATEGORY_PREFERENCE_KEY,$scope.category,false);$scope.redrawPlans();if(!suppressEditReset){$scope.edit.reset();}}};$scope.broadcast=function(name){$scope.$broadcast(name);};function addPlan(data){return MaintenanceService.plans.create(data).then(function(plan){$scope.scrollToPlan(plan.maintenance_id);});}$scope.newSuggestion=function(){return addPlan({suggestion:Maintenance.SUGGESTION.PROPOSED,hidden:true});};function init(){$scope.redrawPlans();}$scope.$watchCollection('plans.all',init);$scope.loadPlans(true);SystemsService.getSystemTypesAsync().then(function(systemTypes){$scope.systemTypes=systemTypes;});$rootScope.$on('reload:data',function(){$scope.loader.loading=false;// disable loader throttling for reload $scope.loadPlans(true);});}statesModule.controller('MaintenanceCtrl',MaintenanceCtrl);},{"../":227,"lodash/groupBy":497,"lodash/sortBy":540,"lodash/values":554,"moment-timezone":557}],234:[function(require,module,exports){'use strict';OverviewCtrl.$inject=['$modal','$q','$rootScope','$scope','MaintenanceService','Stats','TrialSku','User'];var statesModule=require('../');/** * @ngInject */function OverviewCtrl($modal,$q,$rootScope,$scope,MaintenanceService,Stats,TrialSku,User){$scope.stats={};$scope.isBeta=$rootScope.isBeta;User.asyncCurrent(function(user){$scope.paid=function(){var ents=user.current_entitlements;var isPaid=false;if(ents){if(ents.whitelist&&(ents.whitelist.rhel||ents.whitelist.osp)){isPaid=true;}else if(ents.unlimitedRHEL){isPaid=true;}// Return true if the user has any SKU that is not the Trial SKU else if(Array.isArray(ents.skus)&&ents.skus.find(function(sku){return sku.skuName!==TrialSku;})!==undefined){isPaid=true;}}return isPaid;};});$scope.edit=function(){$modal.open({templateUrl:'js/components/overview/articleModal/articleModal.html',windowClass:'handcrafted-content-modal ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'ArticleModalCtrl'});};function loadPlans(force){MaintenanceService.plans.load(force);}loadPlans(false);$rootScope.$on('reload:data',function(){loadPlans(true);});function loadStats(){Stats.getRules({include:'ansible'}).then(function(res){$scope.stats.rules=res.data;});}loadStats();// reload actions summary on group change $scope.$on('group:change',function(){loadStats();});}statesModule.controller('OverviewCtrl',OverviewCtrl);},{"../":227}],235:[function(require,module,exports){'use strict';ListRuleCtrl.$inject=['$filter','$q','$rootScope','$scope','$state','Cluster','FilterService','IncidentsService','InsightsConfig','PermalinkService','PreferenceService','QuickFilters','Utils','Rule'];var statesModule=require('../');var reject=require('lodash/reject');/** * @ngInject */function ListRuleCtrl($filter,$q,$rootScope,$scope,$state,Cluster,FilterService,IncidentsService,InsightsConfig,PermalinkService,PreferenceService,QuickFilters,Utils,Rule){FilterService.parseBrowserQueryParams();FilterService.setShowFilters(false);FilterService.setSearchTerm('');$scope.QuickFilters=QuickFilters;var params=$state.params;$state.transitionTo('app.rules',FilterService.updateParams(params),{notify:false});$scope.canIgnoreRules=true;$scope.hideIgnored=false;if(typeof InsightsConfig.canIgnoreRules==='boolean'){$scope.canIgnoreRules=InsightsConfig.canIgnoreRules;}else if(typeof InsightsConfig.canIgnoreRules==='function'){// set to false while we wait for the callback $scope.canIgnoreRules=false;InsightsConfig.canIgnoreRules(function(can){$scope.canIgnoreRules=can;});}$scope.setHideIgnored=function(val){$scope.hideIgnored=val;};var userLoaded=PreferenceService.get('loaded');if(userLoaded){$scope.setHideIgnored(PreferenceService.get('hide_ignored_rules'));}else{$scope.$on('user:loaded',function userLoaded(){var hideIgnored=PreferenceService.get('hide_ignored_rules');$scope.setHideIgnored(hideIgnored);});}$scope.loading=true;$scope.errored=false;$scope.permalink=PermalinkService.make;$scope.persistHideIgnored=function(){PreferenceService.set('hide_ignored_rules',$scope.hideIgnored,true);};$scope.getClusterImpactedText=function(rule){if(rule.impacted_systems>0){return'OpenStack Deployment Impacted';}else{return'OpenStack Deployment not Impacted';}};$scope.$on('filterService:doFilter',getData);$scope.$on('reload:data',getData);$scope.doPage=function(){var itemNumber=$scope.pager.currentPage*$scope.pager.perPage-$scope.pager.perPage;$scope.pagedRules=$scope.rules.slice(itemNumber,itemNumber+$scope.pager.perPage);};function getData(){$scope.loading=true;var promises=[];var query=FilterService.buildRequestQueryParams();query.include='article';var ruleSummaryPromise=Rule.getRulesLatest(query).success(function(ruleResult){$scope.rules=reject(ruleResult.resources,function(r){return r.rule_id==='insights_heartbeat|INSIGHTS_HEARTBEAT';});$scope.pagedRules=$scope.rules.slice(0,$scope.pager.perPage);PermalinkService.scroll(null,30);}).error(function(){$scope.errored=true;});promises.push(ruleSummaryPromise);var incidents=IncidentsService.init();promises.push(incidents);$q.all(promises).then(function listRulesAllPromises(){$scope.loading=false;});$scope.pager=new Utils.Pager(10);}$scope.search=function(model){FilterService.setSearchTerm(model);FilterService.doFilter();};if(InsightsConfig.authenticate&&!PreferenceService.get('loaded')){$rootScope.$on('user:loaded',getData);}else{getData();}}statesModule.controller('ListRuleCtrl',ListRuleCtrl);},{"../":227,"lodash/reject":537}],236:[function(require,module,exports){'use strict';GettingStartedCtrl.$inject=['$scope','InsightsConfig'];var statesModule=require('../');/** * @ngInject */function GettingStartedCtrl($scope,InsightsConfig){$scope.gettingStartedLink=InsightsConfig.gettingStartedLink;}statesModule.controller('SecurityCtrl',GettingStartedCtrl);},{"../":227}],237:[function(require,module,exports){'use strict';SplashCtrl.$inject=['$scope','$state','InsightsConfig'];var statesModule=require('../');/** * @ngInject */function SplashCtrl($scope,$state,InsightsConfig){$scope.gettingStartedLink=InsightsConfig.gettingStartedLink;$scope.logged_in=window.LOGGED_IN;if(window.LOGGED_IN&&$state.current&&$state.current.bounceLoggedin){$state.go('app.initial',{},{location:'replace'});}jQuery('body').addClass('landing-page');$scope.$on('$destroy',function(){jQuery('body').removeClass('landing-page');});//var video = document.getElementById('landingVid'); //video.addEventListener('click',function(){ // if (video.paused) // video.play(); // else // video.pause(); //},false); }statesModule.controller('SplashCtrl',SplashCtrl);},{"../":227}],238:[function(require,module,exports){'use strict';/*global confirm*/EditTopicCtrl.$inject=['$scope','Topic','Rule','$state','$stateParams','DataUtils','Categories','$window','$rootScope','$modal'];var statesModule=require('../../');var find=require('lodash/find');var map=require('lodash/map');var pick=require('lodash/pick');var remove=require('lodash/remove');var reject=require('lodash/reject');var sortBy=require('lodash/sortBy');var capitalize=require('lodash/capitalize');/** * @ngInject */function EditTopicCtrl($scope,Topic,Rule,$state,$stateParams,DataUtils,Categories,$window,$rootScope,$modal){$scope.categories=Categories.slice(1).map(function(cat){return{id:capitalize(cat),label:capitalize(cat)};});$scope.categories.unshift({id:null,label:'Any category'});$scope.severities=[{id:'CRITICAL',label:'Critical'},{id:'ERROR',label:'High'},{id:'WARN',label:'Medium'},{id:'INFO',label:'Low'}];$scope.severities.unshift({id:null,label:'Any Risk'});$scope.error={};$scope.selectedRule='RULE_ID';var allRules=Rule.admin().then(function(res){$scope.availableRules=res.data.filter(function(rule){return rule.rule.active&&!rule.rule.retired;}).map(function(rule){DataUtils.readRule(rule.rule);rule.rule.count=rule.count;return rule.rule;});});$scope.$watchGroup(['topic.category','topic.severity','availableRules'],function(){if(!$scope.topic||!$scope.availableRules){return;}if(!$scope.topic.category&&!$scope.topic.severity){$scope.implicitRules=[];return;}var category=$scope.topic.category;var severity=$scope.topic.severity;$scope.implicitRules=$scope.availableRules.filter(function(rule){return(!category||category===rule.category)&&(!severity||severity===rule.severity);});});$scope.availableTags=[];$scope.select={};Topic.get($stateParams.id).then(function(res){$scope.topic=res.data;$scope.ruleBinding=$scope.topic.ruleBinding;$scope.topic.rules.forEach(function(rule){DataUtils.readRule(rule);});if($scope.ruleBinding==='explicit'){$scope.topic.rules=sortBy($scope.topic.rules,'severityNum').reverse();}else{$scope.topic.rules=[];}$scope.ruleBinding=$scope.topic.ruleBinding;$scope.updateTaggedRules();});Rule.getAvailableTags().then(function(res){$scope.availableTags=map(res.data,'name');});function prepareUpdatePayload(topic){var data=pick(topic,['id','title','summary','content','hidden','listed','tag','category','slug','severity']);if($scope.ruleBinding==='tagged'){data.category=data.severity=null;}else if($scope.ruleBinding==='implicit'){data.tag=null;}else if($scope.ruleBinding==='explicit'){data.tag=data.category=data.severity=null;data.rules=topic.rules.map(function(rule){return{rule_id:rule.rule_id};});}return data;}$scope.update=function(topic){var data=prepareUpdatePayload(topic);Topic.update(data).then($scope.goBack,function(res){$scope.error={};if(res.status===400&&res.data.fields){$scope.error=res.data.fields;}});};$scope.goBack=function(){removeNavigationProtection();$state.go('app.admin-topic');};$scope.addRule=function(rule){$scope.topic.rules.push(rule);delete $scope.select.rule;};$scope.removeRule=function(rule){remove($scope.topic.rules,function(r){return r.rule_id===rule.rule_id;});};$scope.getAvailableRules=function(){if($scope.topic&&$scope.topic.rules&&$scope.topic.rules.length){return reject($scope.availableRules,function(rule){return find($scope.topic.rules,function(r){return rule.rule_id===r.rule_id;});});}return $scope.availableRules;};// TODO fix $scope.updateTaggedRules=function(){if($scope.topic.tag){allRules.then(function(){$scope.taggedRules=$scope.availableRules.filter(function(rule){return find(rule.tags,{name:$scope.topic.tag});});});}};// data loss protection var navigationWarning='Do you want to leave this form? Changes you made might not be saved.';$window.onbeforeunload=function(){return navigationWarning;};var navigationProtectionHandle=$rootScope.$on('$stateChangeStart',function(event){if(!confirm(navigationWarning)){event.preventDefault();}removeNavigationProtection();});function removeNavigationProtection(){$window.onbeforeunload=undefined;navigationProtectionHandle();}$scope.preview=function(topic,_summary,_details){var data=prepareUpdatePayload(topic);$modal.open({templateUrl:'js/components/topics/topicPreviewModal/topicPreviewModal.html',windowClass:'topic-preview-modal ng-animate-enabled',backdropClass:'system-backdrop ng-animate-enabled',controller:'TopicPreviewModalCtrl',resolve:{topic:function topic(){return data;},summary:function summary(){return _summary;},details:function details(){return _details;}}});};}statesModule.controller('EditTopicCtrl',EditTopicCtrl);},{"../../":227,"lodash/capitalize":480,"lodash/find":491,"lodash/map":522,"lodash/pick":533,"lodash/reject":537,"lodash/remove":538,"lodash/sortBy":540}],239:[function(require,module,exports){'use strict';TopicRuleListCtrl.$inject=['$q','$rootScope','$scope','$state','$stateParams','Topic','FilterService','DataUtils','IncidentsService','QuickFilters','PermalinkService','ActionsBreadcrumbs','InsightsConfig','ActionbarService','Export'];var statesModule=require('../../');var get=require('lodash/get');/** * @ngInject */function TopicRuleListCtrl($q,$rootScope,$scope,$state,$stateParams,Topic,FilterService,DataUtils,IncidentsService,QuickFilters,PermalinkService,ActionsBreadcrumbs,InsightsConfig,ActionbarService,Export){FilterService.parseBrowserQueryParams();FilterService.setShowFilters(false);$scope.loading=true;$scope.QuickFilters=QuickFilters;function notFound(){$state.go('app.actions');}function getData(){var product=void 0;var promises=[];if(FilterService.getSelectedProduct()!=='all'){product=FilterService.getSelectedProduct();}// preload incidents topic to prevent multiple calls from incidentIcon var initIncidents=IncidentsService.init();promises.push(initIncidents);var initTopic=Topic.get($stateParams.id,product,'resolution_risk').success(function(topic){if(topic.hidden&&!$scope.isInternal){return notFound();}$scope._topic=topic;topic.rules.forEach(DataUtils.readRule);ActionsBreadcrumbs.init($stateParams);ActionsBreadcrumbs.add({label:topic.title,state:'app.topic',params:{id:$stateParams.id}});PermalinkService.scroll(null,30);$scope.topic=Object.create($scope._topic);}).catch(function(res){if(res.status===404){return notFound();}$scope.errored=true;});promises.push(initTopic);$q.all(promises).finally(function promisesFinally(){$scope.loading=false;});}$scope.search={filters:['description'].map(function(prop){return function(rule,query){return String(get(rule,prop)).toUpperCase().includes(query.toUpperCase());};}),doFilter:function doFilter(query){$scope.loading=true;// refreshes topic and components that reference topic $scope.topic=undefined;$scope.topic=Object.create($scope._topic);if(query){$scope.topic.rules=$scope._topic.rules.filter(function(system){return $scope.search.filters.some(function(f){return f(system,query);});});}else{$scope.topic.rules=$scope._topic.rules;}$scope.loading=false;}};$rootScope.$on('reload:data',getData);$scope.$on('group:change',getData);getData();if(InsightsConfig.allowExport){ActionbarService.addExportAction(function(){Export.getReports($stateParams.id);});}}statesModule.controller('TopicRuleListCtrl',TopicRuleListCtrl);},{"../../":227,"lodash/get":496}],240:[function(require,module,exports){'use strict';TopicAdminCtrl.$inject=['$scope','sweetAlert','Topic','$state','Utils'];var statesModule=require('../../');/** * @ngInject */function TopicAdminCtrl($scope,sweetAlert,Topic,$state,Utils){$scope.loader=new Utils.Loader();var loadData=$scope.loader.bind(function(){return Topic.admin().then(function(resp){$scope.topics=resp.data;});});loadData();$scope.$watchCollection('topics',updateTopicOrder);$scope.newTopic=function(){Topic.create({title:'Unnamed topic'}).then(function(res){$state.go('app.edit-topic',{id:res.data.id});});};function updateTopicOrder(){if(angular.isDefined($scope.topics)){for(var i=0;i<$scope.topics.length;i++){$scope.topics[i].priority=i;}}}$scope.move=function(topic,up){if(topic.priority===0&&up||topic.priority===$scope.topics.length-1&&!up){return;}var otherIndex=(up?-1:1)+topic.priority;var t1=$scope.topics[topic.priority]=$scope.topics[otherIndex];var t2=$scope.topics[otherIndex]=topic;updateTopicOrder();Topic.update({id:t1.id,priority:t1.priority});Topic.update({id:t2.id,priority:t2.priority});};$scope.delete=function(topic){createAlert('Are you sure?','Topic will be lost.','Yes',function(){Topic.remove(topic.id).then(function(){$scope.topics.splice($scope.topics.indexOf(topic),1);});});};function hideInternal(topic,value){Topic.update({id:topic.id,hidden:value}).then(function(){topic.hidden=value;});}$scope.hide=function(topic,value){if(!value){return createAlert('Are you sure?','Publishing this topic makes it visible to customers.','Publish',function(){hideInternal(topic,value);});}hideInternal(topic,value);};function createAlert(title,text,confirmButtonText,cb){sweetAlert({title:title,text:text}).then(cb);}}statesModule.controller('TopicAdminCtrl',TopicAdminCtrl);},{"../../":227}],241:[function(require,module,exports){'use strict';module.exports=angular.module('insights.templates',[]).run(['$templateCache',function($templateCache){$templateCache.put('js/components/accountSelect/accountSelect.html','');$templateCache.put('js/components/actionbar/actionbar.html','
      ');$templateCache.put('js/components/actions/actions.html','
      {{getTotal()}}
      Action

      Use this chart to drill down and discover problems within your organization.


      \nThere is 1 action detected from systems in your organization.

      \nThere is 1{{getCategory() | translate}} action detected from systems in your organization.

      ');$templateCache.put('js/components/ansibleIcon/ansibleIcon.html','{{tooltip}}');$templateCache.put('js/components/betaSwitchButton/betaSwitchButton.html','');$templateCache.put('js/components/bullhorn/bullhorn.html','{{unackedCount}}');$templateCache.put('js/components/card/card.html','
      ');$templateCache.put('js/components/card/cardContent.html','
      ');$templateCache.put('js/components/card/cardFooter.html','');$templateCache.put('js/components/card/cardHeader.html','
      ');$templateCache.put('js/components/categoryIcon/categoryIcon.html','');$templateCache.put('js/components/collapsibleFilter/collapsibleFilter.html','
      Active filters: 
      ');$templateCache.put('js/components/collapsibleFilter/collapsibleFilterContent.html','
      ');$templateCache.put('js/components/demoAccountSwitcher/demoAccountSwitcher.html','');$templateCache.put('js/components/digest/digestGraph.html','
      ');$templateCache.put('js/components/expandAllButton/expandAllButton.html','Collapse AllExpand All');$templateCache.put('js/components/feedback/feedbackButton.html','Provide feedback');$templateCache.put('js/components/feedback/feedbackModal.html','

      {{ feedbackInfo.fromPage }}

      {{ feedbackInfo.user.email }} (to change your Red Hat email address click here)

      An error occurred. Please try again later. 
      ');$templateCache.put('js/components/gaugeMarker/gaugeMarker.html','
      {{difference}}
      ');$templateCache.put('js/components/gettingStartedTabs/gettingStartedTabs.html','');$templateCache.put('js/components/groupSelect/groupSelect.html','
      ');$templateCache.put('js/components/insightsLogo/insightsLogo.html','

      Red Hat
      Insights

      ');$templateCache.put('js/components/insightsTagline/insightsTagline.html','');$templateCache.put('js/components/linkGroup/linkGroup.html','
            {{linkGroupText}}
      ');$templateCache.put('js/components/listType/listType.html','
      ');$templateCache.put('js/components/mitigationTrend/mitigationTrend.html','
      ');$templateCache.put('js/components/notifications/notifications.html','
      ');$templateCache.put('js/components/pageHeader/pageHeader.html','');$templateCache.put('js/components/platformFilter/platformFilter.html','
      {{filter.displayName}}
      {{item}}
      ');$templateCache.put('js/components/recommendedKbase/recommendedKbase.html','
      Related Knowledgebase articles: {{solution.title}}
      ');$templateCache.put('js/components/riskOfChange/riskOfChange.html','
      Risk of change iconRisk of change: Very LowLowModerateHigh
      ');$templateCache.put('js/components/ruleStateIcon/ruleStateIcon.html','');$templateCache.put('js/components/severityIcon/allSeverityIcons.html','
      ');$templateCache.put('js/components/severityIcon/severityIcon.html','
      {{label}}
      ');$templateCache.put('js/components/sideNav/sideNav.html','');$templateCache.put('js/components/simpleSelect/simpleSelect.html','');$templateCache.put('js/components/statusIcon/statusIcon.html','');$templateCache.put('js/components/tag/tag.html','');$templateCache.put('js/components/systemMetadata/systemMetadata.html','
      Loading...
      ');$templateCache.put('js/components/timeRange/timeRange.html','');$templateCache.put('js/components/topbar/topbar.html','');$templateCache.put('js/components/typeIcon/typeIcon.html','  {{systemTypeDisplayNameShort}}');$templateCache.put('js/components/userInfo/userInfo.html','');$templateCache.put('js/components/yesNo/yesNo.html',' {{text}}');$templateCache.put('js/states/actions/actions.html','
      [edit]

      Featured Topics

      No topics currently available. Register more systems.
      ');$templateCache.put('js/states/analytics/analytics.html','

      Analytics

      ');$templateCache.put('js/states/announcements/announcements.html','');$templateCache.put('js/states/announcements/edit-announcement.html','

      Edit Announcement

      {{announcementError}}
      Back to Announcements
      ');$templateCache.put('js/states/announcements/new-announcement.html','

      New Announcement

      {{announcementError}}
      Back to Announcements
      ');$templateCache.put('js/states/announcements/view-announcement.html','
      {{announcement.created_at | date}}

      {{announcement.title}}

      ');$templateCache.put('js/states/base/app.html','
      ');$templateCache.put('js/states/base/base.html','
      ');$templateCache.put('js/states/base/info.html','
      ');$templateCache.put('js/states/digests/digests.html','
      {{digestError}}

      Executive Reporting requires 10 or more systems to be registered with Insights.

      Please register{{systemsTilTen}} more system to enable reporting for your account.

      Overall score

      {{latest_score}}

      Score needs attentionScore is poorScore is goodScore is excellent
      250850

      Weekly action count by category

      {{cat.current}}
       {{cat.label}}

       You\'ve resolved {{resolvedIssues}} issue in the past {{timePeriod()}} days.
      RuleImpacted SystemsTotal Risk
      {{ rule.description }}{{rule.report_count}}

      Ten Most Impacted Systems

      HostnameHits
      {{ system | getSystemDisplayName }}{{ system.report_count }}

      Ten Most Frequently Hit Rules

      RuleImpacted SystemsTotal Risk
      {{ rule.description }}{{rule.impacted_systems}}
      ');$templateCache.put('js/states/evaluation/evaluation.html','

      You are currently on a {{status.current.systems}} system evaluation of Red Hat Insights


      Your evaluation will expire on {{getExpiration()}}.

      Your {{getNonLegacyEval().systems}} system {{getNonLegacyEval().duration}} day evaluation is ready.


      READ AND ACCEPT THESE TERMS TO GET STARTED.

      Red Hat is providing each Red Hat Subscription Evaluation for evaluation purposes subject to the terms of the Red Hat Enterprise Agreement. If you use the Red Hat Subscription for any purpose other than evaluation, you agree to pay Red Hat the Subscription Fee(s) for each Unit pursuant to the Enterprise Agreement, which is in addition to any and all other remedies available to Red Hat under applicable law.

      Examples of situations where you would incur additional fees and be in violation of the Agreement include, but are not limited to:
      • Using the service provided under the evaluation program for a production installation,
      • Offering support services to third parties, or
      • Complementing or supplementing third party support services with services received through the Red Hat Subscription evaluation program.

      By proceeding, I acknowledge that I have read and agree to the terms and conditions of the Red Hat Enterprise Agreement which governs my use.

      Decline
      If you decline, you will not receive an evaluation at this time and will be redirected to additional information on Red Hat Insights

      Thank you!


      Thank you for purchasing Red Hat Insights

      Oops!

      There was an error


      {{error}}

      ');$templateCache.put('js/states/info/info.html','

      Proactive systems management

      Red Hat Insights is a hosted service designed to help you proactively identify and resolve technical issues in Red Hat Enterprise Linux and Red Hat Cloud Infrastructure environments. Decreasing time spent discovering, researching, and resolving critical issues allows you to focus on driving high value, strategic initiatives.

      Management

      Insights powered by Red Hat

      The Insights engineis powered by the unparalleled expertise that comes from over 700 Red Hat Certified Engineers, over 30,000 documented technical solutions, and over 1,000,000 resolved issues. Based on Red Hat\u2019s continuously expanding knowledge base, which includes expertise in third-party hardware and software integration, deterministic rules are constantly being created.

      Data Collection

      Lightweight data collection

      For analysis by the Insights engine, a minimal subset of information commonly provided in a standard support case is collected by the Insights client. This data is collected via secure protocols at defined intervals then analyzed and discarded at the next upload, or no longer than two weeks later.

      Clear, tailored, and actionable intelligence

      The Insights engine delivers results for review through the Insights UI in Red Hat Satellite and in the Red Hat Customer Portal. Through the intuitive Insights UI, configuration issues discovered during analysis are clearly displayed and prioritized by severity. Tailored remediation steps are presented to help you optimize workloads and confidently resolve critical issues and prevent future issues.

      Actionable Intelligences

      Availability

      Red Hat Insights is available for current Red Hat customers on Red Hat Enterprise Linux, delivered through either the Customer Portal or Red Hat Satellite. Your current subscription includes Red Hat Insights for up to 10 systems. To enroll additional systems, contactsales.

      Red Hat Insights supports
      Red Hat Enterprise Linux 6
      Red Hat Enterprise Linux 7
      Delivered via
      Red Hat Customer Portal
      Red Hat Satellite 5
      Red Hat Satellite 6

      Security comes standard

      Like all products and services from Red Hat, Red Hat Insights is developed with a focus on security and rigorous deployment controls.

      Learn more about how Red Hat Insights keeps your data secure while enabling you to proactively manage systems.

      ');$templateCache.put('js/states/inventory/inventory.html','
      Name
      Action Count
      System Type
      System Name
      Last Check In
      Status
      {{system.toString}}{{ system.last_check_in | timeAgo }}{{system.report_count}} Action
      Loading system information\u2026
      ');$templateCache.put('js/states/invites/invites.html','

      Your account is not authorized to view this page.

       Insights Evaluation

      This internal form is used to invite an account to a 30 day evaluation of Red Hat Insights (more info available at  Insights Evaluation guide)

      You have successfully invited {{successfulInvite.email}} with account number {{successfulInvite.account_number}}.

      Invitation to {{failedInvite.email}} with account number {{failedInvite.account_number}} failed.  This account has previously activated a sales invited evaluation.  \nPlease contact insights@redhat.com  to grant additional invitations.{{error.data}}

      ');$templateCache.put('js/states/maintenance/maintenance.html','
      Loading plans\u2026

      No plans

      Suggested Plans

      Plans

      Scheduled Plans

      ');$templateCache.put('js/states/overview/overview.html','
      [edit]

      Latest

      Actions SummaryView actions

       Optimize Your Experience

      ');$templateCache.put('js/states/rules/list-rules.html','
      INACTIVE 
      {{ ::rule.category }} > {{ ::rule.description }}
      There are no rules for the selected filters. 
      ');$templateCache.put('js/states/security/security.html','

      Security

      Introduction

      Red Hat Insights provides a mechanism for customers to get actionable intelligence regarding suggested improvements to deployed Red Hat software. As part of this optional offering, Red Hat processes configuration data about a customer\u2019s environment using Red Hat hosted and/or provided tools; this document covers the security measures Red Hat puts in place to provide secure transmission, processing, and analysis of this data by those tools.

      Red Hat is committed to evaluating, implementing and monitoring the industry security standards for those tools as they continue to evolve.

      On Premise Security

      Security of customer systems is paramount and Red Hat Insights has made extensive efforts to ensure that our shipped software meets the highest standards.

      • Insights software which executes on premise has been through a security review by Red Hat Product Security which is the same team that addresses security concerns in all other products across the Red Hat portfolio.
      • Communication with Red Hat can be sent through secure proxies for audit and network ACLs.
      • Enrollment is opt-in, which means we\u2019ll never collect data from systems without your explicit approval and can be scheduled daily or weekly.
      • All communication with Red Hat occurs over encrypted channels using TLS
      • All TLS traffic with Red Hat servers is verified with a trusted certificate that is bundled with the application, ensuring that communications can not be intercepted, such as by a man in the middle attack.
      • The default communication model from client systems to Red Hat servers occurs with mutual TLS or two-way authentication using digital certificates.

      Client Side On Premise Data Control

      Red Hat Insights allows customers to have control of the information that is monitored for analytical purposes; and Red Hat Insights makes it simple for subscribers to view the information which is monitored.

      • Red Hat Insights collects both targeted and minimal system information rather than entire logs.
      • Red Hat automatically attempts to remove critical security information such as passwords before uploading when stored in standard locations
      • Common types of sensitive information can be opted out of through pre-built configuration profiles
      • Subscribers can blacklist any command, file, or piece of metadata that they prefer not be monitored by Red Hat Insights.
      • The commands, files, and metadata monitored by Red Hat Insights are listed in a single file that is GPG signed and available at any time for easy inspection.
      • Personally Identifiable Information is not collected.

      Infrastructure Security

      Security of customer data in Red Hat is a priority and every effort is made to ensure that information is not unnecessarily persisted and that it is secured using industry standard best practices.

      • All customer data is stored within a secure physical data center with controlled access.
      • Our internal 3-tiered infrastructure is compartmentalized into different physical networks and firewalled to ensure services are isolated for maximum security.
      • All volumes containing customer data at rest are encrypted with LUKS encryption
      • All parts of the internal infrastructure transmit their logs to a centralized log aggregator for inspection and analysis.

      Development Best Practices

      • All software is analyzed with static code analyzers and all reported issues are fixed before code is deployed into production.
      • Code is peer reviewed.
      ');$templateCache.put('js/states/splash/splash.html','

      You can\'t fix what you can\'t see

      Take the guesswork out of infrastructure optimization with Red Hat Insights proactive analytics and real-time intelligence.

      Introduction to Red Hat Insights

      Automate discovery and remediation

      Automate the resolution of Insights findings through Ansible Playbooks.

      Red Hat Insights creates significant time savings and helps avoid firefighting by enabling automatic resolution of critical issues before they affect your business. Optimize your environment and boost security withRed Hat Insights.

      Avoid firefighting

      Get in-depth analysis of risks on an individual system, topic, or site-wide level, so you can address them before they affect your business.

      Act on advice you can trust

      Resolve issues quickly and take the guesswork out of remediation using tailored resolutions created by domain experts and cross-referenced against thousands of similar environments.

      Master complexity

      Ensure integration of high-priority security analysis in an operations-friendly workflow with a view of all actions prioritized by level of risk.

      ...With Red Hat Insights, we can resolve critical issues before they occur. We no longer have to look at individual systems because we have one tool that gives us much more comprehensive and actionable information on our infrastructure.

      It\'s important to have a company like Red Hat that\'s working toward the same goals we are, that understands our business and what we\'re trying to accomplish, and understands our infrastructure and our businesses. As the market continues to change and new technologies are released, we\'ll continue to change as well, and we expect Red Hat to be an important partner to help us on that journey.

      Jason Cornell, Manager of Cloud and Infrastructure Automation, Cox Automotive

      Real-Time Risk Assessment

      Related Resources

      See Insights in Action

      ');$templateCache.put('js/components/actions/actionsBreadcrumbs/actionsBreadcrumbs.html','
      ');$templateCache.put('js/components/actions/actionsRule/actionsRule.html','

       This issue is an incident, it has already occurred.

      {{ruleDetails.description}}

      1 Impacted System

      1 Impacted Deployment

        ({{numberOfSelected()}} Selected. Select All Systems. Deselect All Systems.)
      Name  
      Action Count
      Type
      Name
      Reported
      {{ system.toString }}{{ ::system.last_check_in | timeAgo}}
      ');$templateCache.put('js/components/actions/actionsRuleFilters/actionsRuleFilters.html','
      ');$templateCache.put('js/components/actions/severitySummary/severitySummary.html','
      • {{\'Low\' | translate}} ({{100 * ruleCount.info / ruleCount.total || 0 | number:1}}%)
        You have no issues of low severity
      • {{\'Medium\' | translate}} ({{100 * ruleCount.warn / ruleCount.total || 0 | number:1}}%)
        You have no issues of medium severity
      • {{\'High\' | translate}} ({{100 * ruleCount.error / ruleCount.total || 0 | number:1}}%)
        You have no issues of high severity
      • {{\'Critical\' | translate}} ({{100 * ruleCount.critical / ruleCount.total || 0 | number:1}}%)
        You have no issues of critical severity
      ');$templateCache.put('js/components/actions/systemSummary/systemSummary.html','
      {{stats.systems.affected}} of {{stats.systems.total}} systems have critical issues
      You have no issues of critical severity
      ');$templateCache.put('js/components/announcements/announcementsScroll/announcementsScroll.html','

      You have no announcements.

      {{a.title}}
      {{ (a.start || a.created_at) | date }}  
      Start/End: {{a.start | date}} - {{a.end | date}}

      {{a.body}}

      {{a.body}}

      {{a.body}}

      ');$templateCache.put('js/components/card/systemCard/systemCard.html','
        
       (stale system)
       {{getSystemName(system)}}
      Last checkin: never{{system.last_check_in | timeAgo}}

      Related Systems

      Unregister
      ');$templateCache.put('js/components/config/dev/dev.html','

      Demo Mode

      API Prefix

      Presets
      Choose your own adventure

      API Version

      Presets

      Fake user info

      email
      name

      Fake Entitlements

      Presets
      State
      {{getPretty(user.current_entitlements)}}
      ');$templateCache.put('js/components/config/groups/groups.html','
      Loading group list info \u2026

      Groups List

      Creating group \u2026
      ');$templateCache.put('js/components/config/hidden/hidden.html','

      Hidden Rules

      You currently have 1 hidden rule.

      Note: These are account wide. You must be an Org Admin to make these changes.

      Loading hidden rules\u2026
      CategoryRule
      {{ack.rule.category}}{{ack.rule.description}}
      ');$templateCache.put('js/components/config/messaging/messaging.html','

      Messaging Preferences

      Manage Red Hat Insights email subscriptions for your personal login. All emails will be sent to the email address associated with your login. To update your address see Personal Info.

      ');$templateCache.put('js/components/config/permissions/permissions.html','
      Add
      {{p.permission.label}}
      ');$templateCache.put('js/components/config/settings/settings.html','

      Settings

      Note: These are account wide. You must be an Org Admin to make these changes.

      Loading Settings
      SettingValue
      {{setting.name}}
      ');$templateCache.put('js/components/config/webhooks/webhooks.html','

      Webhooks

      Webhooks allow external services to be notified of updates in Red Hat Insights. An HTTP POST request will be issued for a configured URL every time an event occurs in Red Hat Insights.  See webhook documentation  for more details.

      No webhooks configured

      URLEvent TypesActiveActions
      {{webhook.url}}{{webhook.event_types.length}}
      ');$templateCache.put('js/components/filterComponents/actionsSelect/actionsSelect.html','
      ');$templateCache.put('js/components/filterComponents/ansibleSupportTriState/ansibleSupportTriState.html','
      ');$templateCache.put('js/components/filterComponents/categorySelect/categorySelect.html','
      ');$templateCache.put('js/components/filterComponents/checkInSelect/checkInSelect.html','
      ');$templateCache.put('js/components/filterComponents/filterButton/filterButton.html','Filter');$templateCache.put('js/components/filterComponents/hideIgnoredToggle/hideIgnoredToggle.html','
      ');$templateCache.put('js/components/filterComponents/impactSelect/impactSelect.html','
      ');$templateCache.put('js/components/filterComponents/likelihoodSelect/likelihoodSelect.html','
      ');$templateCache.put('js/components/filterComponents/incidentsTriState/incidentsTriState.html','
      ');$templateCache.put('js/components/filterComponents/productSelect/productSelect.html','
      ');$templateCache.put('js/components/filterComponents/riskOfChangeSelect/riskOfChangeSelect.html','
      ');$templateCache.put('js/components/filterComponents/ruleStatusTriState/ruleStatusTriState.html','
      ');$templateCache.put('js/components/filterComponents/searchBox/searchBox.html','');$templateCache.put('js/components/filterComponents/totalRiskSelect/totalRiskSelect.html','
      ');$templateCache.put('js/components/incident/incidentIcon/incidentIcon.html','');$templateCache.put('js/components/incident/incidentLite/incidentLite.html','
      {{incidentCount}} of {{totalHits}}
      Issues are incidents
      An incident is an issue that is currently or has already affected systems. View incidents
      0
      Incidents
      An incident is an issue that is currently or has already affected systems. You currently have no incidents.
      ');$templateCache.put('js/components/incident/incidentSummary/incidentSummary.html','

      {{incidentCount}}

      Incidents

      {{incidentSystemCount}}

      Systems

      0

      incidents

      An incident is an issue that is currently or has already affected systems. {{incidentCount}} of {{ruleHits}} issue  is an INCIDENT affecting  {{incidentSystemCount}} system.  It is imperative that these incidents be addressed to ensure optimal performance.
      View incidents

      An incident is an issue that is currently or has already affected systems. You have 0 issues that are INCIDENTS.

      ');$templateCache.put('js/components/inventory/inventoryActions/inventoryActions.html','');$templateCache.put('js/components/inventory/inventoryActions/unregisterConflict.html','

      The following systems types cannot be unregistered on their own:

      • {{role.name}} ({{role.count}}x)

      Unregister their entire deployment to remove them.

      \nDo you want to proceed with the remaining 1 system?

      ');$templateCache.put('js/components/inventory/inventoryFilters/inventoryFilters.html','
      ');$templateCache.put('js/components/inventory/system/system.html','');$templateCache.put('js/components/maintenance/expandableSystemList/expandableSystemList.html','');$templateCache.put('js/components/maintenance/maintenanceCategorySelect/maintenanceCategorySelect.html','
      ');$templateCache.put('js/components/maintenance/maintenanceModal/maintenanceModal.html','
      Unnamed plan ({{selected.plan.maintenance_id}}){{selected.plan.name}} ({{selected.plan.maintenance_id}}) - {{selected.plan.start | moment:\'LL\'}}Unnamed plan ({{plan.maintenance_id}}){{plan.name}} ({{plan.maintenance_id}})  - {{plan.start | moment:\'LL\'}}

      {{selected.group.display_name}}{{group.display_name}}
      {{selected.system.toString}} ({{selected.system.report_count}}){{system.toString}} ({{system.report_count}})

      {{rule.description}}
      {{selected.system.toString}}   
      Last check in:  {{selected.system.last_check_in | timeAgo}}
      Actions available for selected systemActions available for group: {{selected.group.display_name}} (1 system)Actions available for your inventory
      No actions available on selected systems.
      ');$templateCache.put('js/components/maintenance/maintenancePlan/maintenancePlan.html','

      {{plan.name}} ({{plan.maintenance_id}})Unnamed plan ({{plan.maintenance_id}})

      {{error}}

      {{plan.start | moment:\'dddd, LL\'}}
      UTC 

      {{editBasic.start | moment:\'LLL\':\'UTC\'}}

      {{plan.description}}  This is a plan suggested by Red Hat

      {{plan.actionsDone}}/{{plan.actions.length}} Actions resolved
      {{rule.description}}Edit
      This plan is empty
        {{system._name}}   Last check in:  {{system.last_check_in | timeAgo}}Edit
      This plan is empty
      Playbook
      {{ play.rule.description }}

      Systems:

      Resolution:

      {{ play.ansible_resolutions[0].description }}  - edit ({{play.ansible_resolutions.length}} options)

      • Reboot Required
      System reboot summary

      This playbook does not require a system reboot.

      Systems:

      Reboot systems automatically

      Some of the resolutions above require system reboot in order for the changes to take effect. This playbook will reboot the systems automatically.will not reboot the systems. The systems will need to be rebooted manually.

      ');$templateCache.put('js/components/maintenance/maintenanceTable/maintenanceTableActions.html','
      Action
      Total Risk
      Status
      {{action.display}}
      ');$templateCache.put('js/components/maintenance/maintenanceTable/maintenanceTableFooter.html','
      ');$templateCache.put('js/components/maintenance/maintenanceTable/maintenanceTableMulti.html','
      Action
      Total Risk
      Ansible
      Affected Systems
      {{action.display}}{{action.rule.report_count}}
      ');$templateCache.put('js/components/maintenance/maintenanceTable/maintenanceTableSystems.html','
      System
      Last check in
      Status
      {{::action.system.last_check_in | timeAgo}}--
      ');$templateCache.put('js/components/maintenance/planList/planList.html','

      {{month[0].start | moment:\'MMMM YYYY\'}}

      ');$templateCache.put('js/components/maintenance/resolutionModal/resolutionModal.html','');$templateCache.put('js/components/misc/trimmedText/trimmedText.html','{{trimmedText}}{{trimmedText}}');$templateCache.put('js/components/overview/articleModal/articleModal.html','

      Overview content

      {{selected.article.id}}{{article.id}}
       

      {{previewData.title}}

      ');$templateCache.put('js/components/rule/ruleBreadcrumb/ruleBreadcrumb.html','');$templateCache.put('js/components/rule/ruleFilter/ruleFilter.html','
      ');$templateCache.put('js/components/rule/ruleGroupCard/ruleGroupCard.html','
      1 System
      Group:  {{plugin.categories[0]}} >  {{plugin.display_name}}
      1 Rule in this group
      {{index + 1}} of  {{plugin.rules.length}}: {{rule.description}}
      inactive
      ');$templateCache.put('js/components/rule/ruleListSimple/ruleListSimple.html','');$templateCache.put('js/components/rule/ruleReason/ruleReason.html','

      Detected issues

      There was a problem obtaining action details. Please try again later.

      ');$templateCache.put('js/components/rule/ruleResolution/ruleResolution.html','

      Steps to resolve

      There was a problem obtaining action details. Please try again later.

      Affected Hosts
      {{getSystemNameFromId(affected_host)}}
      ');$templateCache.put('js/components/rule/ruleSummaries/ruleSummaries.html','
      {{getLoadingMessage()}}

      No actions

      ');$templateCache.put('js/components/rule/ruleSummary/ruleSummary.html','

      {{ report.rule.category }} > {{ report.rule.description }}

      ');$templateCache.put('js/components/rule/ruleToggle/ruleToggle.html','
      Enable Rule
      ');$templateCache.put('js/components/sideNav/actionsSideNav/actionsSideNav.html','
    • Actions
    • ');$templateCache.put('js/components/system/addSystemModal/addSystemModal.html','

      Install the Red Hat Insights Client and Register More Systems

      Register your system for updates with Red Hat Subscription Manager to resolve software dependencies:

       # subscription-manager register --auto-attach

      Install the Red Hat Insights RPM:

       # yum install redhat-access-insights

      Register the system to Red Hat Insights

      # redhat-access-insights --register

      After registration, the Insights client will upload initial system information to Red Hat Insights. You should be able to immediately see your system in the Insights user interface.

      The initial analysis results will be available shorty thereafter.

      Advanced configuration

      For more advanced configuration see the getting started guide.

      ');$templateCache.put('js/components/system/addSystemModal/upgradeSubscription.html','

      You have reached your maximum
      number of registered systems!

      You have reached your evaluation limit of 10 registered systems for Red Hat Insights.You have reached your maximum number of registered systems for Red Hat Insights.  To increase your available Red Hat Insights registrations, please contact your Red Hat sales representative to purchase Insights or request a 90 day evaluation.

      Learn more about Red Hat Insights availability

      ');$templateCache.put('js/components/system/systemModal/systemModal.html','
      This system is not checking in.
      ');$templateCache.put('js/components/system/systemTable/systemTable.html','
      Items
      ');$templateCache.put('js/components/topbar/alerts/alerts.html','
      {{service.unackedCount}}
      ');$templateCache.put('js/components/topics/otherTopic/otherTopic.html','
      ');$templateCache.put('js/components/topics/topicAdminHelpBar/topicAdminHelpBar.html','
      Show help
      Dynamic content

      There are multiple ways to make the content of a topic dynamic:

      Here are some examples of values you can use for interpolation:
      {{tip.key}}{{tip.value}}

      Also, you may find  doT docs  useful

      ');$templateCache.put('js/components/topics/topicDetails/topicDetails.html','
      [edit]

      {{::topic.title}}

      ');$templateCache.put('js/components/topics/topicPreviewModal/topicPreviewModal.html','
      Modify sample data
      Rule IDDescriptionNumber of hitsIgnored by user (acked)
      {{rule.rule_id}}{{rule.description}}

      This topic will not be shown when there are no hits for it (tweak sample data or switch to \'Shown always\' topic type to see it)

      Topics

      ');$templateCache.put('js/components/topics/topicRuleFilters/topicRuleFilters.html','
      ');$templateCache.put('js/components/topics/topicRuleList/topicRuleList.html','
      {{ ::plugin.rule.category }} > {{ ::plugin.rule.description }}
      1 System
      INACTIVE 
      Rule
      Likelihood
      Impact
      Total Risk
      Systems
      Ansible
      {{rule.description}}
      INACTIVE{{rule.description}}
      {{ rule.hitCount }}
      There are no actions for the selected filters. 
      ');$templateCache.put('js/states/config/views/config.html','
      ');$templateCache.put('js/states/config/views/webhook-edit.html','
      Must be a valid https URL

      Due to sensitive nature of data that Red Hat Insights operates on HTTPS is required. For cases when your server does not use a certificate signed by a trusted authority a custom certificate can be configured.

      {{webhook.certificateInfo.subject.cn}}

      {{webhook.certificateInfo.subject.o}}

      {{webhook.certificateInfo.issuer.cn}}

      {{webhook.certificateInfo.issuer.o}}

      {{webhook.certificateInfo.validUntil | moment:\'LLL\'}}

      Invalid certificate
      ');$templateCache.put('js/states/getting-started/views/getting-started-cloudforms.html','
      1

      Prerequisites

      • Red Hat CloudForms version 4.0 or later
      • The CloudForms 4.0 appliance must be registered to either Satellite Server version 6.1.5 or later or to Customer Portal Subscription Management hosted subscription service.
      • Smart State Analysis capability is enabled for Red Hat Enterprise Linux virtual machines.
      2

      Activate your evaluation

      If you have purchased an Insights subscription, please skip this step and proceed to the next step.

      To begin using Red Hat Insights, please visit https://access.redhat.com/insights/evaluation to activate any available evaluations associated with your account. This step must be completed prior to any system registration with Insights.

      3

      Verify CloudForms Appliance registration

      All appliances configured for the UI role that will be used to view Red Hat Insights reports must be registered for Red Hat updates. In the CloudForms Management Engine UI, navigate to Configure -> Configuration. Select a zone and verify that the appropriate appliance is registered.

      Note: The Satellite Server organization to which the CloudForms appliance is registered must have Insights enabled.

      4

      Configure Smart State Analysis

      Configure the Smart State Analysis profile for your appliances to collect the contents of the file /etc/redhat-access-insights/machine-id

      5

      Register Your Virtual Machines

      Important: All virtual machines must be registered to the same Red Hat account as the CloudForms appliance.

      To register virtual machines to Red Hat Insights, you\'ll need to install the Insights client RPM and then register virtual machines to the Insights service.

      Install the RPM in the virtual machine:

      # yum install redhat-access-insights

      Register the system to Insights service:

      # redhat-access-insights --register

      These steps can be automated using the configuration tool of your choice.

      6

      Run Smart State Analysis on RHEL virtual machines

      A Smart State Analysis must be run once on all RHEL virtual machines you have registered to Red Hat Insights. This step is only required once. If a new virtual machine is created afterward, then a Smart State Analysis need only be run on that new virtual machine after it has been registered to Insights.

      For more information on how to configure and run Smart State Analysis, see the official CloudForms  documentation.

      7

      View Reports and Manage Red Hat Insights From the CloudForms UI

      To view Insights reports in the CloudForms UI, navigate to Red Hat Insights -> Overview.

      To view and manage Insights rules, navigate to Red Hat Insights -> Rules.

      To view the list of systems that are reporting data to Insights, navigate to Red Hat Insights -> Systems.

      8

      Assign Red Hat Insights to user roles

      By default, only super administrators can view Insights Report and Configuration screens. You can give users who are not super administrators access to the Insights screens by assigning the appropriate features to their roles.

      In addition to the feature controls above, a user can only view Insights reports on those virtual machines to which the user has permission to view in CloudForms.

      ');$templateCache.put('js/states/getting-started/views/getting-started-containers.html','

      Prerequisites for Installing Red Hat Insights

      Before proceeding with installing Red Hat Insights, ensure the following prerequisites are met:

      • Red Hat Enterprise Linux (RHEL) 7.0 or later host configured to run containers
      • RHEL firewall rules must allow Red Hat Insights to make outbound connections to https://api.access.redhat.com:443 and https://cert-api.access.redhat.com:443
      1

      Enable the Red Hat Insights Repository

      To start the installation, the Red Hat Insights repository must be enabled:

       ~ $ sudo yum-config-manager --add-repo http://copr.fedorainfracloud.org/coprs/insights/insights-client-beta/repo/epel-7/insights-insights-client-beta-epel-7.repo
      2

      Install the Red Hat Insights Client

      The following command will install the Red Hat Insights client:

      ~ $ yum install insights-client -y --nogpgcheck
      3

      Run and Register the Red Hat Insights Client

      The following command will run and register the Insights client:

      ~ $ sudo insights-client --container --register  

      After running the Insights client, it will upload initial system and image information to Red Hat Insights. You should be able to immediately see your system and image information in the Insights user interface.

      The initial analysis results will be available shortly thereafter.

      Optional Configuration Steps

      3

      Configure Insights Client to use an HTTP proxy

      If you have a web-based proxy between your system and the Internet, you can configure the Insights client to connect through it:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Optional proxy configuration. Example: http://user:pass@192.168.100.50:8080
      proxy=
      4

      Red Hat Network Classic Instructions

      If you\'ve registered your system with Red Hat Network Classic you\'ll also need to add a username and password:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Red Hat Customer Portal Credentials
      username=
      password=
      ');$templateCache.put('js/states/getting-started/views/getting-started-direct.html','
      1

      Activate your evaluation

      If you have purchased an Insights subscription, please skip this step and proceed to the next step.

      To begin using Red Hat Insights, please visit https://access.redhat.com/insights/evaluation to activate any available evaluations associated with your account. This step must be completed prior to any system registration with Insights.

      2

      Install the Red Hat Insights Client and Register Your Systems

      Register your system for updates with Red Hat Subscription Manager to resolve software dependencies:

       # subscription-manager register --auto-attach

      Install the Red Hat Insights RPM:

       # yum install redhat-access-insights
      3

      Register the system to Red Hat Insights

      Note: Registration with Red Hat Network Classic is also available but requires additional configuration steps.

      # redhat-access-insights --register

      After registration, the Insights client will upload initial system information to Red Hat Insights. You should be able to immediately see your system in the Insights user interface.

      The initial analysis results will be available shorty thereafter.

      Optional Configuration Steps

      4

      Configure Insights Client to use an HTTP proxy

      If you have a web based proxy between your system and the Internet, you can configure the Insights client to connect through it:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Optional proxy configuration. Example: http://user:pass@192.168.100.50:8080
      proxy=
      5

      Red Hat Network Classic Instructions

      If you\'ve registered your system with Red Hat Network Classic you\'ll also need to add a username and password:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Red Hat Customer Portal Credentials
      username=
      password=
      ');$templateCache.put('js/states/getting-started/views/getting-started-osp.html','
      1

      Prerequisites for Installing the Red Hat Insights for OpenStack Private Beta

      Before proceeding with installing the Red Hat Insights for OpenStack Private Beta, ensure the following prerequisites are met:

      • The OpenStack deployment must be Red Hat OpenStack Platform (OSP) 7 or later and set up with the OSP Director.
      • The OSP Director node (also referred to as the Undercloud in these directions) must have the rhel-7-server-rh-common-rpms repository\nenabled.
      • Firewall rules must allow the Insights Coordinator to make outbound connections to https://api.access.redhat.com:443 and https://cert-api.access.redhat.com:443
      2

      Install the Red Hat Insights for OpenStack Private Beta Repositories on the Undercloud

      To start the installation, the Red Hat Insights for OpenStack Private Beta repositories must be installed on the OSP Director node.

       stack@undercloud $ sudo yum-config-manager --add-repo http://copr.fedorainfracloud.org/coprs/insights/insights-client/repo/epel-7/insights-insights-client-epel-7.repo
      stack@undercloud $ sudo yum-config-manager --add-repo http://copr.fedorainfracloud.org/coprs/insights/insights-coordinator-rhosp/repo/epel-7/insights-insights-coordinator-rhosp-epel-7.repo
      3

      Install the Red Hat Insights for OpenStack Private Beta Components

      Follow this set of instructions to install the Insights Coordinator and Client packages.

      The following command will install the OpenStack Coordinator on the Undercloud node:

       stack@undercloud $ sudo yum install insights-coordinator-rhosp --nogpgcheck -y

      The following command will install the Client on the Undercloud and Overcloud nodes and register the stack with Red Hat Insights:

      stack@undercloud $ sudo redhat-access-insights --register --no-schedule --no-upload --silent

      The Client cron job and Coordinator cron jobs cannot be symlinked simultaneously or conflicts will occur. The following command will remove the symlink that will have been created in /etc/cron.daily for the Client:

      sudo redhat-access-insights --no-schedule

      Edit /etc/redhat-access-insights/redhat-access-insights.conf and add no_schedule=True. This value should already exist at the bottom of the file so it will just need to be uncommented and changed to True, but should it not exist it will need to be added.

      The following command will configure the Coordinator and Client installations:

      stack@undercloud $ sudo insights-coordinator-rhosp --configure

      Note: The Coordinator will attempt to auto-configure the communication to Red Hat based on the subscription-management information on the Undercloud node. However, using a proxy and/or using Red Hat Network Classiccredentials is available but requires additional configuration steps.

      If issues arise with these commands, examine /etc/redhat-access-insights/redhat-access-insights.conf and ensure everything is set properly.

      Last, run the following command to complete the installation and view any messages from the Coordinator during the execution and upload of the archive:

       stack@undercloud $ sudo insights-coordinator-rhosp --verbose

      After the above command finishes, the Insights Coordinator has uploaded the initial deployment information to Red Hat Insights. \nYou should be able to immediately see your stack in the Insights user interface.

      The initial analysis results will be available shortly thereafter.

      Optional Configuration Steps

      5

      Configure Insights Client to use an HTTP proxy

      If you have a web-based proxy between your system and the Internet, you can configure the Insights client to connect through it:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Optional proxy configuration. Example: http://user:pass@192.168.100.50:8080
      proxy=
      6

      Red Hat Network Classic Instructions

      If you\'ve registered your system with Red Hat Network Classic you\'ll also need to add a username and password:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Red Hat Customer Portal Credentials
      username=
      password=
      ');$templateCache.put('js/states/getting-started/views/getting-started-rhv.html','
      1

      Prerequisites for Installing Red Hat Insights for RHV

      Before proceeding with installing Red Hat Insights on RHV, ensure the following prerequisites are met:

      • The RHV deployment must be Red Hat Enterprise Virtualization (RHV) 3.5 or later and RHV Manager must be running Red Hat Enterprise Linux 6.7 or later.
      • Firewall rules must allow Red Hat Insights to make outbound connections to https://api.redhat.com:443 and https://cert-api.access.redhat.com:443
      • Ensure that the RHV Manager can SSH into all individual hypervisor nodes, and that the SSH fingerprints have been accepted for all hypervisor nodes. The manager must have access to all of the individual hypervisors via public key access (defined in the configuration files, or by running --configure).
      2

      Install the repositories

      To start the installation, the Red Hat Insights repositories must be installed.

      Run the following commands on the RHV Manager to obtain the Red Hat Insights repositories:

       sudo yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/insights/insights-client/repo/epel-6/insights-insights-client-epel-6.repo
      sudo yum-config-manager --add-repo http://copr.fedorainfracloud.org/coprs/insights/insights-coordinator-rhev/repo/epel-6/insights-insights-coordinator-rhev-epel-6.repo
      3

      Install the Red Hat Insights client

      The following command installs Red Hat Insights on the RHV Manager, which will collect information from all the virtualization hosts in the environment.

      Note: Registration with Red Hat Network Classic is also available but requires additional configuration steps.

       sudo yum install insights-coordinator-rhev --nogpgcheck
      3

      Registration

      When configuring your RHV Manager for Red Hat Insights, be sure to have your RHV Manager API and Hypervisor SSH information ready; Red Hat Insights will guide you through the configuration.

      # sudo insights-coordinator-rhev --configure

      Once configured, this next step installs Red Hat Insights on the RHV Manager and all of the hypervisor nodes.

      # sudo insights-coordinator-rhev --verbose

      After registration, the Insights client will upload initial system metadata to Red Hat Insights. You should be able to immediately see your system in the Insights user interface.

      The initial analysis results will be available shortly thereafter.

      Optional Configuration Steps

      5

      Configure Insights Client to use an HTTP proxy

      If you have a web-based proxy between your system and the Internet, you can configure the Insights client to connect through it using the following commands on the RHV Manager:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Optional proxy configuration. Example: http://user:pass@192.168.100.50:8080
      proxy=
      6

      Red Hat Network Classic Instructions

      If you\'ve registered your system with Red Hat Network Classic you\'ll also need to add a username and password using the following commands on the RHV Manager:

      # vi /etc/redhat-access-insights/redhat-access-insights.conf
      # Red Hat Customer Portal Credentials
      username=
      password=
      ');$templateCache.put('js/states/getting-started/views/getting-started-sat5.html','
      1

      Prerequisites

      • Ensure that you\'re running Satellite 5.7.
      • Install redhat-access-plugin-sat5:
      • # yum install redhat-access-plugin-sat5
      • Restart Satellite
      • # rhn-satellite restart
      • Satellite Organizations with Insights must be running in Connected mode. Any firewalls or proxies through which the Satellite server communicates to Red Hat must allow https communications to https://access.redhat.com
      2

      Activate your evaluation

      If you have purchased an Insights subscription, please skip this step and proceed to the next step.

      To begin using Red Hat Insights, please visit https://access.redhat.com/insights/evaluation to activate any available evaluations associated with your account. This step must be completed prior to any system registration with Insights.

      3

      Enable Red Hat Insights

      Log in to the Satellite Administrator account. Navigate to the Admin --> Insights page. Before enabling Insights, click \'Test Connection to Red Hat Insights API\'. If the test is successful you will see a green check circle next to the button. Otherwise you will see a red circle and a log will appear. When the test is successful, select "Enable Insights Service" then click the "Update" button. The Red Hat Insights GUI pieces will now appear in Satellite.

      4

      Install RPM on Systems

      There are multiple ways to automate this process. See the Register Systems section for details.

      Navigate to the Systems --> Insights --> Setup tab as an Organization Administrator. Select the systems to install the redhat-access-insights RPM onto, then click "Apply".

      Check the status of the RPM installation by navigating to the Schedule --> Pending Actions page.

      This could take some time depending on your Satellite configuration. Enabling push to clients will speed up the install. When the RPM is installed you are ready to move on to the next step.

      5

      Register Your Systems

      To register a system manually:

      # redhat-access-insights --register

      There are multiple ways to automate this process including Red Hat Satellite\'s built-in Configuration Management Channels.

      We\'ve also provided several options for popular configuration management tools below:

      6

      Validate Data was Reported

      Shortly after you execute `redhat-access-insights --register` on at least one machine, navigate to Systems --> Overview in Satellite. Find the system you registered. The status should either be a green check or a red exclamation point.

      If you see a red exclamation, click it to see the reports about your system.

      To view an overview of all system reports, navigate to Systems --> Insights.

      ');$templateCache.put('js/states/getting-started/views/getting-started-sat6.html','
      1

      Prerequisites

      • Ensure that you\u2019re running the latest Satellite 6.1.
      • Ensure that you\u2019ve synchronized the latest RHEL 6.7 and RHEL 7.1.z RPMs to your Satellite, and that they are available to your clients.
      • In order for the Red Hat Satellite Server to be an Insights client, please follow the direct registration instructions and run the following command prior to installing the redhat-access-insights client.
      • # yum update python-requests
      • A manifest must be downloaded and imported into the Satellite Organization in which Insights will be enabled.\nFor more information on managing manifests, see the official Satellite 6 documentation.
      • Satellite Organizations with Insights must be running in Connected mode. Any firewalls or proxies through which the Satellite server communicates to Red Hat must allow https communications to https://cert-api.access.redhat.com
      2

      Activate your evaluation

      If you have purchased an Insights subscription, please skip this step and proceed to the next step.

      To begin using Red Hat Insights, please visit https://access.redhat.com/insights/evaluation to activate any available evaluations associated with your account. This step must be completed prior to any system registration with Insights.

      3

      Verify Connection to Red Hat Insights

      Verify that the Satellite server can successfully communicate with Red Hat Insights. In the Satellite UI, navigate to Insights -> Manage. Verify that the engine connection status is "Connected" and that the account number displayed is correct for your organization.

      4

      Register Your Systems

      To register Satellite clients to Red Hat Insights, you\'ll need to install the client rpm and then register the system.

      Install the RPM:

      # yum install redhat-access-insights

      Register the system:

      # redhat-access-insights --register

      If you are using Red Hat Satellite\u2019s configuration management provided by Puppet this process can be automated by applying the preinstalled Puppet class \'access_insights_clients\'. This class can be imported from the Puppet Master into the appropriate Puppet environment and applied to hosts that you wish to subscribe to Red Hat Insights.\nFor more information on using on Puppet with Satellite 6, refer to the official documentation.

      5

      Viewing Reports and Managing Access From the Satellite UI

      To view Insights reports, navigate to Insights -> Overview in the Satellite 6 UI.

      To view and manage Insights rules, navigate to Insights -> Rules.

      To view the list of systems that are reporting data to Red Hat Insights, navigate to Insights -> Systems.

      To view connection status or enable/disable Red Hat Insights globally for an Organization, navigate to Insights -> Manage.

      6

      Assign Red Hat Insights Roles to Users

      By default, only administrators can view Insights Report and Configuration screens. You can give users who are not administrators access to the Insights screens by assigning them the appropriate roles as follows.

      • To give users access to all Insights screens, including the "Manage" screen, assign them the "Insights Admin" role. In addition, users must be granted the "view_content_host" permission.
      • To give users Insights report and rule viewing access, assign them the "Insights Viewer" role. In addition, users must be granted the "view_content_host" permission.

      For more information on roles refer to the official Satellite 6 documentation.

      ');$templateCache.put('js/states/getting-started/views/getting-started.html','
      ');$templateCache.put('js/states/hidden/components/components.html','

      Jade/HTML Only


      Buttons

      Flat Buttons

      Basic
      md-button
      Primary (No Ink)
      md-button.md-primary(md-no-ink)
      Disabled
      md-button.md-primary(ng-disabled="true")
      Warning
      md-button.md-warn

      Raised Buttons

      Basic
      md-button.md-raised
      Primary (No Ink)
      md-button.md-raised.md-primary(md-no-ink)
      Disabled
      md-button.md-raised.md-primary(ng-disabled="true")
      Warning
      md-button.md-raised.md-warn

      Form Example

      Checkbox

      Checkbox 1: Default
      md-checkbox(aria-label=\'Checkbox 1\')
      Checkbox 2 (md-warn)
      md-checkbox.md-warn(aria-label=\'Checkbox 2\', ng-true-value="\'yup\'", ng-false-value="\'nope\'")
      Checkbox: Disabled
      md-checkbox(ng-disabled=\'true\', aria-label=\'Disabled checkbox\')
      Checkbox (md-primary): No Ink
      md-checkbox.md-primary(md-no-ink=\'\', aria-label=\'Checkbox No Ink\')
      Checkbox: Indeterminate
      md-checkbox.md-primary(md-indeterminate=\'\', aria-label=\'Checkbox Indeterminate\')
      Checkbox: Disabled, Indeterminate
      md-checkbox.md-primary(md-indeterminate=\'\', aria-label=\'Checkbox Disabled Indeterminate\', ng-disabled=\'true\')

      Inputs

      md-input-container.md-block\n label [insert label]\n input

      Radio Buttons

      FooBarInsights
      md-radio-button.md-primary(value="Foo") Foo\nmd-radio-button.md-accent(value="Bar") Bar\nmd-radio-button.md-warn(value="Insights") Insights

      Loading Icons

      All loading icons can be configured to load based on percentage (determinate)

      Progress Linear

      md-progress-linear(md-mode="indeterminate")

      Progress Circular

      md-progress-circular(md-mode="indeterminate")

      Needs Javascript

      Dialog Box

      Confirm Dialog
      md-button.md-primary.md-raised(ng-click="showConfirm($event)") Comfirm Dialog
      function [file-controller]Ctrl(\n $scope, \n $mdDialog) {\n $scope.status = \' \';\n $scope.customFullscreen = false;\n $scope.showConfirm = function (ev) {\n $mdDialog.show(\n .parent(angular.element(document.querySelector(\'#popupContainer\')))\n .clickOutsideToClose(true)\n .title(\'This is a title\')\n .textContent(\'This is the content\')\n .ariaLabel(\'Confirm Dialog Demo\')\n .ok(\'Confirm\')\n .cancel(\'Cancel\');\n .targetEvent(ev)\n );\n };\n}

      Toasts

      Top Left Fail ToastBottom Right Success Toast
      md-button.md-primary.md-raised(ng-click="showToast(\'top\', \'left\', \'fail\')") Top Left Fail Toast\n \nmd-button.md-primary.md-raised(ng-click="showToast(\'bottom\', \'right\', \'success\')") Bottom Right Success Toast
      function [file-controller]Ctrl(\n $scope, \n $mdToast) {\n $scope.showToast = function (h,v,state) {\n $mdToast.show(\n $mdToast.simple()\n .content(\'Toast Content!\')\n .hideDelay(\'3000\')\n .position(h + \' \' + v)\n .toastClass(\'stickyToast\' + \' \' + state)\n );\n }

      Swiping

      Swipe Me Up

      [.element](md-swipe-up="onSwipeUp()")\n h4 Swipe Me Up
      $scope.onSwipeUp = function () {\n window.alert(\'You swiped up!!\');\n};

      Tab Three

      Integer turpis erat, porttitor vitae mi faucibus, laoreet interdum tellus. Curabitur posuere molestie dictum. Morbi eget congue risus, quis rhoncus quam. Suspendisse vitae hendrerit erat, at posuere mi. Cras eu fermentum nunc. Sed id ante eu orci commodo volutpat non ac est. Praesent ligula diam, congue eu enim scelerisque, finibus commodo lectus.

      ');$templateCache.put('js/states/pages/views/402.html','

      402 No Red Hat Account Found

      ');$templateCache.put('js/states/pages/views/403.html','

      403 Forbidden

      ');$templateCache.put('js/states/topics/views/edit-topics.html','

      Topic details

      /actions/

      {{topic.priority + 1}}

      Priority is determined from the order of topics in the  topic admin table

      {{error.content}}
      {{error.summary}}

      Rule selection

      There are multiple ways of associating rules with this topic. Choose the one that best suits the type of this topic.

      {{$select.selected}}{{tag}}

      There is 1 rule currently tagged with this tag. This number may change in the future if additional rules are tagged with  {{topic.tag}}.the tag.(or if the tag is removed from any of these rules)

      If you select an existing tag then all the rules tagged with this tag become part of this topic.\nThis binding is dynamic so if more rules are tagged by this tag in the future, these rules will become part of this topic.\nYou can also create a new tag

      {{$select.selected.description}}
      {{rule.description}}

      You can explicitly pick one or multiple rules.

      No rules selected

      AND

      No rules with this category

      Binds all rules that are of certain severity, belong to a certain category or combination of there of.\nThis binding is dynamic so if more rules are added for this category/severity in the future, these rules will become part of this topic.


      ');$templateCache.put('js/states/topics/views/topic-admin.html','

      Topic administration

      Title URLRulesVisibilityActions
      {{topic.title}}/actions/{{topic.slug ? topic.slug : topic.id}}{{topic.rules.length}} ({{topic.ruleBinding}}) - {{topic.tag}} - {{topic.category}}{{topic.severity}}AlwaysIf systems affectedNever
      No topics
      ');$templateCache.put('js/states/topics/views/topic-list.html','
      ');$templateCache.put('js/components/overview/widgets/goals/goalsLite.html','

      Systems Registered

      {{maxFreeSystems - systemCount}} more Insights entitlements available 
      Your evaluation will expire on {{expiration}}
      Congratulations! You have registered all available systems.
      {{securityErrors}}

      Protect yourself!

      Fix all of your
      {{securityErrors}} security issues

      View security issues

      You have no high severity security errors

      {{stabilityErrors}}

      Increase your uptime!

      Fix all of your
      {{stabilityErrors}} stability issues

      View stability issues

      You have no high severity stability errors

      You have no systems registered. Take full advantage of Red Hat Insights by registering systems.

      ');$templateCache.put('js/components/overview/widgets/handcraftedContent/handcraftedContent.html','

      {{overview.article.title}}

      ');$templateCache.put('js/components/overview/widgets/highPrio/highPrio.html','

      Alerts 

      {{items.staleSystems}} Systems not checking inView Offline Systems
      ');$templateCache.put('js/components/overview/widgets/maintenancePlanLite/maintenancePlanLite.html','

      Plan SummaryView planner

      Newest PlansActions
      {{MaintenanceService.getPlanName(plan)}}{{plan.actionsDone}}/{{plan.actions.length}}
      1 issue can be resolved automatically using Ansible
      ');$templateCache.put('js/components/overview/widgets/newSystems/newSystems.html','

      Newest SystemsView inventory

      SystemRegistration Date
      {{system.toString}}{{system.created_at | timeAgo}}

      No systems registered

      ');$templateCache.put('js/components/overview/widgets/goals/goalsBar/goalsBar.html','
      ');$templateCache.put('js/components/overview/widgets/goals/goalsGauge/goalsGauge.html','
      {{gaugeNumber}}{{gaugeNumber + \'/\' + gaugeMax}}
      ');$templateCache.put('js/components/overview/widgets/maintenancePlanLite/maintenancePlanLiteTable/maintenancePlanLiteTable.html','
      {{plans.length}}Scheduled PlansStartDurationActions
      {{plan.start | moment:\'LL\'}}{{plan.start | moment:\'LT\' | lowercase}}1 minute{{plan.actionsDone}}/{{plan.actions.length}}
      ');}]);},{}],242:[function(require,module,exports){/** * @license AngularJS v1.5.11 * (c) 2010-2017 Google, Inc. 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

      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 link(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). * * ## Usage * 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 Angular. 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];}function createLocalCacheLookup(){var cache=Object.create(null);return{flush:function flush(){cache=Object.create(null);},count:function count(key){var entry=cache[key];return entry?entry.total:0;},get:function get(key){var entry=cache[key];return entry&&entry.value;},put:function put(key,value){if(!cache[key]){cache[key]={total:1,value:value};}else{cache[key].total++;}}};}// 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){var gcsLookup=createLocalCacheLookup();var gcsStaggerLookup=createLocalCacheLookup();this.$get=['$window','$$jqLite','$$AnimateRunner','$timeout','$$forceReflow','$sniffer','$$rAFScheduler','$$animateQueue',function($window,$$jqLite,$$AnimateRunner,$timeout,$$forceReflow,$sniffer,$$rAFScheduler,$$animateQueue){var applyAnimationClasses=applyAnimationClassesFactory($$jqLite);var parentCounter=0;function gcsHashFn(node,extraClasses){var KEY='$$ngAnimateParentKey';var parentNode=node.parentNode;var parentID=parentNode[KEY]||(parentNode[KEY]=++parentCounter);return parentID+'-'+node.getAttribute('class')+'-'+extraClasses;}function computeCachedCssStyles(node,className,cacheKey,properties){var timings=gcsLookup.get(cacheKey);if(!timings){timings=computeCssStyles($window,node,properties);if(timings.animationIterationCount==='infinite'){timings.animationIterationCount=1;}}// 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. gcsLookup.put(cacheKey,timings);return timings;}function computeCachedCssStaggerStyles(node,className,cacheKey,properties){var stagger;// 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(gcsLookup.count(cacheKey)>0){stagger=gcsStaggerLookup.get(cacheKey);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);gcsStaggerLookup.put(cacheKey,stagger);}}return stagger||{};}var rafWaitQueue=[];function waitUntilQuiet(callback){rafWaitQueue.push(callback);$$rAFScheduler.waitUntilQuiet(function(){gcsLookup.flush();gcsStaggerLookup.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;i0;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 cacheKey,stagger;if(options.stagger>0){var staggerVal=parseFloat(options.stagger);stagger={transitionDelay:staggerVal,animationDelay:staggerVal,transitionDuration:0,animationDuration:0};}else{cacheKey=gcsHashFn(node,fullClassName);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:gcsLookup.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){blockTransitions(node,SAFE_FAST_FORWARD_DURATION_VALUE);}var timings=computeTimings(node,fullClassName,cacheKey);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();}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){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 start(){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(_start2);// 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(!options.$$skipPreparationClasses){$$jqLite.removeClass(element,preparationClasses);}$$jqLite.removeClass(element,activeClasses);blockKeyframeAnimations(node,false);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){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 start(){return runner;},end:endFn};}function onAnimationProgress(event){event.stopPropagation();var ev=event.originalEvent||event;// 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 _start2(){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 playPause(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.className+' '+preparationClasses;cacheKey=gcsHashFn(node,fullClassName);timings=computeTimings(node,fullClassName,cacheKey);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;i0;var b=(animation.removeClass||'').length>0;return and?a&&b:a||b;}rules.join.push(function(element,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(element,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(element,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(element,newAnimation,currentAnimation){// if there is an ongoing current animation then don't even bother running the class-based animation return currentAnimation.structural&¤tAnimation.state===RUNNING_STATE&&!newAnimation.structural;});rules.cancel.push(function(element,newAnimation,currentAnimation){// there can never be two structural animations running at the same time return currentAnimation.structural&&newAnimation.structural;});rules.cancel.push(function(element,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(element,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','$$HashMap','$$animation','$$AnimateRunner','$templateRequest','$$jqLite','$$forceReflow',function($$rAF,$rootScope,$rootElement,$document,$$HashMap,$$animation,$$AnimateRunner,$templateRequest,$$jqLite,$$forceReflow){var activeAnimationsLookup=new $$HashMap();var disabledElementsLookup=new $$HashMap();var animationsEnabled=null;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 classNameFilter is set during the provider/config // stage therefore we can optimize here and setup a helper function var classNameFilter=$animateProvider.classNameFilter();var isAnimatableClassName=!classNameFilter?function(){return true;}:function(className){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(parent,element,event){var targetNode=getDomNode(element);var targetParentNode=getDomNode(parent);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,element){if(phase==='close'&&!element[0].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(element);}}var $animate={on:function on(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 off(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 pin(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 push(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 enabled(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 disabledElementsLookup.put(node,!bool);}}}return bool;}};return $animate;function queueAnimation(element,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 node,parent;element=stripCommentsFromElement(element);if(element){node=getDomNode(element);parent=element.parent();}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;}// there are situations where a directive issues an animation for // a jqLite wrapper that contains only comment nodes... If this // happens then there is no way we can perform an animation if(!node){close();return runner;}var className=[node.className,options.addClass,options.removeClass].join(' ');if(!isAnimatableClassName(className)){close();return runner;}var isStructural=['enter','move','leave'].indexOf(event)>=0;var documentHidden=$document[0].hidden;// this is a hard disable of all animations for the application or on // 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=!animationsEnabled||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(element,parent,event);}if(skipAnimations){// Callbacks should fire even if the document is hidden (regression fix for issue #14120) if(documentHidden)notifyProgress(runner,event,'start');close();if(documentHidden)notifyProgress(runner,event,'close');return runner;}if(isStructural){closeChildAnimations(element);}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',element,newAnimation,existingAnimation);if(skipAnimationFlag){if(existingAnimation.state===RUNNING_STATE){close();return runner;}else{mergeAnimationDetails(element,existingAnimation,newAnimation);return existingAnimation.runner;}}var cancelAnimationFlag=isAllowed('cancel',element,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',element,newAnimation,existingAnimation);if(joinAnimationFlag){if(existingAnimation.state===RUNNING_STATE){normalizeAnimationDetails(element,newAnimation);}else{applyGeneratedPreparationClasses(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(element);return runner;}// the counter keeps track of cancelled animations var counter=(existingAnimation.counter||0)+1;newAnimation.counter=counter;markElementAnimationState(element,PRE_DIGEST_STATE,newAnimation);$rootScope.$$postDigest(function(){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(element);}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(element,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',{});realRunner.done(function(status){close(!status);var animationDetails=activeAnimationsLookup.get(node);if(animationDetails&&animationDetails.counter===counter){clearElementAnimationState(getDomNode(element));}notifyProgress(runner,event,'close',{});});});return runner;function notifyProgress(runner,event,phase,data){runInNextPostDigestOrNow(function(){var callbacks=findCallbacks(parent,element,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,element);});}else{cleanupEventListeners(phase,element);}});runner.progress(event,phase,data);}function close(reject){clearGeneratedClasses(element,options);applyAnimationClasses(element,options);applyAnimationStyles(element,options);options.domOperation();runner.complete(!reject);}}function closeChildAnimations(element){var node=getDomNode(element);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.remove(child);break;}}});}function clearElementAnimationState(element){var node=getDomNode(element);node.removeAttribute(NG_ANIMATE_ATTR_NAME);activeAnimationsLookup.remove(node);}function isMatchingElement(nodeOrElmA,nodeOrElmB){return getDomNode(nodeOrElmA)===getDomNode(nodeOrElmB);}/** * 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(element,parentElement,event){var bodyElement=jqLite($document[0].body);var bodyElementDetected=isMatchingElement(element,bodyElement)||element[0].nodeName==='HTML';var rootElementDetected=isMatchingElement(element,$rootElement);var parentAnimationDetected=false;var animateChildren;var elementDisabled=disabledElementsLookup.get(getDomNode(element));var parentHost=jqLite.data(element[0],NG_ANIMATE_PIN_DATA);if(parentHost){parentElement=parentHost;}parentElement=getDomNode(parentElement);while(parentElement){if(!rootElementDetected){// angular 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 rootElementDetected=isMatchingElement(parentElement,$rootElement);}if(parentElement.nodeType!==ELEMENT_NODE){// no point in inspecting the #document element break;}var details=activeAnimationsLookup.get(parentElement)||{};// 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 parentElementDisabled=disabledElementsLookup.get(parentElement);if(parentElementDisabled===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(parentElementDisabled===false){elementDisabled=false;}parentAnimationDetected=details.structural;}if(isUndefined(animateChildren)||animateChildren===true){var value=jqLite.data(parentElement,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(!bodyElementDetected){// 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 bodyElementDetected=isMatchingElement(parentElement,bodyElement);}if(bodyElementDetected&&rootElementDetected){// If both body and root have been found, any other checks are pointless, // as no animation data should live outside the application break;}if(!rootElementDetected){// If no rootElement is detected, check if the parentElement is pinned to another element parentHost=jqLite.data(parentElement,NG_ANIMATE_PIN_DATA);if(parentHost){// The pin target element becomes the next parent element parentElement=getDomNode(parentHost);continue;}}parentElement=parentElement.parentNode;}var allowAnimation=(!parentAnimationDetected||animateChildren)&&elementDisabled!==true;return allowAnimation&&rootElementDetected&&bodyElementDetected;}function markElementAnimationState(element,state,details){details=details||{};details.state=state;var node=getDomNode(element);node.setAttribute(NG_ANIMATE_ATTR_NAME,state);var oldValue=activeAnimationsLookup.get(node);var newValue=oldValue?extend(oldValue,details):details;activeAnimationsLookup.put(node,newValue);}}];}];/* 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';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','$$HashMap','$$rAFScheduler',function($$jqLite,$rootScope,$injector,$$AnimateRunner,$$HashMap,$$rAFScheduler){var animationQueue=[];var applyAnimationClasses=applyAnimationClassesFactory($$jqLite);function sortAnimations(animations){var tree={children:[]};var i,lookup=new $$HashMap();// this is done first beforehand so that the hashmap // is filled with a list of the elements that will be animated for(i=0;i=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 end(){close();},cancel:function cancel(){close(true);}});if(!drivers.length){close();return runner;}setRunner(element,runner);var classes=mergeClasses(element.attr('class'),mergeClasses(options.addClass,options.removeClass));var tempClasses=options.tempClasses;if(tempClasses){classes+=' '+tempClasses;options.tempClasses=null;}var prepareClassName;if(isStructural){prepareClassName='ng-'+event+PREPARE_CLASS_SUFFIX;$$jqLite.addClass(element,prepareClassName);}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){toBeSortedAnimations.push({domNode:getDomNode(animationEntry.from?animationEntry.from.element:animationEntry.element),fn:function triggerAnimationStart(){// 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();var startAnimationFn,closeFn=animationEntry.close;// 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. $$rAFScheduler(sortAnimations(toBeSortedAnimations));});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 beforeStart(){fromAnimation.beforeStart();toAnimation.beforeStart();},close:function close(){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=0;i--){var driverName=drivers[i];var factory=$injector.get(driverName);var driver=factory(animationDetails);if(driver){return driver;}}}function beforeStart(){element.addClass(NG_ANIMATE_CLASSNAME);if(tempClasses){$$jqLite.addClass(element,tempClasses);}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);}element.removeClass(NG_ANIMATE_CLASSNAME);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','$rootScope',function($animate,$rootScope){return{restrict:'A',transclude:'element',terminal:true,priority:600,// we use 600 here to ensure that the directive is caught before others link:function link(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){previousScope=scope.$new();$transclude(previousScope,function(element){previousElement=element;$animate.enter(element,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 Angular 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:ngRepeat#animations ngRepeat} | enter, leave and move | * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave | * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | * | {@link ng.directive:ngClass#animations ngClass} | add and remove (the CSS class(es) present) | * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide} | add and remove (the ng-hide class value) | * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) | * | {@link module:ngMessages#animations ngMessages} | add and remove (ng-active & ng-inactive) | * | {@link module:ngMessages#animations ngMessage} | enter and leave | * * (More information can be found by visiting each the documentation associated with each directive.) * * ## 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 Angular 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; * } * .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; * } * * ``` * * ## 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 angular * // 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 Angular, * 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 Angular 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 Angular 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 angular 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 angular 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;}).directive('ngAnimateSwap',ngAnimateSwapDirective).directive('ngAnimateChildren',$$AnimateChildrenDirective).factory('$$rAFScheduler',$$rAFSchedulerFactory).provider('$$animateQueue',$$AnimateQueueProvider).provider('$$animation',$$AnimationProvider).provider('$animateCss',$AnimateCssProvider).provider('$$animateCssDriver',$$AnimateCssDriverProvider).provider('$$animateJs',$$AnimateJsProvider).provider('$$animateJsDriver',$$AnimateJsDriverProvider);})(window,window.angular);},{}],243:[function(require,module,exports){require('./angular-animate');module.exports='ngAnimate';},{"./angular-animate":242}],244:[function(require,module,exports){/** * @license AngularJS v1.5.11 * (c) 2010-2017 Google, Inc. 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, keypress event, button role | * * Find out more information about each directive by reading the * {@link guide/accessibility ngAria Developer Guide}. * * ## Example * Using ngDisabled with ngAria: * ```html * * ``` * Becomes: * ```html * * ``` * * ## Disabling Attributes * It's possible to disable individual attributes added by ngAria with the * {@link ngAria.$ariaProvider#config config} method. For more details, see the * {@link guide/accessibility Developer Guide}. */var ngAriaModule=angular.module('ngAria',['ng']).provider('$aria',$AriaProvider);/** * Internal Utilities */var nodeBlackList=['BUTTON','A','INPUT','TEXTAREA','SELECT','DETAILS','SUMMARY'];var isNodeOneOf=function isNodeOneOf(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 _config2={ariaHidden:true,ariaChecked:true,ariaReadonly:true,ariaDisabled:true,ariaRequired:true,ariaInvalid:true,ariaValue:true,tabindex:true,bindKeypress:true,bindRoleForClick:true};/** * @ngdoc method * @name $ariaProvider#config * * @param {object} config object to enable/disable specific ARIA attributes * * - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags * - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags * - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags * - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags * - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags * - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags * - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags * - **tabindex** – `{boolean}` – Enables/disables tabindex tags * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and * `li` elements with ng-click * - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div` * using ng-click, making them more accessible to users of assistive technologies * * @description * Enables/disables various ARIA attributes */this.config=function(newConfig){_config2=angular.extend(_config2,newConfig);};function watchExpr(attrName,ariaAttr,nodeBlackList,negate){return function(scope,elem,attr){var ariaCamelName=attr.$normalize(ariaAttr);if(_config2[ariaCamelName]&&!isNodeOneOf(elem,nodeBlackList)&&!attr[ariaCamelName]){scope.$watch(attr[attrName],function(boolVal){// ensure boolean value boolVal=negate?!boolVal:!!boolVal;elem.attr(ariaAttr,boolVal);});}};}/** * @ngdoc service * @name $aria * * @description * @priority 200 * * The $aria service contains helper methods for applying common * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives. * * ngAria injects common accessibility attributes that tell assistive technologies when HTML * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria, * let's review a code snippet from ngAria itself: * *```js * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) { * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false); * }]) *``` * Shown above, the ngAria module creates a directive with the same signature as the * traditional `ng-disabled` directive. But this ngAria version is dedicated to * solely managing accessibility attributes on custom elements. The internal `$aria` service is * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the * developer, `aria-disabled` is injected as an attribute with its value synchronized to the * value in `ngDisabled`. * * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do * anything to enable this feature. The `aria-disabled` attribute is automatically managed * simply as a silent side-effect of using `ng-disabled` with the ngAria module. * * The full list of directives that interface with ngAria: * * **ngModel** * * **ngChecked** * * **ngReadonly** * * **ngRequired** * * **ngDisabled** * * **ngValue** * * **ngShow** * * **ngHide** * * **ngClick** * * **ngDblclick** * * **ngMessages** * * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each * directive. * * * ## Dependencies * Requires the {@link ngAria} module to be installed. */this.$get=function(){return{config:function config(key){return _config2[key];},$$watchExpr:watchExpr};};}ngAriaModule.directive('ngShow',['$aria',function($aria){return $aria.$$watchExpr('ngShow','aria-hidden',[],true);}]).directive('ngHide',['$aria',function($aria){return $aria.$$watchExpr('ngHide','aria-hidden',[],false);}]).directive('ngValue',['$aria',function($aria){return $aria.$$watchExpr('ngValue','aria-checked',nodeBlackList,false);}]).directive('ngChecked',['$aria',function($aria){return $aria.$$watchExpr('ngChecked','aria-checked',nodeBlackList,false);}]).directive('ngReadonly',['$aria',function($aria){return $aria.$$watchExpr('ngReadonly','aria-readonly',nodeBlackList,false);}]).directive('ngRequired',['$aria',function($aria){return $aria.$$watchExpr('ngRequired','aria-required',nodeBlackList,false);}]).directive('ngModel',['$aria',function($aria){function shouldAttachAttr(attr,normalizedAttr,elem,allowBlacklistEls){return $aria.config(normalizedAttr)&&!elem.attr(attr)&&(allowBlacklistEls||!isNodeOneOf(elem,nodeBlackList));}function shouldAttachRole(role,elem){// if element does not have role attribute // AND element type is equal to role (if custom element has a type equaling shape) <-- remove? // AND element is not INPUT return!elem.attr('role')&&elem.attr('type')===role&&elem[0].nodeName!=='INPUT';}function getShape(attr,elem){var type=attr.type,role=attr.role;return(type||role)==='checkbox'||role==='menuitemcheckbox'?'checkbox':(type||role)==='radio'||role==='menuitemradio'?'radio':type==='range'||role==='progressbar'||role==='slider'?'range':'';}return{restrict:'A',require:'ngModel',priority:200,//Make sure watches are fired after any other directives that affect the ngModel value compile:function compile(elem,attr){var shape=getShape(attr,elem);return{pre:function pre(scope,elem,attr,ngModel){if(shape==='checkbox'){//Use the input[checkbox] $isEmpty implementation for elements with checkbox roles ngModel.$isEmpty=function(value){return value===false;};}},post:function post(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',nodeBlackList,false);}]).directive('ngMessages',function(){return{restrict:'A',require:'?ngMessages',link:function link(scope,elem,attr,ngMessages){if(!elem.attr('aria-live')){elem.attr('aria-live','assertive');}}};}).directive('ngClick',['$aria','$parse',function($aria,$parse){return{restrict:'A',compile:function compile(elem,attr){var fn=$parse(attr.ngClick,/* interceptorFn */null,/* expensiveChecks */true);return function(scope,elem,attr){if(!isNodeOneOf(elem,nodeBlackList)){if($aria.config('bindRoleForClick')&&!elem.attr('role')){elem.attr('role','button');}if($aria.config('tabindex')&&!elem.attr('tabindex')){elem.attr('tabindex',0);}if($aria.config('bindKeypress')&&!attr.ngKeypress){elem.on('keypress',function(event){var keyCode=event.which||event.keyCode;if(keyCode===32||keyCode===13){scope.$apply(callback);}function callback(){fn(scope,{$event:event});}});}}};}};}]).directive('ngDblclick',['$aria',function($aria){return function(scope,elem,attr){if($aria.config('tabindex')&&!elem.attr('tabindex')&&!isNodeOneOf(elem,nodeBlackList)){elem.attr('tabindex',0);}};}]);})(window,window.angular);},{}],245:[function(require,module,exports){require('./angular-aria');module.exports='ngAria';},{"./angular-aria":244}],246:[function(require,module,exports){/** * @license AngularJS v1.5.11 * (c) 2010-2017 Google, Inc. http://angularjs.org * License: MIT */(function(window,angular){'use strict';/** * @ngdoc module * @name ngCookies * @description * * # ngCookies * * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies. * * *
      * * See {@link ngCookies.$cookies `$cookies`} for usage. */$$CookieWriter.$inject=['$document','$log','$browser'];angular.module('ngCookies',['ng'])./** * @ngdoc provider * @name $cookiesProvider * @description * Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service. * */provider('$cookies',[/** @this */function $CookiesProvider(){/** * @ngdoc property * @name $cookiesProvider#defaults * @description * * Object containing default options to pass when setting cookies. * * The object may have following properties: * * - **path** - `{string}` - The cookie will be available only for this path and its * sub-paths. By default, this is the URL that appears in your `` tag. * - **domain** - `{string}` - The cookie will be available only for this domain and * its sub-domains. For security reasons the user agent will not accept the cookie * if the current domain is not a sub-domain of this domain or equal to it. * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT" * or a Date object indicating the exact date/time this cookie will expire. * - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a * secured connection. * * Note: By default, the address that appears in your `` tag will be used as the path. * This is important so that cookies will be visible for all routes when html5mode is enabled. * * @example * * ```js * angular.module('cookiesProviderExample', ['ngCookies']) * .config(['$cookiesProvider', function($cookiesProvider) { * // Setting default options * $cookiesProvider.defaults.domain = 'foo.com'; * $cookiesProvider.defaults.secure = true; * }]); * ``` **/var defaults=this.defaults={};function calcOptions(options){return options?angular.extend({},defaults,options):defaults;}/** * @ngdoc service * @name $cookies * * @description * Provides read/write access to browser's cookies. * *
      * Up until Angular 1.3, `$cookies` exposed properties that represented the * current browser cookie values. In version 1.4, this behavior has changed, and * `$cookies` now provides a standard api of getters, setters etc. *
      * * Requires the {@link ngCookies `ngCookies`} module to be installed. * * @example * * ```js * angular.module('cookiesExample', ['ngCookies']) * .controller('ExampleController', ['$cookies', function($cookies) { * // Retrieving a cookie * var favoriteCookie = $cookies.get('myFavorite'); * // Setting a cookie * $cookies.put('myFavorite', 'oatmeal'); * }]); * ``` */this.$get=['$$cookieReader','$$cookieWriter',function($$cookieReader,$$cookieWriter){return{/** * @ngdoc method * @name $cookies#get * * @description * Returns the value of given cookie key * * @param {string} key Id to use for lookup. * @returns {string} Raw cookie value. */get:function get(key){return $$cookieReader()[key];},/** * @ngdoc method * @name $cookies#getObject * * @description * Returns the deserialized value of given cookie key * * @param {string} key Id to use for lookup. * @returns {Object} Deserialized cookie value. */getObject:function getObject(key){var value=this.get(key);return value?angular.fromJson(value):value;},/** * @ngdoc method * @name $cookies#getAll * * @description * Returns a key value object with all the cookies * * @returns {Object} All cookies */getAll:function getAll(){return $$cookieReader();},/** * @ngdoc method * @name $cookies#put * * @description * Sets a value for given cookie key * * @param {string} key Id for the `value`. * @param {string} value Raw value to be stored. * @param {Object=} options Options object. * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} */put:function put(key,value,options){$$cookieWriter(key,value,calcOptions(options));},/** * @ngdoc method * @name $cookies#putObject * * @description * Serializes and sets a value for given cookie key * * @param {string} key Id for the `value`. * @param {Object} value Value to be stored. * @param {Object=} options Options object. * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} */putObject:function putObject(key,value,options){this.put(key,angular.toJson(value),options);},/** * @ngdoc method * @name $cookies#remove * * @description * Remove given cookie * * @param {string} key Id of the key-value pair to delete. * @param {Object=} options Options object. * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} */remove:function remove(key,options){$$cookieWriter(key,undefined,calcOptions(options));}};}];}]);angular.module('ngCookies')./** * @ngdoc service * @name $cookieStore * @deprecated * sinceVersion="v1.4.0" * Please use the {@link ngCookies.$cookies `$cookies`} service instead. * * @requires $cookies * * @description * Provides a key-value (string-object) storage, that is backed by session cookies. * Objects put or retrieved from this storage are automatically serialized or * deserialized by angular's toJson/fromJson. * * Requires the {@link ngCookies `ngCookies`} module to be installed. * * @example * * ```js * angular.module('cookieStoreExample', ['ngCookies']) * .controller('ExampleController', ['$cookieStore', function($cookieStore) { * // Put cookie * $cookieStore.put('myFavorite','oatmeal'); * // Get cookie * var favoriteCookie = $cookieStore.get('myFavorite'); * // Removing a cookie * $cookieStore.remove('myFavorite'); * }]); * ``` */factory('$cookieStore',['$cookies',function($cookies){return{/** * @ngdoc method * @name $cookieStore#get * * @description * Returns the value of given cookie key * * @param {string} key Id to use for lookup. * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist. */get:function get(key){return $cookies.getObject(key);},/** * @ngdoc method * @name $cookieStore#put * * @description * Sets a value for given cookie key * * @param {string} key Id for the `value`. * @param {Object} value Value to be stored. */put:function put(key,value){$cookies.putObject(key,value);},/** * @ngdoc method * @name $cookieStore#remove * * @description * Remove given cookie * * @param {string} key Id of the key-value pair to delete. */remove:function remove(key){$cookies.remove(key);}};}]);/** * @name $$cookieWriter * @requires $document * * @description * This is a private service for writing cookies * * @param {string} name Cookie name * @param {string=} value Cookie value (if undefined, cookie will be deleted) * @param {Object=} options Object with options that need to be stored for the cookie. */function $$CookieWriter($document,$log,$browser){var cookiePath=$browser.baseHref();var rawDocument=$document[0];function buildCookieString(name,value,options){var path,expires;options=options||{};expires=options.expires;path=angular.isDefined(options.path)?options.path:cookiePath;if(angular.isUndefined(value)){expires='Thu, 01 Jan 1970 00:00:00 GMT';value='';}if(angular.isString(expires)){expires=new Date(expires);}var str=encodeURIComponent(name)+'='+encodeURIComponent(value);str+=path?';path='+path:'';str+=options.domain?';domain='+options.domain:'';str+=expires?';expires='+expires.toUTCString():'';str+=options.secure?';secure':'';// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: // - 300 cookies // - 20 cookies per unique domain // - 4096 bytes per cookie var cookieLength=str.length+1;if(cookieLength>4096){$log.warn('Cookie \''+name+'\' possibly not set or overflowed because it was too large ('+cookieLength+' > 4096 bytes)!');}return str;}return function(name,value,options){rawDocument.cookie=buildCookieString(name,value,options);};}$$CookieWriter.$inject=['$document','$log','$browser'];angular.module('ngCookies').provider('$$cookieWriter',/** @this */function $$CookieWriterProvider(){this.$get=$$CookieWriter;});})(window,window.angular);},{}],247:[function(require,module,exports){require('./angular-cookies');module.exports='ngCookies';},{"./angular-cookies":246}],248:[function(require,module,exports){(function(global,factory){'use strict';var fnc;fnc=(typeof exports==='undefined'?'undefined':_typeof2(exports))==='object'&&typeof module!=='undefined'?module.exports=factory(require('angular'),require('moment')):typeof define==='function'&&define.amd?define(['angular','moment'],factory):factory(global.angular,global.moment);})(this,function(angular,moment){// workaround for a weird insights layout if(!angular.module){angular=window.angular;// jshint ignore:line }var Module=angular.module('datePicker',[]);Module.constant('datePickerConfig',{template:'templates/datepicker.html',view:'month',views:['year','month','date','hours','minutes'],momentNames:{year:'year',month:'month',date:'day',hours:'hours',minutes:'minutes'},viewConfig:{year:['years','isSameYear'],month:['months','isSameMonth'],hours:['hours','isSameHour'],minutes:['minutes','isSameMinutes']},step:5});//Moment format filter. Module.filter('mFormat',function(){return function(m,format,tz){if(!moment.isMoment(m)){return moment(m).format(format);}return tz?moment.tz(m,tz).format(format):m.format(format);};});Module.directive('datePicker',['datePickerConfig','datePickerUtils','$interpolate',function datePickerDirective(datePickerConfig,datePickerUtils,$interpolate){//noinspection JSUnusedLocalSymbols return{// this is a bug ? require:'?ngModel',template:'
      ',scope:{model:'=?datePicker',after:'=?',before:'=?',highlights:'=?'},link:function link(scope,element,attrs,ngModel){function prepareViews(){scope.views=datePickerConfig.views.concat();scope.view=attrs.view||datePickerConfig.view;scope.views=scope.views.slice(scope.views.indexOf(attrs.maxView||'year'),scope.views.indexOf(attrs.minView||'minutes')+1);if(scope.views.length===1||scope.views.indexOf(scope.view)===-1){scope.view=scope.views[0];}}function getDate(name){return datePickerUtils.getDate(scope,attrs,name);}var arrowClick=false,tz=scope.tz=attrs.timezone,createMoment=datePickerUtils.createMoment,eventIsForPicker=datePickerUtils.eventIsForPicker,step=parseInt(attrs.step||datePickerConfig.step,10),partial=!!attrs.partial,minDate=getDate('minDate'),maxDate=getDate('maxDate'),pickerID=$interpolate(element[0].id)(scope),now=scope.now=createMoment(),selected=scope.date=createMoment(scope.model||now),autoclose=attrs.autoClose==='true',// Either gets the 1st day from the attributes, or asks moment.js to give it to us as it is localized. firstDay=attrs.firstDay&&attrs.firstDay>=0&&attrs.firstDay<=6?parseInt(attrs.firstDay,10):moment().weekday(0).day(),setDate,prepareViewData,isSame,clipDate,isNow,inValidRange,highlights;datePickerUtils.setParams(tz,firstDay);if(!scope.model){selected.minute(Math.ceil(selected.minute()/step)*step).second(0);}scope.template=attrs.template||datePickerConfig.template;scope.watchDirectChanges=attrs.watchDirectChanges!==undefined;scope.callbackOnSetDate=attrs.dateChange?datePickerUtils.findFunction(scope,attrs.dateChange):undefined;prepareViews();scope.setView=function(nextView){if(scope.views.indexOf(nextView)!==-1){scope.view=nextView;}};scope.selectDate=function(date){if(attrs.disabled){return false;}if(isSame(scope.date,date)){date=scope.date;}date=clipDate(date);if(!date){return false;}scope.date=date;var nextView=scope.views[scope.views.indexOf(scope.view)+1];if(!nextView||partial||scope.model){setDate(date,true);}if(nextView){scope.setView(nextView);}else if(autoclose){element.addClass('hidden');scope.$emit('hidePicker');}else{prepareViewData();}};setDate=function setDate(date,explicit){if(date){if(scope.model!==0&&attrs.datePicker!=='date-picker'||!angular.isDefined(scope.model)&&!angular.isDefined(attrs.datePicker)){scope.model=date;}if(ngModel){ngModel.$setViewValue(date);}}scope.$emit('setDate',scope.model,scope.view,explicit);//This is duplicated in the new functionality. if(scope.callbackOnSetDate){scope.callbackOnSetDate(attrs.datePicker,scope.date,explicit);}};function update(){var view=scope.view;datePickerUtils.setParams(tz,firstDay);if(scope.model&&!arrowClick){scope.date=createMoment(scope.model);arrowClick=false;}var date=scope.date;switch(view){case'year':scope.years=datePickerUtils.getVisibleYears(date);break;case'month':scope.months=datePickerUtils.getVisibleMonths(date);break;case'date':scope.weekdays=scope.weekdays||datePickerUtils.getDaysOfWeek();scope.weeks=datePickerUtils.getVisibleWeeks(date);break;case'hours':scope.hours=datePickerUtils.getVisibleHours(date);break;case'minutes':scope.minutes=datePickerUtils.getVisibleMinutes(date,step);break;}prepareViewData();}function watch(){if(scope.view!=='date'){return scope.view;}return scope.date?scope.date.month():null;}scope.$watch(watch,update);if(scope.watchDirectChanges){scope.$watch('model',function(){arrowClick=false;update();});}if(angular.isDefined(attrs.highlights)){scope.$watchCollection('highlights',function(value){if(angular.isDefined(value)){if(!angular.isArray(value)){throw new Error('Invalid highlights object '+value);}highlights=value.map(function(date){return moment(date);});update();}});}prepareViewData=function prepareViewData(){var view=scope.view,date=scope.date,classes=[],classList='',i,j,k;datePickerUtils.setParams(tz,firstDay);if(view==='date'){var weeks=scope.weeks,week;for(i=0;i=day?6:-1)));var weeks=[];while(weeks.length<6){if(m.year()===startYear&&m.month()>startMonth){break;}weeks.push(this.getDaysOfWeek(m));m.add(7,'d');}return weeks;},getVisibleYears:function getVisibleYears(d){var m=moment(d),year=m.year();m.year(year-year%10);year=m.year();var offset=m.utcOffset()/60,years=[],pushedDate,actualOffset;for(var i=0;i<12;i++){pushedDate=createNewDate(year,0,1,0-offset);actualOffset=pushedDate.utcOffset()/60;if(actualOffset!==offset){pushedDate=createNewDate(year,0,1,0-actualOffset);offset=actualOffset;}years.push(pushedDate);year++;}return years;},getDaysOfWeek:function getDaysOfWeek(m){m=m?m:tz?moment.tz(tz).day(firstDay):moment().day(firstDay);var year=m.year(),month=m.month(),day=m.date(),days=[],pushedDate,offset=m.utcOffset()/60,actualOffset;for(var i=0;i<7;i++){pushedDate=createNewDate(year,month,day,0-offset,0,false);actualOffset=pushedDate.utcOffset()/60;if(actualOffset!==offset){pushedDate=createNewDate(year,month,day,0-actualOffset,0,false);}days.push(pushedDate);day++;}return days;},getVisibleMonths:function getVisibleMonths(m){var year=m.year(),offset=m.utcOffset()/60,months=[],pushedDate,actualOffset;for(var month=0;month<12;month++){pushedDate=createNewDate(year,month,1,0-offset,0,false);actualOffset=pushedDate.utcOffset()/60;if(actualOffset!==offset){pushedDate=createNewDate(year,month,1,0-actualOffset,0,false);}months.push(pushedDate);}return months;},getVisibleHours:function getVisibleHours(m){var year=m.year(),month=m.month(),day=m.date(),hours=[],hour,pushedDate,actualOffset,offset=m.utcOffset()/60;for(hour=0;hour<24;hour++){pushedDate=createNewDate(year,month,day,hour-offset,0,false);actualOffset=pushedDate.utcOffset()/60;if(actualOffset!==offset){pushedDate=createNewDate(year,month,day,hour-actualOffset,0,false);}hours.push(pushedDate);}return hours;},isAfter:function isAfter(model,date){return model&&model.unix()>=date.unix();},isBefore:function isBefore(model,date){return model.unix()<=date.unix();},isSameYear:function isSameYear(model,date){return model&&model.year()===date.year();},isSameMonth:function isSameMonth(model,date){return this.isSameYear(model,date)&&model.month()===date.month();},isSameDay:function isSameDay(model,date){return this.isSameMonth(model,date)&&model.date()===date.date();},isSameHour:function isSameHour(model,date){return this.isSameDay(model,date)&&model.hours()===date.hours();},isSameMinutes:function isSameMinutes(model,date){return this.isSameHour(model,date)&&model.minutes()===date.minutes();},setParams:function setParams(zone,fd){tz=zone;firstDay=fd;},scopeSearch:function scopeSearch(scope,name,comparisonFn){var parentScope=scope,nameArray=name.split('.'),target,i,j=nameArray.length;do{target=parentScope=parentScope.$parent;//Loop through provided names. for(i=0;i'+getTemplate(attrs,pickerIDs[1],'end',scope.start,false)+'';var picker=$compile(template)(scope);element.append(picker);}};}]);var PRISTINE_CLASS='ng-pristine',DIRTY_CLASS='ng-dirty';var Module=angular.module('datePicker');Module.constant('dateTimeConfig',{template:function template(attrs,id){return''+'
      ';},format:'YYYY-MM-DD HH:mm',views:['date','year','month','hours','minutes'],autoClose:false,position:'relative'});Module.directive('dateTimeAppend',function(){return{link:function link(scope,element){element.bind('click',function(){element.find('input')[0].focus();});}};});Module.directive('dateTime',['$compile','$document','$filter','dateTimeConfig','$parse','datePickerUtils','$interpolate',function($compile,$document,$filter,dateTimeConfig,$parse,datePickerUtils,$interpolate){var body=$document.find('body');var dateFilter=$filter('mFormat');return{require:'ngModel',scope:true,link:function link(scope,element,attrs,ngModel){var format=attrs.format||dateTimeConfig.format,parentForm=element.inheritedData('$formController'),views=$parse(attrs.views)(scope)||dateTimeConfig.views.concat(),view=attrs.view||views[0],index=views.indexOf(view),dismiss=attrs.autoClose?$parse(attrs.autoClose)(scope):dateTimeConfig.autoClose,picker=null,pickerID=$interpolate(element[0].id)(scope),position=attrs.position||dateTimeConfig.position,container=null,minDate=null,minValid=null,maxDate=null,maxValid=null,timezone=attrs.timezone||false,eventIsForPicker=datePickerUtils.eventIsForPicker,dateChange=null,shownOnce=false,template;if(index===-1){views.splice(index,1);}views.unshift(view);function formatter(value){if(value){return dateFilter(value,format,timezone);}}function parser(viewValue){if(!viewValue){return'';}var parsed=moment(viewValue,format);if(parsed.isValid()){return parsed;}}function setMin(date){minDate=date;attrs.minDate=date?date.format():date;minValid=moment.isMoment(date);}function setMax(date){maxDate=date;attrs.maxDate=date?date.format():date;maxValid=moment.isMoment(date);}ngModel.$formatters.push(formatter);ngModel.$parsers.unshift(parser);if(angular.isDefined(attrs.minDate)){setMin(datePickerUtils.findParam(scope,attrs.minDate));ngModel.$validators.min=function(value){//If we don't have a min / max value, then any value is valid. return minValid?moment.isMoment(value)&&(minDate.isSame(value)||minDate.isBefore(value)):true;};}if(angular.isDefined(attrs.maxDate)){setMax(datePickerUtils.findParam(scope,attrs.maxDate));ngModel.$validators.max=function(value){return maxValid?moment.isMoment(value)&&(maxDate.isSame(value)||maxDate.isAfter(value)):true;};}if(angular.isDefined(attrs.dateChange)){dateChange=datePickerUtils.findFunction(scope,attrs.dateChange);}function getTemplate(){template=dateTimeConfig.template(attrs);}function updateInput(event){event.stopPropagation();if(ngModel.$pristine){ngModel.$dirty=true;ngModel.$pristine=false;element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);if(parentForm){parentForm.$setDirty();}ngModel.$render();}}function clear(){if(picker){picker.remove();picker=null;}if(container){container.remove();container=null;}}if(pickerID){scope.$on('pickerUpdate',function(event,pickerIDs,data){if(eventIsForPicker(pickerIDs,pickerID)){if(picker){//Need to handle situation where the data changed but the picker is currently open. //To handle this, we can create the inner picker with a random ID, then forward //any events received to it. }else{var validateRequired=false;if(angular.isDefined(data.minDate)){setMin(data.minDate);validateRequired=true;}if(angular.isDefined(data.maxDate)){setMax(data.maxDate);validateRequired=true;}if(angular.isDefined(data.minView)){attrs.minView=data.minView;}if(angular.isDefined(data.maxView)){attrs.maxView=data.maxView;}if(angular.isDefined(data.timezone)){timezone=attrs.timezone=data.timezone;validateRequired=true;}attrs.view=data.view||attrs.view;if(validateRequired){ngModel.$validate();}if(angular.isDefined(data.format)){format=attrs.format=data.format||dateTimeConfig.format;ngModel.$modelValue=-1;//Triggers formatters. This value will be discarded. }getTemplate();}}});}function showPicker(){if(picker){return;}// create picker element picker=$compile(template)(scope);scope.$digest();//If the picker has already been shown before then we shouldn't be binding to events, as these events are already bound to in this scope. if(!shownOnce){scope.$on('setDate',function(event,date,view,explicit){updateInput(event);if(dateChange){dateChange(attrs.ngModel,date,explicit);}if(dismiss&&views[views.length-1]===view){clear();}});scope.$on('hidePicker',function(){element.triggerHandler('blur');});scope.$on('$destroy',clear);shownOnce=true;}// move picker below input element if(position==='absolute'){var pos=element[0].getBoundingClientRect();// Support IE8 var height=pos.height||element[0].offsetHeight;picker.css({top:pos.top+height+'px',left:pos.left+'px',display:'block',position:position});body.append(picker);}else{// relative container=angular.element('
      ');element[0].parentElement.insertBefore(container[0],element[0]);container.append(picker);// this approach doesn't work // element.before(picker); picker.css({top:element[0].offsetHeight+'px',display:'block'});}picker.bind('mousedown',function(evt){evt.preventDefault();});}element.bind('focus',showPicker);element.bind('blur',clear);getTemplate();}};}]);angular.module('datePicker').run(['$templateCache',function($templateCache){$templateCache.put('templates/datepicker.html',"
      \n"+"
      \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+"
      \n"+" \n"+"
      \n"+"
      \n"+"
      \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+"
      \n"+" \n"+"
      \n"+"
      \n"+"
      \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+"
      \n"+" \n"+"
      \n"+"
      \n"+"
      \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+"
      \n"+" \n"+"
      \n"+"
      \n"+"
      \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+" \n"+"
      \n"+" \n"+"
      \n"+"
      \n"+"
      ");}]);});},{"angular":261,"moment":559}],249:[function(require,module,exports){angular.module('gettext',[]);angular.module('gettext').constant('gettext',function(str){/* * Does nothing, simply returns the input string. * * This function serves as a marker for `grunt-angular-gettext` to know that * this string should be extracted for translations. */return str;});angular.module('gettext').factory('gettextCatalog',["gettextPlurals","$http","$cacheFactory","$interpolate","$rootScope",function(gettextPlurals,$http,$cacheFactory,$interpolate,$rootScope){var catalog;var noContext='$$noContext';// IE8 returns UPPER CASE tags, even though the source is lower case. // This can causes the (key) string in the DOM to have a different case to // the string in the `po` files. // IE9, IE10 and IE11 reorders the attributes of tags. var test='test';var isHTMLModified=angular.element(''+test+'').html()!==test;var prefixDebug=function prefixDebug(string){if(catalog.debug&&catalog.currentLanguage!==catalog.baseLanguage){return catalog.debugPrefix+string;}else{return string;}};var addTranslatedMarkers=function addTranslatedMarkers(string){if(catalog.showTranslatedMarkers){return catalog.translatedMarkerPrefix+string+catalog.translatedMarkerSuffix;}else{return string;}};function broadcastUpdated(){$rootScope.$broadcast('gettextLanguageChanged');}catalog={debug:false,debugPrefix:'[MISSING]: ',showTranslatedMarkers:false,translatedMarkerPrefix:'[',translatedMarkerSuffix:']',strings:{},baseLanguage:'en',currentLanguage:'en',cache:$cacheFactory('strings'),setCurrentLanguage:function setCurrentLanguage(lang){this.currentLanguage=lang;broadcastUpdated();},getCurrentLanguage:function getCurrentLanguage(){return this.currentLanguage;},setStrings:function setStrings(language,strings){if(!this.strings[language]){this.strings[language]={};}for(var key in strings){var val=strings[key];if(isHTMLModified){// Use the DOM engine to render any HTML in the key (#131). key=angular.element(''+key+'').html();}if(angular.isString(val)||angular.isArray(val)){// No context, wrap it in $$noContext. var obj={};obj[noContext]=val;val=obj;}// Expand single strings for each context. for(var context in val){var str=val[context];val[context]=angular.isArray(str)?str:[str];}this.strings[language][key]=val;}broadcastUpdated();},getStringForm:function getStringForm(string,n,context){var stringTable=this.strings[this.currentLanguage]||{};var contexts=stringTable[string]||{};var plurals=contexts[context||noContext]||[];return plurals[n];},getString:function getString(string,scope,context){string=this.getStringForm(string,0,context)||prefixDebug(string);string=scope?$interpolate(string)(scope):string;return addTranslatedMarkers(string);},getPlural:function getPlural(n,string,stringPlural,scope,context){var form=gettextPlurals(this.currentLanguage,n);string=this.getStringForm(string,form,context)||prefixDebug(n===1?string:stringPlural);if(scope){scope.$count=n;string=$interpolate(string)(scope);}return addTranslatedMarkers(string);},loadRemote:function loadRemote(url){return $http({method:'GET',url:url,cache:catalog.cache}).then(function(response){var data=response.data;for(var lang in data){catalog.setStrings(lang,data[lang]);}return response;});}};return catalog;}]);angular.module('gettext').directive('translate',["gettextCatalog","$parse","$animate","$compile","$window",function(gettextCatalog,$parse,$animate,$compile,$window){// Trim polyfill for old browsers (instead of jQuery) // Based on AngularJS-v1.2.2 (angular.js#620) var trim=function(){if(!String.prototype.trim){return function(value){return typeof value==='string'?value.replace(/^\s*/,'').replace(/\s*$/,''):value;};}return function(value){return typeof value==='string'?value.trim():value;};}();function assert(condition,missing,found){if(!condition){throw new Error('You should add a '+missing+' attribute whenever you add a '+found+' attribute.');}}var msie=parseInt((/msie (\d+)/.exec(angular.lowercase($window.navigator.userAgent))||[])[1],10);return{restrict:'AE',terminal:true,compile:function compile(element,attrs){// Validate attributes assert(!attrs.translatePlural||attrs.translateN,'translate-n','translate-plural');assert(!attrs.translateN||attrs.translatePlural,'translate-plural','translate-n');var msgid=trim(element.html());var translatePlural=attrs.translatePlural;var translateContext=attrs.translateContext;if(msie<=8){// Workaround fix relating to angular adding a comment node to // anchors. angular/angular.js/#1949 / angular/angular.js/#2013 if(msgid.slice(-13)===''){msgid=msgid.slice(0,-13);}}return{post:function post(scope,element,attrs){var countFn=$parse(attrs.translateN);var pluralScope=null;var linking=true;function update(){// Fetch correct translated string. var translated;if(translatePlural){scope=pluralScope||(pluralScope=scope.$new());scope.$count=countFn(scope);translated=gettextCatalog.getPlural(scope.$count,msgid,translatePlural,null,translateContext);}else{translated=gettextCatalog.getString(msgid,null,translateContext);}var oldContents=element.contents();if(oldContents.length===0){return;}// Avoid redundant swaps if(translated===trim(oldContents.html())){// Take care of unlinked content if(linking){$compile(oldContents)(scope);}return;}// Swap in the translation var newWrapper=angular.element(''+translated+'');$compile(newWrapper.contents())(scope);var newContents=newWrapper.contents();$animate.enter(newContents,element);$animate.leave(oldContents);}if(attrs.translateN){scope.$watch(attrs.translateN,update);}scope.$on('gettextLanguageChanged',update);update();linking=false;}};}};}]);angular.module('gettext').filter('translate',["gettextCatalog",function(gettextCatalog){function filter(input,context){return gettextCatalog.getString(input,null,context);}filter.$stateful=true;return filter;}]);// Do not edit this file, it is autogenerated using genplurals.py! angular.module("gettext").factory("gettextPlurals",function(){return function(langCode,n){switch(langCode){case"ay":// Aymará case"bo":// Tibetan case"cgg":// Chiga case"dz":// Dzongkha case"fa":// Persian case"id":// Indonesian case"ja":// Japanese case"jbo":// Lojban case"ka":// Georgian case"kk":// Kazakh case"km":// Khmer case"ko":// Korean case"ky":// Kyrgyz case"lo":// Lao case"ms":// Malay case"my":// Burmese case"sah":// Yakut case"su":// Sundanese case"th":// Thai case"tt":// Tatar case"ug":// Uyghur case"vi":// Vietnamese case"wo":// Wolof case"zh":// Chinese // 1 form return 0;case"is":// Icelandic // 2 forms return n%10!=1||n%100==11?1:0;case"jv":// Javanese // 2 forms return n!=0?1:0;case"mk":// Macedonian // 2 forms return n==1||n%10==1?0:1;case"ach":// Acholi case"ak":// Akan case"am":// Amharic case"arn":// Mapudungun case"br":// Breton case"fil":// Filipino case"fr":// French case"gun":// Gun case"ln":// Lingala case"mfe":// Mauritian Creole case"mg":// Malagasy case"mi":// Maori case"oc":// Occitan case"pt_BR":// Brazilian Portuguese case"tg":// Tajik case"ti":// Tigrinya case"tr":// Turkish case"uz":// Uzbek case"wa":// Walloon case"zh":// Chinese // 2 forms return n>1?1:0;case"lv":// Latvian // 3 forms return n%10==1&&n%100!=11?0:n!=0?1:2;case"lt":// Lithuanian // 3 forms return n%10==1&&n%100!=11?0:n%10>=2&&(n%100<10||n%100>=20)?1:2;case"be":// Belarusian case"bs":// Bosnian case"hr":// Croatian case"ru":// Russian case"sr":// Serbian case"uk":// Ukrainian // 3 forms return n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2;case"mnk":// Mandinka // 3 forms return n==0?0:n==1?1:2;case"ro":// Romanian // 3 forms return n==1?0:n==0||n%100>0&&n%100<20?1:2;case"pl":// Polish // 3 forms return n==1?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2;case"cs":// Czech case"sk":// Slovak // 3 forms return n==1?0:n>=2&&n<=4?1:2;case"sl":// Slovenian // 4 forms return n%100==1?1:n%100==2?2:n%100==3||n%100==4?3:0;case"mt":// Maltese // 4 forms return n==1?0:n==0||n%100>1&&n%100<11?1:n%100>10&&n%100<20?2:3;case"gd":// Scottish Gaelic // 4 forms return n==1||n==11?0:n==2||n==12?1:n>2&&n<20?2:3;case"cy":// Welsh // 4 forms return n==1?0:n==2?1:n!=8&&n!=11?2:3;case"kw":// Cornish // 4 forms return n==1?0:n==2?1:n==3?2:3;case"ga":// Irish // 5 forms return n==1?0:n==2?1:n<7?2:n<11?3:4;case"ar":// Arabic // 6 forms return n==0?0:n==1?1:n==2?2:n%100>=3&&n%100<=10?3:n%100>=11?4:5;default:// Everything else return n!=1?1:0;}};});},{}],250:[function(require,module,exports){/* jshint ignore:start *//* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorithm, as defined in RFC 1321. * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for more info. */angular.module('md5',[]).constant('md5',function(){/* * Configurable variables. You may need to tweak these to be compatible with * the server-side, but the defaults work in most cases. */var hexcase=0;/* hex output format. 0 - lowercase; 1 - uppercase */var b64pad="";/* base-64 pad character. "=" for strict RFC compliance *//* * These are the functions you'll usually want to call * They take string arguments and return either hex or base-64 encoded strings */function hex_md5(s){return rstr2hex(rstr_md5(str2rstr_utf8(s)));}function b64_md5(s){return rstr2b64(rstr_md5(str2rstr_utf8(s)));}function any_md5(s,e){return rstr2any(rstr_md5(str2rstr_utf8(s)),e);}function hex_hmac_md5(k,d){return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d)));}function b64_hmac_md5(k,d){return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d)));}function any_hmac_md5(k,d,e){return rstr2any(rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d)),e);}/* * Perform a simple self-test to see if the VM is working */function md5_vm_test(){return hex_md5("abc").toLowerCase()=="900150983cd24fb0d6963f7d28e17f72";}/* * Calculate the MD5 of a raw string */function rstr_md5(s){return binl2rstr(binl_md5(rstr2binl(s),s.length*8));}/* * Calculate the HMAC-MD5, of a key and some data (raw strings) */function rstr_hmac_md5(key,data){var bkey=rstr2binl(key);if(bkey.length>16)bkey=binl_md5(bkey,key.length*8);var ipad=Array(16),opad=Array(16);for(var i=0;i<16;i++){ipad[i]=bkey[i]^0x36363636;opad[i]=bkey[i]^0x5C5C5C5C;}var hash=binl_md5(ipad.concat(rstr2binl(data)),512+data.length*8);return binl2rstr(binl_md5(opad.concat(hash),512+128));}/* * Convert a raw string to a hex string */function rstr2hex(input){try{hexcase;}catch(e){hexcase=0;}var hex_tab=hexcase?"0123456789ABCDEF":"0123456789abcdef";var output="";var x;for(var i=0;i>>4&0x0F)+hex_tab.charAt(x&0x0F);}return output;}/* * Convert a raw string to a base-64 string */function rstr2b64(input){try{b64pad;}catch(e){b64pad='';}var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var output="";var len=input.length;for(var i=0;iinput.length*8)output+=b64pad;else output+=tab.charAt(triplet>>>6*(3-j)&0x3F);}}return output;}/* * Convert a raw string to an arbitrary string encoding */function rstr2any(input,encoding){var divisor=encoding.length;var i,j,q,x,quotient;/* Convert to an array of 16-bit big-endian values, forming the dividend */var dividend=Array(Math.ceil(input.length/2));for(i=0;i0||q>0)quotient[quotient.length]=q;}remainders[j]=x;dividend=quotient;}/* Convert the remainders to the output string */var output="";for(i=remainders.length-1;i>=0;i--){output+=encoding.charAt(remainders[i]);}return output;}/* * Encode a string as utf-8. * For efficiency, this assumes the input is valid utf-16. */function str2rstr_utf8(input){var output="";var i=-1;var x,y;while(++i>>6&0x1F,0x80|x&0x3F);else if(x<=0xFFFF)output+=String.fromCharCode(0xE0|x>>>12&0x0F,0x80|x>>>6&0x3F,0x80|x&0x3F);else if(x<=0x1FFFFF)output+=String.fromCharCode(0xF0|x>>>18&0x07,0x80|x>>>12&0x3F,0x80|x>>>6&0x3F,0x80|x&0x3F);}return output;}/* * Encode a string as utf-16 */function str2rstr_utf16le(input){var output="";for(var i=0;i>>8&0xFF);}return output;}function str2rstr_utf16be(input){var output="";for(var i=0;i>>8&0xFF,input.charCodeAt(i)&0xFF);}return output;}/* * Convert a raw string to an array of little-endian words * Characters >255 have their high-byte silently ignored. */function rstr2binl(input){var output=Array(input.length>>2);for(var i=0;i>5]|=(input.charCodeAt(i/8)&0xFF)<>5]>>>i%32&0xFF);}return output;}/* * Calculate the MD5 of an array of little-endian words, and a bit length. */function binl_md5(x,len){/* append padding */x[len>>5]|=0x80<>>9<<4)+14]=len;var a=1732584193;var b=-271733879;var c=-1732584194;var d=271733878;for(var i=0;i>16)+(y>>16)+(lsw>>16);return msw<<16|lsw&0xFFFF;}/* * Bitwise rotate a 32-bit number to the left. */function bit_rol(num,cnt){return num<>>32-cnt;}return hex_md5;}());/* jshint ignore:end */(function(){var gravatarDirectiveFactory;gravatarDirectiveFactory=function gravatarDirectiveFactory(bindOnce){return['gravatarService',function(gravatarService){var filterKeys;filterKeys=function filterKeys(prefix,object){var k,retVal,v;retVal={};for(k in object){v=object[k];if(k.indexOf(prefix)!==0){continue;}k=k.substr(prefix.length).toLowerCase();if(k.length>0){retVal[k]=v;}}return retVal;};return{restrict:'A',link:function link(scope,element,attrs){var directiveName,item,opts,unbind;directiveName=bindOnce?'gravatarSrcOnce':'gravatarSrc';item=attrs[directiveName];delete attrs[directiveName];opts=filterKeys('gravatar',attrs);unbind=scope.$watch(item,function(newVal){element.attr('src',gravatarService.url(newVal,opts));if(bindOnce){if(newVal==null){return;}unbind();}});}};}];};angular.module('ui.gravatar',['md5']).provider('gravatarService',['md5',function(md5){var hashRegex,self,serialize;self=this;hashRegex=/^[0-9a-f]{32}$/i;serialize=function serialize(object){var k,params,v;params=[];for(k in object){v=object[k];params.push(""+k+"="+encodeURIComponent(v));}return params.join('&');};this.defaults={};this.secure=false;this.protocol=null;this.urlFunc=function(opts){var params,pieces,prefix,src,urlBase;prefix=opts.protocol?opts.protocol+':':'';urlBase=opts.secure?'https://secure':prefix+'//www';src=hashRegex.test(opts.src)?opts.src:md5(opts.src);pieces=[urlBase,'.gravatar.com/avatar/',src];params=serialize(opts.params);if(params.length>0){pieces.push('?'+params);}return pieces.join('');};this.$get=[function(){return{url:function url(src,params){if(src==null){src='';}if(params==null){params={};}return self.urlFunc({params:angular.extend(angular.copy(self.defaults),params),protocol:self.protocol,secure:self.secure,src:src});}};}];return this;}]).directive('gravatarSrc',gravatarDirectiveFactory()).directive('gravatarSrcOnce',gravatarDirectiveFactory(true));}).call(this);},{}],251:[function(require,module,exports){/* * angular-loading-bar * * intercepts XHR requests and creates a loading bar. * Based on the excellent nprogress work by rstacruz (more info in readme) * * (c) 2013 Wes Cruver * License: MIT */(function(){'use strict';// Alias the loading bar for various backwards compatibilities since the project has matured: angular.module('angular-loading-bar',['cfp.loadingBarInterceptor']);angular.module('chieffancypants.loadingBar',['cfp.loadingBarInterceptor']);/** * loadingBarInterceptor service * * Registers itself as an Angular interceptor and listens for XHR requests. */angular.module('cfp.loadingBarInterceptor',['cfp.loadingBar']).config(['$httpProvider',function($httpProvider){var interceptor=['$q','$cacheFactory','$timeout','$rootScope','$log','cfpLoadingBar',function($q,$cacheFactory,$timeout,$rootScope,$log,cfpLoadingBar){/** * The total number of requests made */var reqsTotal=0;/** * The number of requests completed (either successfully or not) */var reqsCompleted=0;/** * The amount of time spent fetching before showing the loading bar */var latencyThreshold=cfpLoadingBar.latencyThreshold;/** * $timeout handle for latencyThreshold */var startTimeout;/** * calls cfpLoadingBar.complete() which removes the * loading bar from the DOM. */function setComplete(){$timeout.cancel(startTimeout);cfpLoadingBar.complete();reqsCompleted=0;reqsTotal=0;}/** * Determine if the response has already been cached * @param {Object} config the config option from the request * @return {Boolean} retrns true if cached, otherwise false */function isCached(config){var cache;var defaultCache=$cacheFactory.get('$http');var defaults=$httpProvider.defaults;// Choose the proper cache source. Borrowed from angular: $http service if((config.cache||defaults.cache)&&config.cache!==false&&(config.method==='GET'||config.method==='JSONP')){cache=angular.isObject(config.cache)?config.cache:angular.isObject(defaults.cache)?defaults.cache:defaultCache;}var cached=cache!==undefined?cache.get(config.url)!==undefined:false;if(config.cached!==undefined&&cached!==config.cached){return config.cached;}config.cached=cached;return cached;}return{'request':function request(config){// Check to make sure this request hasn't already been cached and that // the requester didn't explicitly ask us to ignore this request: if(!config.ignoreLoadingBar&&!isCached(config)){$rootScope.$broadcast('cfpLoadingBar:loading',{url:config.url});if(reqsTotal===0){startTimeout=$timeout(function(){cfpLoadingBar.start();},latencyThreshold);}reqsTotal++;cfpLoadingBar.set(reqsCompleted/reqsTotal);}return config;},'response':function response(_response){if(!_response||!_response.config){$log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');return _response;}if(!_response.config.ignoreLoadingBar&&!isCached(_response.config)){reqsCompleted++;$rootScope.$broadcast('cfpLoadingBar:loaded',{url:_response.config.url,result:_response});if(reqsCompleted>=reqsTotal){setComplete();}else{cfpLoadingBar.set(reqsCompleted/reqsTotal);}}return _response;},'responseError':function responseError(rejection){if(!rejection||!rejection.config){$log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');return $q.reject(rejection);}if(!rejection.config.ignoreLoadingBar&&!isCached(rejection.config)){reqsCompleted++;$rootScope.$broadcast('cfpLoadingBar:loaded',{url:rejection.config.url,result:rejection});if(reqsCompleted>=reqsTotal){setComplete();}else{cfpLoadingBar.set(reqsCompleted/reqsTotal);}}return $q.reject(rejection);}};}];$httpProvider.interceptors.push(interceptor);}]);/** * Loading Bar * * This service handles adding and removing the actual element in the DOM. * Generally, best practices for DOM manipulation is to take place in a * directive, but because the element itself is injected in the DOM only upon * XHR requests, and it's likely needed on every view, the best option is to * use a service. */angular.module('cfp.loadingBar',[]).provider('cfpLoadingBar',function(){this.includeSpinner=true;this.includeBar=true;this.latencyThreshold=100;this.startSize=0.02;this.parentSelector='body';this.spinnerTemplate='
      ';this.loadingBarTemplate='
      ';this.$get=['$injector','$document','$timeout','$rootScope',function($injector,$document,$timeout,$rootScope){var $animate;var $parentSelector=this.parentSelector,loadingBarContainer=angular.element(this.loadingBarTemplate),loadingBar=loadingBarContainer.find('div').eq(0),spinner=angular.element(this.spinnerTemplate);var incTimeout,completeTimeout,started=false,status=0;var includeSpinner=this.includeSpinner;var includeBar=this.includeBar;var startSize=this.startSize;/** * Inserts the loading bar element into the dom, and sets it to 2% */function _start(){if(!$animate){$animate=$injector.get('$animate');}var $parent=$document.find($parentSelector).eq(0);$timeout.cancel(completeTimeout);// do not continually broadcast the started event: if(started){return;}$rootScope.$broadcast('cfpLoadingBar:started');started=true;if(includeBar){$animate.enter(loadingBarContainer,$parent,angular.element($parent[0].lastChild));}if(includeSpinner){$animate.enter(spinner,$parent,angular.element($parent[0].lastChild));}_set(startSize);}/** * Set the loading bar's width to a certain percent. * * @param n any value between 0 and 1 */function _set(n){if(!started){return;}var pct=n*100+'%';loadingBar.css('width',pct);status=n;// increment loadingbar to give the illusion that there is always // progress but make sure to cancel the previous timeouts so we don't // have multiple incs running at the same time. $timeout.cancel(incTimeout);incTimeout=$timeout(function(){_inc();},250);}/** * Increments the loading bar by a random amount * but slows down as it progresses */function _inc(){if(_status()>=1){return;}var rnd=0;// TODO: do this mathmatically instead of through conditions var stat=_status();if(stat>=0&&stat<0.25){// Start out between 3 - 6% increments rnd=(Math.random()*(5-3+1)+3)/100;}else if(stat>=0.25&&stat<0.65){// increment between 0 - 3% rnd=Math.random()*3/100;}else if(stat>=0.65&&stat<0.9){// increment between 0 - 2% rnd=Math.random()*2/100;}else if(stat>=0.9&&stat<0.99){// finally, increment it .5 % rnd=0.005;}else{// after 99%, don't increment: rnd=0;}var pct=_status()+rnd;_set(pct);}function _status(){return status;}function _completeAnimation(){status=0;started=false;}function _complete(){if(!$animate){$animate=$injector.get('$animate');}$rootScope.$broadcast('cfpLoadingBar:completed');_set(1);$timeout.cancel(completeTimeout);// Attempt to aggregate any start/complete calls within 500ms: completeTimeout=$timeout(function(){var promise=$animate.leave(loadingBarContainer,_completeAnimation);if(promise&&promise.then){promise.then(_completeAnimation);}$animate.leave(spinner);},500);}return{start:_start,set:_set,status:_status,inc:_inc,complete:_complete,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize};}];// });// wtf javascript. srsly })();// },{}],252:[function(require,module,exports){/*! * AngularJS Material Design * https://github.com/angular/material * @license MIT * v1.1.7 */(function(window,angular,undefined){"use strict";(function(){"use strict";angular.module('ngMaterial',["ng","ngAnimate","ngAria","material.core","material.core.interaction","material.core.gestures","material.core.layout","material.core.meta","material.core.theming.palette","material.core.theming","material.core.animate","material.components.autocomplete","material.components.backdrop","material.components.button","material.components.bottomSheet","material.components.card","material.components.checkbox","material.components.chips","material.components.colors","material.components.content","material.components.dialog","material.components.datepicker","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.showHide","material.components.select","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. */qDecorator.$inject=['$delegate'];rAFDecorator.$inject=['$delegate'];DetectNgTouch.$inject=['$log','$injector'];MdCoreConfigure.$inject=['$provide','$mdThemingProvider'];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} callback 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'];MdAutofocusDirective.$inject=["$parse"];angular.module('material.core').directive('mdAutofocus',MdAutofocusDirective)// Support the deprecated md-auto-focus and md-sidenav-focus as well .directive('mdAutoFocus',MdAutofocusDirective).directive('mdSidenavFocus',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 color {string} * @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 color {string} * @returns {string} */function rgbToRgba(color){return color.replace(')',', 0.1)').replace('(','a(');}/** * Converts an RGBA color to RGB * @param color {string} * @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;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]);}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 isInputKey(e){return e.keyCode>=31&&e.keyCode<=90;},isNumPadKey:function isNumPadKey(e){return 3===e.location&&e.keyCode>=97&&e.keyCode<=105;},isMetaKey:function isMetaKey(e){return e.keyCode>=91&&e.keyCode<=93;},isFnLockKey:function isFnLockKey(e){return e.keyCode>=112&&e.keyCode<=145;},isNavigationKey:function isNavigationKey(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 hasModifierKey(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 items Array list which this iterator will enumerate * @param reloop Boolean enables iterator to consider the list as an endless reloop */function MdIterator(items,reloop){var trueFn=function trueFn(){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'];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;}]);}]);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-1;}function hasPercent(value){return String(value).indexOf('%')>-1;}var $mdUtil={dom:{},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 {!angular.ngModelCtrl} ngModelCtrl * @param {!string} optionName * @returns {Object|undefined} */getModelOption:function getModelOption(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];},/** * Bi-directional accessor/mutator used to easily update an element's * property based on the current 'dir'ectional value. */bidi:function bidi(element,property,lValue,rValue){var ltr=!($document[0].dir=='rtl'||$document[0].body.dir=='rtl');// 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 bidiProperty(element,lProperty,rProperty,value){var ltr=!($document[0].dir=='rtl'||$document[0].body.dir=='rtl');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 clientRect(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 offsetRect(element,offsetParent){return $mdUtil.clientRect(element,offsetParent,true);},// Annoying method to copy nodes to an array, thanks to IE nodesToArray:function nodesToArray(nodes){nodes=nodes||[];var results=[];for(var i=0;i'+'
      '+'');element.append(scrollMask);}scrollMask.on('wheel',preventDefault);scrollMask.on('touchmove',preventDefault);return function restoreElementScroll(){scrollMask.off('wheel');scrollMask.off('touchmove');if(!options.disableScrollMask&&scrollMask[0].parentNode){scrollMask[0].parentNode.removeChild(scrollMask[0]);}};function preventDefault(e){e.preventDefault();}}// 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();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
      ').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... forceFocus:function forceFocus(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 createBackdrop(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 supplant(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 fakeNgModel(){return{$fake:true,$setTouched:angular.noop,$setViewValue:function $setViewValue(value){this.$viewValue=value;this.$render(value);this.$viewChangeListeners.forEach(function(cb){cb();});},$isEmpty:function $isEmpty(value){return(''+value).length===0;},$parsers:[],$formatters:[],$viewChangeListeners:[],$render:angular.noop};},// Returns 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. // @param wait Integer value of msecs to delay (since last debounce reset); default value 10 msecs // @param invokeApply should the $timeout trigger $digest() dirty checking debounce:function debounce(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);};},// Returns a function that can only be triggered every `delay` milliseconds. // In other words, the function will not be called unless it has been more // than `delay` milliseconds since the last call. 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 valueOnUse(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 get(){if(value===null)value=getter.apply(scope,params);return value;}});},/** * Get a unique ID. * * @returns {string} an unique numeric string */nextUid:function nextUid(){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. 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 above. 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;}},/* * getClosest replicates jQuery.closest() to walk up the DOM tree until it finds a matching nodeName * * @param el Element to start walking the DOM from * @param check Either a string or a function. 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 onlyParent Only start checking from the parent element, not `el`. */getClosest:function getClosest(el,validateWith,onlyParent){if(angular.isString(validateWith)){var tagName=validateWith.toUpperCase();validateWith=function validateWith(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) */elementContains:function elementContains(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 {[]} elements to scan * @param {string} name of node to find (e.g. 'md-dialog') * @param {boolean=} optional flag to allow deep scans; defaults to 'false'. * @param {boolean=} optional flag to enable log warnings; defaults to false */extractElementByName:function extractElementByName(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');$document[0].body.appendChild(testEl[0]);var stickyProps=['sticky','-webkit-sticky'];for(var i=0;iscrollEnd){$$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} A copy of the array containing only unique values. */uniq:function uniq(array){if(!array){return;}return array.filter(function(value,index,self){return self.indexOf(value)===index;});}};// 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";/** * @ngdoc module * @name material.core.aria * @description * Aria Expectations for AngularJS Material components. */MdAriaService.$inject=['$$rAF','$log','$window','$interpolate'];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 element * @param attrName * @param {optional} 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);}}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()||'';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 * var lastType = $mdInteraction.getLastInteractionType(); * * if (lastType === 'keyboard') { * // We only restore the focus for keyboard users. * restoreFocus(); * } * * */function MdInteractionService($timeout,$mdUtil){this.$timeout=$timeout;this.$mdUtil=$mdUtil;this.bodyElement=angular.element(document.body);this.isBuffering=false;this.bufferTimeout=null;this.lastInteractionType=null;this.lastInteractionTime=null;// 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();}/** * Initializes the interaction service, by registering all interaction events to the * body element. */MdInteractionService.prototype.initializeEvents=function(){// IE browsers can also trigger pointer events, which also leads to an interaction. var pointerEvent='MSPointerEvent'in window?'MSPointerDown':'PointerEvent'in window?'pointerdown':null;this.bodyElement.on('keydown mousedown',this.onInputEvent.bind(this));if('ontouchstart'in document.documentElement){this.bodyElement.on('touchstart',this.onBufferInputEvent.bind(this));}if(pointerEvent){this.bodyElement.on(pointerEvent,this.onInputEvent.bind(this));}};/** * 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";attachToDocument.$inject=['$mdGesture','$$MdGestureHandler'];MdGesture.$inject=["$$MdGestureHandler","$$rAF","$timeout"];attachToDocument.$inject=["$mdGesture","$$MdGestureHandler"];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,forceSkipClickHijack=false,maxClickDistance=6;/** * The position of the most recent click if that click was on a label element. * @type {{x: number, y: number}?} */var lastLabelClickPos=null;// Used to attach event listeners once when multiple ng-apps are running. var isInitialized=false;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 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 hijcacking clicks, change default 6px click distance * $mdGestureProvider.setMaxClickDistance(12); * * }); * * */function MdGestureProvider(){}MdGestureProvider.prototype={// Publish access to setter to configure a variable BEFORE the // $mdGesture service is instantiated... skipClickHijack:function skipClickHijack(){return forceSkipClickHijack=true;},setMaxClickDistance:function setMaxClickDistance(clickDistance){maxClickDistance=parseInt(clickDistance);},/** * $get is used to build an instance of $mdGesture * @ngInject */$get:["$$MdGestureHandler","$$rAF","$timeout",function($$MdGestureHandler,$$rAF,$timeout){return new MdGesture($$MdGestureHandler,$$rAF,$timeout);}]};/** * MdGesture factory construction function * @ngInject */function MdGesture($$MdGestureHandler,$$rAF,$timeout){var userAgent=navigator.userAgent||navigator.vendor||window.opera;var isIos=userAgent.match(/ipad|iphone|ipod/i);var isAndroid=userAgent.match(/android/i);var touchActionProperty=getTouchAction();var hasJQuery=typeof window.jQuery!=='undefined'&&angular.element===window.jQuery;var self={handler:addHandler,register:register,isAndroid:isAndroid,isIos:isIos,// On mobile w/out jQuery, we normally intercept clicks. Should we skip that? isHijackingClicks:(isIos||isAndroid)&&!hasJQuery&&!forceSkipClickHijack};if(self.isHijackingClicks){self.handler('click',{options:{maxDistance:maxClickDistance},onEnd:checkDistanceAndEmit('click')});self.handler('focus',{options:{maxDistance:maxClickDistance},onEnd:function onEnd(ev,pointer){if(pointer.distance 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 onCancel(){$timeout.cancel(this.state.timeout);},onStart:function onStart(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 onMove(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 onEnd(){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},onSetup:function onSetup(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';}},onCleanup:function onCleanup(element){if(this.oldTouchAction){element[0].style[touchActionProperty]=this.oldTouchAction;}},onStart:function onStart(ev){// For drag, require a parent to be registered with $mdGesture.register() if(!this.state.registeredParent)this.cancel();},onMove:function onMove(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 onEnd(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 onEnd(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);}}});function getTouchAction(){var testEl=document.createElement('div');var vendorPrefixes=['','webkit','Moz','MS','ms','o'];for(var i=0;i0?'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.compiler * @description * AngularJS Material template and element compiler. */MdCompilerProvider.$inject=['$compileProvider'];angular.module('material.core').provider('$mdCompiler',MdCompilerProvider);/** * @ngdoc service * @name $mdCompiler * @module material.core.compiler * @description * The $mdCompiler service is an abstraction of AngularJS's compiler, that allows developers * to easily 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`. * * @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. * }); * * */MdCompilerProvider.$inject=['$compileProvider'];function MdCompilerProvider($compileProvider){var provider=this;/** * @name $mdCompilerProvider#respectPreAssignBindingsEnabled * * @param {boolean=} respected update the respectPreAssignBindingsEnabled state if provided, otherwise just return * the current Material preAssignBindingsEnabled state * @returns {*} current value if used as getter or itself (chaining) if used as setter * * @description * Call this method to enable/disable whether Material-specific (dialogs/toasts) controllers respect the AngularJS * `$compile.preAssignBindingsEnabled` flag. Note that this doesn't affect directives/components created via * regular AngularJS methods which constitute most Material & user-created components. * * @see [AngularJS documentation for `$compile.preAssignBindingsEnabled` * ](https://code.angularjs.org/1.6.4/docs/api/ng/provider/$compileProvider#preAssignBindingsEnabled) * for more information. * * If disabled (false), the compiler assigns the value of each of the bindings to the * properties of the controller object before the constructor of this object is called. * * If enabled (true) the behavior depends on the AngularJS version used: * * - `<1.5.10` - bindings are pre-assigned * - `>=1.5.10 <1.7` - behaves like set to whatever `$compileProvider.preAssignBindingsEnabled()` reports; if * the `$compileProvider` flag wasn't set manually, it defaults to pre-assigning bindings * with AngularJS `1.5.x` & to calling the constructor first with AngularJS `1.6.x`. * - `>=1.7` - the compiler calls the constructor first before assigning bindings * * The default value is `false` but will change to `true` in AngularJS Material 1.2. * * It is recommended to set this flag to `true` in AngularJS Material 1.1.x; the only reason it's not set that way * by default is backwards compatibility. Not setting the flag to `true` when * `$compileProvider.preAssignBindingsEnabled()` is set to `false` (i.e. default behavior in AngularJS 1.6.0 or newer) * makes it hard to unit test Material Dialog/Toast controllers using the `$controller` helper as it always follows * the `$compileProvider.preAssignBindingsEnabled()` value. */// TODO change it to `true` in Material 1.2. var respectPreAssignBindingsEnabled=false;this.respectPreAssignBindingsEnabled=function(respected){if(angular.isDefined(respected)){respectPreAssignBindingsEnabled=respected;return this;}return respectPreAssignBindingsEnabled;};/** * @description * This function returns `true` if Material-specific (dialogs/toasts) controllers have bindings pre-assigned in * controller constructors and `false` otherwise. * * Note that this doesn't affect directives/components created via regular AngularJS methods which constitute most * Material & user-created components; their behavior can be checked via `$compileProvider.preAssignBindingsEnabled()` * in AngularJS `>=1.5.10 <1.7.0`. * * @returns {*} current preAssignBindingsEnabled state */function getPreAssignBindingsEnabled(){if(!respectPreAssignBindingsEnabled){// respectPreAssignBindingsEnabled === false // We're ignoring the AngularJS `$compileProvider.preAssignBindingsEnabled()` value in this case. return true;}// respectPreAssignBindingsEnabled === true if(typeof $compileProvider.preAssignBindingsEnabled==='function'){return $compileProvider.preAssignBindingsEnabled();}// Flag respected but not present => apply logic based on AngularJS version used. if(angular.version.major===1&&angular.version.minor<6){// AngularJS <1.5.10 return true;}// AngularJS >=1.7.0 return false;}this.$get=["$q","$templateRequest","$injector","$compile","$controller",function($q,$templateRequest,$injector,$compile,$controller){return new MdCompilerService($q,$templateRequest,$injector,$compile,$controller);}];function MdCompilerService($q,$templateRequest,$injector,$compile,$controller){/** @private @const {!angular.$q} */this.$q=$q;/** @private @const {!angular.$templateRequest} */this.$templateRequest=$templateRequest;/** @private @const {!angular.$injector} */this.$injector=$injector;/** @private @const {!angular.$compile} */this.$compile=$compile;/** @private @const {!angular.$controller} */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 {Object} promise A promise, which will be resolved with a `compileData` object. * `compileData` has the following properties: * * - `element` - `{element}`: 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 coppied 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. */MdCompilerService.prototype._prepareContentElement=function(options){var contentElement=this._fetchContentElement(options);return this.$q.resolve({element:contentElement.element,cleanup:contentElement.restore,locals:{},link:function link(){return contentElement.element;}});};/** * Compiles a template by considering all options and waiting for all resolves to be ready. * @param {!Object} options Compile options * @returns {!Object} 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 {!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);// 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 * @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){// The third and fourth arguments to $controller are considered private and are undocumented: // https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L86 // Passing `true` as the third argument causes `$controller` to return a function that // gets the controller instance instead returning of the instance directly. When the // controller is defined as a function, `invokeCtrl.instance` is the *same instance* as // `invokeCtrl()`. However, then the controller is an ES6 class, `invokeCtrl.instance` is a // *different instance* from `invokeCtrl()`. var invokeCtrl=this.$controller(options.controller,injectLocals,true,options.controllerAs);if(getPreAssignBindingsEnabled()&&options.bindToController){angular.extend(invokeCtrl.instance,locals);}// Instantiate and initialize the specified controller. var ctrl=invokeCtrl();if(!getPreAssignBindingsEnabled()&&options.bindToController){angular.extend(ctrl,locals);}// 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=null;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 restoreFn(){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";angular.module('material.core').provider('$$interimElement',InterimElementProvider);/* * @ngdoc service * @name $$interimElement * @module material.core * * @description * * Factory that contructs `$$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. * * ```js * 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'];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'];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 $$interimElement.$service * * @description * A service used to control inserting and removing an element into the DOM. * */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; // ... used as $mdDialog, $mdToast, $mdMenu, and $mdSelect return service={show:show,hide:waitForInterim(hide),cancel:waitForInterim(cancel),destroy:destroy,$injector_:$injector};/* * @ngdoc method * @name $$interimElement.$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. To external cancel/hide, developers should use the * * @param {*} options is hashMap of settings * @returns a Promise * */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().catch(function(reason){return reason;}).finally(function(){showPromises.splice(showPromises.indexOf(showAction),1);showingInterims.push(interimElement);});});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 $$interimElement.$service#hide * @kind function * * @description * Removes the `$interimElement` from the DOM and resolves the promise returned from `show` * * @param {*} resolveParam Data to resolve the promise with * @returns a Promise that will be resolved after the element has been removed. * */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]);function closeElement(interim){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 $$interimElement.$service#cancel * @kind function * * @description * Removes the `$interimElement` from the DOM and rejects the promise returned from `show` * * @param {*} reason Data to reject the promise with * @returns Promise that will be resolved after the element has been removed. * */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);};}/* * Special method to quick-remove the interim element without animations * 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 autoHided and transition-in effects */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 */function compileElement(options){var compiled=!options.skipCompile?$mdCompiler.compile(options):null;return compiled||$q(function(resolve){resolve({locals:{},link:function link(){return options.element;}});});}/** * Link an element with compiled configuration */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 */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 cancelAutoHide(){$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-Hide */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{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,controller)).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 disableLayouts(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'))// !! Deprecated attributes: use the `-lt` (aka less-than) notations .directive('layoutLtMd',warnAttrNotSupported('layout-lt-md',true)).directive('layoutLtLg',warnAttrNotSupported('layout-lt-lg',true)).directive('flexLtMd',warnAttrNotSupported('flex-lt-md',true)).directive('flexLtLg',warnAttrNotSupported('flex-lt-lg',true)).directive('layoutAlignLtMd',warnAttrNotSupported('layout-align-lt-md')).directive('layoutAlignLtLg',warnAttrNotSupported('layout-align-lt-lg')).directive('flexOrderLtMd',warnAttrNotSupported('flex-order-lt-md')).directive('flexOrderLtLg',warnAttrNotSupported('flex-order-lt-lg')).directive('offsetLtMd',warnAttrNotSupported('flex-offset-lt-md')).directive('offsetLtLg',warnAttrNotSupported('flex-offset-lt-lg')).directive('hideLtMd',warnAttrNotSupported('hide-lt-md')).directive('hideLtLg',warnAttrNotSupported('hide-lt-lg')).directive('showLtMd',warnAttrNotSupported('show-lt-md')).directive('showLtLg',warnAttrNotSupported('show-lt-lg'))// 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 compile(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 compile(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;}};}];/** * Add as transformed class selector(s), then * remove the deprecated attribute selector */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 compile(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 as transformed class selector, then * remove the deprecated attribute 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);}};}/** * Provide console warning that this layout attribute has been deprecated * */function warnAttrNotSupported(className){var parts=className.split("-");return["$log",function($log){$log.warn(className+"has been deprecated. Please use a `"+parts[0]+"-gt-` variant.");return angular.noop;}];}/** * 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;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'];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 $get(){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'];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 notFoundError(handle,msgContext){$log.error((msgContext||"")+'No instance found for handle',handle);},/** * Return all registered instances as an array. */getInstances:function getInstances(){return instances;},/** * Get a registered instance. * @param handle the String handle to look up for a registered instance. */get:function get(handle){if(!isValidID(handle))return null;var i,j,instance;for(i=0,j=instances.length;i * * 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 link(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 }); * } * }); * * * ### Disabling ripples globally * If you want to disable ink ripples globally, for all components, you can call the * `disableInkRipple` method in your app's config. * * * 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 $mdInkRipple#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 color {string} * @param [multiplier] {int} * @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(){autoCleanup(this,this.clearRipples);};/** * 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.$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'];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 A100','contrastStrongLightColors':'400 500 600 700 A200 A400 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 A100','contrastStrongLightColors':'500 600 A200 A400 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 A100','contrastStrongLightColors':'300 400 A200 A400 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 A100','contrastStrongLightColors':'300 400 A200'},'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 A100','contrastStrongLightColors':'300 400 A200 A400'},'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','contrastDarkColors':'50 100 200 300 400 A100','contrastStrongLightColors':'500 600 700 A200 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','contrastLightColors':'600 700 800 900 A700','contrastStrongLightColors':'600 700 800 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','contrastLightColors':'700 800 900','contrastStrongLightColors':'700 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','contrastLightColors':'500 600 700 800 900','contrastStrongLightColors':'500 600 700'},'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','contrastLightColors':'500 600 700 800 900','contrastStrongLightColors':'500 600 700'},'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','contrastLightColors':'700 800 900','contrastStrongLightColors':'700 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','contrastLightColors':'900','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','contrastLightColors':'800 900','contrastStrongLightColors':'800 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':'light','contrastDarkColors':'50 100 200 300 400 A100 A200','contrastStrongLightColors':'500 600 700 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 A100 A200','contrastStrongLightColors':'300 400'},'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':'600 700 800 900 A200 A400 A700'},'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 A100 A200','contrastStrongLightColors':'400 500 700'}});})();(function(){"use strict";(function(angular){'use strict';/** * @ngdoc module * @name material.core.theming * @description * Theming */generateAllThemes.$inject=['$injector','$mdTheming'];detectDisabledThemes.$inject=['$mdThemingProvider'];ThemingProvider.$inject=['$mdColorPalette','$$mdMetaProvider'];ThemableDirective.$inject=['$mdTheming'];ThemingDirective.$inject=['$mdTheming','$interpolate','$parse','$mdUtil','$q','$log'];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' * }); * }); * *//** * @ngdoc method * @name $mdThemingProvider#registerStyles * @param {string} styles The styles to be appended to AngularJS Material's built in theme css. *//** * @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. *//** * @ngdoc method * @name $mdThemingProvider#setDefaultTheme * @param {string} themeName Default theme name to be applied to elements. Default value is `default`. *//** * @ngdoc method * @name $mdThemingProvider#alwaysWatchTheme * @param {boolean} watch Whether or not to always watch themes for changes and re-apply * classes when they change. Default is `false`. Enabling can reduce performance. *//** * @ngdoc method * @name $mdThemingProvider#enableBrowserColor * @param {Object=} options Options object for the browser color
      * `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`
      * @returns {Function} remove function of the browser color *//* 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 * * Foreground expansion: Applies rgba to black/white foreground text * * {{foreground-1}} - used for primary text * {{foreground-2}} - used for secondary text/divider * {{foreground-3}} - used for disabled text * {{foreground-4}} - used for dividers * */// 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 on light and dark backgrounds // @see https://www.google.com/design/spec/style/color.html#color-text-background-colors var DARK_FOREGROUND={name:'dark','1':'rgba(0,0,0,0.87)','2':'rgba(0,0,0,0.54)','3':'rgba(0,0,0,0.38)','4':'rgba(0,0,0,0.12)'};var LIGHT_FOREGROUND={name:'light','1':'rgba(255,255,255,1.0)','2':'rgba(255,255,255,0.7)','3':'rgba(255,255,255,0.5)','4':'rgba(255,255,255,0.12)'};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'}};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'];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 setBrowserColor(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();};};/** * Enables browser header coloring * for more info please visit: * https://developers.google.com/web/fundamentals/design-and-ui/browser-customization/theme-color * * The default color is `800` from `primary` palette of the `default` theme * * options are: * `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` * * @param {Object=} options Options object for the browser color * @returns {Function} remove function of the browser color */var enableBrowserColor=function enableBrowserColor(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];return setBrowserColor(color);};return themingProvider={definePalette:definePalette,extendPalette:extendPalette,theme:registerTheme,/** * return a read-only clone of the current theme configuration */configuration:function configuration(){return angular.extend({},themeConfig,{defaultTheme:defaultTheme,alwaysWatchTheme:_alwaysWatchTheme,registeredStyles:[].concat(themeConfig.registeredStyles)});},/** * Easy way to disable theming without having to use * `.constant("$MD_THEME_CSS","");` This disables * all dynamic theme style sheet generations and injections... */disableTheming:function disableTheming(isDisabled){themeConfig.disableTheming=angular.isUndefined(isDisabled)||!!isDisabled;},registerStyles:function registerStyles(styles){themeConfig.registeredStyles.push(styles);},setNonce:function setNonce(nonceValue){themeConfig.nonce=nonceValue;},generateThemesOnDemand:function generateThemesOnDemand(onDemand){themeConfig.generateOnDemand=onDemand;},setDefaultTheme:function setDefaultTheme(theme){defaultTheme=theme;},alwaysWatchTheme:function alwaysWatchTheme(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};// Example: $mdThemingProvider.definePalette('neonRed', { 50: '#f5fafa', ... }); function definePalette(name,map){map=map||{};PALETTES[name]=checkPaletteValid(name,map);return themingProvider;}// Returns an new object which is a copy of a given palette `name` with variables from // `map` overwritten // Example: var neonRedMap = $mdThemingProvider.extendPalette('red', { 50: '#f5fafafa' }); 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;}// Register a theme (which is a collection of color palettes to use with various states // ie. warn, accent, primary ) // Optionally inherit from an existing theme // $mdThemingProvider.theme('custom-theme').primaryPalette('red'); 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;};self[colorType+'Color']=function(){var args=Array.prototype.slice.call(arguments);// eslint-disable-next-line no-console console.warn('$mdThemingProviderTheme.'+colorType+'Color() has been deprecated. '+'Use $mdThemingProviderTheme.'+colorType+'Palette() instead.');return self[colorType+'Palette'].apply(self,args);};});}/** * @ngdoc service * @name $mdTheming * @module material.core.theming * * @description * * Service that makes an element apply theming related classes to itself. * * * app.directive('myFancyDirective', function($mdTheming) { * return { * restrict: 'e', * link: function(scope, el, attrs) { * $mdTheming(el); * * $mdTheming.defineTheme('myTheme', { * primary: 'blue', * accent: 'pink', * dark: true * }) * } * }; * }); * * @param {element=} element to apply theming to *//** * @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 name {string} theme name to generate *//** * @ngdoc method * @name $mdTheming#setBrowserColor * @description * Sets browser header coloring * for more info please visit: * https://developers.google.com/web/fundamentals/design-and-ui/browser-customization/theme-color * * The default color is `800` from `primary` palette of the `default` theme * * options are:
      * `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` * * @param {Object} options Options object for the browser color * @returns {Function} remove function of the browser color *//** * @ngdoc method * @name $mdTheming#defineTheme * @description * Dynamically define a theme by an options object * * options are:
      * `primary` - The primary palette of the theme.
      * `accent` - The accent palette of the theme.
      * `warn` - The warn palette of the theme.
      * `background` - The background palette of the theme.
      * `dark` - Indicates if it's a dark theme.
      * * @param {String} name Theme name to define * @param {Object} options Theme definition options * @returns {Promise} A resolved promise with the theme name *//* @ngInject */function ThemingService($rootScope,$mdUtil,$q,$log){// Allow us to be invoked via a linking function signature. var applyTheme=function applyTheme(scope,el){if(el===undefined){el=scope;scope=undefined;}if(scope===undefined){scope=$rootScope;}applyTheme.inherit(el,el);};Object.defineProperty(applyTheme,'THEMES',{get:function get(){return angular.extend({},THEMES);}});Object.defineProperty(applyTheme,'PALETTES',{get:function get(){return angular.extend({},PALETTES);}});Object.defineProperty(applyTheme,'ALWAYS_WATCH',{get:function get(){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);}if(options.accent){theme.accentPalette(options.accent);}if(options.warn){theme.warnPalette(options.warn);}if(options.background){theme.backgroundPalette(options.background);}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');updateThemeClass(lookupThemeName());if(ctrl){var watchTheme=_alwaysWatchTheme||ctrl.$shouldWatch||$mdUtil.parseAttributeBoolean(el.attr('md-theme-watch'));var unwatch=ctrl.registerChanges(function(name){updateThemeClass(name);if(!watchTheme){unwatch();}else{el.on('$destroy',unwatch);}});}/** * 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 pre(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 ctrl={registerChanges:function registerChanges(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 $setTheme(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 getTheme=function getTheme(){var interpolation=$interpolate(attrs.mdTheme)(scope);return $parse(interpolation)(scope)||interpolation;};var setParsedTheme=function setParsedTheme(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');var simpleVariableRegex=/'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue-[0-3]|shadow|default)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g;// 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,opacity,contrast){if(colorType==='foreground'){if(hue=='shadow'){return theme.foregroundShadow;}else{return theme.foregroundPalette[hue]||theme.foregroundPalette['1'];}}// `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];}return rgba((PALETTES[theme.colors[colorType].name][hue]||'')[contrast?'contrast':'value'],opacity);});// Matches '{{ primary-color }}', etc var hueRegex=new RegExp('(\'|")?{{\\s*([a-zA-Z]+)-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}("|\')?','g');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,opacity){var color=theme.colors[matchedColorType];var palette=PALETTES[color.name];var hueValue=color.hues[hueName];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 themable // components as templates // Break the CSS into individual rules var rules=themeCss.split(/\}(?!(\}|'|"|;))/).filter(function(rule){return rule&&rule.trim().length;}).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) function sanitizePalette(palette,name){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;// 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));}palette[hueName]={hex:palette[hueName],value:rgbValue,contrast:getContrastColor()};function getContrastColor(){if(defaultContrast==='light'){if(darkColors.indexOf(hueName)>-1){return DARK_CONTRAST_COLOR;}else{return strongLightColors.indexOf(hueName)>-1?STRONG_LIGHT_CONTRAST_COLOR:LIGHT_CONTRAST_COLOR;}}else{if(lightColors.indexOf(hueName)>-1){return strongLightColors.indexOf(hueName)>-1?STRONG_LIGHT_CONTRAST_COLOR:LIGHT_CONTRAST_COLOR;}else{return DARK_CONTRAST_COLOR;}}}});}}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(', ')));}}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,10):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";// 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 translate3d(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 waitTransitionEnd(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 calculateTransformValues(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 */function currentBounds(){var cntr=element?element.parent():null;var parent=cntr?cntr.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 calculateZoomToOrigin(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 calculateSlideToOrigin(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 toCss(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). */toTransformCss:function toTransformCss(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 */copyRect:function copyRect(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 */clientRect:function clientRect(element){var bounds=angular.element(element)[0].getBoundingClientRect();var isPositiveSizeClientRect=function isPositiveSizeClientRect(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 */centerPointFor:function centerPointFor(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 setHost(host){this.host=host||{};},done:function done(fn){if(this._state===DONE_COMPLETE_STATE){fn();}else{this._doneCallbacks.push(fn);}},progress:angular.noop,getPromise:function getPromise(){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 then(resolveHandler,rejectHandler){return this.getPromise().then(resolveHandler,rejectHandler);},'catch':function _catch(handler){return this.getPromise()['catch'](handler);},'finally':function _finally(handler){return this.getPromise()['finally'](handler);},pause:function pause(){if(this.host.pause){this.host.pause();}},resume:function resume(){if(this.host.resume){this.host.resume();}},end:function end(){if(this.host.end){this.host.end();}this._resolve(true);},cancel:function cancel(){if(this.host.cancel){this.host.cancel();}this._resolve(false);},complete:function complete(response){var self=this;if(self._state===INITIAL_STATE){self._state=DONE_PENDING_STATE;self._runInAnimationFrame(function(){self._resolve(response);});}},_resolve:function _resolve(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 start(){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 eventFn(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 animationDuration */function camelCase(str){return str.replace(/-[a-z]/g,function(str){return str.charAt(1).toUpperCase();});}})();}})();(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";/* * @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.button * @description * * Button */MdAnchorDirective.$inject=['$mdTheming'];MdButtonDirective.$inject=['$mdButtonInkRipple','$mdTheming','$mdAria','$mdInteraction'];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 if(isAnchor(attr)&&angular.isDefined(attr.ngDisabled)){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.bottomSheet * @description * BottomSheet */MdBottomSheetProvider.$inject=['$$interimElementProvider'];MdBottomSheetDirective.$inject=['$mdBottomSheet'];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(); * } * * }); * *//** * @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} options 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. * - `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. * - `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 bottomsheet (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){bottomSheetDefaults.$inject=['$animate','$mdConstant','$mdUtil','$mdTheming','$mdBottomSheet','$rootElement','$mdGesture','$log'];// 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};function onShow(scope,element,options,controller){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.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 programatic focus allowed. // This allows clicks on backdrop to propogate 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);}var bottomSheet=new BottomSheet(element,options.parent);options.bottomSheet=bottomSheet;$mdTheming.inherit(bottomSheet.element,options.parent);if(options.disableParentScroll){options.restoreScroll=$mdUtil.disableScrollAround(bottomSheet.element,options.parent);}return $animate.enter(bottomSheet.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){var bottomSheet=options.bottomSheet;if(!options.disableBackdrop)$animate.leave(backdrop);return $animate.leave(bottomSheet.element).then(function(){if(options.disableParentScroll){options.restoreScroll();delete options.restoreScroll;}bottomSheet.cleanup();});}/** * BottomSheet class to apply bottom-sheet behavior to an element */function BottomSheet(element,parent){var deregister=$mdGesture.register(parent,'drag',{horizontal:false});parent.on('$md.dragstart',onDragStart).on('$md.drag',onDrag).on('$md.dragend',onDragEnd);return{element:element,cleanup:function cleanup(){deregister();parent.off('$md.dragstart',onDragStart);parent.off('$md.drag',onDrag);parent.off('$md.dragend',onDragEnd);}};function onDragStart(ev){// 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.card * * @description * Card components. */mdCardDirective.$inject=['$mdTheming'];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 link($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'];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](http://www.google.com/design/spec/style/color.html#color-color-schemes) * the checkbox is in the accent color by default. The primary color palette may be used with * the `md-primary` class. * * @param {string} 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 {string=} ng-change AngularJS expression to be executed when input changes due to user interaction with the input element. * @param {boolean=} md-no-ink Use of attribute indicates use of ripple ink 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);return{pre:function pre(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];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});$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]);}});}}function keypressHandler(ev){var keyCode=ev.which||ev.keyCode;if(keyCode===$mdConstant.KEY_CODE.SPACE||keyCode===$mdConstant.KEY_CODE.ENTER){ev.preventDefault();element.addClass('md-focused');listener(ev);}}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(element[0].hasAttribute('disabled')||scope.skipToggle){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 element.toggleClass('md-checked',!!ngModelCtrl.$viewValue&&!isIndeterminate);}function setIndeterminateState(newValue){isIndeterminate=newValue!==false;if(isIndeterminate){element.attr('aria-checked','mixed');}element.toggleClass('md-indeterminate',isIndeterminate);}}}}})();(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";(function(){"use strict";/** * Use a RegExp to check if the `md-colors=""` is static string * or one that should be observed and dynamically interpolated. */MdColorsService.$inject=['$mdTheming','$mdUtil','$log'];MdColorsDirective.$inject=['$mdColors','$mdUtil','$log','$parse'];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 * With only defining themes, one couldn't get non AngularJS Material elements colored with Material colors, * `$mdColors` service is used by the md-color directive to convert the 1..n color expressions to RGBA values and will apply * those values to element as CSS property values. * * @usage * * angular.controller('myCtrl', function ($mdColors) { * var color = $mdColors.getThemeColor('myTheme-red-200-0.5'); * ... * }); * * */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 * Gets a color json object, keys are css properties and values are string of the wanted color * Then calculate the rgba() values based on the theme color parts * * @param {DOMElement} element the element to apply the styles on. * @param {object} colorExpression json object, keys are css properties and values are string of the wanted color, * for example: `{color: 'red-A200-0.3'}`. * * @usage * * app.directive('myDirective', function($mdColors) { * return { * ... * link: function (scope, elem) { * $mdColors.applyThemeColors(elem, {color: 'red'}); * } * } * }); * */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 parsed color from expression * * @param {string} expression string of a color expression (for instance `'red-700-0.8'`) * * @returns {string} a css color expression (for instance `rgba(211, 47, 47, 0.8)`) * * @usage * * angular.controller('myCtrl', function ($mdColors) { * var color = $mdColors.getThemeColor('myTheme-red-200-0.5'); * ... * }); * */function getThemeColor(expression){var color=extractColorOptions(expression);return parseColor(color);}/** * Return the parsed color * @param color hashmap of color definitions * @param contrast whether use contrast color for foreground * @returns 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 * * @results Hashmap of CSS properties with associated `rgba( )` string vales * * */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 * e.g. * 'myTheme-primary' => true * 'red-800' => false */function hasTheme(expression){return angular.isDefined($mdTheming.THEMES[expression.split('-')[0]]);}/** * For the evaluated expression, extract the color parts into a hash map */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 */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;}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 our color defining in the scss 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 *
      *
      *
      * * `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 compile(tElem,tAttrs){var shouldWatch=shouldColorsWatch();return function(scope,element,attrs,ctrl){var mdThemeController=ctrl[0];var lastColors={};var parseColors=function parseColors(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 up the DOM tree * we add mdTheme theme to colors who doesn't specified a theme * * # example * *
      *
      * Color demo *
      *
      *
      * * 'primary-600' will be 'myTheme-primary-600', * but 'mySecondTheme-accent-200' will stay the same cause it has a theme prefix */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;};var cleanElement=function cleanElement(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);}};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'];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 link(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.dialog */MdDialogProvider.$inject=['$$interimElementProvider'];MdDialogDirective.$inject=['$$rAF','$mdTheming','$mdDialog'];MdDialogDirective.$inject=["$$rAF","$mdTheming","$mdDialog"];MdDialogProvider.$inject=["$$interimElementProvider"];angular.module('material.components.dialog',['material.core','material.components.backdrop']).directive('mdDialog',MdDialogDirective).provider('$mdDialog',MdDialogProvider);/** * @ngdoc directive * @name mdDialog * @module material.components.dialog * * @restrict E * * @description * `` - The dialog's template must be inside this element. * * Inside, use an `` element for the dialog's content, and use * an `` element for the dialog's actions. * * ## CSS * - `.md-dialog-content` - class that sets the padding on the content as the spec file * * ## Notes * - If you specify an `id` for the ``, the `` will have the same `id` * prefixed with `dialogContent_`. * * @usage * ### Dialog template * * * * * *

      Number {{item}}

      *
      *
      *
      * * Close Dialog * *
      *
      */function MdDialogDirective($$rAF,$mdTheming,$mdDialog){return{restrict:'E',link:function link(scope,element){element.addClass('_md');// private md component indicator for styling $mdTheming(element);$$rAF(function(){var images;var content=element[0].querySelector('md-dialog-content');if(content){images=content.getElementsByTagName('img');addOverflowClass();//-- delayed image loading may impact scroll height, check after images are loaded angular.element(images).on('load',addOverflowClass);}scope.$on('$destroy',function(){$mdDialog.destroy(element);});/** * */function addOverflowClass(){element.toggleClass('md-content-overflow',content.scrollHeight>content.clientHeight);}});}};}/** * @ngdoc service * @name $mdDialog * @module material.components.dialog * * @description * `$mdDialog` opens a dialog over the app to inform users about critical information or require * them to make decisions. There are two approaches for setup: a simple promise API * and regular object syntax. * * ## Restrictions * * - The dialog is always given an isolate scope. * - The dialog's template must have an outer `` element. * Inside, use an `` element for the dialog's content, and use * an `` element for the dialog's actions. * - Dialogs must cover the entire application to keep interactions inside of them. * Use the `parent` option to change where dialogs are appended. * * ## Sizing * - Complex dialogs can be sized with `flex="percentage"`, i.e. `flex="66"`. * - Default max-width is 80% of the `rootElement` or `parent`. * * ## CSS * - `.md-dialog-content` - class that sets the padding on the content as the spec file * * @usage * *
      *
      * * Employee Alert! * *
      *
      * * Custom Dialog * *
      *
      * * Close Alert * *
      *
      * * Greet Employee * *
      *
      *
      * * ### JavaScript: object syntax * * (function(angular, undefined){ * "use strict"; * * angular * .module('demoApp', ['ngMaterial']) * .controller('AppCtrl', AppController); * * function AppController($scope, $mdDialog) { * var alert; * $scope.showAlert = showAlert; * $scope.showDialog = showDialog; * $scope.items = [1, 2, 3]; * * // Internal method * function showAlert() { * alert = $mdDialog.alert({ * title: 'Attention', * textContent: 'This is an example of how easy dialogs can be!', * ok: 'Close' * }); * * $mdDialog * .show( alert ) * .finally(function() { * alert = undefined; * }); * } * * function showDialog($event) { * var parentEl = angular.element(document.body); * $mdDialog.show({ * parent: parentEl, * targetEvent: $event, * template: * '' + * ' '+ * ' '+ * ' '+ * '

      Number {{item}}

      ' + * ' '+ * '
      '+ * '
      ' + * ' ' + * ' ' + * ' Close Dialog' + * ' ' + * ' ' + * '
      ', * locals: { * items: $scope.items * }, * controller: DialogController * }); * function DialogController($scope, $mdDialog, items) { * $scope.items = items; * $scope.closeDialog = function() { * $mdDialog.hide(); * } * } * } * } * })(angular); *
      * * ### Multiple Dialogs * Using the `multiple` option for the `$mdDialog` service allows developers to show multiple dialogs * at the same time. * * * // From plain options * $mdDialog.show({ * multiple: true * }); * * // From a dialog preset * $mdDialog.show( * $mdDialog * .alert() * .multiple(true) * ); * * * * ### Pre-Rendered Dialogs * By using the `contentElement` option, it is possible to use an already existing element in the DOM. * * > Pre-rendered dialogs will be not linked to any scope and will not instantiate any new controller.
      * > You can manually link the elements to a scope or instantiate a controller from the template (`ng-controller`) * * * $scope.showPrerenderedDialog = function() { * $mdDialog.show({ * contentElement: '#myStaticDialog', * parent: angular.element(document.body) * }); * }; * * * When using a string as value, `$mdDialog` will automatically query the DOM for the specified CSS selector. * * *
      *
      * * This is a pre-rendered dialog. * *
      *
      *
      * * **Notice**: It is important, to use the `.md-dialog-container` as the content element, otherwise the dialog * will not show up. * * It also possible to use a DOM element for the `contentElement` option. * - `contentElement: document.querySelector('#myStaticDialog')` * - `contentElement: angular.element(TEMPLATE)` * * When using a `template` as content element, it will be not compiled upon open. * This allows you to compile the element yourself and use it each time the dialog opens. * * ### Custom Presets * Developers are also able to create their own preset, which can be easily used without repeating * their options each time. * * * $mdDialogProvider.addPreset('testPreset', { * options: function() { * return { * template: * '' + * 'This is a custom preset' + * '', * controllerAs: 'dialog', * bindToController: true, * clickOutsideToClose: true, * escapeToClose: true * }; * } * }); * * * After you created your preset at config phase, you can easily access it. * * * $mdDialog.show( * $mdDialog.testPreset() * ); * * * ### JavaScript: promise API syntax, custom dialog template * * (function(angular, undefined){ * "use strict"; * * angular * .module('demoApp', ['ngMaterial']) * .controller('EmployeeController', EmployeeEditor) * .controller('GreetingController', GreetingController); * * // Fictitious Employee Editor to show how to use simple and complex dialogs. * * function EmployeeEditor($scope, $mdDialog) { * var alert; * * $scope.showAlert = showAlert; * $scope.closeAlert = closeAlert; * $scope.showGreeting = showCustomGreeting; * * $scope.hasAlert = function() { return !!alert }; * $scope.userName = $scope.userName || 'Bobby'; * * // Dialog #1 - Show simple alert dialog and cache * // reference to dialog instance * * function showAlert() { * alert = $mdDialog.alert() * .title('Attention, ' + $scope.userName) * .textContent('This is an example of how easy dialogs can be!') * .ok('Close'); * * $mdDialog * .show( alert ) * .finally(function() { * alert = undefined; * }); * } * * // Close the specified dialog instance and resolve with 'finished' flag * // Normally this is not needed, just use '$mdDialog.hide()' to close * // the most recent dialog popup. * * function closeAlert() { * $mdDialog.hide( alert, "finished" ); * alert = undefined; * } * * // Dialog #2 - Demonstrate more complex dialogs construction and popup. * * function showCustomGreeting($event) { * $mdDialog.show({ * targetEvent: $event, * template: * '' + * * ' Hello {{ employee }}!' + * * ' ' + * ' ' + * ' Close Greeting' + * ' ' + * ' ' + * '', * controller: 'GreetingController', * onComplete: afterShowAnimation, * locals: { employee: $scope.userName } * }); * * // When the 'enter' animation finishes... * * function afterShowAnimation(scope, element, options) { * // post-show code here: DOM element focus, etc. * } * } * * // Dialog #3 - Demonstrate use of ControllerAs and passing $scope to dialog * // Here we used ng-controller="GreetingController as vm" and * // $scope.vm === * * function showCustomGreeting() { * * $mdDialog.show({ * clickOutsideToClose: true, * * scope: $scope, // use parent scope in template * preserveScope: true, // do not forget this if use parent scope * // Since GreetingController is instantiated with ControllerAs syntax * // AND we are passing the parent '$scope' to the dialog, we MUST * // use 'vm.' in the template markup * * template: '' + * ' ' + * ' Hi There {{vm.employee}}' + * ' ' + * '', * * controller: function DialogController($scope, $mdDialog) { * $scope.closeDialog = function() { * $mdDialog.hide(); * } * } * }); * } * * } * * // Greeting controller used with the more complex 'showCustomGreeting()' custom dialog * * function GreetingController($scope, $mdDialog, employee) { * // Assigned from construction locals options... * $scope.employee = employee; * * $scope.closeDialog = function() { * // Easily hides most recent dialog shown... * // no specific instance reference is needed. * $mdDialog.hide(); * }; * } * * })(angular); * *//** * @ngdoc method * @name $mdDialog#alert * * @description * Builds a preconfigured dialog with the specified message. * * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods: * * - $mdDialogPreset#title(string) - Sets the alert title. * - $mdDialogPreset#textContent(string) - Sets the alert message. * - $mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize * module to be loaded. HTML is not run through Angular's compiler. * - $mdDialogPreset#ok(string) - Sets the alert "Okay" button text. * - $mdDialogPreset#theme(string) - Sets the theme of the alert dialog. * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * *//** * @ngdoc method * @name $mdDialog#confirm * * @description * Builds a preconfigured dialog with the specified message. You can call show and the promise returned * will be resolved only if the user clicks the confirm action on the dialog. * * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods: * * Additionally, it supports the following methods: * * - $mdDialogPreset#title(string) - Sets the confirm title. * - $mdDialogPreset#textContent(string) - Sets the confirm message. * - $mdDialogPreset#htmlContent(string) - Sets the confirm message as HTML. Requires ngSanitize * module to be loaded. HTML is not run through Angular's compiler. * - $mdDialogPreset#ok(string) - Sets the confirm "Okay" button text. * - $mdDialogPreset#cancel(string) - Sets the confirm "Cancel" button text. * - $mdDialogPreset#theme(string) - Sets the theme of the confirm dialog. * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * *//** * @ngdoc method * @name $mdDialog#prompt * * @description * Builds a preconfigured dialog with the specified message and input box. You can call show and the promise returned * will be resolved only if the user clicks the prompt action on the dialog, passing the input value as the first argument. * * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods: * * Additionally, it supports the following methods: * * - $mdDialogPreset#title(string) - Sets the prompt title. * - $mdDialogPreset#textContent(string) - Sets the prompt message. * - $mdDialogPreset#htmlContent(string) - Sets the prompt message as HTML. Requires ngSanitize * module to be loaded. HTML is not run through Angular's compiler. * - $mdDialogPreset#placeholder(string) - Sets the placeholder text for the input. * - $mdDialogPreset#initialValue(string) - Sets the initial value for the prompt input. * - $mdDialogPreset#ok(string) - Sets the prompt "Okay" button text. * - $mdDialogPreset#cancel(string) - Sets the prompt "Cancel" button text. * - $mdDialogPreset#theme(string) - Sets the theme of the prompt dialog. * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * *//** * @ngdoc method * @name $mdDialog#show * * @description * Show a dialog with the specified options. * * @param {object} optionsOrPreset Either provide an `$mdDialogPreset` returned from `alert()`, and * `confirm()`, or an options object with the following properties: * - `templateUrl` - `{string=}`: The url of a template that will be used as the content * of the dialog. * - `template` - `{string=}`: HTML template to show in the dialog. This **must** be trusted HTML * with respect to Angular's [$sce service](https://docs.angularjs.org/api/ng/service/$sce). * This template should **never** be constructed with any kind of user input or user data. * - `contentElement` - `{string|Element}`: Instead of using a template, which will be compiled each time a * dialog opens, you can also use a DOM element.
      * * When specifying an element, which is present on the DOM, `$mdDialog` will temporary fetch the element into * the dialog and restores it at the old DOM position upon close. * * When specifying a string, the string be used as a CSS selector, to lookup for the element in the DOM. * - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template with a * `` tag if one is not provided. Defaults to true. Can be disabled if you provide a * custom dialog directive. * - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * - `openFrom` - `{string|Element|object}`: The query selector, DOM element or the Rect object * that is used to determine the bounds (top, left, height, width) from which the Dialog will * originate. * - `closeTo` - `{string|Element|object}`: The query selector, DOM element or the Rect object * that is used to determine the bounds (top, left, height, width) to which the Dialog will * target. * - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified, * it will create a new isolate scope. * This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true. * - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. Default is false * - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the dialog is open. * Default true. * - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop behind the dialog. * Default true. * - `clickOutsideToClose` - `{boolean=}`: Whether the user can click outside the dialog to * close it. Default false. * - `escapeToClose` - `{boolean=}`: Whether the user can press escape to close the dialog. * Default true. * - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on open. Only disable if * focusing some other way, as focus management is required for dialogs to be accessible. * Defaults to true. * - `controller` - `{function|string=}`: The controller to associate with the dialog. The controller * will be injected with the local `$mdDialog`, which passes along a scope for the dialog. * - `locals` - `{object=}`: 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 3. If `bindToController` is true, they will be * copied to the controller instead. * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in. * - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope. * - `parent` - `{element=}`: The element to append the dialog to. Defaults to appending * to the root element of the application. * - `onShowing` - `function(scope, element)`: Callback function used to announce the show() action is * starting. * - `onComplete` - `function(scope, element)`: Callback function used to announce when the show() action is * finished. * - `onRemoving` - `function(element, removePromise)`: Callback function used to announce the * close/hide() action is starting. This allows developers to run custom animations * in parallel the close animations. * - `fullscreen` `{boolean=}`: An option to toggle whether the dialog should show in fullscreen * or not. Defaults to `false`. * - `multiple` `{boolean=}`: An option to allow this dialog to display over one that's currently open. * @returns {promise} A promise that can be resolved with `$mdDialog.hide()` or * rejected with `$mdDialog.cancel()`. *//** * @ngdoc method * @name $mdDialog#hide * * @description * Hide an existing dialog and resolve the promise returned from `$mdDialog.show()`. * * @param {*=} response An argument for the resolved promise. * * @returns {promise} A promise that is resolved when the dialog has been closed. *//** * @ngdoc method * @name $mdDialog#cancel * * @description * Hide an existing dialog and reject the promise returned from `$mdDialog.show()`. * * @param {*=} response An argument for the rejected promise. * * @returns {promise} A promise that is resolved when the dialog has been closed. */function MdDialogProvider($$interimElementProvider){dialogDefaultOptions.$inject=['$mdDialog','$mdAria','$mdUtil','$mdConstant','$animate','$document','$window','$rootElement','$log','$injector','$mdTheming','$interpolate','$mdInteraction'];// Elements to capture and redirect focus when the user presses tab at the dialog boundary. MdDialogController.$inject=["$mdDialog","$mdConstant"];dialogDefaultOptions.$inject=["$mdDialog","$mdAria","$mdUtil","$mdConstant","$animate","$document","$window","$rootElement","$log","$injector","$mdTheming","$interpolate","$mdInteraction"];var topFocusTrap,bottomFocusTrap;return $$interimElementProvider('$mdDialog').setDefaults({methods:['disableParentScroll','hasBackdrop','clickOutsideToClose','escapeToClose','targetEvent','closeTo','openFrom','parent','fullscreen','multiple'],options:dialogDefaultOptions}).addPreset('alert',{methods:['title','htmlContent','textContent','content','ariaLabel','ok','theme','css'],options:advancedDialogOptions}).addPreset('confirm',{methods:['title','htmlContent','textContent','content','ariaLabel','ok','cancel','theme','css'],options:advancedDialogOptions}).addPreset('prompt',{methods:['title','htmlContent','textContent','initialValue','content','placeholder','ariaLabel','ok','cancel','theme','css','required'],options:advancedDialogOptions});/* @ngInject */function advancedDialogOptions(){return{template:['',' ','

      {{ dialog.title }}

      ','
      ','
      ','

      {{::dialog.mdTextContent}}

      ','
      ',' ',' ',' ','
      ',' ',' ',' {{ dialog.cancel }}',' ',' ',' {{ dialog.ok }}',' ',' ','
      '].join('').replace(/\s\s+/g,''),controller:MdDialogController,controllerAs:'dialog',bindToController:true};}/** * Controller for the md-dialog interim elements * @ngInject */function MdDialogController($mdDialog,$mdConstant){// For compatibility with AngularJS 1.6+, we should always use the $onInit hook in // interimElements. The $mdCompiler simulates the $onInit hook for all versions. this.$onInit=function(){var isPrompt=this.$type=='prompt';if(isPrompt&&this.initialValue){this.result=this.initialValue;}this.hide=function(){$mdDialog.hide(isPrompt?this.result:true);};this.abort=function(){$mdDialog.cancel();};this.keypress=function($event){if($event.keyCode===$mdConstant.KEY_CODE.ENTER){$mdDialog.hide(this.result);}};};}/* @ngInject */function dialogDefaultOptions($mdDialog,$mdAria,$mdUtil,$mdConstant,$animate,$document,$window,$rootElement,$log,$injector,$mdTheming,$interpolate,$mdInteraction){return{hasBackdrop:true,isolateScope:true,onCompiling:beforeCompile,onShow:onShow,onShowing:beforeShow,onRemove:onRemove,clickOutsideToClose:false,escapeToClose:true,targetEvent:null,closeTo:null,openFrom:null,focusOnOpen:true,disableParentScroll:true,autoWrap:true,fullscreen:false,transformTemplate:function transformTemplate(template,options){// Make the dialog container focusable, because otherwise the focus will be always redirected to // an element outside of the container, and the focus trap won't work probably.. // Also the tabindex is needed for the `escapeToClose` functionality, because // the keyDown event can't be triggered when the focus is outside of the container. var startSymbol=$interpolate.startSymbol();var endSymbol=$interpolate.endSymbol();var theme=startSymbol+(options.themeWatch?'':'::')+'theme'+endSymbol;return'
      '+validatedTemplate(template)+'
      ';/** * The specified template should contain a wrapper element.... */function validatedTemplate(template){if(options.autoWrap&&!/<\/md-dialog>/g.test(template)){return''+(template||'')+'';}else{return template||'';}}}};function beforeCompile(options){// Automatically apply the theme, if the user didn't specify a theme explicitly. // Those option changes need to be done, before the compilation has started, because otherwise // the option changes will be not available in the $mdCompilers locales. options.defaultTheme=$mdTheming.defaultTheme();detectTheming(options);}function beforeShow(scope,element,options,controller){if(controller){var mdHtmlContent=controller.htmlContent||options.htmlContent||'';var mdTextContent=controller.textContent||options.textContent||controller.content||options.content||'';if(mdHtmlContent&&!$injector.has('$sanitize')){throw Error('The ngSanitize module must be loaded in order to use htmlContent.');}if(mdHtmlContent&&mdTextContent){throw Error('md-dialog cannot have both `htmlContent` and `textContent`');}// Only assign the content if nothing throws, otherwise it'll still be compiled. controller.mdHtmlContent=mdHtmlContent;controller.mdTextContent=mdTextContent;}}/** Show method for dialogs */function onShow(scope,element,options,controller){angular.element($document[0].body).addClass('md-dialog-is-showing');var dialogElement=element.find('md-dialog');// Once a dialog has `ng-cloak` applied on his template the dialog animation will not work properly. // This is a very common problem, so we have to notify the developer about this. if(dialogElement.hasClass('ng-cloak')){var message='$mdDialog: using `` will affect the dialog opening animations.';$log.warn(message,element[0]);}captureParentAndFromToElements(options);configureAria(dialogElement,options);showBackdrop(scope,element,options);activateListeners(element,options);return dialogPopIn(element,options).then(function(){lockScreenReader(element,options);warnDeprecatedActions();focusOnOpen();});/** * Check to see if they used the deprecated .md-actions class and log a warning */function warnDeprecatedActions(){if(element[0].querySelector('.md-actions')){$log.warn('Using a class of md-actions is deprecated, please use .');}}/** * For alerts, focus on content... otherwise focus on * the close button (or equivalent) */function focusOnOpen(){if(options.focusOnOpen){var target=$mdUtil.findFocusTarget(element)||findCloseButton()||dialogElement;target.focus();}/** * If no element with class dialog-close, try to find the last * button child in md-actions and assume it is a close button. * * If we find no actions at all, log a warning to the console. */function findCloseButton(){return element[0].querySelector('.dialog-close, md-dialog-actions button:last-child');}}}/** * Remove function for all dialogs */function onRemove(scope,element,options){options.deactivateListeners();options.unlockScreenReader();options.hideBackdrop(options.$destroy);// Remove the focus traps that we added earlier for keeping focus within the dialog. if(topFocusTrap&&topFocusTrap.parentNode){topFocusTrap.parentNode.removeChild(topFocusTrap);}if(bottomFocusTrap&&bottomFocusTrap.parentNode){bottomFocusTrap.parentNode.removeChild(bottomFocusTrap);}// For navigation $destroy events, do a quick, non-animated removal, // but for normal closes (from clicks, etc) animate the removal return options.$destroy?detachAndClean():animateRemoval().then(detachAndClean);/** * For normal closes, animate the removal. * For forced closes (like $destroy events), skip the animations */function animateRemoval(){return dialogPopOut(element,options);}/** * Detach the element */function detachAndClean(){angular.element($document[0].body).removeClass('md-dialog-is-showing');// Reverse the container stretch if using a content element. if(options.contentElement){options.reverseContainerStretch();}// Exposed cleanup function from the $mdCompiler. options.cleanupElement();// Restores the focus to the origin element if the last interaction upon opening was a keyboard. if(!options.$destroy&&options.originInteraction==='keyboard'){options.origin.focus();}}}function detectTheming(options){// Once the user specifies a targetEvent, we will automatically try to find the correct // nested theme. var targetEl;if(options.targetEvent&&options.targetEvent.target){targetEl=angular.element(options.targetEvent.target);}var themeCtrl=targetEl&&targetEl.controller('mdTheme');if(!themeCtrl){return;}options.themeWatch=themeCtrl.$shouldWatch;var theme=options.theme||themeCtrl.$mdTheme;if(theme){options.scope.theme=theme;}var unwatch=themeCtrl.registerChanges(function(newTheme){options.scope.theme=newTheme;if(!options.themeWatch){unwatch();}});}/** * Capture originator/trigger/from/to element information (if available) * and the parent container for the dialog; defaults to the $rootElement * unless overridden in the options.parent */function captureParentAndFromToElements(options){options.origin=angular.extend({element:null,bounds:null,focus:angular.noop},options.origin||{});options.parent=getDomElement(options.parent,$rootElement);options.closeTo=getBoundingClientRect(getDomElement(options.closeTo));options.openFrom=getBoundingClientRect(getDomElement(options.openFrom));if(options.targetEvent){options.origin=getBoundingClientRect(options.targetEvent.target,options.origin);options.originInteraction=$mdInteraction.getLastInteractionType();}/** * Identify the bounding RECT for the target element * */function getBoundingClientRect(element,orig){var source=angular.element(element||{});if(source&&source.length){// Compute and save the target element's bounding rect, so that if the // element is hidden when the dialog closes, we can shrink the dialog // back to the same position it expanded from. // // Checking if the source is a rect object or a DOM element var bounds={top:0,left:0,height:0,width:0};var hasFn=angular.isFunction(source[0].getBoundingClientRect);return angular.extend(orig||{},{element:hasFn?source:undefined,bounds:hasFn?source[0].getBoundingClientRect():angular.extend({},bounds,source[0]),focus:angular.bind(source,source.focus)});}}/** * If the specifier is a simple string selector, then query for * the DOM element. */function getDomElement(element,defaultElement){if(angular.isString(element)){element=$document[0].querySelector(element);}// If we have a reference to a raw dom element, always wrap it in jqLite return angular.element(element||defaultElement);}}/** * Listen for escape keys and outside clicks to auto close */function activateListeners(element,options){var window=angular.element($window);var onWindowResize=$mdUtil.debounce(function(){stretchDialogContainerToViewport(element,options);},60);var removeListeners=[];var smartClose=function smartClose(){// Only 'confirm' dialogs have a cancel button... escape/clickOutside will // cancel or fallback to hide. var closeFn=options.$type=='alert'?$mdDialog.hide:$mdDialog.cancel;$mdUtil.nextTick(closeFn,true);};if(options.escapeToClose){var parentTarget=options.parent;var keyHandlerFn=function keyHandlerFn(ev){if(ev.keyCode===$mdConstant.KEY_CODE.ESCAPE){ev.stopPropagation();ev.preventDefault();smartClose();}};// Add keydown listeners element.on('keydown',keyHandlerFn);parentTarget.on('keydown',keyHandlerFn);// Queue remove listeners function removeListeners.push(function(){element.off('keydown',keyHandlerFn);parentTarget.off('keydown',keyHandlerFn);});}// Register listener to update dialog on window resize window.on('resize',onWindowResize);removeListeners.push(function(){window.off('resize',onWindowResize);});if(options.clickOutsideToClose){var target=element;var sourceElem;// Keep track of the element on which the mouse originally went down // so that we can only close the backdrop when the 'click' started on it. // A simple 'click' handler does not work, // it sets the target object as the element the mouse went down on. var mousedownHandler=function mousedownHandler(ev){sourceElem=ev.target;};// We check if our original element and the target is the backdrop // because if the original was the backdrop and the target was inside the dialog // we don't want to dialog to close. var mouseupHandler=function mouseupHandler(ev){if(sourceElem===target[0]&&ev.target===target[0]){ev.stopPropagation();ev.preventDefault();smartClose();}};// Add listeners target.on('mousedown',mousedownHandler);target.on('mouseup',mouseupHandler);// Queue remove listeners function removeListeners.push(function(){target.off('mousedown',mousedownHandler);target.off('mouseup',mouseupHandler);});}// Attach specific `remove` listener handler options.deactivateListeners=function(){removeListeners.forEach(function(removeFn){removeFn();});options.deactivateListeners=null;};}/** * Show modal backdrop element... */function showBackdrop(scope,element,options){if(options.disableParentScroll){// !! DO this before creating the backdrop; since disableScrollAround() // configures the scroll offset; which is used by mdBackDrop postLink() options.restoreScroll=$mdUtil.disableScrollAround(element,options.parent);}if(options.hasBackdrop){options.backdrop=$mdUtil.createBackdrop(scope,"md-dialog-backdrop md-opaque");$animate.enter(options.backdrop,options.parent);}/** * Hide modal backdrop element... */options.hideBackdrop=function hideBackdrop($destroy){if(options.backdrop){if($destroy)options.backdrop.remove();else $animate.leave(options.backdrop);}if(options.disableParentScroll){options.restoreScroll&&options.restoreScroll();delete options.restoreScroll;}options.hideBackdrop=null;};}/** * Inject ARIA-specific attributes appropriate for Dialogs */function configureAria(element,options){var role=options.$type==='alert'?'alertdialog':'dialog';var dialogContent=element.find('md-dialog-content');var existingDialogId=element.attr('id');var dialogContentId='dialogContent_'+(existingDialogId||$mdUtil.nextUid());element.attr({'role':role,'tabIndex':'-1'});if(dialogContent.length===0){dialogContent=element;// If the dialog element already had an ID, don't clobber it. if(existingDialogId){dialogContentId=existingDialogId;}}dialogContent.attr('id',dialogContentId);element.attr('aria-describedby',dialogContentId);if(options.ariaLabel){$mdAria.expect(element,'aria-label',options.ariaLabel);}else{$mdAria.expectAsync(element,'aria-label',function(){// If dialog title is specified, set aria-label with it // See https://github.com/angular/material/issues/10582 if(options.title){return options.title;}else{var words=dialogContent.text().split(/\s+/);if(words.length>3)words=words.slice(0,3).concat('...');return words.join(' ');}});}// Set up elements before and after the dialog content to capture focus and // redirect back into the dialog. topFocusTrap=document.createElement('div');topFocusTrap.classList.add('md-dialog-focus-trap');topFocusTrap.tabIndex=0;bottomFocusTrap=topFocusTrap.cloneNode(false);// When focus is about to move out of the dialog, we want to intercept it and redirect it // back to the dialog element. var focusHandler=function focusHandler(){element.focus();};topFocusTrap.addEventListener('focus',focusHandler);bottomFocusTrap.addEventListener('focus',focusHandler);// The top focus trap inserted immeidately before the md-dialog element (as a sibling). // The bottom focus trap is inserted at the very end of the md-dialog element (as a child). element[0].parentNode.insertBefore(topFocusTrap,element[0]);element.after(bottomFocusTrap);}/** * Prevents screen reader interaction behind modal window * on swipe interfaces */function lockScreenReader(element,options){var isHidden=true;// get raw DOM node walkDOM(element[0]);options.unlockScreenReader=function(){isHidden=false;walkDOM(element[0]);options.unlockScreenReader=null;};/** * Walk DOM to apply or remove aria-hidden on sibling nodes * and parent sibling nodes * */function walkDOM(element){while(element.parentNode){if(element===document.body){return;}var children=element.parentNode.children;for(var i=0;i * * * * * */function MdDividerDirective($mdTheming){return{restrict:'E',link:$mdTheming};}})();(function(){"use strict";(function(){'use strict';/** * @ngdoc module * @name material.components.fabActions */MdFabActionsDirective.$inject=['$mdUtil'];MdFabActionsDirective.$inject=["$mdUtil"];angular.module('material.components.fabActions',['material.core']).directive('mdFabActions',MdFabActionsDirective);/** * @ngdoc directive * @name mdFabActions * @module material.components.fabActions * * @restrict E * * @description * The `` directive is used inside of a `` or * `` directive to mark an element (or elements) as the actions and setup the * proper event listeners. * * @usage * See the `` or `` directives for example usage. */function MdFabActionsDirective($mdUtil){return{restrict:'E',require:['^?mdFabSpeedDial','^?mdFabToolbar'],compile:function compile(element,attributes){var children=element.children();var hasNgRepeat=$mdUtil.prefixer().hasAttribute(children,'ng-repeat');// Support both ng-repeat and static content if(hasNgRepeat){children.addClass('md-fab-action-item');}else{// Wrap every child in a new div and add a class that we can scale/fling independently children.wrap('
      ');}}};}})();})();(function(){"use strict";(function(){'use strict';MdFabController.$inject=['$scope','$element','$animate','$mdUtil','$mdConstant','$timeout'];MdFabController.$inject=["$scope","$element","$animate","$mdUtil","$mdConstant","$timeout"];angular.module('material.components.fabShared',['material.core']).controller('MdFabController',MdFabController);function MdFabController($scope,$element,$animate,$mdUtil,$mdConstant,$timeout){var vm=this;var initialAnimationAttempts=0;// NOTE: We use async eval(s) below to avoid conflicts with any existing digest loops vm.open=function(){$scope.$evalAsync("vm.isOpen = true");};vm.close=function(){// Async eval to avoid conflicts with existing digest loops $scope.$evalAsync("vm.isOpen = false");// Focus the trigger when the element closes so users can still tab to the next item $element.find('md-fab-trigger')[0].focus();};// Toggle the open/close state when the trigger is clicked vm.toggle=function(){$scope.$evalAsync("vm.isOpen = !vm.isOpen");};/* * 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. */vm.$onInit=function(){setupDefaults();setupListeners();setupWatchers();fireInitialAnimations();};// 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();}function setupDefaults(){// Set the default direction to 'down' if none is specified vm.direction=vm.direction||'down';// Set the default to be closed vm.isOpen=vm.isOpen||false;// Start the keyboard interaction at the first action resetActionIndex();// Add an animations waiting class so we know not to run $element.addClass('md-animations-waiting');}function setupListeners(){var eventTypes=['click','focusin','focusout'];// Add our listeners angular.forEach(eventTypes,function(eventType){$element.on(eventType,parseEvents);});// Remove our listeners when destroyed $scope.$on('$destroy',function(){angular.forEach(eventTypes,function(eventType){$element.off(eventType,parseEvents);});// remove any attached keyboard handlers in case element is removed while // speed dial is open disableKeyboard();});}var closeTimeout;function parseEvents(event){// If the event is a click, just handle it if(event.type=='click'){handleItemClick(event);}// If we focusout, set a timeout to close the element if(event.type=='focusout'&&!closeTimeout){closeTimeout=$timeout(function(){vm.close();},100,false);}// If we see a focusin and there is a timeout about to run, cancel it so we stay open if(event.type=='focusin'&&closeTimeout){$timeout.cancel(closeTimeout);closeTimeout=null;}}function resetActionIndex(){vm.currentActionIndex=-1;}function setupWatchers(){// Watch for changes to the direction and update classes/attributes $scope.$watch('vm.direction',function(newDir,oldDir){// Add the appropriate classes so we can target the direction in the CSS $animate.removeClass($element,'md-'+oldDir);$animate.addClass($element,'md-'+newDir);// Reset the action index since it may have changed resetActionIndex();});var trigger,actions;// Watch for changes to md-open $scope.$watch('vm.isOpen',function(isOpen){// Reset the action index since it may have changed resetActionIndex();// We can't get the trigger/actions outside of the watch because the component hasn't been // linked yet, so we wait until the first watch fires to cache them. if(!trigger||!actions){trigger=getTriggerElement();actions=getActionsElement();}if(isOpen){enableKeyboard();}else{disableKeyboard();}var toAdd=isOpen?'md-is-open':'';var toRemove=isOpen?'':'md-is-open';// Set the proper ARIA attributes trigger.attr('aria-haspopup',true);trigger.attr('aria-expanded',isOpen);actions.attr('aria-hidden',!isOpen);// Animate the CSS classes $animate.setClass($element,toAdd,toRemove);});}function fireInitialAnimations(){// If the element is actually visible on the screen if($element[0].scrollHeight>0){// Fire our animation $animate.addClass($element,'_md-animations-ready').then(function(){// Remove the waiting class $element.removeClass('md-animations-waiting');});}// Otherwise, try for up to 1 second before giving up else if(initialAnimationAttempts<10){$timeout(fireInitialAnimations,100);// Increment our counter initialAnimationAttempts=initialAnimationAttempts+1;}}function enableKeyboard(){$element.on('keydown',keyPressed);// On the next tick, setup a check for outside clicks; we do this on the next tick to avoid // clicks/touches that result in the isOpen attribute changing (e.g. a bound radio button) $mdUtil.nextTick(function(){angular.element(document).on('click touchend',checkForOutsideClick);});// TODO: On desktop, we should be able to reset the indexes so you cannot tab through, but // this breaks accessibility, especially on mobile, since you have no arrow keys to press //resetActionTabIndexes(); }function disableKeyboard(){$element.off('keydown',keyPressed);angular.element(document).off('click touchend',checkForOutsideClick);}function checkForOutsideClick(event){if(event.target){var closestTrigger=$mdUtil.getClosest(event.target,'md-fab-trigger');var closestActions=$mdUtil.getClosest(event.target,'md-fab-actions');if(!closestTrigger&&!closestActions){vm.close();}}}function keyPressed(event){switch(event.which){case $mdConstant.KEY_CODE.ESCAPE:vm.close();event.preventDefault();return false;case $mdConstant.KEY_CODE.LEFT_ARROW:doKeyLeft(event);return false;case $mdConstant.KEY_CODE.UP_ARROW:doKeyUp(event);return false;case $mdConstant.KEY_CODE.RIGHT_ARROW:doKeyRight(event);return false;case $mdConstant.KEY_CODE.DOWN_ARROW:doKeyDown(event);return false;}}function doActionPrev(event){focusAction(event,-1);}function doActionNext(event){focusAction(event,1);}function focusAction(event,direction){var actions=resetActionTabIndexes();// Increment/decrement the counter with restrictions vm.currentActionIndex=vm.currentActionIndex+direction;vm.currentActionIndex=Math.min(actions.length-1,vm.currentActionIndex);vm.currentActionIndex=Math.max(0,vm.currentActionIndex);// Focus the element var focusElement=angular.element(actions[vm.currentActionIndex]).children()[0];angular.element(focusElement).attr('tabindex',0);focusElement.focus();// Make sure the event doesn't bubble and cause something else event.preventDefault();event.stopImmediatePropagation();}function resetActionTabIndexes(){// Grab all of the actions var actions=getActionsElement()[0].querySelectorAll('.md-fab-action-item');// Disable all other actions for tabbing angular.forEach(actions,function(action){angular.element(angular.element(action).children()[0]).attr('tabindex',-1);});return actions;}function doKeyLeft(event){if(vm.direction==='left'){doActionNext(event);}else{doActionPrev(event);}}function doKeyUp(event){if(vm.direction==='down'){doActionPrev(event);}else{doActionNext(event);}}function doKeyRight(event){if(vm.direction==='left'){doActionPrev(event);}else{doActionNext(event);}}function doKeyDown(event){if(vm.direction==='up'){doActionPrev(event);}else{doActionNext(event);}}function isTrigger(element){return $mdUtil.getClosest(element,'md-fab-trigger');}function isAction(element){return $mdUtil.getClosest(element,'md-fab-actions');}function handleItemClick(event){if(isTrigger(event.target)){vm.toggle();}if(isAction(event.target)){vm.close();}}function getTriggerElement(){return $element.find('md-fab-trigger');}function getActionsElement(){return $element.find('md-fab-actions');}}})();})();(function(){"use strict";(function(){'use strict';/** * The duration of the CSS animation in milliseconds. * * @type {number} */MdFabSpeedDialScaleAnimation.$inject=['$timeout'];MdFabSpeedDialFlingAnimation.$inject=['$timeout'];MdFabSpeedDialFlingAnimation.$inject=["$timeout"];MdFabSpeedDialScaleAnimation.$inject=["$timeout"];var cssAnimationDuration=300;/** * @ngdoc module * @name material.components.fabSpeedDial */angular// Declare our module .module('material.components.fabSpeedDial',['material.core','material.components.fabShared','material.components.fabActions'])// Register our directive .directive('mdFabSpeedDial',MdFabSpeedDialDirective)// Register our custom animations .animation('.md-fling',MdFabSpeedDialFlingAnimation).animation('.md-scale',MdFabSpeedDialScaleAnimation)// Register a service for each animation so that we can easily inject them into unit tests .service('mdFabSpeedDialFlingAnimation',MdFabSpeedDialFlingAnimation).service('mdFabSpeedDialScaleAnimation',MdFabSpeedDialScaleAnimation);/** * @ngdoc directive * @name mdFabSpeedDial * @module material.components.fabSpeedDial * * @restrict E * * @description * The `` directive is used to present a series of popup elements (usually * ``s) for quick access to common actions. * * There are currently two animations available by applying one of the following classes to * the component: * * - `md-fling` - The speed dial items appear from underneath the trigger and move into their * appropriate positions. * - `md-scale` - The speed dial items appear in their proper places by scaling from 0% to 100%. * * You may also easily position the trigger by applying one one of the following classes to the * `` element: * - `md-fab-top-left` * - `md-fab-top-right` * - `md-fab-bottom-left` * - `md-fab-bottom-right` * * These CSS classes use `position: absolute`, so you need to ensure that the container element * also uses `position: absolute` or `position: relative` in order for them to work. * * Additionally, you may use the standard `ng-mouseenter` and `ng-mouseleave` directives to * open or close the speed dial. However, if you wish to allow users to hover over the empty * space where the actions will appear, you must also add the `md-hover-full` class to the speed * dial element. Without this, the hover effect will only occur on top of the trigger. * * See the demos for more information. * * ## Troubleshooting * * If your speed dial shows the closing animation upon launch, you may need to use `ng-cloak` on * the parent container to ensure that it is only visible once ready. We have plans to remove this * necessity in the future. * * @usage * * * * * * * * * * * * * * * * * * * @param {string} md-direction From which direction you would like the speed dial to appear * relative to the trigger element. * @param {expression=} md-open Programmatically control whether or not the speed-dial is visible. */function MdFabSpeedDialDirective(){return{restrict:'E',scope:{direction:'@?mdDirection',isOpen:'=?mdOpen'},bindToController:true,controller:'MdFabController',controllerAs:'vm',link:FabSpeedDialLink};function FabSpeedDialLink(scope,element){// Prepend an element to hold our CSS variables so we can use them in the animations below element.prepend('
      ');}}function MdFabSpeedDialFlingAnimation($timeout){function delayDone(done){$timeout(done,cssAnimationDuration,false);}function runAnimation(element){// Don't run if we are still waiting and we are not ready if(element.hasClass('md-animations-waiting')&&!element.hasClass('_md-animations-ready')){return;}var el=element[0];var ctrl=element.controller('mdFabSpeedDial');var items=el.querySelectorAll('.md-fab-action-item');// Grab our trigger element var triggerElement=el.querySelector('md-fab-trigger');// Grab our element which stores CSS variables var variablesElement=el.querySelector('._md-css-variables');// Setup JS variables based on our CSS variables var startZIndex=parseInt(window.getComputedStyle(variablesElement).zIndex);// Always reset the items to their natural position/state angular.forEach(items,function(item,index){var styles=item.style;styles.transform=styles.webkitTransform='';styles.transitionDelay='';styles.opacity=1;// Make the items closest to the trigger have the highest z-index styles.zIndex=items.length-index+startZIndex;});// Set the trigger to be above all of the actions so they disappear behind it. triggerElement.style.zIndex=startZIndex+items.length+1;// If the control is closed, hide the items behind the trigger if(!ctrl.isOpen){angular.forEach(items,function(item,index){var newPosition,axis;var styles=item.style;// Make sure to account for differences in the dimensions of the trigger verses the items // so that we can properly center everything; this helps hide the item's shadows behind // the trigger. var triggerItemHeightOffset=(triggerElement.clientHeight-item.clientHeight)/2;var triggerItemWidthOffset=(triggerElement.clientWidth-item.clientWidth)/2;switch(ctrl.direction){case'up':newPosition=item.scrollHeight*(index+1)+triggerItemHeightOffset;axis='Y';break;case'down':newPosition=-(item.scrollHeight*(index+1)+triggerItemHeightOffset);axis='Y';break;case'left':newPosition=item.scrollWidth*(index+1)+triggerItemWidthOffset;axis='X';break;case'right':newPosition=-(item.scrollWidth*(index+1)+triggerItemWidthOffset);axis='X';break;}var newTranslate='translate'+axis+'('+newPosition+'px)';styles.transform=styles.webkitTransform=newTranslate;});}}return{addClass:function addClass(element,className,done){if(element.hasClass('md-fling')){runAnimation(element);delayDone(done);}else{done();}},removeClass:function removeClass(element,className,done){runAnimation(element);delayDone(done);}};}function MdFabSpeedDialScaleAnimation($timeout){function delayDone(done){$timeout(done,cssAnimationDuration,false);}var delay=65;function runAnimation(element){var el=element[0];var ctrl=element.controller('mdFabSpeedDial');var items=el.querySelectorAll('.md-fab-action-item');// Grab our element which stores CSS variables var variablesElement=el.querySelector('._md-css-variables');// Setup JS variables based on our CSS variables var startZIndex=parseInt(window.getComputedStyle(variablesElement).zIndex);// Always reset the items to their natural position/state angular.forEach(items,function(item,index){var styles=item.style,offsetDelay=index*delay;styles.opacity=ctrl.isOpen?1:0;styles.transform=styles.webkitTransform=ctrl.isOpen?'scale(1)':'scale(0)';styles.transitionDelay=(ctrl.isOpen?offsetDelay:items.length-offsetDelay)+'ms';// Make the items closest to the trigger have the highest z-index styles.zIndex=items.length-index+startZIndex;});}return{addClass:function addClass(element,className,done){runAnimation(element);delayDone(done);},removeClass:function removeClass(element,className,done){runAnimation(element);delayDone(done);}};}})();})();(function(){"use strict";(function(){'use strict';/** * @ngdoc module * @name material.components.fabToolbar */angular// Declare our module .module('material.components.fabToolbar',['material.core','material.components.fabShared','material.components.fabActions'])// Register our directive .directive('mdFabToolbar',MdFabToolbarDirective)// Register our custom animations .animation('.md-fab-toolbar',MdFabToolbarAnimation)// Register a service for the animation so that we can easily inject it into unit tests .service('mdFabToolbarAnimation',MdFabToolbarAnimation);/** * @ngdoc directive * @name mdFabToolbar * @module material.components.fabToolbar * * @restrict E * * @description * * The `` directive is used to present a toolbar of elements (usually ``s) * for quick access to common actions when a floating action button is activated (via click or * keyboard navigation). * * You may also easily position the trigger by applying one one of the following classes to the * `` element: * - `md-fab-top-left` * - `md-fab-top-right` * - `md-fab-bottom-left` * - `md-fab-bottom-right` * * These CSS classes use `position: absolute`, so you need to ensure that the container element * also uses `position: absolute` or `position: relative` in order for them to work. * * @usage * * * * * * * * * * * * * * * * * * * * * * @param {string} md-direction From which direction you would like the toolbar items to appear * relative to the trigger element. Supports `left` and `right` directions. * @param {expression=} md-open Programmatically control whether or not the toolbar is visible. */function MdFabToolbarDirective(){return{restrict:'E',transclude:true,template:'
      '+'
      '+'
      ',scope:{direction:'@?mdDirection',isOpen:'=?mdOpen'},bindToController:true,controller:'MdFabController',controllerAs:'vm',link:link};function link(scope,element,attributes){// Add the base class for animations element.addClass('md-fab-toolbar');// Prepend the background element to the trigger's button element.find('md-fab-trigger').find('button').prepend('
      ');}}function MdFabToolbarAnimation(){function runAnimation(element,className,done){// If no className was specified, don't do anything if(!className){return;}var el=element[0];var ctrl=element.controller('mdFabToolbar');// Grab the relevant child elements var backgroundElement=el.querySelector('.md-fab-toolbar-background');var triggerElement=el.querySelector('md-fab-trigger button');var toolbarElement=el.querySelector('md-toolbar');var iconElement=el.querySelector('md-fab-trigger button md-icon');var actions=element.find('md-fab-actions').children();// If we have both elements, use them to position the new background if(triggerElement&&backgroundElement){// Get our variables var color=window.getComputedStyle(triggerElement).getPropertyValue('background-color');var width=el.offsetWidth;var height=el.offsetHeight;// Make it twice as big as it should be since we scale from the center var scale=2*(width/triggerElement.offsetWidth);// Set some basic styles no matter what animation we're doing backgroundElement.style.backgroundColor=color;backgroundElement.style.borderRadius=width+'px';// If we're open if(ctrl.isOpen){// Turn on toolbar pointer events when closed toolbarElement.style.pointerEvents='inherit';backgroundElement.style.width=triggerElement.offsetWidth+'px';backgroundElement.style.height=triggerElement.offsetHeight+'px';backgroundElement.style.transform='scale('+scale+')';// Set the next close animation to have the proper delays backgroundElement.style.transitionDelay='0ms';iconElement&&(iconElement.style.transitionDelay='.3s');// Apply a transition delay to actions angular.forEach(actions,function(action,index){action.style.transitionDelay=(actions.length-index)*25+'ms';});}else{// Turn off toolbar pointer events when closed toolbarElement.style.pointerEvents='none';// Scale it back down to the trigger's size backgroundElement.style.transform='scale(1)';// Reset the position backgroundElement.style.top='0';if(element.hasClass('md-right')){backgroundElement.style.left='0';backgroundElement.style.right=null;}if(element.hasClass('md-left')){backgroundElement.style.right='0';backgroundElement.style.left=null;}// Set the next open animation to have the proper delays backgroundElement.style.transitionDelay='200ms';iconElement&&(iconElement.style.transitionDelay='0ms');// Apply a transition delay to actions angular.forEach(actions,function(action,index){action.style.transitionDelay=200+index*25+'ms';});}}}return{addClass:function addClass(element,className,done){runAnimation(element,className,done);done();},removeClass:function removeClass(element,className,done){runAnimation(element,className,done);done();}};}})();})();(function(){"use strict";/** * @ngdoc module * @name material.components.gridList */GridListController.$inject=['$mdUtil'];GridLayoutFactory.$inject=['$mdUtil'];GridTileDirective.$inject=['$mdMedia'];GridListDirective.$inject=['$interpolate','$mdConstant','$mdGridLayout','$mdMedia'];GridListController.$inject=["$mdUtil"];GridLayoutFactory.$inject=["$mdUtil"];GridListDirective.$inject=["$interpolate","$mdConstant","$mdGridLayout","$mdMedia"];GridTileDirective.$inject=["$mdMedia"];angular.module('material.components.gridList',['material.core']).directive('mdGridList',GridListDirective).directive('mdGridTile',GridTileDirective).directive('mdGridTileFooter',GridTileCaptionDirective).directive('mdGridTileHeader',GridTileCaptionDirective).factory('$mdGridLayout',GridLayoutFactory);/** * @ngdoc directive * @name mdGridList * @module material.components.gridList * @restrict E * @description * Grid lists are an alternative to standard list views. Grid lists are distinct * from grids used for layouts and other visual presentations. * * A grid list is best suited to presenting a homogenous data type, typically * images, and is optimized for visual comprehension and differentiating between * like data types. * * A grid list is a continuous element consisting of tessellated, regular * subdivisions called cells that contain tiles (`md-grid-tile`). * * Concept of grid explained visually * Grid concepts legend * * Cells are arrayed vertically and horizontally within the grid. * * Tiles hold content and can span one or more cells vertically or horizontally. * * ### Responsive Attributes * * The `md-grid-list` directive supports "responsive" attributes, which allow * different `md-cols`, `md-gutter` and `md-row-height` values depending on the * currently matching media query. * * In order to set a responsive attribute, first define the fallback value with * the standard attribute name, then add additional attributes with the * following convention: `{base-attribute-name}-{media-query-name}="{value}"` * (ie. `md-cols-lg="8"`) * * @param {number} md-cols Number of columns in the grid. * @param {string} md-row-height One of *
        *
      • CSS length - Fixed height rows (eg. `8px` or `1rem`)
      • *
      • `{width}:{height}` - Ratio of width to height (eg. * `md-row-height="16:9"`)
      • *
      • `"fit"` - Height will be determined by subdividing the available * height by the number of rows
      • *
      * @param {string=} md-gutter The amount of space between tiles in CSS units * (default 1px) * @param {expression=} md-on-layout Expression to evaluate after layout. Event * object is available as `$event`, and contains performance information. * * @usage * Basic: * * * * * * * Fixed-height rows: * * * * * * * Fit rows: * * * * * * * Using responsive attributes: * * * * * */function GridListDirective($interpolate,$mdConstant,$mdGridLayout,$mdMedia){return{restrict:'E',controller:GridListController,scope:{mdOnLayout:'&'},link:postLink};function postLink(scope,element,attrs,ctrl){element.addClass('_md');// private md component indicator for styling // Apply semantics element.attr('role','list');// Provide the controller with a way to trigger layouts. ctrl.layoutDelegate=layoutDelegate;var invalidateLayout=angular.bind(ctrl,ctrl.invalidateLayout),unwatchAttrs=watchMedia();scope.$on('$destroy',unwatchMedia);/** * Watches for changes in media, invalidating layout as necessary. */function watchMedia(){for(var mediaName in $mdConstant.MEDIA){$mdMedia(mediaName);// initialize $mdMedia.getQuery($mdConstant.MEDIA[mediaName]).addListener(invalidateLayout);}return $mdMedia.watchResponsiveAttributes(['md-cols','md-row-height','md-gutter'],attrs,layoutIfMediaMatch);}function unwatchMedia(){ctrl.layoutDelegate=angular.noop;unwatchAttrs();for(var mediaName in $mdConstant.MEDIA){$mdMedia.getQuery($mdConstant.MEDIA[mediaName]).removeListener(invalidateLayout);}}/** * Performs grid layout if the provided mediaName matches the currently * active media type. */function layoutIfMediaMatch(mediaName){if(mediaName==null){// TODO(shyndman): It would be nice to only layout if we have // instances of attributes using this media type ctrl.invalidateLayout();}else if($mdMedia(mediaName)){ctrl.invalidateLayout();}}var lastLayoutProps;/** * Invokes the layout engine, and uses its results to lay out our * tile elements. * * @param {boolean} tilesInvalidated Whether tiles have been * added/removed/moved since the last layout. This is to avoid situations * where tiles are replaced with properties identical to their removed * counterparts. */function layoutDelegate(tilesInvalidated){var tiles=getTileElements();var props={tileSpans:getTileSpans(tiles),colCount:getColumnCount(),rowMode:getRowMode(),rowHeight:getRowHeight(),gutter:getGutter()};if(!tilesInvalidated&&angular.equals(props,lastLayoutProps)){return;}var performance=$mdGridLayout(props.colCount,props.tileSpans,tiles).map(function(tilePositions,rowCount){return{grid:{element:element,style:getGridStyle(props.colCount,rowCount,props.gutter,props.rowMode,props.rowHeight)},tiles:tilePositions.map(function(ps,i){return{element:angular.element(tiles[i]),style:getTileStyle(ps.position,ps.spans,props.colCount,rowCount,props.gutter,props.rowMode,props.rowHeight)};})};}).reflow().performance();// Report layout scope.mdOnLayout({$event:{performance:performance}});lastLayoutProps=props;}// Use $interpolate to do some simple string interpolation as a convenience. var startSymbol=$interpolate.startSymbol();var endSymbol=$interpolate.endSymbol();// Returns an expression wrapped in the interpolator's start and end symbols. function expr(exprStr){return startSymbol+exprStr+endSymbol;}// The amount of space a single 1x1 tile would take up (either width or height), used as // a basis for other calculations. This consists of taking the base size percent (as would be // if evenly dividing the size between cells), and then subtracting the size of one gutter. // However, since there are no gutters on the edges, each tile only uses a fration // (gutterShare = numGutters / numCells) of the gutter size. (Imagine having one gutter per // tile, and then breaking up the extra gutter on the edge evenly among the cells). var UNIT=$interpolate(expr('share')+'% - ('+expr('gutter')+' * '+expr('gutterShare')+')');// The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value. // The position comes the size of a 1x1 tile plus gutter for each previous tile in the // row/column (offset). var POSITION=$interpolate('calc(('+expr('unit')+' + '+expr('gutter')+') * '+expr('offset')+')');// The actual size of a tile, e.g., width or height, taking rowSpan or colSpan into account. // This is computed by multiplying the base unit by the rowSpan/colSpan, and then adding back // in the space that the gutter would normally have used (which was already accounted for in // the base unit calculation). var DIMENSION=$interpolate('calc(('+expr('unit')+') * '+expr('span')+' + ('+expr('span')+' - 1) * '+expr('gutter')+')');/** * Gets the styles applied to a tile element described by the given parameters. * @param {{row: number, col: number}} position The row and column indices of the tile. * @param {{row: number, col: number}} spans The rowSpan and colSpan of the tile. * @param {number} colCount The number of columns. * @param {number} rowCount The number of rows. * @param {string} gutter The amount of space between tiles. This will be something like * '5px' or '2em'. * @param {string} rowMode The row height mode. Can be one of: * 'fixed': all rows have a fixed size, given by rowHeight, * 'ratio': row height defined as a ratio to width, or * 'fit': fit to the grid-list element height, divinding evenly among rows. * @param {string|number} rowHeight The height of a row. This is only used for 'fixed' mode and * for 'ratio' mode. For 'ratio' mode, this is the *ratio* of width-to-height (e.g., 0.75). * @returns {Object} Map of CSS properties to be applied to the style element. Will define * values for top, left, width, height, marginTop, and paddingTop. */function getTileStyle(position,spans,colCount,rowCount,gutter,rowMode,rowHeight){// TODO(shyndman): There are style caching opportunities here. // Percent of the available horizontal space that one column takes up. var hShare=1/colCount*100;// Fraction of the gutter size that each column takes up. var hGutterShare=(colCount-1)/colCount;// Base horizontal size of a column. var hUnit=UNIT({share:hShare,gutterShare:hGutterShare,gutter:gutter});// The width and horizontal position of each tile is always calculated the same way, but the // height and vertical position depends on the rowMode. var ltr=document.dir!='rtl'&&document.body.dir!='rtl';var style=ltr?{left:POSITION({unit:hUnit,offset:position.col,gutter:gutter}),width:DIMENSION({unit:hUnit,span:spans.col,gutter:gutter}),// resets paddingTop:'',marginTop:'',top:'',height:''}:{right:POSITION({unit:hUnit,offset:position.col,gutter:gutter}),width:DIMENSION({unit:hUnit,span:spans.col,gutter:gutter}),// resets paddingTop:'',marginTop:'',top:'',height:''};switch(rowMode){case'fixed':// In fixed mode, simply use the given rowHeight. style.top=POSITION({unit:rowHeight,offset:position.row,gutter:gutter});style.height=DIMENSION({unit:rowHeight,span:spans.row,gutter:gutter});break;case'ratio':// Percent of the available vertical space that one row takes up. Here, rowHeight holds // the ratio value. For example, if the width:height ratio is 4:3, rowHeight = 1.333. var vShare=hShare/rowHeight;// Base veritcal size of a row. var vUnit=UNIT({share:vShare,gutterShare:hGutterShare,gutter:gutter});// padidngTop and marginTop are used to maintain the given aspect ratio, as // a percentage-based value for these properties is applied to the *width* of the // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties style.paddingTop=DIMENSION({unit:vUnit,span:spans.row,gutter:gutter});style.marginTop=POSITION({unit:vUnit,offset:position.row,gutter:gutter});break;case'fit':// Fraction of the gutter size that each column takes up. var vGutterShare=(rowCount-1)/rowCount;// Percent of the available vertical space that one row takes up. vShare=1/rowCount*100;// Base vertical size of a row. vUnit=UNIT({share:vShare,gutterShare:vGutterShare,gutter:gutter});style.top=POSITION({unit:vUnit,offset:position.row,gutter:gutter});style.height=DIMENSION({unit:vUnit,span:spans.row,gutter:gutter});break;}return style;}function getGridStyle(colCount,rowCount,gutter,rowMode,rowHeight){var style={};switch(rowMode){case'fixed':style.height=DIMENSION({unit:rowHeight,span:rowCount,gutter:gutter});style.paddingBottom='';break;case'ratio':// rowHeight is width / height var hGutterShare=colCount===1?0:(colCount-1)/colCount,hShare=1/colCount*100,vShare=hShare*(1/rowHeight),vUnit=UNIT({share:vShare,gutterShare:hGutterShare,gutter:gutter});style.height='';style.paddingBottom=DIMENSION({unit:vUnit,span:rowCount,gutter:gutter});break;case'fit':// noop, as the height is user set break;}return style;}function getTileElements(){return[].filter.call(element.children(),function(ele){return ele.tagName=='MD-GRID-TILE'&&!ele.$$mdDestroyed;});}/** * Gets an array of objects containing the rowspan and colspan for each tile. * @returns {Array<{row: number, col: number}>} */function getTileSpans(tileElements){return[].map.call(tileElements,function(ele){var ctrl=angular.element(ele).controller('mdGridTile');return{row:parseInt($mdMedia.getResponsiveAttribute(ctrl.$attrs,'md-rowspan'),10)||1,col:parseInt($mdMedia.getResponsiveAttribute(ctrl.$attrs,'md-colspan'),10)||1};});}function getColumnCount(){var colCount=parseInt($mdMedia.getResponsiveAttribute(attrs,'md-cols'),10);if(isNaN(colCount)){throw'md-grid-list: md-cols attribute was not found, or contained a non-numeric value';}return colCount;}function getGutter(){return applyDefaultUnit($mdMedia.getResponsiveAttribute(attrs,'md-gutter')||1);}function getRowHeight(){var rowHeight=$mdMedia.getResponsiveAttribute(attrs,'md-row-height');if(!rowHeight){throw'md-grid-list: md-row-height attribute was not found';}switch(getRowMode()){case'fixed':return applyDefaultUnit(rowHeight);case'ratio':var whRatio=rowHeight.split(':');return parseFloat(whRatio[0])/parseFloat(whRatio[1]);case'fit':return 0;// N/A }}function getRowMode(){var rowHeight=$mdMedia.getResponsiveAttribute(attrs,'md-row-height');if(!rowHeight){throw'md-grid-list: md-row-height attribute was not found';}if(rowHeight=='fit'){return'fit';}else if(rowHeight.indexOf(':')!==-1){return'ratio';}else{return'fixed';}}function applyDefaultUnit(val){return /\D$/.test(val)?val:val+'px';}}}/* @ngInject */function GridListController($mdUtil){this.layoutInvalidated=false;this.tilesInvalidated=false;this.$timeout_=$mdUtil.nextTick;this.layoutDelegate=angular.noop;}GridListController.prototype={invalidateTiles:function invalidateTiles(){this.tilesInvalidated=true;this.invalidateLayout();},invalidateLayout:function invalidateLayout(){if(this.layoutInvalidated){return;}this.layoutInvalidated=true;this.$timeout_(angular.bind(this,this.layout));},layout:function layout(){try{this.layoutDelegate(this.tilesInvalidated);}finally{this.layoutInvalidated=false;this.tilesInvalidated=false;}}};/* @ngInject */function GridLayoutFactory($mdUtil){var defaultAnimator=GridTileAnimator;/** * Set the reflow animator callback */GridLayout.animateWith=function(customAnimator){defaultAnimator=!angular.isFunction(customAnimator)?GridTileAnimator:customAnimator;};return GridLayout;/** * Publish layout function */function GridLayout(colCount,tileSpans){var self,_layoutInfo,gridStyles,layoutTime,mapTime,reflowTime;layoutTime=$mdUtil.time(function(){_layoutInfo=calculateGridFor(colCount,tileSpans);});return self={/** * An array of objects describing each tile's position in the grid. */layoutInfo:function layoutInfo(){return _layoutInfo;},/** * Maps grid positioning to an element and a set of styles using the * provided updateFn. */map:function map(updateFn){mapTime=$mdUtil.time(function(){var info=self.layoutInfo();gridStyles=updateFn(info.positioning,info.rowCount);});return self;},/** * Default animator simply sets the element.css( ). An alternate * animator can be provided as an argument. The function has the following * signature: * * function({grid: {element: JQLite, style: Object}, tiles: Array<{element: JQLite, style: Object}>) */reflow:function reflow(animatorFn){reflowTime=$mdUtil.time(function(){var animator=animatorFn||defaultAnimator;animator(gridStyles.grid,gridStyles.tiles);});return self;},/** * Timing for the most recent layout run. */performance:function performance(){return{tileCount:tileSpans.length,layoutTime:layoutTime,mapTime:mapTime,reflowTime:reflowTime,totalTime:layoutTime+mapTime+reflowTime};}};}/** * Default Gridlist animator simple sets the css for each element; * NOTE: any transitions effects must be manually set in the CSS. * e.g. * * md-grid-tile { * transition: all 700ms ease-out 50ms; * } * */function GridTileAnimator(grid,tiles){grid.element.css(grid.style);tiles.forEach(function(t){t.element.css(t.style);});}/** * Calculates the positions of tiles. * * The algorithm works as follows: * An Array with length colCount (spaceTracker) keeps track of * available tiling positions, where elements of value 0 represents an * empty position. Space for a tile is reserved by finding a sequence of * 0s with length <= than the tile's colspan. When such a space has been * found, the occupied tile positions are incremented by the tile's * rowspan value, as these positions have become unavailable for that * many rows. * * If the end of a row has been reached without finding space for the * tile, spaceTracker's elements are each decremented by 1 to a minimum * of 0. Rows are searched in this fashion until space is found. */function calculateGridFor(colCount,tileSpans){var curCol=0,curRow=0,spaceTracker=newSpaceTracker();return{positioning:tileSpans.map(function(spans,i){return{spans:spans,position:reserveSpace(spans,i)};}),rowCount:curRow+Math.max.apply(Math,spaceTracker)};function reserveSpace(spans,i){if(spans.col>colCount){throw'md-grid-list: Tile at position '+i+' has a colspan '+'('+spans.col+') that exceeds the column count '+'('+colCount+')';}var start=0,end=0;// TODO(shyndman): This loop isn't strictly necessary if you can // determine the minimum number of rows before a space opens up. To do // this, recognize that you've iterated across an entire row looking for // space, and if so fast-forward by the minimum rowSpan count. Repeat // until the required space opens up. while(end-start=colCount){nextRow();continue;}start=spaceTracker.indexOf(0,curCol);if(start===-1||(end=findEnd(start+1))===-1){start=end=0;nextRow();continue;}curCol=end+1;}adjustRow(start,spans.col,spans.row);curCol=start+spans.col;return{col:start,row:curRow};}function nextRow(){curCol=0;curRow++;adjustRow(0,colCount,-1);// Decrement row spans by one }function adjustRow(from,cols,by){for(var i=from;i * * *

      This is a header

      *
      *
      * * * With footer: * * * *

      This is a footer

      *
      *
      *
      * * Spanning multiple rows/columns: * * * * * * Responsive attributes: * * * * */function GridTileDirective($mdMedia){return{restrict:'E',require:'^mdGridList',template:'
      ',transclude:true,scope:{},// Simple controller that exposes attributes to the grid directive controller:["$attrs",function($attrs){this.$attrs=$attrs;}],link:postLink};function postLink(scope,element,attrs,gridCtrl){// Apply semantics element.attr('role','listitem');// If our colspan or rowspan changes, trigger a layout var unwatchAttrs=$mdMedia.watchResponsiveAttributes(['md-colspan','md-rowspan'],attrs,angular.bind(gridCtrl,gridCtrl.invalidateLayout));// Tile registration/deregistration gridCtrl.invalidateTiles();scope.$on('$destroy',function(){// Mark the tile as destroyed so it is no longer considered in layout, // even if the DOM element sticks around (like during a leave animation) element[0].$$mdDestroyed=true;unwatchAttrs();gridCtrl.invalidateLayout();});if(angular.isDefined(scope.$parent.$index)){scope.$watch(function(){return scope.$parent.$index;},function indexChanged(newIdx,oldIdx){if(newIdx===oldIdx){return;}gridCtrl.invalidateTiles();});}}}function GridTileCaptionDirective(){return{template:'
      ',transclude:true};}})();(function(){"use strict";/** * @ngdoc module * @name material.components.icon * @description * Icon */angular.module('material.components.icon',['material.core']);})();(function(){"use strict";/** * @ngdoc module * @name material.components.input */ngMessageAnimation.$inject=['$$AnimateRunner','$animateCss','$mdUtil','$log'];ngMessagesAnimation.$inject=['$$AnimateRunner','$animateCss','$mdUtil','$log'];mdInputInvalidMessagesAnimation.$inject=['$$AnimateRunner','$animateCss','$mdUtil','$log'];mdSelectOnFocusDirective.$inject=['$timeout'];ngMessageDirective.$inject=['$mdUtil'];placeholderDirective.$inject=['$compile'];mdMaxlengthDirective.$inject=['$animate','$mdUtil'];inputTextareaDirective.$inject=['$mdUtil','$window','$mdAria','$timeout','$mdGesture'];mdInputContainerDirective.$inject=['$mdTheming','$parse'];mdInputContainerDirective.$inject=["$mdTheming","$parse"];inputTextareaDirective.$inject=["$mdUtil","$window","$mdAria","$timeout","$mdGesture"];mdMaxlengthDirective.$inject=["$animate","$mdUtil"];placeholderDirective.$inject=["$compile"];ngMessageDirective.$inject=["$mdUtil"];mdSelectOnFocusDirective.$inject=["$timeout"];mdInputInvalidMessagesAnimation.$inject=["$$AnimateRunner","$animateCss","$mdUtil","$log"];ngMessagesAnimation.$inject=["$$AnimateRunner","$animateCss","$mdUtil","$log"];ngMessageAnimation.$inject=["$$AnimateRunner","$animateCss","$mdUtil","$log"];var inputModule=angular.module('material.components.input',['material.core']).directive('mdInputContainer',mdInputContainerDirective).directive('label',labelDirective).directive('input',inputTextareaDirective).directive('textarea',inputTextareaDirective).directive('mdMaxlength',mdMaxlengthDirective).directive('placeholder',placeholderDirective).directive('ngMessages',ngMessagesDirective).directive('ngMessage',ngMessageDirective).directive('ngMessageExp',ngMessageDirective).directive('mdSelectOnFocus',mdSelectOnFocusDirective).animation('.md-input-invalid',mdInputInvalidMessagesAnimation).animation('.md-input-messages-animation',ngMessagesAnimation).animation('.md-input-message-animation',ngMessageAnimation);// If we are running inside of tests; expose some extra services so that we can test them if(window._mdMocksIncluded){inputModule.service('$$mdInput',function(){return{// special accessor to internals... useful for testing messages:{show:showInputMessages,hide:hideInputMessages,getElement:getMessagesElement}};})// Register a service for each animation so that we can easily inject them into unit tests .service('mdInputInvalidAnimation',mdInputInvalidMessagesAnimation).service('mdInputMessagesAnimation',ngMessagesAnimation).service('mdInputMessageAnimation',ngMessageAnimation);}/** * @ngdoc directive * @name mdInputContainer * @module material.components.input * * @restrict E * * @description * `` is the parent of any input or textarea element. * * Input and textarea elements will not behave properly unless the md-input-container * parent is provided. * * A single `` should contain only one `` element, otherwise it will throw an error. * * Exception: Hidden inputs (``) are ignored and will not throw an error, so * you may combine these with other inputs. * * Note: When using `ngMessages` with your input element, make sure the message and container elements * are *block* elements, otherwise animations applied to the messages will not look as intended. Either use a `div` and * apply the `ng-message` and `ng-messages` classes respectively, or use the `md-block` class on your element. * * @param md-is-error {expression=} When the given expression evaluates to true, the input container * will go into error state. Defaults to erroring if the input has been touched and is invalid. * @param md-no-float {boolean=} When present, `placeholder` attributes on the input will not be converted to floating * labels. * * @usage * * * * * * * * * * * * * * *

      When disabling floating labels

      * * * * * * * */function mdInputContainerDirective($mdTheming,$parse){ContainerCtrl.$inject=['$scope','$element','$attrs','$animate'];ContainerCtrl.$inject=["$scope","$element","$attrs","$animate"];var INPUT_TAGS=['INPUT','TEXTAREA','SELECT','MD-SELECT'];var LEFT_SELECTORS=INPUT_TAGS.reduce(function(selectors,isel){return selectors.concat(['md-icon ~ '+isel,'.md-icon ~ '+isel]);},[]).join(",");var RIGHT_SELECTORS=INPUT_TAGS.reduce(function(selectors,isel){return selectors.concat([isel+' ~ md-icon',isel+' ~ .md-icon']);},[]).join(",");return{restrict:'E',compile:compile,controller:ContainerCtrl};function compile(tElement){// Check for both a left & right icon var leftIcon=tElement[0].querySelector(LEFT_SELECTORS);var rightIcon=tElement[0].querySelector(RIGHT_SELECTORS);if(leftIcon){tElement.addClass('md-icon-left');}if(rightIcon){tElement.addClass('md-icon-right');}return function postLink(scope,element){$mdTheming(element);};}function ContainerCtrl($scope,$element,$attrs,$animate){var self=this;self.isErrorGetter=$attrs.mdIsError&&$parse($attrs.mdIsError);self.delegateClick=function(){self.input.focus();};self.element=$element;self.setFocused=function(isFocused){$element.toggleClass('md-input-focused',!!isFocused);};self.setHasValue=function(hasValue){$element.toggleClass('md-input-has-value',!!hasValue);};self.setHasPlaceholder=function(hasPlaceholder){$element.toggleClass('md-input-has-placeholder',!!hasPlaceholder);};self.setInvalid=function(isInvalid){if(isInvalid){$animate.addClass($element,'md-input-invalid');}else{$animate.removeClass($element,'md-input-invalid');}};$scope.$watch(function(){return self.label&&self.input;},function(hasLabelAndInput){if(hasLabelAndInput&&!self.label.attr('for')){self.label.attr('for',self.input.attr('id'));}});}}function labelDirective(){return{restrict:'E',require:'^?mdInputContainer',link:function link(scope,element,attr,containerCtrl){if(!containerCtrl||attr.mdNoFloat||element.hasClass('md-container-ignore'))return;containerCtrl.label=element;scope.$on('$destroy',function(){containerCtrl.label=null;});}};}/** * @ngdoc directive * @name mdInput * @restrict E * @module material.components.input * * @description * You can use any `` or ` *
      *
      This is required!
      *
      That's too long!
      *
      *
      * * * * * * * * * *

      Notes

      * * - Requires [ngMessages](https://docs.angularjs.org/api/ngMessages). * - Behaves like the [AngularJS input directive](https://docs.angularjs.org/api/ng/directive/input). * * The `md-input` and `md-input-container` directives use very specific positioning to achieve the * error animation effects. Therefore, it is *not* advised to use the Layout system inside of the * `` tags. Instead, use relative or absolute positioning. * * *

      Textarea directive

      * The `textarea` element within a `md-input-container` has the following specific behavior: * - By default the `textarea` grows as the user types. This can be disabled via the `md-no-autogrow` * attribute. * - If a `textarea` has the `rows` attribute, it will treat the `rows` as the minimum height and will * continue growing as the user types. For example a textarea with `rows="3"` will be 3 lines of text * high initially. If no rows are specified, the directive defaults to 1. * - The textarea's height gets set on initialization, as well as while the user is typing. In certain situations * (e.g. while animating) the directive might have been initialized, before the element got it's final height. In * those cases, you can trigger a resize manually by broadcasting a `md-resize-textarea` event on the scope. * - If you want a `textarea` to stop growing at a certain point, you can specify the `max-rows` attribute. * - The textarea's bottom border acts as a handle which users can drag, in order to resize the element vertically. * Once the user has resized a `textarea`, the autogrowing functionality becomes disabled. If you don't want a * `textarea` to be resizeable by the user, you can add the `md-no-resize` attribute. */function inputTextareaDirective($mdUtil,$window,$mdAria,$timeout,$mdGesture){return{restrict:'E',require:['^?mdInputContainer','?ngModel','?^form'],link:postLink};function postLink(scope,element,attr,ctrls){var containerCtrl=ctrls[0];var hasNgModel=!!ctrls[1];var ngModelCtrl=ctrls[1]||$mdUtil.fakeNgModel();var parentForm=ctrls[2];var isReadonly=angular.isDefined(attr.readonly);var mdNoAsterisk=$mdUtil.parseAttributeBoolean(attr.mdNoAsterisk);var tagName=element[0].tagName.toLowerCase();if(!containerCtrl)return;if(attr.type==='hidden'){element.attr('aria-hidden','true');return;}else if(containerCtrl.input){if(containerCtrl.input[0].contains(element[0])){return;}else{throw new Error(" can only have *one* , * * * */function mdSelectOnFocusDirective($timeout){return{restrict:'A',link:postLink};function postLink(scope,element,attr){if(element[0].nodeName!=='INPUT'&&element[0].nodeName!=="TEXTAREA")return;var preventMouseUp=false;element.on('focus',onFocus).on('mouseup',onMouseUp);scope.$on('$destroy',function(){element.off('focus',onFocus).off('mouseup',onMouseUp);});function onFocus(){preventMouseUp=true;$timeout(function(){// Use HTMLInputElement#select to fix firefox select issues. // The debounce is here for Edge's sake, otherwise the selection doesn't work. element[0].select();// This should be reset from inside the `focus`, because the event might // have originated from something different than a click, e.g. a keyboard event. preventMouseUp=false;},1,false);}// Prevents the default action of the first `mouseup` after a focus. // This is necessary, because browsers fire a `mouseup` right after the element // has been focused. In some browsers (Firefox in particular) this can clear the // selection. There are examples of the problem in issue #7487. function onMouseUp(event){if(preventMouseUp){event.preventDefault();}}}}var visibilityDirectives=['ngIf','ngShow','ngHide','ngSwitchWhen','ngSwitchDefault'];function ngMessagesDirective(){return{restrict:'EA',link:postLink,// This is optional because we don't want target *all* ngMessage instances, just those inside of // mdInputContainer. require:'^^?mdInputContainer'};function postLink(scope,element,attrs,inputContainer){// If we are not a child of an input container, don't do anything if(!inputContainer)return;// Add our animation class element.toggleClass('md-input-messages-animation',true);// Add our md-auto-hide class to automatically hide/show messages when container is invalid element.toggleClass('md-auto-hide',true);// If we see some known visibility directives, remove the md-auto-hide class if(attrs.mdAutoHide=='false'||hasVisibiltyDirective(attrs)){element.toggleClass('md-auto-hide',false);}}function hasVisibiltyDirective(attrs){return visibilityDirectives.some(function(attr){return attrs[attr];});}}function ngMessageDirective($mdUtil){return{restrict:'EA',compile:compile,priority:100};function compile(tElement){if(!isInsideInputContainer(tElement)){// When the current element is inside of a document fragment, then we need to check for an input-container // in the postLink, because the element will be later added to the DOM and is currently just in a temporary // fragment, which causes the input-container check to fail. if(isInsideFragment()){return function(scope,element){if(isInsideInputContainer(element)){// Inside of the postLink function, a ngMessage directive will be a comment element, because it's // currently hidden. To access the shown element, we need to use the element from the compile function. initMessageElement(tElement);}};}}else{initMessageElement(tElement);}function isInsideFragment(){var nextNode=tElement[0];while(nextNode=nextNode.parentNode){if(nextNode.nodeType===Node.DOCUMENT_FRAGMENT_NODE){return true;}}return false;}function isInsideInputContainer(element){return!!$mdUtil.getClosest(element,"md-input-container");}function initMessageElement(element){// Add our animation class element.toggleClass('md-input-message-animation',true);}}}var $$AnimateRunner,$animateCss,$mdUtil,$log;function mdInputInvalidMessagesAnimation($$AnimateRunner,$animateCss,$mdUtil,$log){saveSharedServices($$AnimateRunner,$animateCss,$mdUtil,$log);return{addClass:function addClass(element,className,done){showInputMessages(element,done);}// NOTE: We do not need the removeClass method, because the message ng-leave animation will fire };}function ngMessagesAnimation($$AnimateRunner,$animateCss,$mdUtil,$log){saveSharedServices($$AnimateRunner,$animateCss,$mdUtil,$log);return{enter:function enter(element,done){showInputMessages(element,done);},leave:function leave(element,done){hideInputMessages(element,done);},addClass:function addClass(element,className,done){if(className=="ng-hide"){hideInputMessages(element,done);}else{done();}},removeClass:function removeClass(element,className,done){if(className=="ng-hide"){showInputMessages(element,done);}else{done();}}};}function ngMessageAnimation($$AnimateRunner,$animateCss,$mdUtil,$log){saveSharedServices($$AnimateRunner,$animateCss,$mdUtil,$log);return{enter:function enter(element,done){var animator=showMessage(element);animator.start().done(done);},leave:function leave(element,done){var animator=hideMessage(element);animator.start().done(done);}};}function showInputMessages(element,done){var animators=[],animator;var messages=getMessagesElement(element);var children=messages.children();if(messages.length==0||children.length==0){$log.warn('mdInput messages show animation called on invalid messages element: ',element);done();return;}angular.forEach(children,function(child){animator=showMessage(angular.element(child));animators.push(animator.start());});$$AnimateRunner.all(animators,done);}function hideInputMessages(element,done){var animators=[],animator;var messages=getMessagesElement(element);var children=messages.children();if(messages.length==0||children.length==0){$log.warn('mdInput messages hide animation called on invalid messages element: ',element);done();return;}angular.forEach(children,function(child){animator=hideMessage(angular.element(child));animators.push(animator.start());});$$AnimateRunner.all(animators,done);}function showMessage(element){var height=parseInt(window.getComputedStyle(element[0]).height);var topMargin=parseInt(window.getComputedStyle(element[0]).marginTop);var messages=getMessagesElement(element);var container=getInputElement(element);// Check to see if the message is already visible so we can skip var alreadyVisible=topMargin>-height;// If we have the md-auto-hide class, the md-input-invalid animation will fire, so we can skip if(alreadyVisible||messages.hasClass('md-auto-hide')&&!container.hasClass('md-input-invalid')){return $animateCss(element,{});}return $animateCss(element,{event:'enter',structural:true,from:{"opacity":0,"margin-top":-height+"px"},to:{"opacity":1,"margin-top":"0"},duration:0.3});}function hideMessage(element){var height=element[0].offsetHeight;var styles=window.getComputedStyle(element[0]);// If we are already hidden, just return an empty animation if(parseInt(styles.opacity)===0){return $animateCss(element,{});}// Otherwise, animate return $animateCss(element,{event:'leave',structural:true,from:{"opacity":1,"margin-top":0},to:{"opacity":0,"margin-top":-height+"px"},duration:0.3});}function getInputElement(element){var inputContainer=element.controller('mdInputContainer');return inputContainer.element;}function getMessagesElement(element){// If we ARE the messages element, just return ourself if(element.hasClass('md-input-messages-animation')){return element;}// If we are a ng-message element, we need to traverse up the DOM tree if(element.hasClass('md-input-message-animation')){return angular.element($mdUtil.getClosest(element,function(node){return node.classList.contains('md-input-messages-animation');}));}// Otherwise, we can traverse down return angular.element(element[0].querySelector('.md-input-messages-animation'));}function saveSharedServices(_$$AnimateRunner_,_$animateCss_,_$mdUtil_,_$log_){$$AnimateRunner=_$$AnimateRunner_;$animateCss=_$animateCss_;$mdUtil=_$mdUtil_;$log=_$log_;}})();(function(){"use strict";/** * @ngdoc module * @name material.components.list * @description * List module */mdListItemDirective.$inject=['$mdAria','$mdConstant','$mdUtil','$timeout'];mdListDirective.$inject=['$mdTheming'];MdListController.$inject=['$scope','$element','$mdListInkRipple'];MdListController.$inject=["$scope","$element","$mdListInkRipple"];mdListDirective.$inject=["$mdTheming"];mdListItemDirective.$inject=["$mdAria","$mdConstant","$mdUtil","$timeout"];angular.module('material.components.list',['material.core']).controller('MdListController',MdListController).directive('mdList',mdListDirective).directive('mdListItem',mdListItemDirective);/** * @ngdoc directive * @name mdList * @module material.components.list * * @restrict E * * @description * The `` directive is a list container for 1..n `` tags. * * @usage * * * * *
      *

      {{item.title}}

      *

      {{item.description}}

      *
      *
      *
      *
      */function mdListDirective($mdTheming){return{restrict:'E',compile:function compile(tEl){tEl[0].setAttribute('role','list');return $mdTheming;}};}/** * @ngdoc directive * @name mdListItem * @module material.components.list * * @restrict E * * @description * A `md-list-item` element can be used to represent some information in a row.
      * * @usage * ### Single Row Item * * * Single Row Item * * * * ### Multiple Lines * By using the following markup, you will be able to have two lines inside of one `md-list-item`. * * * *
      *

      First Line

      *

      Second Line

      *
      *
      *
      * * It is also possible to have three lines inside of one list item. * * * *
      *

      First Line

      *

      Second Line

      *

      Third Line

      *
      *
      *
      * * ### Secondary Items * Secondary items are elements which will be aligned at the end of the `md-list-item`. * * * * Single Row Item * * Secondary Button * * * * * It also possible to have multiple secondary items inside of one `md-list-item`. * * * * Single Row Item * First Button * Second Button * * * * ### Proxy Item * Proxies are elements, which will execute their specific action on click
      * Currently supported proxy items are * - `md-checkbox` (Toggle) * - `md-switch` (Toggle) * - `md-menu` (Open) * * This means, when using a supported proxy item inside of `md-list-item`, the list item will * automatically become clickable and executes the associated action of the proxy element on click. * * It is possible to disable this behavior by applying the `md-no-proxy` class to the list item. * * * * No Proxy List * * * * * Here are a few examples of proxy elements inside of a list item. * * * * First Line * * * * * The `md-checkbox` element will be automatically detected as a proxy element and will toggle on click. * * * * First Line * * * * * The recognized `md-switch` will toggle its state, when the user clicks on the `md-list-item`. * * It is also possible to have a `md-menu` inside of a `md-list-item`. * * *

      Click anywhere to fire the secondary action

      * * * * * * * * Redial * * * * * Check voicemail * * * * * * Notifications * * * * *
      *
      * * The menu will automatically open, when the users clicks on the `md-list-item`.
      * * If the developer didn't specify any position mode on the menu, the `md-list-item` will automatically detect the * position mode and applies it to the `md-menu`. * * ### Avatars * Sometimes you may want to have some avatars inside of the `md-list-item `.
      * You are able to create a optimized icon for the list item, by applying the `.md-avatar` class on the `` element. * * * * * Alan Turing * * * When using `` for an avatar, you have to use the `.md-avatar-icon` class. * * * * Timothy Kopra * * * * In cases, you have a `md-list-item`, which doesn't have any avatar, * but you want to align it with the other avatar items, you have to use the `.md-offset` class. * * * * Jon Doe * * * * ### DOM modification * The `md-list-item` component automatically detects if the list item should be clickable. * * --- * If the `md-list-item` is clickable, we wrap all content inside of a `
      ` and create * an overlaying button, which will will execute the given actions (like `ng-href`, `ng-click`) * * We create an overlaying button, instead of wrapping all content inside of the button, * because otherwise some elements may not be clickable inside of the button. * * --- * When using a secondary item inside of your list item, the `md-list-item` component will automatically create * a secondary container at the end of the `md-list-item`, which contains all secondary items. * * The secondary item container is not static, because otherwise the overflow will not work properly on the * list item. * */function mdListItemDirective($mdAria,$mdConstant,$mdUtil,$timeout){var proxiedTypes=['md-checkbox','md-switch','md-menu'];return{restrict:'E',controller:'MdListController',compile:function compile(tEl,tAttrs){// Check for proxy controls (no ng-click on parent, and a control inside) var secondaryItems=tEl[0].querySelectorAll('.md-secondary');var hasProxiedElement;var proxyElement;var itemContainer=tEl;tEl[0].setAttribute('role','listitem');if(tAttrs.ngClick||tAttrs.ngDblclick||tAttrs.ngHref||tAttrs.href||tAttrs.uiSref||tAttrs.ngAttrUiSref){wrapIn('button');}else if(!tEl.hasClass('md-no-proxy')){for(var i=0,type;type=proxiedTypes[i];++i){if(proxyElement=tEl[0].querySelector(type)){hasProxiedElement=true;break;}}if(hasProxiedElement){wrapIn('div');}else{tEl.addClass('md-no-proxy');}}wrapSecondaryItems();setupToggleAria();if(hasProxiedElement&&proxyElement.nodeName==="MD-MENU"){setupProxiedMenu();}function setupToggleAria(){var toggleTypes=['md-switch','md-checkbox'];var toggle;for(var i=0,toggleType;toggleType=toggleTypes[i];++i){if(toggle=tEl.find(toggleType)[0]){if(!toggle.hasAttribute('aria-label')){var p=tEl.find('p')[0];if(!p)return;toggle.setAttribute('aria-label','Toggle '+p.textContent);}}}}function setupProxiedMenu(){var menuEl=angular.element(proxyElement);var isEndAligned=menuEl.parent().hasClass('md-secondary-container')||proxyElement.parentNode.firstElementChild!==proxyElement;var xAxisPosition='left';if(isEndAligned){// When the proxy item is aligned at the end of the list, we have to set the origin to the end. xAxisPosition='right';}// Set the position mode / origin of the proxied menu. if(!menuEl.attr('md-position-mode')){menuEl.attr('md-position-mode',xAxisPosition+' target');}// Apply menu open binding to menu button var menuOpenButton=menuEl.children().eq(0);if(!hasClickEvent(menuOpenButton[0])){menuOpenButton.attr('ng-click','$mdMenu.open($event)');}if(!menuOpenButton.attr('aria-label')){menuOpenButton.attr('aria-label','Open List Menu');}}function wrapIn(type){if(type=='div'){itemContainer=angular.element('
      ');itemContainer.append(tEl.contents());tEl.addClass('md-proxy-focus');}else{// Element which holds the default list-item content. itemContainer=angular.element('
      '+'
      '+'
      ');// Button which shows ripple and executes primary action. var buttonWrap=angular.element('');copyAttributes(tEl[0],buttonWrap[0]);// If there is no aria-label set on the button (previously copied over if present) // we determine the label from the content and copy it to the button. if(!buttonWrap.attr('aria-label')){buttonWrap.attr('aria-label',$mdAria.getText(tEl));}// We allow developers to specify the `md-no-focus` class, to disable the focus style // on the button executor. Once more classes should be forwarded, we should probably make the // class forward more generic. if(tEl.hasClass('md-no-focus')){buttonWrap.addClass('md-no-focus');}// Append the button wrap before our list-item content, because it will overlay in relative. itemContainer.prepend(buttonWrap);itemContainer.children().eq(1).append(tEl.contents());tEl.addClass('_md-button-wrap');}tEl[0].setAttribute('tabindex','-1');tEl.append(itemContainer);}function wrapSecondaryItems(){var secondaryItemsWrapper=angular.element('
      ');angular.forEach(secondaryItems,function(secondaryItem){wrapSecondaryItem(secondaryItem,secondaryItemsWrapper);});itemContainer.append(secondaryItemsWrapper);}function wrapSecondaryItem(secondaryItem,container){// If the current secondary item is not a button, but contains a ng-click attribute, // the secondary item will be automatically wrapped inside of a button. if(secondaryItem&&!isButton(secondaryItem)&&secondaryItem.hasAttribute('ng-click')){$mdAria.expect(secondaryItem,'aria-label');var buttonWrapper=angular.element('');// Copy the attributes from the secondary item to the generated button. // We also support some additional attributes from the secondary item, // because some developers may use a ngIf, ngHide, ngShow on their item. copyAttributes(secondaryItem,buttonWrapper[0],['ng-if','ng-hide','ng-show']);secondaryItem.setAttribute('tabindex','-1');buttonWrapper.append(secondaryItem);secondaryItem=buttonWrapper[0];}if(secondaryItem&&(!hasClickEvent(secondaryItem)||!tAttrs.ngClick&&isProxiedElement(secondaryItem))){// In this case we remove the secondary class, so we can identify it later, when we searching for the // proxy items. angular.element(secondaryItem).removeClass('md-secondary');}tEl.addClass('md-with-secondary');container.append(secondaryItem);}/** * Copies attributes from a source element to the destination element * By default the function will copy the most necessary attributes, supported * by the button executor for clickable list items. * @param source Element with the specified attributes * @param destination Element which will retrieve the attributes * @param extraAttrs Additional attributes, which will be copied over. */function copyAttributes(source,destination,extraAttrs){var copiedAttrs=$mdUtil.prefixer(['ng-if','ng-click','ng-dblclick','aria-label','ng-disabled','ui-sref','href','ng-href','rel','target','ng-attr-ui-sref','ui-sref-opts','download']);if(extraAttrs){copiedAttrs=copiedAttrs.concat($mdUtil.prefixer(extraAttrs));}angular.forEach(copiedAttrs,function(attr){if(source.hasAttribute(attr)){destination.setAttribute(attr,source.getAttribute(attr));source.removeAttribute(attr);}});}function isProxiedElement(el){return proxiedTypes.indexOf(el.nodeName.toLowerCase())!=-1;}function isButton(el){var nodeName=el.nodeName.toUpperCase();return nodeName=="MD-BUTTON"||nodeName=="BUTTON";}function hasClickEvent(element){var attr=element.attributes;for(var i=0;i` directive renders a list of material tabs that can be used * for top-level page navigation. Unlike ``, it has no concept of a tab * body and no bar pagination. * * Because it deals with page navigation, certain routing concepts are built-in. * Route changes via ng-href, ui-sref, or ng-click events are supported. * Alternatively, the user could simply watch currentNavItem for changes. * * Accessibility functionality is implemented as a site navigator with a * listbox, according to * https://www.w3.org/TR/wai-aria-practices/#Site_Navigator_Tabbed_Style * * @param {string=} mdSelectedNavItem The name of the current tab; this must * match the name attribute of `` * @param {boolean=} mdNoInkBar If set to true, the ink bar will be hidden. * @param {string=} navBarAriaLabel An aria-label for the nav-bar * * @usage * * * * Page One * * Page Two * Page Three * * Page Four * * * * * (function() { * 'use strict'; * * $rootScope.$on('$routeChangeSuccess', function(event, current) { * $scope.currentLink = getCurrentLinkFromRoute(current); * }); * }); * *//***************************************************************************** * mdNavItem *****************************************************************************//** * @ngdoc directive * @name mdNavItem * @module material.components.navBar * * @restrict E * * @description * `` describes a page navigation link within the `` * component. It renders an md-button as the actual link. * * Exactly one of the mdNavClick, mdNavHref, mdNavSref attributes are required * to be specified. * * @param {Function=} mdNavClick Function which will be called when the * link is clicked to change the page. Renders as an `ng-click`. * @param {string=} mdNavHref url to transition to when this link is clicked. * Renders as an `ng-href`. * @param {string=} mdNavSref Ui-router state to transition to when this link is * clicked. Renders as a `ui-sref`. * @param {!Object=} srefOpts Ui-router options that are passed to the * `$state.go()` function. See the [Ui-router documentation for details] * (https://ui-router.github.io/docs/latest/interfaces/transition.transitionoptions.html). * @param {string=} name The name of this link. Used by the nav bar to know * which link is currently selected. * @param {string=} aria-label Adds alternative text for accessibility * * @usage * See `` for usage. *//***************************************************************************** * IMPLEMENTATION * *****************************************************************************/function MdNavBar($mdAria,$mdTheming){return{restrict:'E',transclude:true,controller:MdNavBarController,controllerAs:'ctrl',bindToController:true,scope:{'mdSelectedNavItem':'=?','mdNoInkBar':'=?','navBarAriaLabel':'@?'},template:'
      '+''+''+'
      ',link:function link(scope,element,attrs,ctrl){$mdTheming(element);if(!ctrl.navBarAriaLabel){$mdAria.expectAsync(element,'aria-label',angular.noop);}}};}/** * Controller for the nav-bar component. * * Accessibility functionality is implemented as a site navigator with a * listbox, according to * https://www.w3.org/TR/wai-aria-practices/#Site_Navigator_Tabbed_Style * @param {!angular.JQLite} $element * @param {!angular.Scope} $scope * @param {!angular.Timeout} $timeout * @param {!Object} $mdConstant * @constructor * @final * @ngInject */function MdNavBarController($element,$scope,$timeout,$mdConstant){// Injected variables /** @private @const {!angular.Timeout} */this._$timeout=$timeout;/** @private @const {!angular.Scope} */this._$scope=$scope;/** @private @const {!Object} */this._$mdConstant=$mdConstant;// Data-bound variables. /** @type {string} */this.mdSelectedNavItem;/** @type {string} */this.navBarAriaLabel;// State variables. /** @type {?angular.JQLite} */this._navBarEl=$element[0];/** @type {?angular.JQLite} */this._inkbar;var self=this;// need to wait for transcluded content to be available var deregisterTabWatch=this._$scope.$watch(function(){return self._navBarEl.querySelectorAll('._md-nav-button').length;},function(newLength){if(newLength>0){self._initTabs();deregisterTabWatch();}});}/** * Initializes the tab components once they exist. * @private */MdNavBarController.prototype._initTabs=function(){this._inkbar=angular.element(this._navBarEl.querySelector('md-nav-ink-bar'));var self=this;this._$timeout(function(){self._updateTabs(self.mdSelectedNavItem,undefined);});this._$scope.$watch('ctrl.mdSelectedNavItem',function(newValue,oldValue){// Wait a digest before update tabs for products doing // anything dynamic in the template. self._$timeout(function(){self._updateTabs(newValue,oldValue);});});};/** * Set the current tab to be selected. * @param {string|undefined} newValue New current tab name. * @param {string|undefined} oldValue Previous tab name. * @private */MdNavBarController.prototype._updateTabs=function(newValue,oldValue){var self=this;var tabs=this._getTabs();// this._getTabs can return null if nav-bar has not yet been initialized if(!tabs)return;var oldIndex=-1;var newIndex=-1;var newTab=this._getTabByName(newValue);var oldTab=this._getTabByName(oldValue);if(oldTab){oldTab.setSelected(false);oldIndex=tabs.indexOf(oldTab);}if(newTab){newTab.setSelected(true);newIndex=tabs.indexOf(newTab);}this._$timeout(function(){self._updateInkBarStyles(newTab,newIndex,oldIndex);});};/** * Repositions the ink bar to the selected tab. * @private */MdNavBarController.prototype._updateInkBarStyles=function(tab,newIndex,oldIndex){this._inkbar.toggleClass('_md-left',newIndexoldIndex);this._inkbar.css({display:newIndex<0?'none':''});if(tab){var tabEl=tab.getButtonEl();var left=tabEl.offsetLeft;this._inkbar.css({left:left+'px',width:tabEl.offsetWidth+'px'});}};/** * Returns an array of the current tabs. * @return {!Array} * @private */MdNavBarController.prototype._getTabs=function(){var controllers=Array.prototype.slice.call(this._navBarEl.querySelectorAll('.md-nav-item')).map(function(el){return angular.element(el).controller('mdNavItem');});return controllers.indexOf(undefined)?controllers:null;};/** * Returns the tab with the specified name. * @param {string} name The name of the tab, found in its name attribute. * @return {!NavItemController|undefined} * @private */MdNavBarController.prototype._getTabByName=function(name){return this._findTab(function(tab){return tab.getName()==name;});};/** * Returns the selected tab. * @return {!NavItemController|undefined} * @private */MdNavBarController.prototype._getSelectedTab=function(){return this._findTab(function(tab){return tab.isSelected();});};/** * Returns the focused tab. * @return {!NavItemController|undefined} */MdNavBarController.prototype.getFocusedTab=function(){return this._findTab(function(tab){return tab.hasFocus();});};/** * Find a tab that matches the specified function. * @private */MdNavBarController.prototype._findTab=function(fn){var tabs=this._getTabs();for(var i=0;i0){this._moveFocus(focusedTab,tabs[focusedTabIndex-1]);}break;case keyCodes.DOWN_ARROW:case keyCodes.RIGHT_ARROW:if(focusedTabIndex1){throw Error('Must not specify more than one of the md-nav-click, md-nav-href, '+'or md-nav-sref attributes per nav-item directive.');}if(hasNavClick){navigationAttribute='ng-click="ctrl.mdNavClick()"';}else if(hasNavHref){navigationAttribute='ng-href="{{ctrl.mdNavHref}}"';}else if(hasNavSref){navigationAttribute='ui-sref="{{ctrl.mdNavSref}}"';}navigationOptions=hasSrefOpts?'ui-sref-opts="{{ctrl.srefOpts}}" ':'';if(navigationAttribute){buttonTemplate=''+''+''+'';}return''+'
    • '+(buttonTemplate||'')+'
    • ';},scope:{'mdNavClick':'&?','mdNavHref':'@?','mdNavSref':'@?','srefOpts':'=?','name':'@'},link:function link(scope,element,attrs,controllers){// When accessing the element's contents synchronously, they // may not be defined yet because of transclusion. There is a higher // chance that it will be accessible if we wait one frame. $$rAF(function(){var mdNavItem=controllers[0];var mdNavBar=controllers[1];var navButton=angular.element(element[0].querySelector('._md-nav-button'));if(!mdNavItem.name){mdNavItem.name=angular.element(element[0].querySelector('._md-nav-button-text')).text().trim();}navButton.on('click',function(){mdNavBar.mdSelectedNavItem=mdNavItem.name;scope.$apply();});$mdAria.expectWithText(element,'aria-label');});}};}/** * Controller for the nav-item component. * @param {!angular.JQLite} $element * @constructor * @final * @ngInject */function MdNavItemController($element){/** @private @const {!angular.JQLite} */this._$element=$element;// Data-bound variables /** @const {?Function} */this.mdNavClick;/** @const {?string} */this.mdNavHref;/** @const {?string} */this.mdNavSref;/** @const {?Object} */this.srefOpts;/** @const {?string} */this.name;// State variables /** @private {boolean} */this._selected=false;/** @private {boolean} */this._focused=false;}/** * Returns a map of class names and values for use by ng-class. * @return {!Object} */MdNavItemController.prototype.getNgClassMap=function(){return{'md-active':this._selected,'md-primary':this._selected,'md-unselected':!this._selected,'md-focused':this._focused};};/** * Get the name attribute of the tab. * @return {string} */MdNavItemController.prototype.getName=function(){return this.name;};/** * Get the button element associated with the tab. * @return {!Element} */MdNavItemController.prototype.getButtonEl=function(){return this._$element[0].querySelector('._md-nav-button');};/** * Set the selected state of the tab. * @param {boolean} isSelected */MdNavItemController.prototype.setSelected=function(isSelected){this._selected=isSelected;};/** * @return {boolean} */MdNavItemController.prototype.isSelected=function(){return this._selected;};/** * Set the focused state of the tab. * @param {boolean} isFocused */MdNavItemController.prototype.setFocused=function(isFocused){this._focused=isFocused;if(isFocused){this.getButtonEl().focus();}};/** * @return {boolean} */MdNavItemController.prototype.hasFocus=function(){return this._focused;};})();(function(){"use strict";/** * @ngdoc module * @name material.components.panel */MdPanelService.$inject=["presets","$rootElement","$rootScope","$injector","$window"];angular.module('material.components.panel',['material.core','material.components.backdrop']).provider('$mdPanel',MdPanelProvider);/***************************************************************************** * PUBLIC DOCUMENTATION * *****************************************************************************//** * @ngdoc service * @name $mdPanelProvider * @module material.components.panel * * @description * `$mdPanelProvider` allows users to create configuration presets that will be * stored within a cached presets object. When the configuration is needed, the * user can request the preset by passing it as the first parameter in the * `$mdPanel.create` or `$mdPanel.open` methods. * * @usage * * (function(angular, undefined) { * 'use strict'; * * angular * .module('demoApp', ['ngMaterial']) * .config(DemoConfig) * .controller('DemoCtrl', DemoCtrl) * .controller('DemoMenuCtrl', DemoMenuCtrl); * * function DemoConfig($mdPanelProvider) { * $mdPanelProvider.definePreset('demoPreset', { * attachTo: angular.element(document.body), * controller: DemoMenuCtrl, * controllerAs: 'ctrl', * template: '' + * '', * panelClass: 'menu-panel-container', * focusOnOpen: false, * zIndex: 100, * propagateContainerEvents: true, * groupName: 'menus' * }); * } * * function PanelProviderCtrl($mdPanel) { * this.navigation = { * name: 'navigation', * items: [ * 'Home', * 'About', * 'Contact' * ] * }; * this.favorites = { * name: 'favorites', * items: [ * 'Add to Favorites' * ] * }; * this.more = { * name: 'more', * items: [ * 'Account', * 'Sign Out' * ] * }; * * $mdPanel.newPanelGroup('menus', { * maxOpen: 2 * }); * * this.showMenu = function($event, menu) { * $mdPanel.open('demoPreset', { * id: 'menu_' + menu.name, * position: $mdPanel.newPanelPosition() * .relativeTo($event.srcElement) * .addPanelPosition( * $mdPanel.xPosition.ALIGN_START, * $mdPanel.yPosition.BELOW * ), * locals: { * items: menu.items * }, * openFrom: $event * }); * }; * } * * function PanelMenuCtrl(mdPanelRef) { * // The controller is provided with an import named 'mdPanelRef' * this.closeMenu = function() { * mdPanelRef && mdPanelRef.close(); * }; * } * })(angular); * *//** * @ngdoc method * @name $mdPanelProvider#definePreset * @description * Takes the passed in preset name and preset configuration object and adds it * to the `_presets` object of the provider. This `_presets` object is then * passed along to the `$mdPanel` service. * * @param {string} name Preset name. * @param {!Object} preset Specific configuration object that can contain any * and all of the parameters avaialble within the `$mdPanel.create` method. * However, parameters that pertain to id, position, animation, and user * interaction are not allowed and will be removed from the preset * configuration. *//***************************************************************************** * MdPanel Service * *****************************************************************************//** * @ngdoc service * @name $mdPanel * @module material.components.panel * * @description * `$mdPanel` is a robust, low-level service for creating floating panels on * the screen. It can be used to implement tooltips, dialogs, pop-ups, etc. * * @usage * * (function(angular, undefined) { * 'use strict'; * * angular * .module('demoApp', ['ngMaterial']) * .controller('DemoDialogController', DialogController); * * var panelRef; * * function showPanel($event) { * var panelPosition = $mdPanel.newPanelPosition() * .absolute() * .top('50%') * .left('50%'); * * var panelAnimation = $mdPanel.newPanelAnimation() * .targetEvent($event) * .defaultAnimation('md-panel-animate-fly') * .closeTo('.show-button'); * * var config = { * attachTo: angular.element(document.body), * controller: DialogController, * controllerAs: 'ctrl', * position: panelPosition, * animation: panelAnimation, * targetEvent: $event, * templateUrl: 'dialog-template.html', * clickOutsideToClose: true, * escapeToClose: true, * focusOnOpen: true * }; * * $mdPanel.open(config) * .then(function(result) { * panelRef = result; * }); * } * * function DialogController(MdPanelRef) { * function closeDialog() { * if (MdPanelRef) MdPanelRef.close(); * } * } * })(angular); * *//** * @ngdoc method * @name $mdPanel#create * @description * Creates a panel with the specified options. * * @param config {!Object=} Specific configuration object that may contain the * following properties: * * - `id` - `{string=}`: An ID to track the panel by. When an ID is provided, * the created panel is added to a tracked panels object. Any subsequent * requests made to create a panel with that ID are ignored. This is useful * in having the panel service not open multiple panels from the same user * interaction when there is no backdrop and events are propagated. Defaults * to an arbitrary string that is not tracked. * - `template` - `{string=}`: HTML template to show in the panel. This * **must** be trusted HTML with respect to AngularJS’s * [$sce service](https://docs.angularjs.org/api/ng/service/$sce). * - `templateUrl` - `{string=}`: The URL that will be used as the content of * the panel. * - `contentElement` - `{(string|!angular.JQLite|!Element)=}`: Pre-compiled * element to be used as the panel's content. * - `controller` - `{(function|string)=}`: The controller to associate with * the panel. The controller can inject a reference to the returned * panelRef, which allows the panel to be closed, hidden, and shown. Any * fields passed in through locals or resolve will be bound to the * controller. * - `controllerAs` - `{string=}`: An alias to assign the controller to on * the scope. * - `bindToController` - `{boolean=}`: Binds locals to the controller * instead of passing them in. Defaults to true, as this is a best * practice. * - `locals` - `{Object=}`: 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 3. 'mdPanelRef' is a reserved key, and will always * be set to the created MdPanelRef instance. * - `resolve` - `{Object=}`: Similar to locals, except it takes promises as * values. The panel will not open until all of the promises resolve. * - `attachTo` - `{(string|!angular.JQLite|!Element)=}`: The element to * attach the panel to. Defaults to appending to the root element of the * application. * - `propagateContainerEvents` - `{boolean=}`: Whether pointer or touch * events should be allowed to propagate 'go through' the container, aka the * wrapper, of the panel. Defaults to false. * - `panelClass` - `{string=}`: A css class to apply to the panel element. * This class should define any borders, box-shadow, etc. for the panel. * - `zIndex` - `{number=}`: The z-index to place the panel at. * Defaults to 80. * - `position` - `{MdPanelPosition=}`: An MdPanelPosition object that * specifies the alignment of the panel. For more information, see * `MdPanelPosition`. * - `clickOutsideToClose` - `{boolean=}`: Whether the user can click * outside the panel to close it. Defaults to false. * - `escapeToClose` - `{boolean=}`: Whether the user can press escape to * close the panel. Defaults to false. * - `onCloseSuccess` - `{function(!panelRef, string)=}`: Function that is * called after the close successfully finishes. The first parameter passed * into this function is the current panelRef and the 2nd is an optional * string explaining the close reason. The currently supported closeReasons * can be found in the MdPanelRef.closeReasons enum. These are by default * passed along by the panel. * - `trapFocus` - `{boolean=}`: Whether focus should be trapped within the * panel. If `trapFocus` is true, the user will not be able to interact * with the rest of the page until the panel is dismissed. Defaults to * false. * - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on * open. Only disable if focusing some other way, as focus management is * required for panels to be accessible. Defaults to true. * - `fullscreen` - `{boolean=}`: Whether the panel should be full screen. * Applies the class `._md-panel-fullscreen` to the panel on open. Defaults * to false. * - `animation` - `{MdPanelAnimation=}`: An MdPanelAnimation object that * specifies the animation of the panel. For more information, see * `MdPanelAnimation`. * - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop * behind the panel. Defaults to false. * - `disableParentScroll` - `{boolean=}`: Whether the user can scroll the * page behind the panel. Defaults to false. * - `onDomAdded` - `{function=}`: Callback function used to announce when * the panel is added to the DOM. * - `onOpenComplete` - `{function=}`: Callback function used to announce * when the open() action is finished. * - `onRemoving` - `{function=}`: Callback function used to announce the * close/hide() action is starting. * - `onDomRemoved` - `{function=}`: Callback function used to announce when * the panel is removed from the DOM. * - `origin` - `{(string|!angular.JQLite|!Element)=}`: The element to focus * on when the panel closes. This is commonly the element which triggered * the opening of the panel. If you do not use `origin`, you need to control * the focus manually. * - `groupName` - `{(string|!Array)=}`: A group name or an array of * group names. The group name is used for creating a group of panels. The * group is used for configuring the number of open panels and identifying * specific behaviors for groups. For instance, all tooltips could be * identified using the same groupName. * * @returns {!MdPanelRef} panelRef *//** * @ngdoc method * @name $mdPanel#open * @description * Calls the create method above, then opens the panel. This is a shortcut for * creating and then calling open manually. If custom methods need to be * called when the panel is added to the DOM or opened, do not use this method. * Instead create the panel, chain promises on the domAdded and openComplete * methods, and call open from the returned panelRef. * * @param {!Object=} config Specific configuration object that may contain * the properties defined in `$mdPanel.create`. * @returns {!angular.$q.Promise} panelRef A promise that resolves * to an instance of the panel. *//** * @ngdoc method * @name $mdPanel#newPanelPosition * @description * Returns a new instance of the MdPanelPosition object. Use this to create * the position config object. * * @returns {!MdPanelPosition} panelPosition *//** * @ngdoc method * @name $mdPanel#newPanelAnimation * @description * Returns a new instance of the MdPanelAnimation object. Use this to create * the animation config object. * * @returns {!MdPanelAnimation} panelAnimation *//** * @ngdoc method * @name $mdPanel#newPanelGroup * @description * Creates a panel group and adds it to a tracked list of panel groups. * * @param {string} groupName Name of the group to create. * @param {!Object=} config Specific configuration object that may contain the * following properties: * * - `maxOpen` - `{number=}`: The maximum number of panels that are allowed to * be open within a defined panel group. * * @returns {!Object, * openPanels: !Array, * maxOpen: number}>} panelGroup *//** * @ngdoc method * @name $mdPanel#setGroupMaxOpen * @description * Sets the maximum number of panels in a group that can be opened at a given * time. * * @param {string} groupName The name of the group to configure. * @param {number} maxOpen The maximum number of panels that can be * opened. Infinity can be passed in to remove the maxOpen limit. *//***************************************************************************** * MdPanelRef * *****************************************************************************//** * @ngdoc type * @name MdPanelRef * @module material.components.panel * @description * A reference to a created panel. This reference contains a unique id for the * panel, along with the following properties: * * - `id` - `{string}`: The unique id for the panel. This id is used to track * when a panel was interacted with. * - `config` - `{!Object=}`: The entire config object that was used in * create. * - `isAttached` - `{boolean}`: Whether the panel is attached to the DOM. * Visibility to the user does not factor into isAttached. * - `panelContainer` - `{angular.JQLite}`: The wrapper element containing the * panel. This property is added in order to have access to the `addClass`, * `removeClass`, `toggleClass`, etc methods. * - `panelEl` - `{angular.JQLite}`: The panel element. This property is added * in order to have access to the `addClass`, `removeClass`, `toggleClass`, * etc methods. *//** * @ngdoc method * @name MdPanelRef#open * @description * Attaches and shows the panel. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * opened. *//** * @ngdoc method * @name MdPanelRef#close * @description * Hides and detaches the panel. Note that this will **not** destroy the panel. * If you don't intend on using the panel again, call the {@link #destroy * destroy} method afterwards. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * closed. *//** * @ngdoc method * @name MdPanelRef#attach * @description * Create the panel elements and attach them to the DOM. The panel will be * hidden by default. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * attached. *//** * @ngdoc method * @name MdPanelRef#detach * @description * Removes the panel from the DOM. This will NOT hide the panel before removing * it. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * detached. *//** * @ngdoc method * @name MdPanelRef#show * @description * Shows the panel. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * shown and animations are completed. *//** * @ngdoc method * @name MdPanelRef#hide * @description * Hides the panel. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * hidden and animations are completed. *//** * @ngdoc method * @name MdPanelRef#destroy * @description * Destroys the panel. The panel cannot be opened again after this is called. *//** * @ngdoc method * @name MdPanelRef#addClass * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * @description * Adds a class to the panel. DO NOT use this hide/show the panel. * * @param {string} newClass class to be added. * @param {boolean} toElement Whether or not to add the class to the panel * element instead of the container. *//** * @ngdoc method * @name MdPanelRef#removeClass * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * @description * Removes a class from the panel. DO NOT use this to hide/show the panel. * * @param {string} oldClass Class to be removed. * @param {boolean} fromElement Whether or not to remove the class from the * panel element instead of the container. *//** * @ngdoc method * @name MdPanelRef#toggleClass * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * @description * Toggles a class on the panel. DO NOT use this to hide/show the panel. * * @param {string} toggleClass Class to be toggled. * @param {boolean} onElement Whether or not to remove the class from the panel * element instead of the container. *//** * @ngdoc method * @name MdPanelRef#updatePosition * @description * Updates the position configuration of a panel. Use this to update the * position of a panel that is open, without having to close and re-open the * panel. * * @param {!MdPanelPosition} position *//** * @ngdoc method * @name MdPanelRef#addToGroup * @description * Adds a panel to a group if the panel does not exist within the group already. * A panel can only exist within a single group. * * @param {string} groupName The name of the group to add the panel to. *//** * @ngdoc method * @name MdPanelRef#removeFromGroup * @description * Removes a panel from a group if the panel exists within that group. The group * must be created ahead of time. * * @param {string} groupName The name of the group. *//** * @ngdoc method * @name MdPanelRef#registerInterceptor * @description * Registers an interceptor with the panel. The callback should return a promise, * which will allow the action to continue when it gets resolved, or will * prevent an action if it is rejected. The interceptors are called sequentially * and it reverse order. `type` must be one of the following * values available on `$mdPanel.interceptorTypes`: * * `CLOSE` - Gets called before the panel begins closing. * * @param {string} type Type of interceptor. * @param {!angular.$q.Promise} callback Callback to be registered. * @returns {!MdPanelRef} *//** * @ngdoc method * @name MdPanelRef#removeInterceptor * @description * Removes a registered interceptor. * * @param {string} type Type of interceptor to be removed. * @param {function(): !angular.$q.Promise} callback Interceptor to be removed. * @returns {!MdPanelRef} *//** * @ngdoc method * @name MdPanelRef#removeAllInterceptors * @description * Removes all interceptors. If a type is supplied, only the * interceptors of that type will be cleared. * * @param {string=} type Type of interceptors to be removed. * @returns {!MdPanelRef} *//** * @ngdoc method * @name MdPanelRef#updateAnimation * @description * Updates the animation configuration for a panel. You can use this to change * the panel's animation without having to re-create it. * * @param {!MdPanelAnimation} animation *//***************************************************************************** * MdPanelPosition * *****************************************************************************//** * @ngdoc type * @name MdPanelPosition * @module material.components.panel * @description * * Object for configuring the position of the panel. * * @usage * * #### Centering the panel * * * new MdPanelPosition().absolute().center(); * * * #### Overlapping the panel with an element * * * new MdPanelPosition() * .relativeTo(someElement) * .addPanelPosition( * $mdPanel.xPosition.ALIGN_START, * $mdPanel.yPosition.ALIGN_TOPS * ); * * * #### Aligning the panel with the bottom of an element * * * new MdPanelPosition() * .relativeTo(someElement) * .addPanelPosition($mdPanel.xPosition.CENTER, $mdPanel.yPosition.BELOW); * *//** * @ngdoc method * @name MdPanelPosition#absolute * @description * Positions the panel absolutely relative to the parent element. If the parent * is document.body, this is equivalent to positioning the panel absolutely * within the viewport. * * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#relativeTo * @description * Positions the panel relative to a specific element. * * @param {string|!Element|!angular.JQLite} element Query selector, DOM element, * or angular element to position the panel with respect to. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#top * @description * Sets the value of `top` for the panel. Clears any previously set vertical * position. * * @param {string=} top Value of `top`. Defaults to '0'. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#bottom * @description * Sets the value of `bottom` for the panel. Clears any previously set vertical * position. * * @param {string=} bottom Value of `bottom`. Defaults to '0'. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#start * @description * Sets the panel to the start of the page - `left` if `ltr` or `right` for * `rtl`. Clears any previously set horizontal position. * * @param {string=} start Value of position. Defaults to '0'. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#end * @description * Sets the panel to the end of the page - `right` if `ltr` or `left` for `rtl`. * Clears any previously set horizontal position. * * @param {string=} end Value of position. Defaults to '0'. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#left * @description * Sets the value of `left` for the panel. Clears any previously set * horizontal position. * * @param {string=} left Value of `left`. Defaults to '0'. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#right * @description * Sets the value of `right` for the panel. Clears any previously set * horizontal position. * * @param {string=} right Value of `right`. Defaults to '0'. * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#centerHorizontally * @description * Centers the panel horizontally in the viewport. Clears any previously set * horizontal position. * * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#centerVertically * @description * Centers the panel vertically in the viewport. Clears any previously set * vertical position. * * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#center * @description * Centers the panel horizontally and vertically in the viewport. This is * equivalent to calling both `centerHorizontally` and `centerVertically`. * Clears any previously set horizontal and vertical positions. * * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#addPanelPosition * @description * Sets the x and y position for the panel relative to another element. Can be * called multiple times to specify an ordered list of panel positions. The * first position which allows the panel to be completely on-screen will be * chosen; the last position will be chose whether it is on-screen or not. * * xPosition must be one of the following values available on * $mdPanel.xPosition: * * * CENTER | ALIGN_START | ALIGN_END | OFFSET_START | OFFSET_END * *
       *    *************
       *    *           *
       *    *   PANEL   *
       *    *           *
       *    *************
       *   A B    C    D E
       *
       * A: OFFSET_START (for LTR displays)
       * B: ALIGN_START (for LTR displays)
       * C: CENTER
       * D: ALIGN_END (for LTR displays)
       * E: OFFSET_END (for LTR displays)
       * 
      * * yPosition must be one of the following values available on * $mdPanel.yPosition: * * CENTER | ALIGN_TOPS | ALIGN_BOTTOMS | ABOVE | BELOW * *
       *   F
       *   G *************
       *     *           *
       *   H *   PANEL   *
       *     *           *
       *   I *************
       *   J
       *
       * F: BELOW
       * G: ALIGN_TOPS
       * H: CENTER
       * I: ALIGN_BOTTOMS
       * J: ABOVE
       * 
      * * @param {string} xPosition * @param {string} yPosition * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#withOffsetX * @description * Sets the value of the offset in the x-direction. * * @param {string} offsetX * @returns {!MdPanelPosition} *//** * @ngdoc method * @name MdPanelPosition#withOffsetY * @description * Sets the value of the offset in the y-direction. * * @param {string} offsetY * @returns {!MdPanelPosition} *//***************************************************************************** * MdPanelAnimation * *****************************************************************************//** * @ngdoc type * @name MdPanelAnimation * @module material.components.panel * @description * Animation configuration object. To use, create an MdPanelAnimation with the * desired properties, then pass the object as part of $mdPanel creation. * * @usage * * * var panelAnimation = new MdPanelAnimation() * .openFrom(myButtonEl) * .duration(1337) * .closeTo('.my-button') * .withAnimation($mdPanel.animation.SCALE); * * $mdPanel.create({ * animation: panelAnimation * }); * *//** * @ngdoc method * @name MdPanelAnimation#openFrom * @description * Specifies where to start the open animation. `openFrom` accepts a * click event object, query selector, DOM element, or a Rect object that * is used to determine the bounds. When passed a click event, the location * of the click will be used as the position to start the animation. * * @param {string|!Element|!Event|{top: number, left: number}} * @returns {!MdPanelAnimation} *//** * @ngdoc method * @name MdPanelAnimation#closeTo * @description * Specifies where to animate the panel close. `closeTo` accepts a * query selector, DOM element, or a Rect object that is used to determine * the bounds. * * @param {string|!Element|{top: number, left: number}} * @returns {!MdPanelAnimation} *//** * @ngdoc method * @name MdPanelAnimation#withAnimation * @description * Specifies the animation class. * * There are several default animations that can be used: * ($mdPanel.animation) * SLIDE: The panel slides in and out from the specified * elements. It will not fade in or out. * SCALE: The panel scales in and out. Slide and fade are * included in this animation. * FADE: The panel fades in and out. * * Custom classes will by default fade in and out unless * "transition: opacity 1ms" is added to the to custom class. * * @param {string|{open: string, close: string}} cssClass * @returns {!MdPanelAnimation} *//** * @ngdoc method * @name MdPanelAnimation#duration * @description * Specifies the duration of the animation in milliseconds. The `duration` * method accepts either a number or an object with separate open and close * durations. * * @param {number|{open: number, close: number}} duration * @returns {!MdPanelAnimation} *//***************************************************************************** * PUBLIC DOCUMENTATION * *****************************************************************************/var MD_PANEL_Z_INDEX=80;var MD_PANEL_HIDDEN='_md-panel-hidden';var FOCUS_TRAP_TEMPLATE=angular.element('
      ');var _presets={};/** * A provider that is used for creating presets for the panel API. * @final @constructor @ngInject */function MdPanelProvider(){return{'definePreset':definePreset,'getAllPresets':getAllPresets,'clearPresets':clearPresets,'$get':$getProvider()};}/** * Takes the passed in panel configuration object and adds it to the `_presets` * object at the specified name. * @param {string} name Name of the preset to set. * @param {!Object} preset Specific configuration object that can contain any * and all of the parameters avaialble within the `$mdPanel.create` method. * However, parameters that pertain to id, position, animation, and user * interaction are not allowed and will be removed from the preset * configuration. */function definePreset(name,preset){if(!name||!preset){throw new Error('mdPanelProvider: The panel preset definition is '+'malformed. The name and preset object are required.');}else if(_presets.hasOwnProperty(name)){throw new Error('mdPanelProvider: The panel preset you have requested '+'has already been defined.');}// Delete any property on the preset that is not allowed. delete preset.id;delete preset.position;delete preset.animation;_presets[name]=preset;}/** * Gets a clone of the `_presets`. * @return {!Object} */function getAllPresets(){return angular.copy(_presets);}/** * Clears all of the stored presets. */function clearPresets(){_presets={};}/** * Represents the `$get` method of the AngularJS provider. From here, a new * reference to the MdPanelService is returned where the needed arguments are * passed in including the MdPanelProvider `_presets`. * @param {!Object} _presets * @param {!angular.JQLite} $rootElement * @param {!angular.Scope} $rootScope * @param {!angular.$injector} $injector * @param {!angular.$window} $window */function $getProvider(){return['$rootElement','$rootScope','$injector','$window',function($rootElement,$rootScope,$injector,$window){return new MdPanelService(_presets,$rootElement,$rootScope,$injector,$window);}];}/***************************************************************************** * MdPanel Service * *****************************************************************************//** * A service that is used for controlling/displaying panels on the screen. * @param {!Object} presets * @param {!angular.JQLite} $rootElement * @param {!angular.Scope} $rootScope * @param {!angular.$injector} $injector * @param {!angular.$window} $window * @final @constructor @ngInject */function MdPanelService(presets,$rootElement,$rootScope,$injector,$window){/** * Default config options for the panel. * Anything angular related needs to be done later. Therefore * scope: $rootScope.$new(true), * attachTo: $rootElement, * are added later. * @private {!Object} */this._defaultConfigOptions={bindToController:true,clickOutsideToClose:false,disableParentScroll:false,escapeToClose:false,focusOnOpen:true,fullscreen:false,hasBackdrop:false,propagateContainerEvents:false,transformTemplate:angular.bind(this,this._wrapTemplate),trapFocus:false,zIndex:MD_PANEL_Z_INDEX};/** @private {!Object} */this._config={};/** @private {!Object} */this._presets=presets;/** @private @const */this._$rootElement=$rootElement;/** @private @const */this._$rootScope=$rootScope;/** @private @const */this._$injector=$injector;/** @private @const */this._$window=$window;/** @private @const */this._$mdUtil=this._$injector.get('$mdUtil');/** @private {!Object} */this._trackedPanels={};/** * @private {!Object, * openPanels: !Array, * maxOpen: number}>} */this._groups=Object.create(null);/** * Default animations that can be used within the panel. * @type {enum} */this.animation=MdPanelAnimation.animation;/** * Possible values of xPosition for positioning the panel relative to * another element. * @type {enum} */this.xPosition=MdPanelPosition.xPosition;/** * Possible values of yPosition for positioning the panel relative to * another element. * @type {enum} */this.yPosition=MdPanelPosition.yPosition;/** * Possible values for the interceptors that can be registered on a panel. * @type {enum} */this.interceptorTypes=MdPanelRef.interceptorTypes;/** * Possible values for closing of a panel. * @type {enum} */this.closeReasons=MdPanelRef.closeReasons;/** * Possible values of absolute position. * @type {enum} */this.absPosition=MdPanelPosition.absPosition;}/** * Creates a panel with the specified options. * @param {string=} preset Name of a preset configuration that can be used to * extend the panel configuration. * @param {!Object=} config Configuration object for the panel. * @returns {!MdPanelRef} */MdPanelService.prototype.create=function(preset,config){if(typeof preset==='string'){preset=this._getPresetByName(preset);}else if((typeof preset==='undefined'?'undefined':_typeof2(preset))==='object'&&(angular.isUndefined(config)||!config)){config=preset;preset={};}preset=preset||{};config=config||{};// If the passed-in config contains an ID and the ID is within _trackedPanels, // return the tracked panel after updating its config with the passed-in // config. if(angular.isDefined(config.id)&&this._trackedPanels[config.id]){var trackedPanel=this._trackedPanels[config.id];angular.extend(trackedPanel.config,config);return trackedPanel;}// Combine the passed-in config, the _defaultConfigOptions, and the preset // configuration into the `_config`. this._config=angular.extend({// If no ID is set within the passed-in config, then create an arbitrary ID. id:config.id||'panel_'+this._$mdUtil.nextUid(),scope:this._$rootScope.$new(true),attachTo:this._$rootElement},this._defaultConfigOptions,config,preset);// Create the panelRef and add it to the `_trackedPanels` object. var panelRef=new MdPanelRef(this._config,this._$injector);this._trackedPanels[config.id]=panelRef;// Add the panel to each of its requested groups. if(this._config.groupName){if(angular.isString(this._config.groupName)){this._config.groupName=[this._config.groupName];}angular.forEach(this._config.groupName,function(group){panelRef.addToGroup(group);});}this._config.scope.$on('$destroy',angular.bind(panelRef,panelRef.detach));return panelRef;};/** * Creates and opens a panel with the specified options. * @param {string=} preset Name of a preset configuration that can be used to * extend the panel configuration. * @param {!Object=} config Configuration object for the panel. * @returns {!angular.$q.Promise} The panel created from create. */MdPanelService.prototype.open=function(preset,config){var panelRef=this.create(preset,config);return panelRef.open().then(function(){return panelRef;});};/** * Gets a specific preset configuration object saved within `_presets`. * @param {string} preset Name of the preset to search for. * @returns {!Object} The preset configuration object. */MdPanelService.prototype._getPresetByName=function(preset){if(!this._presets[preset]){throw new Error('mdPanel: The panel preset configuration that you '+'requested does not exist. Use the $mdPanelProvider to create a '+'preset before requesting one.');}return this._presets[preset];};/** * Returns a new instance of the MdPanelPosition. Use this to create the * positioning object. * @returns {!MdPanelPosition} */MdPanelService.prototype.newPanelPosition=function(){return new MdPanelPosition(this._$injector);};/** * Returns a new instance of the MdPanelAnimation. Use this to create the * animation object. * @returns {!MdPanelAnimation} */MdPanelService.prototype.newPanelAnimation=function(){return new MdPanelAnimation(this._$injector);};/** * Creates a panel group and adds it to a tracked list of panel groups. * @param groupName {string} Name of the group to create. * @param config {!Object=} Specific configuration object that may contain the * following properties: * * - `maxOpen` - `{number=}`: The maximum number of panels that are allowed * open within a defined panel group. * * @returns {!Object, * openPanels: !Array, * maxOpen: number}>} panelGroup */MdPanelService.prototype.newPanelGroup=function(groupName,config){if(!this._groups[groupName]){config=config||{};var group={panels:[],openPanels:[],maxOpen:config.maxOpen>0?config.maxOpen:Infinity};this._groups[groupName]=group;}return this._groups[groupName];};/** * Sets the maximum number of panels in a group that can be opened at a given * time. * @param {string} groupName The name of the group to configure. * @param {number} maxOpen The maximum number of panels that can be * opened. Infinity can be passed in to remove the maxOpen limit. */MdPanelService.prototype.setGroupMaxOpen=function(groupName,maxOpen){if(this._groups[groupName]){this._groups[groupName].maxOpen=maxOpen;}else{throw new Error('mdPanel: Group does not exist yet. Call newPanelGroup().');}};/** * Determines if the current number of open panels within a group exceeds the * limit of allowed open panels. * @param {string} groupName The name of the group to check. * @returns {boolean} true if open count does exceed maxOpen and false if not. * @private */MdPanelService.prototype._openCountExceedsMaxOpen=function(groupName){if(this._groups[groupName]){var group=this._groups[groupName];return group.maxOpen>0&&group.openPanels.length>group.maxOpen;}return false;};/** * Closes the first open panel within a specific group. * @param {string} groupName The name of the group. * @private */MdPanelService.prototype._closeFirstOpenedPanel=function(groupName){this._groups[groupName].openPanels[0].close();};/** * Wraps the users template in two elements, md-panel-outer-wrapper, which * covers the entire attachTo element, and md-panel, which contains only the * template. This allows the panel control over positioning, animations, * and similar properties. * @param {string} origTemplate The original template. * @returns {string} The wrapped template. * @private */MdPanelService.prototype._wrapTemplate=function(origTemplate){var template=origTemplate||'';// The panel should be initially rendered offscreen so we can calculate // height and width for positioning. return''+'
      '+'
      '+template+'
      '+'
      ';};/** * Wraps a content element in a md-panel-outer wrapper and * positions it off-screen. Allows for proper control over positoning * and animations. * @param {!angular.JQLite} contentElement Element to be wrapped. * @return {!angular.JQLite} Wrapper element. * @private */MdPanelService.prototype._wrapContentElement=function(contentElement){var wrapper=angular.element('
      ');contentElement.addClass('md-panel _md-panel-offscreen');wrapper.append(contentElement);return wrapper;};/***************************************************************************** * MdPanelRef * *****************************************************************************//** * A reference to a created panel. This reference contains a unique id for the * panel, along with properties/functions used to control the panel. * @param {!Object} config * @param {!angular.$injector} $injector * @final @constructor */function MdPanelRef(config,$injector){// Injected variables. /** @private @const {!angular.$q} */this._$q=$injector.get('$q');/** @private @const {!angular.$mdCompiler} */this._$mdCompiler=$injector.get('$mdCompiler');/** @private @const {!angular.$mdConstant} */this._$mdConstant=$injector.get('$mdConstant');/** @private @const {!angular.$mdUtil} */this._$mdUtil=$injector.get('$mdUtil');/** @private @const {!angular.$mdTheming} */this._$mdTheming=$injector.get('$mdTheming');/** @private @const {!angular.Scope} */this._$rootScope=$injector.get('$rootScope');/** @private @const {!angular.$animate} */this._$animate=$injector.get('$animate');/** @private @const {!MdPanelRef} */this._$mdPanel=$injector.get('$mdPanel');/** @private @const {!angular.$log} */this._$log=$injector.get('$log');/** @private @const {!angular.$window} */this._$window=$injector.get('$window');/** @private @const {!Function} */this._$$rAF=$injector.get('$$rAF');// Public variables. /** * Unique id for the panelRef. * @type {string} */this.id=config.id;/** @type {!Object} */this.config=config;/** @type {!angular.JQLite|undefined} */this.panelContainer;/** @type {!angular.JQLite|undefined} */this.panelEl;/** * Whether the panel is attached. This is synchronous. When attach is called, * isAttached is set to true. When detach is called, isAttached is set to * false. * @type {boolean} */this.isAttached=false;// Private variables. /** @private {Array} */this._removeListeners=[];/** @private {!angular.JQLite|undefined} */this._topFocusTrap;/** @private {!angular.JQLite|undefined} */this._bottomFocusTrap;/** @private {!$mdPanel|undefined} */this._backdropRef;/** @private {Function?} */this._restoreScroll=null;/** * Keeps track of all the panel interceptors. * @private {!Object} */this._interceptors=Object.create(null);/** * Cleanup function, provided by `$mdCompiler` and assigned after the element * has been compiled. When `contentElement` is used, the function is used to * restore the element to it's proper place in the DOM. * @private {!Function} */this._compilerCleanup=null;/** * Cache for saving and restoring element inline styles, CSS classes etc. * @type {{styles: string, classes: string}} */this._restoreCache={styles:'',classes:''};}MdPanelRef.interceptorTypes={CLOSE:'onClose'};/** * Opens an already created and configured panel. If the panel is already * visible, does nothing. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is opened and animations finish. */MdPanelRef.prototype.open=function(){var self=this;return this._$q(function(resolve,reject){var done=self._done(resolve,self);var show=self._simpleBind(self.show,self);var checkGroupMaxOpen=function checkGroupMaxOpen(){if(self.config.groupName){angular.forEach(self.config.groupName,function(group){if(self._$mdPanel._openCountExceedsMaxOpen(group)){self._$mdPanel._closeFirstOpenedPanel(group);}});}};self.attach().then(show).then(checkGroupMaxOpen).then(done).catch(reject);});};/** * Closes the panel. * @param {string} closeReason The event type that triggered the close. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is closed and animations finish. */MdPanelRef.prototype.close=function(closeReason){var self=this;return this._$q(function(resolve,reject){self._callInterceptors(MdPanelRef.interceptorTypes.CLOSE).then(function(){var done=self._done(resolve,self);var detach=self._simpleBind(self.detach,self);var onCloseSuccess=self.config['onCloseSuccess']||angular.noop;onCloseSuccess=angular.bind(self,onCloseSuccess,self,closeReason);self.hide().then(detach).then(done).then(onCloseSuccess).catch(reject);},reject);});};/** * Attaches the panel. The panel will be hidden afterwards. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is attached. */MdPanelRef.prototype.attach=function(){if(this.isAttached&&this.panelEl){return this._$q.when(this);}var self=this;return this._$q(function(resolve,reject){var done=self._done(resolve,self);var onDomAdded=self.config['onDomAdded']||angular.noop;var addListeners=function addListeners(response){self.isAttached=true;self._addEventListeners();return response;};self._$q.all([self._createBackdrop(),self._createPanel().then(addListeners).catch(reject)]).then(onDomAdded).then(done).catch(reject);});};/** * Only detaches the panel. Will NOT hide the panel first. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is detached. */MdPanelRef.prototype.detach=function(){if(!this.isAttached){return this._$q.when(this);}var self=this;var onDomRemoved=self.config['onDomRemoved']||angular.noop;var detachFn=function detachFn(){self._removeEventListeners();// Remove the focus traps that we added earlier for keeping focus within // the panel. if(self._topFocusTrap&&self._topFocusTrap.parentNode){self._topFocusTrap.parentNode.removeChild(self._topFocusTrap);}if(self._bottomFocusTrap&&self._bottomFocusTrap.parentNode){self._bottomFocusTrap.parentNode.removeChild(self._bottomFocusTrap);}if(self._restoreCache.classes){self.panelEl[0].className=self._restoreCache.classes;}// Either restore the saved styles or clear the ones set by mdPanel. self.panelEl[0].style.cssText=self._restoreCache.styles||'';self._compilerCleanup();self.panelContainer.remove();self.isAttached=false;return self._$q.when(self);};if(this._restoreScroll){this._restoreScroll();this._restoreScroll=null;}return this._$q(function(resolve,reject){var done=self._done(resolve,self);self._$q.all([detachFn(),self._backdropRef?self._backdropRef.detach():true]).then(onDomRemoved).then(done).catch(reject);});};/** * Destroys the panel. The Panel cannot be opened again after this. */MdPanelRef.prototype.destroy=function(){var self=this;if(this.config.groupName){angular.forEach(this.config.groupName,function(group){self.removeFromGroup(group);});}this.config.scope.$destroy();this.config.locals=null;this._interceptors=null;};/** * Shows the panel. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel has shown and animations finish. */MdPanelRef.prototype.show=function(){if(!this.panelContainer){return this._$q(function(resolve,reject){reject('mdPanel: Panel does not exist yet. Call open() or attach().');});}if(!this.panelContainer.hasClass(MD_PANEL_HIDDEN)){return this._$q.when(this);}var self=this;var animatePromise=function animatePromise(){self.panelContainer.removeClass(MD_PANEL_HIDDEN);return self._animateOpen();};return this._$q(function(resolve,reject){var done=self._done(resolve,self);var onOpenComplete=self.config['onOpenComplete']||angular.noop;var addToGroupOpen=function addToGroupOpen(){if(self.config.groupName){angular.forEach(self.config.groupName,function(group){self._$mdPanel._groups[group].openPanels.push(self);});}};self._$q.all([self._backdropRef?self._backdropRef.show():self,animatePromise().then(function(){self._focusOnOpen();},reject)]).then(onOpenComplete).then(addToGroupOpen).then(done).catch(reject);});};/** * Hides the panel. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel has hidden and animations finish. */MdPanelRef.prototype.hide=function(){if(!this.panelContainer){return this._$q(function(resolve,reject){reject('mdPanel: Panel does not exist yet. Call open() or attach().');});}if(this.panelContainer.hasClass(MD_PANEL_HIDDEN)){return this._$q.when(this);}var self=this;return this._$q(function(resolve,reject){var done=self._done(resolve,self);var onRemoving=self.config['onRemoving']||angular.noop;var hidePanel=function hidePanel(){self.panelContainer.addClass(MD_PANEL_HIDDEN);};var removeFromGroupOpen=function removeFromGroupOpen(){if(self.config.groupName){var group,index;angular.forEach(self.config.groupName,function(group){group=self._$mdPanel._groups[group];index=group.openPanels.indexOf(self);if(index>-1){group.openPanels.splice(index,1);}});}};var focusOnOrigin=function focusOnOrigin(){var origin=self.config['origin'];if(origin){getElement(origin).focus();}};self._$q.all([self._backdropRef?self._backdropRef.hide():self,self._animateClose().then(onRemoving).then(hidePanel).then(removeFromGroupOpen).then(focusOnOrigin).catch(reject)]).then(done,reject);});};/** * Add a class to the panel. DO NOT use this to hide/show the panel. * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * * @param {string} newClass Class to be added. * @param {boolean} toElement Whether or not to add the class to the panel * element instead of the container. */MdPanelRef.prototype.addClass=function(newClass,toElement){this._$log.warn('mdPanel: The addClass method is in the process of being deprecated. '+'Full deprecation is scheduled for the AngularJS Material 1.2 release. '+'To achieve the same results, use the panelContainer or panelEl '+'JQLite elements that are referenced in MdPanelRef.');if(!this.panelContainer){throw new Error('mdPanel: Panel does not exist yet. Call open() or attach().');}if(!toElement&&!this.panelContainer.hasClass(newClass)){this.panelContainer.addClass(newClass);}else if(toElement&&!this.panelEl.hasClass(newClass)){this.panelEl.addClass(newClass);}};/** * Remove a class from the panel. DO NOT use this to hide/show the panel. * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * * @param {string} oldClass Class to be removed. * @param {boolean} fromElement Whether or not to remove the class from the * panel element instead of the container. */MdPanelRef.prototype.removeClass=function(oldClass,fromElement){this._$log.warn('mdPanel: The removeClass method is in the process of being deprecated. '+'Full deprecation is scheduled for the AngularJS Material 1.2 release. '+'To achieve the same results, use the panelContainer or panelEl '+'JQLite elements that are referenced in MdPanelRef.');if(!this.panelContainer){throw new Error('mdPanel: Panel does not exist yet. Call open() or attach().');}if(!fromElement&&this.panelContainer.hasClass(oldClass)){this.panelContainer.removeClass(oldClass);}else if(fromElement&&this.panelEl.hasClass(oldClass)){this.panelEl.removeClass(oldClass);}};/** * Toggle a class on the panel. DO NOT use this to hide/show the panel. * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * * @param {string} toggleClass The class to toggle. * @param {boolean} onElement Whether or not to toggle the class on the panel * element instead of the container. */MdPanelRef.prototype.toggleClass=function(toggleClass,onElement){this._$log.warn('mdPanel: The toggleClass method is in the process of being deprecated. '+'Full deprecation is scheduled for the AngularJS Material 1.2 release. '+'To achieve the same results, use the panelContainer or panelEl '+'JQLite elements that are referenced in MdPanelRef.');if(!this.panelContainer){throw new Error('mdPanel: Panel does not exist yet. Call open() or attach().');}if(!onElement){this.panelContainer.toggleClass(toggleClass);}else{this.panelEl.toggleClass(toggleClass);}};/** * Compiles the panel, according to the passed in config and appends it to * the DOM. Helps normalize differences in the compilation process between * using a string template and a content element. * @returns {!angular.$q.Promise} Promise that is resolved when * the element has been compiled and added to the DOM. * @private */MdPanelRef.prototype._compile=function(){var self=this;// Compile the element via $mdCompiler. Note that when using a // contentElement, the element isn't actually being compiled, rather the // compiler saves it's place in the DOM and provides a way of restoring it. return self._$mdCompiler.compile(self.config).then(function(compileData){var config=self.config;if(config.contentElement){var panelEl=compileData.element;// Since mdPanel modifies the inline styles and CSS classes, we need // to save them in order to be able to restore on close. self._restoreCache.styles=panelEl[0].style.cssText;self._restoreCache.classes=panelEl[0].className;self.panelContainer=self._$mdPanel._wrapContentElement(panelEl);self.panelEl=panelEl;}else{self.panelContainer=compileData.link(config['scope']);self.panelEl=angular.element(self.panelContainer[0].querySelector('.md-panel'));}// Save a reference to the cleanup function from the compiler. self._compilerCleanup=compileData.cleanup;// Attach the panel to the proper place in the DOM. getElement(self.config['attachTo']).append(self.panelContainer);return self;});};/** * Creates a panel and adds it to the dom. * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * created. * @private */MdPanelRef.prototype._createPanel=function(){var self=this;return this._$q(function(resolve,reject){if(!self.config.locals){self.config.locals={};}self.config.locals.mdPanelRef=self;self._compile().then(function(){if(self.config['disableParentScroll']){self._restoreScroll=self._$mdUtil.disableScrollAround(null,self.panelContainer,{disableScrollMask:true});}// Add a custom CSS class to the panel element. if(self.config['panelClass']){self.panelEl.addClass(self.config['panelClass']);}// Handle click and touch events for the panel container. if(self.config['propagateContainerEvents']){self.panelContainer.css('pointer-events','none');self.panelEl.css('pointer-events','all');}// Panel may be outside the $rootElement, tell ngAnimate to animate // regardless. if(self._$animate.pin){self._$animate.pin(self.panelContainer,getElement(self.config['attachTo']));}self._configureTrapFocus();self._addStyles().then(function(){resolve(self);},reject);},reject);});};/** * Adds the styles for the panel, such as positioning and z-index. Also, * themes the panel element and panel container using `$mdTheming`. * @returns {!angular.$q.Promise} * @private */MdPanelRef.prototype._addStyles=function(){var self=this;return this._$q(function(resolve){self.panelContainer.css('z-index',self.config['zIndex']);self.panelEl.css('z-index',self.config['zIndex']+1);var hideAndResolve=function hideAndResolve(){// Theme the element and container. self._setTheming();// Remove offscreen class and add hidden class. self.panelEl.removeClass('_md-panel-offscreen');self.panelContainer.addClass(MD_PANEL_HIDDEN);resolve(self);};if(self.config['fullscreen']){self.panelEl.addClass('_md-panel-fullscreen');hideAndResolve();return;// Don't setup positioning. }var positionConfig=self.config['position'];if(!positionConfig){hideAndResolve();return;// Don't setup positioning. }// Wait for angular to finish processing the template self._$rootScope['$$postDigest'](function(){// Position it correctly. This is necessary so that the panel will have a // defined height and width. self._updatePosition(true);// Theme the element and container. self._setTheming();resolve(self);});});};/** * Sets the `$mdTheming` classes on the `panelContainer` and `panelEl`. * @private */MdPanelRef.prototype._setTheming=function(){this._$mdTheming(this.panelEl);this._$mdTheming(this.panelContainer);};/** * Updates the position configuration of a panel * @param {!MdPanelPosition} position */MdPanelRef.prototype.updatePosition=function(position){if(!this.panelContainer){throw new Error('mdPanel: Panel does not exist yet. Call open() or attach().');}this.config['position']=position;this._updatePosition();};/** * Calculates and updates the position of the panel. * @param {boolean=} init * @private */MdPanelRef.prototype._updatePosition=function(init){var positionConfig=this.config['position'];if(positionConfig){positionConfig._setPanelPosition(this.panelEl);// Hide the panel now that position is known. if(init){this.panelEl.removeClass('_md-panel-offscreen');this.panelContainer.addClass(MD_PANEL_HIDDEN);}this.panelEl.css(MdPanelPosition.absPosition.TOP,positionConfig.getTop());this.panelEl.css(MdPanelPosition.absPosition.BOTTOM,positionConfig.getBottom());this.panelEl.css(MdPanelPosition.absPosition.LEFT,positionConfig.getLeft());this.panelEl.css(MdPanelPosition.absPosition.RIGHT,positionConfig.getRight());}};/** * Focuses on the panel or the first focus target. * @private */MdPanelRef.prototype._focusOnOpen=function(){if(this.config['focusOnOpen']){// Wait for the template to finish rendering to guarantee md-autofocus has // finished adding the class md-autofocus, otherwise the focusable element // isn't available to focus. var self=this;this._$rootScope['$$postDigest'](function(){var target=self._$mdUtil.findFocusTarget(self.panelEl)||self.panelEl;target.focus();});}};/** * Shows the backdrop. * @returns {!angular.$q.Promise} A promise that is resolved when the backdrop * is created and attached. * @private */MdPanelRef.prototype._createBackdrop=function(){if(this.config.hasBackdrop){if(!this._backdropRef){var backdropAnimation=this._$mdPanel.newPanelAnimation().openFrom(this.config.attachTo).withAnimation({open:'_md-opaque-enter',close:'_md-opaque-leave'});if(this.config.animation){backdropAnimation.duration(this.config.animation._rawDuration);}var backdropConfig={animation:backdropAnimation,attachTo:this.config.attachTo,focusOnOpen:false,panelClass:'_md-panel-backdrop',zIndex:this.config.zIndex-1};this._backdropRef=this._$mdPanel.create(backdropConfig);}if(!this._backdropRef.isAttached){return this._backdropRef.attach();}}};/** * Listen for escape keys and outside clicks to auto close. * @private */MdPanelRef.prototype._addEventListeners=function(){this._configureEscapeToClose();this._configureClickOutsideToClose();this._configureScrollListener();};/** * Remove event listeners added in _addEventListeners. * @private */MdPanelRef.prototype._removeEventListeners=function(){this._removeListeners&&this._removeListeners.forEach(function(removeFn){removeFn();});this._removeListeners=[];};/** * Setup the escapeToClose event listeners. * @private */MdPanelRef.prototype._configureEscapeToClose=function(){if(this.config['escapeToClose']){var parentTarget=getElement(this.config['attachTo']);var self=this;var keyHandlerFn=function keyHandlerFn(ev){if(ev.keyCode===self._$mdConstant.KEY_CODE.ESCAPE){ev.stopPropagation();ev.preventDefault();self.close(MdPanelRef.closeReasons.ESCAPE);}};// Add keydown listeners this.panelContainer.on('keydown',keyHandlerFn);parentTarget.on('keydown',keyHandlerFn);// Queue remove listeners function this._removeListeners.push(function(){self.panelContainer.off('keydown',keyHandlerFn);parentTarget.off('keydown',keyHandlerFn);});}};/** * Setup the clickOutsideToClose event listeners. * @private */MdPanelRef.prototype._configureClickOutsideToClose=function(){if(this.config['clickOutsideToClose']){var target=this.config['propagateContainerEvents']?angular.element(document.body):this.panelContainer;var sourceEl;// Keep track of the element on which the mouse originally went down // so that we can only close the backdrop when the 'click' started on it. // A simple 'click' handler does not work, it sets the target object as the // element the mouse went down on. var mousedownHandler=function mousedownHandler(ev){sourceEl=ev.target;};// We check if our original element and the target is the backdrop // because if the original was the backdrop and the target was inside the // panel we don't want to panel to close. var self=this;var mouseupHandler=function mouseupHandler(ev){if(self.config['propagateContainerEvents']){// We check if the sourceEl of the event is the panel element or one // of it's children. If it is not, then close the panel. if(sourceEl!==self.panelEl[0]&&!self.panelEl[0].contains(sourceEl)){self.close();}}else if(sourceEl===target[0]&&ev.target===target[0]){ev.stopPropagation();ev.preventDefault();self.close(MdPanelRef.closeReasons.CLICK_OUTSIDE);}};// Add listeners target.on('mousedown',mousedownHandler);target.on('mouseup',mouseupHandler);// Queue remove listeners function this._removeListeners.push(function(){target.off('mousedown',mousedownHandler);target.off('mouseup',mouseupHandler);});}};/** * Configures the listeners for updating the panel position on scroll. * @private */MdPanelRef.prototype._configureScrollListener=function(){// No need to bind the event if scrolling is disabled. if(!this.config['disableParentScroll']){var updatePosition=angular.bind(this,this._updatePosition);var debouncedUpdatePosition=this._$$rAF.throttle(updatePosition);var self=this;var onScroll=function onScroll(){debouncedUpdatePosition();};// Add listeners. this._$window.addEventListener('scroll',onScroll,true);// Queue remove listeners function. this._removeListeners.push(function(){self._$window.removeEventListener('scroll',onScroll,true);});}};/** * Setup the focus traps. These traps will wrap focus when tabbing past the * panel. When shift-tabbing, the focus will stick in place. * @private */MdPanelRef.prototype._configureTrapFocus=function(){// Focus doesn't remain inside of the panel without this. this.panelEl.attr('tabIndex','-1');if(this.config['trapFocus']){var element=this.panelEl;// Set up elements before and after the panel to capture focus and // redirect back into the panel. this._topFocusTrap=FOCUS_TRAP_TEMPLATE.clone()[0];this._bottomFocusTrap=FOCUS_TRAP_TEMPLATE.clone()[0];// When focus is about to move out of the panel, we want to intercept it // and redirect it back to the panel element. var focusHandler=function focusHandler(){element.focus();};this._topFocusTrap.addEventListener('focus',focusHandler);this._bottomFocusTrap.addEventListener('focus',focusHandler);// Queue remove listeners function this._removeListeners.push(this._simpleBind(function(){this._topFocusTrap.removeEventListener('focus',focusHandler);this._bottomFocusTrap.removeEventListener('focus',focusHandler);},this));// The top focus trap inserted immediately before the md-panel element (as // a sibling). The bottom focus trap inserted immediately after the // md-panel element (as a sibling). element[0].parentNode.insertBefore(this._topFocusTrap,element[0]);element.after(this._bottomFocusTrap);}};/** * Updates the animation of a panel. * @param {!MdPanelAnimation} animation */MdPanelRef.prototype.updateAnimation=function(animation){this.config['animation']=animation;if(this._backdropRef){this._backdropRef.config.animation.duration(animation._rawDuration);}};/** * Animate the panel opening. * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * animated open. * @private */MdPanelRef.prototype._animateOpen=function(){this.panelContainer.addClass('md-panel-is-showing');var animationConfig=this.config['animation'];if(!animationConfig){// Promise is in progress, return it. this.panelContainer.addClass('_md-panel-shown');return this._$q.when(this);}var self=this;return this._$q(function(resolve){var done=self._done(resolve,self);var warnAndOpen=function warnAndOpen(){self._$log.warn('mdPanel: MdPanel Animations failed. '+'Showing panel without animating.');done();};animationConfig.animateOpen(self.panelEl).then(done,warnAndOpen);});};/** * Animate the panel closing. * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * animated closed. * @private */MdPanelRef.prototype._animateClose=function(){var animationConfig=this.config['animation'];if(!animationConfig){this.panelContainer.removeClass('md-panel-is-showing');this.panelContainer.removeClass('_md-panel-shown');return this._$q.when(this);}var self=this;return this._$q(function(resolve){var done=function done(){self.panelContainer.removeClass('md-panel-is-showing');resolve(self);};var warnAndClose=function warnAndClose(){self._$log.warn('mdPanel: MdPanel Animations failed. '+'Hiding panel without animating.');done();};animationConfig.animateClose(self.panelEl).then(done,warnAndClose);});};/** * Registers a interceptor with the panel. The callback should return a promise, * which will allow the action to continue when it gets resolved, or will * prevent an action if it is rejected. * @param {string} type Type of interceptor. * @param {!angular.$q.Promise} callback Callback to be registered. * @returns {!MdPanelRef} */MdPanelRef.prototype.registerInterceptor=function(type,callback){var error=null;if(!angular.isString(type)){error='Interceptor type must be a string, instead got '+(typeof type==='undefined'?'undefined':_typeof2(type));}else if(!angular.isFunction(callback)){error='Interceptor callback must be a function, instead got '+(typeof callback==='undefined'?'undefined':_typeof2(callback));}if(error){throw new Error('MdPanel: '+error);}var interceptors=this._interceptors[type]=this._interceptors[type]||[];if(interceptors.indexOf(callback)===-1){interceptors.push(callback);}return this;};/** * Removes a registered interceptor. * @param {string} type Type of interceptor to be removed. * @param {Function} callback Interceptor to be removed. * @returns {!MdPanelRef} */MdPanelRef.prototype.removeInterceptor=function(type,callback){var index=this._interceptors[type]?this._interceptors[type].indexOf(callback):-1;if(index>-1){this._interceptors[type].splice(index,1);}return this;};/** * Removes all interceptors. * @param {string=} type Type of interceptors to be removed. * If ommited, all interceptors types will be removed. * @returns {!MdPanelRef} */MdPanelRef.prototype.removeAllInterceptors=function(type){if(type){this._interceptors[type]=[];}else{this._interceptors=Object.create(null);}return this;};/** * Invokes all the interceptors of a certain type sequantially in * reverse order. Works in a similar way to `$q.all`, except it * respects the order of the functions. * @param {string} type Type of interceptors to be invoked. * @returns {!angular.$q.Promise} * @private */MdPanelRef.prototype._callInterceptors=function(type){var self=this;var $q=self._$q;var interceptors=self._interceptors&&self._interceptors[type]||[];return interceptors.reduceRight(function(promise,interceptor){var isPromiseLike=interceptor&&angular.isFunction(interceptor.then);var response=isPromiseLike?interceptor:null;/** * For interceptors to reject/cancel subsequent portions of the chain, simply * return a `$q.reject()` */return promise.then(function(){if(!response){try{response=interceptor(self);}catch(e){response=$q.reject(e);}}return response;});},$q.resolve(self));};/** * Faster, more basic than angular.bind * http://jsperf.com/angular-bind-vs-custom-vs-native * @param {function} callback * @param {!Object} self * @return {function} Callback function with a bound self. */MdPanelRef.prototype._simpleBind=function(callback,self){return function(value){return callback.apply(self,value);};};/** * @param {function} callback * @param {!Object} self * @return {function} Callback function with a self param. */MdPanelRef.prototype._done=function(callback,self){return function(){callback(self);};};/** * Adds a panel to a group if the panel does not exist within the group already. * A panel can only exist within a single group. * @param {string} groupName The name of the group. */MdPanelRef.prototype.addToGroup=function(groupName){if(!this._$mdPanel._groups[groupName]){this._$mdPanel.newPanelGroup(groupName);}var group=this._$mdPanel._groups[groupName];var index=group.panels.indexOf(this);if(index<0){group.panels.push(this);}};/** * Removes a panel from a group if the panel exists within that group. The group * must be created ahead of time. * @param {string} groupName The name of the group. */MdPanelRef.prototype.removeFromGroup=function(groupName){if(!this._$mdPanel._groups[groupName]){throw new Error('mdPanel: The group '+groupName+' does not exist.');}var group=this._$mdPanel._groups[groupName];var index=group.panels.indexOf(this);if(index>-1){group.panels.splice(index,1);}};/** * Possible default closeReasons for the close function. * @enum {string} */MdPanelRef.closeReasons={CLICK_OUTSIDE:'clickOutsideToClose',ESCAPE:'escapeToClose'};/***************************************************************************** * MdPanelPosition * *****************************************************************************//** * Position configuration object. To use, create an MdPanelPosition with the * desired properties, then pass the object as part of $mdPanel creation. * * Example: * * var panelPosition = new MdPanelPosition() * .relativeTo(myButtonEl) * .addPanelPosition( * $mdPanel.xPosition.CENTER, * $mdPanel.yPosition.ALIGN_TOPS * ); * * $mdPanel.create({ * position: panelPosition * }); * * @param {!angular.$injector} $injector * @final @constructor */function MdPanelPosition($injector){/** @private @const {!angular.$window} */this._$window=$injector.get('$window');/** @private {boolean} */this._isRTL=$injector.get('$mdUtil').bidi()==='rtl';/** @private @const {!angular.$mdConstant} */this._$mdConstant=$injector.get('$mdConstant');/** @private {boolean} */this._absolute=false;/** @private {!angular.JQLite} */this._relativeToEl;/** @private {string} */this._top='';/** @private {string} */this._bottom='';/** @private {string} */this._left='';/** @private {string} */this._right='';/** @private {!Array} */this._translateX=[];/** @private {!Array} */this._translateY=[];/** @private {!Array<{x:string, y:string}>} */this._positions=[];/** @private {?{x:string, y:string}} */this._actualPosition;}/** * Possible values of xPosition. * @enum {string} */MdPanelPosition.xPosition={CENTER:'center',ALIGN_START:'align-start',ALIGN_END:'align-end',OFFSET_START:'offset-start',OFFSET_END:'offset-end'};/** * Possible values of yPosition. * @enum {string} */MdPanelPosition.yPosition={CENTER:'center',ALIGN_TOPS:'align-tops',ALIGN_BOTTOMS:'align-bottoms',ABOVE:'above',BELOW:'below'};/** * Possible values of absolute position. * @enum {string} */MdPanelPosition.absPosition={TOP:'top',RIGHT:'right',BOTTOM:'bottom',LEFT:'left'};/** * Margin between the edges of a panel and the viewport. * @const {number} */MdPanelPosition.viewportMargin=8;/** * Sets absolute positioning for the panel. * @return {!MdPanelPosition} */MdPanelPosition.prototype.absolute=function(){this._absolute=true;return this;};/** * Sets the value of a position for the panel. Clears any previously set * position. * @param {string} position Position to set * @param {string=} value Value of the position. Defaults to '0'. * @returns {!MdPanelPosition} * @private */MdPanelPosition.prototype._setPosition=function(position,value){if(position===MdPanelPosition.absPosition.RIGHT||position===MdPanelPosition.absPosition.LEFT){this._left=this._right='';}else if(position===MdPanelPosition.absPosition.BOTTOM||position===MdPanelPosition.absPosition.TOP){this._top=this._bottom='';}else{var positions=Object.keys(MdPanelPosition.absPosition).join().toLowerCase();throw new Error('mdPanel: Position must be one of '+positions+'.');}this['_'+position]=angular.isString(value)?value:'0';return this;};/** * Sets the value of `top` for the panel. Clears any previously set vertical * position. * @param {string=} top Value of `top`. Defaults to '0'. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.top=function(top){return this._setPosition(MdPanelPosition.absPosition.TOP,top);};/** * Sets the value of `bottom` for the panel. Clears any previously set vertical * position. * @param {string=} bottom Value of `bottom`. Defaults to '0'. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.bottom=function(bottom){return this._setPosition(MdPanelPosition.absPosition.BOTTOM,bottom);};/** * Sets the panel to the start of the page - `left` if `ltr` or `right` for * `rtl`. Clears any previously set horizontal position. * @param {string=} start Value of position. Defaults to '0'. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.start=function(start){var position=this._isRTL?MdPanelPosition.absPosition.RIGHT:MdPanelPosition.absPosition.LEFT;return this._setPosition(position,start);};/** * Sets the panel to the end of the page - `right` if `ltr` or `left` for `rtl`. * Clears any previously set horizontal position. * @param {string=} end Value of position. Defaults to '0'. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.end=function(end){var position=this._isRTL?MdPanelPosition.absPosition.LEFT:MdPanelPosition.absPosition.RIGHT;return this._setPosition(position,end);};/** * Sets the value of `left` for the panel. Clears any previously set * horizontal position. * @param {string=} left Value of `left`. Defaults to '0'. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.left=function(left){return this._setPosition(MdPanelPosition.absPosition.LEFT,left);};/** * Sets the value of `right` for the panel. Clears any previously set * horizontal position. * @param {string=} right Value of `right`. Defaults to '0'. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.right=function(right){return this._setPosition(MdPanelPosition.absPosition.RIGHT,right);};/** * Centers the panel horizontally in the viewport. Clears any previously set * horizontal position. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.centerHorizontally=function(){this._left='50%';this._right='';this._translateX=['-50%'];return this;};/** * Centers the panel vertically in the viewport. Clears any previously set * vertical position. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.centerVertically=function(){this._top='50%';this._bottom='';this._translateY=['-50%'];return this;};/** * Centers the panel horizontally and vertically in the viewport. This is * equivalent to calling both `centerHorizontally` and `centerVertically`. * Clears any previously set horizontal and vertical positions. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.center=function(){return this.centerHorizontally().centerVertically();};/** * Sets element for relative positioning. * @param {string|!Element|!angular.JQLite} element Query selector, DOM element, * or angular element to set the panel relative to. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.relativeTo=function(element){this._absolute=false;this._relativeToEl=getElement(element);return this;};/** * Sets the x and y positions for the panel relative to another element. * @param {string} xPosition must be one of the MdPanelPosition.xPosition * values. * @param {string} yPosition must be one of the MdPanelPosition.yPosition * values. * @returns {!MdPanelPosition} */MdPanelPosition.prototype.addPanelPosition=function(xPosition,yPosition){if(!this._relativeToEl){throw new Error('mdPanel: addPanelPosition can only be used with '+'relative positioning. Set relativeTo first.');}this._validateXPosition(xPosition);this._validateYPosition(yPosition);this._positions.push({x:xPosition,y:yPosition});return this;};/** * Ensures that yPosition is a valid position name. Throw an exception if not. * @param {string} yPosition */MdPanelPosition.prototype._validateYPosition=function(yPosition){// empty is ok if(yPosition==null){return;}var positionKeys=Object.keys(MdPanelPosition.yPosition);var positionValues=[];for(var key,i=0;key=positionKeys[i];i++){var position=MdPanelPosition.yPosition[key];positionValues.push(position);if(position===yPosition){return;}}throw new Error('mdPanel: Panel y position only accepts the following '+'values:\n'+positionValues.join(' | '));};/** * Ensures that xPosition is a valid position name. Throw an exception if not. * @param {string} xPosition */MdPanelPosition.prototype._validateXPosition=function(xPosition){// empty is ok if(xPosition==null){return;}var positionKeys=Object.keys(MdPanelPosition.xPosition);var positionValues=[];for(var key,i=0;key=positionKeys[i];i++){var position=MdPanelPosition.xPosition[key];positionValues.push(position);if(position===xPosition){return;}}throw new Error('mdPanel: Panel x Position only accepts the following '+'values:\n'+positionValues.join(' | '));};/** * Sets the value of the offset in the x-direction. This will add to any * previously set offsets. * @param {string|function(MdPanelPosition): string} offsetX * @returns {!MdPanelPosition} */MdPanelPosition.prototype.withOffsetX=function(offsetX){this._translateX.push(offsetX);return this;};/** * Sets the value of the offset in the y-direction. This will add to any * previously set offsets. * @param {string|function(MdPanelPosition): string} offsetY * @returns {!MdPanelPosition} */MdPanelPosition.prototype.withOffsetY=function(offsetY){this._translateY.push(offsetY);return this;};/** * Gets the value of `top` for the panel. * @returns {string} */MdPanelPosition.prototype.getTop=function(){return this._top;};/** * Gets the value of `bottom` for the panel. * @returns {string} */MdPanelPosition.prototype.getBottom=function(){return this._bottom;};/** * Gets the value of `left` for the panel. * @returns {string} */MdPanelPosition.prototype.getLeft=function(){return this._left;};/** * Gets the value of `right` for the panel. * @returns {string} */MdPanelPosition.prototype.getRight=function(){return this._right;};/** * Gets the value of `transform` for the panel. * @returns {string} */MdPanelPosition.prototype.getTransform=function(){var translateX=this._reduceTranslateValues('translateX',this._translateX);var translateY=this._reduceTranslateValues('translateY',this._translateY);// It's important to trim the result, because the browser will ignore the set // operation if the string contains only whitespace. return(translateX+' '+translateY).trim();};/** * Sets the `transform` value for a panel element. * @param {!angular.JQLite} panelEl * @returns {!angular.JQLite} * @private */MdPanelPosition.prototype._setTransform=function(panelEl){return panelEl.css(this._$mdConstant.CSS.TRANSFORM,this.getTransform());};/** * True if the panel is completely on-screen with this positioning; false * otherwise. * @param {!angular.JQLite} panelEl * @return {boolean} * @private */MdPanelPosition.prototype._isOnscreen=function(panelEl){// this works because we always use fixed positioning for the panel, // which is relative to the viewport. var left=parseInt(this.getLeft());var top=parseInt(this.getTop());if(this._translateX.length||this._translateY.length){var prefixedTransform=this._$mdConstant.CSS.TRANSFORM;var offsets=getComputedTranslations(panelEl,prefixedTransform);left+=offsets.x;top+=offsets.y;}var right=left+panelEl[0].offsetWidth;var bottom=top+panelEl[0].offsetHeight;return left>=0&&top>=0&&bottom<=this._$window.innerHeight&&right<=this._$window.innerWidth;};/** * Gets the first x/y position that can fit on-screen. * @returns {{x: string, y: string}} */MdPanelPosition.prototype.getActualPosition=function(){return this._actualPosition;};/** * Reduces a list of translate values to a string that can be used within * transform. * @param {string} translateFn * @param {!Array} values * @returns {string} * @private */MdPanelPosition.prototype._reduceTranslateValues=function(translateFn,values){return values.map(function(translation){// TODO(crisbeto): this should add the units after #9609 is merged. var translationValue=angular.isFunction(translation)?translation(this):translation;return translateFn+'('+translationValue+')';},this).join(' ');};/** * Sets the panel position based on the created panel element and best x/y * positioning. * @param {!angular.JQLite} panelEl * @private */MdPanelPosition.prototype._setPanelPosition=function(panelEl){// Remove the "position adjusted" class in case it has been added before. panelEl.removeClass('_md-panel-position-adjusted');// Only calculate the position if necessary. if(this._absolute){this._setTransform(panelEl);return;}if(this._actualPosition){this._calculatePanelPosition(panelEl,this._actualPosition);this._setTransform(panelEl);this._constrainToViewport(panelEl);return;}for(var i=0;iviewportHeight){this._top=top-(bottom-viewportHeight+margin)+'px';}}if(this.getLeft()){var left=parseInt(this.getLeft());var right=panelEl[0].offsetWidth+left;var viewportWidth=this._$window.innerWidth;if(leftviewportWidth){this._left=left-(right-viewportWidth+margin)+'px';}}// Class that can be used to re-style the panel if it was repositioned. panelEl.toggleClass('_md-panel-position-adjusted',this._top!==initialTop||this._left!==initialLeft);};/** * Switches between 'start' and 'end'. * @param {string} position Horizontal position of the panel * @returns {string} Reversed position * @private */MdPanelPosition.prototype._reverseXPosition=function(position){if(position===MdPanelPosition.xPosition.CENTER){return position;}var start='start';var end='end';return position.indexOf(start)>-1?position.replace(start,end):position.replace(end,start);};/** * Handles horizontal positioning in rtl or ltr environments. * @param {string} position Horizontal position of the panel * @returns {string} The correct position according the page direction * @private */MdPanelPosition.prototype._bidi=function(position){return this._isRTL?this._reverseXPosition(position):position;};/** * Calculates the panel position based on the created panel element and the * provided positioning. * @param {!angular.JQLite} panelEl * @param {!{x:string, y:string}} position * @private */MdPanelPosition.prototype._calculatePanelPosition=function(panelEl,position){var panelBounds=panelEl[0].getBoundingClientRect();var panelWidth=Math.max(panelBounds.width,panelEl[0].clientWidth);var panelHeight=Math.max(panelBounds.height,panelEl[0].clientHeight);var targetBounds=this._relativeToEl[0].getBoundingClientRect();var targetLeft=targetBounds.left;var targetRight=targetBounds.right;var targetWidth=targetBounds.width;switch(this._bidi(position.x)){case MdPanelPosition.xPosition.OFFSET_START:this._left=targetLeft-panelWidth+'px';break;case MdPanelPosition.xPosition.ALIGN_END:this._left=targetRight-panelWidth+'px';break;case MdPanelPosition.xPosition.CENTER:var left=targetLeft+0.5*targetWidth-0.5*panelWidth;this._left=left+'px';break;case MdPanelPosition.xPosition.ALIGN_START:this._left=targetLeft+'px';break;case MdPanelPosition.xPosition.OFFSET_END:this._left=targetRight+'px';break;}var targetTop=targetBounds.top;var targetBottom=targetBounds.bottom;var targetHeight=targetBounds.height;switch(position.y){case MdPanelPosition.yPosition.ABOVE:this._top=targetTop-panelHeight+'px';break;case MdPanelPosition.yPosition.ALIGN_BOTTOMS:this._top=targetBottom-panelHeight+'px';break;case MdPanelPosition.yPosition.CENTER:var top=targetTop+0.5*targetHeight-0.5*panelHeight;this._top=top+'px';break;case MdPanelPosition.yPosition.ALIGN_TOPS:this._top=targetTop+'px';break;case MdPanelPosition.yPosition.BELOW:this._top=targetBottom+'px';break;}};/***************************************************************************** * MdPanelAnimation * *****************************************************************************//** * Animation configuration object. To use, create an MdPanelAnimation with the * desired properties, then pass the object as part of $mdPanel creation. * * Example: * * var panelAnimation = new MdPanelAnimation() * .openFrom(myButtonEl) * .closeTo('.my-button') * .withAnimation($mdPanel.animation.SCALE); * * $mdPanel.create({ * animation: panelAnimation * }); * * @param {!angular.$injector} $injector * @final @constructor */function MdPanelAnimation($injector){/** @private @const {!angular.$mdUtil} */this._$mdUtil=$injector.get('$mdUtil');/** * @private {{element: !angular.JQLite|undefined, bounds: !DOMRect}| * undefined} */this._openFrom;/** * @private {{element: !angular.JQLite|undefined, bounds: !DOMRect}| * undefined} */this._closeTo;/** @private {string|{open: string, close: string}} */this._animationClass='';/** @private {number} */this._openDuration;/** @private {number} */this._closeDuration;/** @private {number|{open: number, close: number}} */this._rawDuration;}/** * Possible default animations. * @enum {string} */MdPanelAnimation.animation={SLIDE:'md-panel-animate-slide',SCALE:'md-panel-animate-scale',FADE:'md-panel-animate-fade'};/** * Specifies where to start the open animation. `openFrom` accepts a * click event object, query selector, DOM element, or a Rect object that * is used to determine the bounds. When passed a click event, the location * of the click will be used as the position to start the animation. * @param {string|!Element|!Event|{top: number, left: number}} openFrom * @returns {!MdPanelAnimation} */MdPanelAnimation.prototype.openFrom=function(openFrom){// Check if 'openFrom' is an Event. openFrom=openFrom.target?openFrom.target:openFrom;this._openFrom=this._getPanelAnimationTarget(openFrom);if(!this._closeTo){this._closeTo=this._openFrom;}return this;};/** * Specifies where to animate the panel close. `closeTo` accepts a * query selector, DOM element, or a Rect object that is used to determine * the bounds. * @param {string|!Element|{top: number, left: number}} closeTo * @returns {!MdPanelAnimation} */MdPanelAnimation.prototype.closeTo=function(closeTo){this._closeTo=this._getPanelAnimationTarget(closeTo);return this;};/** * Specifies the duration of the animation in milliseconds. * @param {number|{open: number, close: number}} duration * @returns {!MdPanelAnimation} */MdPanelAnimation.prototype.duration=function(duration){if(duration){if(angular.isNumber(duration)){this._openDuration=this._closeDuration=toSeconds(duration);}else if(angular.isObject(duration)){this._openDuration=toSeconds(duration.open);this._closeDuration=toSeconds(duration.close);}}// Save the original value so it can be passed to the backdrop. this._rawDuration=duration;return this;function toSeconds(value){if(angular.isNumber(value))return value/1000;}};/** * Returns the element and bounds for the animation target. * @param {string|!Element|{top: number, left: number}} location * @returns {{element: !angular.JQLite|undefined, bounds: !DOMRect}} * @private */MdPanelAnimation.prototype._getPanelAnimationTarget=function(location){if(angular.isDefined(location.top)||angular.isDefined(location.left)){return{element:undefined,bounds:{top:location.top||0,left:location.left||0}};}else{return this._getBoundingClientRect(getElement(location));}};/** * Specifies the animation class. * * There are several default animations that can be used: * (MdPanelAnimation.animation) * SLIDE: The panel slides in and out from the specified * elements. * SCALE: The panel scales in and out. * FADE: The panel fades in and out. * * @param {string|{open: string, close: string}} cssClass * @returns {!MdPanelAnimation} */MdPanelAnimation.prototype.withAnimation=function(cssClass){this._animationClass=cssClass;return this;};/** * Animate the panel open. * @param {!angular.JQLite} panelEl * @returns {!angular.$q.Promise} A promise that is resolved when the open * animation is complete. */MdPanelAnimation.prototype.animateOpen=function(panelEl){var animator=this._$mdUtil.dom.animator;this._fixBounds(panelEl);var animationOptions={};// Include the panel transformations when calculating the animations. var panelTransform=panelEl[0].style.transform||'';var openFrom=animator.toTransformCss(panelTransform);var openTo=animator.toTransformCss(panelTransform);switch(this._animationClass){case MdPanelAnimation.animation.SLIDE:// Slide should start with opacity: 1. panelEl.css('opacity','1');animationOptions={transitionInClass:'_md-panel-animate-enter'};var openSlide=animator.calculateSlideToOrigin(panelEl,this._openFrom)||'';openFrom=animator.toTransformCss(openSlide+' '+panelTransform);break;case MdPanelAnimation.animation.SCALE:animationOptions={transitionInClass:'_md-panel-animate-enter'};var openScale=animator.calculateZoomToOrigin(panelEl,this._openFrom)||'';openFrom=animator.toTransformCss(openScale+' '+panelTransform);break;case MdPanelAnimation.animation.FADE:animationOptions={transitionInClass:'_md-panel-animate-enter'};break;default:if(angular.isString(this._animationClass)){animationOptions={transitionInClass:this._animationClass};}else{animationOptions={transitionInClass:this._animationClass['open'],transitionOutClass:this._animationClass['close']};}}animationOptions.duration=this._openDuration;return animator.translate3d(panelEl,openFrom,openTo,animationOptions);};/** * Animate the panel close. * @param {!angular.JQLite} panelEl * @returns {!angular.$q.Promise} A promise that resolves when the close * animation is complete. */MdPanelAnimation.prototype.animateClose=function(panelEl){var animator=this._$mdUtil.dom.animator;var reverseAnimationOptions={};// Include the panel transformations when calculating the animations. var panelTransform=panelEl[0].style.transform||'';var closeFrom=animator.toTransformCss(panelTransform);var closeTo=animator.toTransformCss(panelTransform);switch(this._animationClass){case MdPanelAnimation.animation.SLIDE:// Slide should start with opacity: 1. panelEl.css('opacity','1');reverseAnimationOptions={transitionInClass:'_md-panel-animate-leave'};var closeSlide=animator.calculateSlideToOrigin(panelEl,this._closeTo)||'';closeTo=animator.toTransformCss(closeSlide+' '+panelTransform);break;case MdPanelAnimation.animation.SCALE:reverseAnimationOptions={transitionInClass:'_md-panel-animate-scale-out _md-panel-animate-leave'};var closeScale=animator.calculateZoomToOrigin(panelEl,this._closeTo)||'';closeTo=animator.toTransformCss(closeScale+' '+panelTransform);break;case MdPanelAnimation.animation.FADE:reverseAnimationOptions={transitionInClass:'_md-panel-animate-fade-out _md-panel-animate-leave'};break;default:if(angular.isString(this._animationClass)){reverseAnimationOptions={transitionOutClass:this._animationClass};}else{reverseAnimationOptions={transitionInClass:this._animationClass['close'],transitionOutClass:this._animationClass['open']};}}reverseAnimationOptions.duration=this._closeDuration;return animator.translate3d(panelEl,closeFrom,closeTo,reverseAnimationOptions);};/** * Set the height and width to match the panel if not provided. * @param {!angular.JQLite} panelEl * @private */MdPanelAnimation.prototype._fixBounds=function(panelEl){var panelWidth=panelEl[0].offsetWidth;var panelHeight=panelEl[0].offsetHeight;if(this._openFrom&&this._openFrom.bounds.height==null){this._openFrom.bounds.height=panelHeight;}if(this._openFrom&&this._openFrom.bounds.width==null){this._openFrom.bounds.width=panelWidth;}if(this._closeTo&&this._closeTo.bounds.height==null){this._closeTo.bounds.height=panelHeight;}if(this._closeTo&&this._closeTo.bounds.width==null){this._closeTo.bounds.width=panelWidth;}};/** * Identify the bounding RECT for the target element. * @param {!angular.JQLite} element * @returns {{element: !angular.JQLite|undefined, bounds: !DOMRect}} * @private */MdPanelAnimation.prototype._getBoundingClientRect=function(element){if(element instanceof angular.element){return{element:element,bounds:element[0].getBoundingClientRect()};}};/***************************************************************************** * Util Methods * *****************************************************************************//** * Returns the angular element associated with a css selector or element. * @param el {string|!angular.JQLite|!Element} * @returns {!angular.JQLite} */function getElement(el){var queryResult=angular.isString(el)?document.querySelector(el):el;return angular.element(queryResult);}/** * Gets the computed values for an element's translateX and translateY in px. * @param {!angular.JQLite|!Element} el * @param {string} property * @return {{x: number, y: number}} */function getComputedTranslations(el,property){// The transform being returned by `getComputedStyle` is in the format: // `matrix(a, b, c, d, translateX, translateY)` if defined and `none` // if the element doesn't have a transform. var transform=getComputedStyle(el[0]||el)[property];var openIndex=transform.indexOf('(');var closeIndex=transform.lastIndexOf(')');var output={x:0,y:0};if(openIndex>-1&&closeIndex>-1){var parsedValues=transform.substring(openIndex+1,closeIndex).split(', ').slice(-2);output.x=parseInt(parsedValues[0]);output.y=parseInt(parsedValues[1]);}return output;}})();(function(){"use strict";/** * @ngdoc module * @name material.components.progressCircular * @description Module for a circular progressbar */angular.module('material.components.progressCircular',['material.core']);})();(function(){"use strict";/** * @ngdoc module * @name material.components.progressLinear * @description Linear Progress module! */MdProgressLinearDirective.$inject=['$mdTheming','$mdUtil','$log'];MdProgressLinearDirective.$inject=["$mdTheming","$mdUtil","$log"];angular.module('material.components.progressLinear',['material.core']).directive('mdProgressLinear',MdProgressLinearDirective);/** * @ngdoc directive * @name mdProgressLinear * @module material.components.progressLinear * @restrict E * * @description * The linear progress directive is used to make loading content * in your app as delightful and painless as possible by minimizing * the amount of visual change a user sees before they can view * and interact with content. * * Each operation should only be represented by one activity indicator * For example: one refresh operation should not display both a * refresh bar and an activity circle. * * For operations where the percentage of the operation completed * can be determined, use a determinate indicator. They give users * a quick sense of how long an operation will take. * * For operations where the user is asked to wait a moment while * something finishes up, and it’s not necessary to expose what's * happening behind the scenes and how long it will take, use an * indeterminate indicator. * * @param {string} md-mode Select from one of four modes: determinate, indeterminate, buffer or query. * * Note: if the `md-mode` value is set as undefined or specified as 1 of the four (4) valid modes, then `indeterminate` * will be auto-applied as the mode. * * Note: if not configured, the `md-mode="indeterminate"` will be auto injected as an attribute. If `value=""` is also specified, however, * then `md-mode="determinate"` would be auto-injected instead. * @param {number=} value In determinate and buffer modes, this number represents the percentage of the primary progress bar. Default: 0 * @param {number=} md-buffer-value In the buffer mode, this number represents the percentage of the secondary progress bar. Default: 0 * @param {boolean=} ng-disabled Determines whether to disable the progress element. * * @usage * * * * * * * * * * * */function MdProgressLinearDirective($mdTheming,$mdUtil,$log){var MODE_DETERMINATE="determinate";var MODE_INDETERMINATE="indeterminate";var MODE_BUFFER="buffer";var MODE_QUERY="query";var DISABLED_CLASS="_md-progress-linear-disabled";return{restrict:'E',template:'
      '+'
      '+'
      '+'
      '+'
      ',compile:compile};function compile(tElement,tAttrs,transclude){tElement.attr('aria-valuemin',0);tElement.attr('aria-valuemax',100);tElement.attr('role','progressbar');return postLink;}function postLink(scope,element,attr){$mdTheming(element);var lastMode;var isDisabled=attr.hasOwnProperty('disabled');var toVendorCSS=$mdUtil.dom.animator.toCss;var bar1=angular.element(element[0].querySelector('.md-bar1'));var bar2=angular.element(element[0].querySelector('.md-bar2'));var container=angular.element(element[0].querySelector('.md-container'));element.attr('md-mode',mode()).toggleClass(DISABLED_CLASS,isDisabled);validateMode();watchAttributes();/** * Watch the value, md-buffer-value, and md-mode attributes */function watchAttributes(){attr.$observe('value',function(value){var percentValue=clamp(value);element.attr('aria-valuenow',percentValue);if(mode()!=MODE_QUERY)animateIndicator(bar2,percentValue);});attr.$observe('mdBufferValue',function(value){animateIndicator(bar1,clamp(value));});attr.$observe('disabled',function(value){if(value===true||value===false){isDisabled=!!value;}else{isDisabled=angular.isDefined(value);}element.toggleClass(DISABLED_CLASS,isDisabled);container.toggleClass(lastMode,!isDisabled);});attr.$observe('mdMode',function(mode){if(lastMode)container.removeClass(lastMode);switch(mode){case MODE_QUERY:case MODE_BUFFER:case MODE_DETERMINATE:case MODE_INDETERMINATE:container.addClass(lastMode="md-mode-"+mode);break;default:container.addClass(lastMode="md-mode-"+MODE_INDETERMINATE);break;}});}/** * Auto-defaults the mode to either `determinate` or `indeterminate` mode; if not specified */function validateMode(){if(angular.isUndefined(attr.mdMode)){var hasValue=angular.isDefined(attr.value);var mode=hasValue?MODE_DETERMINATE:MODE_INDETERMINATE;var info="Auto-adding the missing md-mode='{0}' to the ProgressLinear element";//$log.debug( $mdUtil.supplant(info, [mode]) ); element.attr("md-mode",mode);attr.mdMode=mode;}}/** * Is the md-mode a valid option? */function mode(){var value=(attr.mdMode||"").trim();if(value){switch(value){case MODE_DETERMINATE:case MODE_INDETERMINATE:case MODE_BUFFER:case MODE_QUERY:break;default:value=MODE_INDETERMINATE;break;}}return value;}/** * Manually set CSS to animate the Determinate indicator based on the specified * percentage value (0-100). */function animateIndicator(target,value){if(isDisabled||!mode())return;var to=$mdUtil.supplant("translateX({0}%) scale({1},1)",[(value-100)/2,value/100]);var styles=toVendorCSS({transform:to});angular.element(target).css(styles);}}/** * Clamps the value to be between 0 and 100. * @param {number} value The value to clamp. * @returns {number} */function clamp(value){return Math.max(0,Math.min(value||0,100));}}})();(function(){"use strict";/** * @ngdoc module * @name material.components.radioButton * @description radioButton module! */mdRadioButtonDirective.$inject=['$mdAria','$mdUtil','$mdTheming'];mdRadioGroupDirective.$inject=['$mdUtil','$mdConstant','$mdTheming','$timeout'];mdRadioGroupDirective.$inject=["$mdUtil","$mdConstant","$mdTheming","$timeout"];mdRadioButtonDirective.$inject=["$mdAria","$mdUtil","$mdTheming"];angular.module('material.components.radioButton',['material.core']).directive('mdRadioGroup',mdRadioGroupDirective).directive('mdRadioButton',mdRadioButtonDirective);/** * @ngdoc directive * @module material.components.radioButton * @name mdRadioGroup * * @restrict E * * @description * The `` directive identifies a grouping * container for the 1..n grouped radio buttons; specified using nested * `` tags. * * As per the [material design spec](http://www.google.com/design/spec/style/color.html#color-ui-color-application) * the radio button is in the accent color by default. The primary color palette may be used with * the `md-primary` class. * * Note: `` and `` handle tabindex differently * than the native `` controls. Whereas the native controls * force the user to tab through all the radio buttons, `` * is focusable, and by default the ``s are not. * * @param {string} ng-model Assignable angular expression to data-bind to. * @param {boolean=} md-no-ink Use of attribute indicates flag to disable ink ripple effects. * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} ngChange AngularJS expression to be executed when input changes due to user * interaction with the input element. * * @usage * * * * * * {{ d.label }} * * * * * * */function mdRadioGroupDirective($mdUtil,$mdConstant,$mdTheming,$timeout){RadioGroupController.prototype=createRadioGroupControllerProto();return{restrict:'E',controller:['$element',RadioGroupController],require:['mdRadioGroup','?ngModel'],link:{pre:linkRadioGroup}};function linkRadioGroup(scope,element,attr,ctrls){element.addClass('_md');// private md component indicator for styling $mdTheming(element);var rgCtrl=ctrls[0];var ngModelCtrl=ctrls[1]||$mdUtil.fakeNgModel();rgCtrl.init(ngModelCtrl);scope.mouseActive=false;element.attr({'role':'radiogroup','tabIndex':element.attr('tabindex')||'0'}).on('keydown',keydownListener).on('mousedown',function(event){scope.mouseActive=true;$timeout(function(){scope.mouseActive=false;},100);}).on('focus',function(){if(scope.mouseActive===false){rgCtrl.$element.addClass('md-focused');}}).on('blur',function(){rgCtrl.$element.removeClass('md-focused');});/** * */function setFocus(){if(!element.hasClass('md-focused')){element.addClass('md-focused');}}/** * */function keydownListener(ev){var keyCode=ev.which||ev.keyCode;// Only listen to events that we originated ourselves // so that we don't trigger on things like arrow keys in // inputs. if(keyCode!=$mdConstant.KEY_CODE.ENTER&&ev.currentTarget!=ev.target){return;}switch(keyCode){case $mdConstant.KEY_CODE.LEFT_ARROW:case $mdConstant.KEY_CODE.UP_ARROW:ev.preventDefault();rgCtrl.selectPrevious();setFocus();break;case $mdConstant.KEY_CODE.RIGHT_ARROW:case $mdConstant.KEY_CODE.DOWN_ARROW:ev.preventDefault();rgCtrl.selectNext();setFocus();break;case $mdConstant.KEY_CODE.ENTER:var form=angular.element($mdUtil.getClosest(element[0],'form'));if(form.length>0){form.triggerHandler('submit');}break;}}}function RadioGroupController($element){this._radioButtonRenderFns=[];this.$element=$element;}function createRadioGroupControllerProto(){return{init:function init(ngModelCtrl){this._ngModelCtrl=ngModelCtrl;this._ngModelCtrl.$render=angular.bind(this,this.render);},add:function add(rbRender){this._radioButtonRenderFns.push(rbRender);},remove:function remove(rbRender){var index=this._radioButtonRenderFns.indexOf(rbRender);if(index!==-1){this._radioButtonRenderFns.splice(index,1);}},render:function render(){this._radioButtonRenderFns.forEach(function(rbRender){rbRender();});},setViewValue:function setViewValue(value,eventType){this._ngModelCtrl.$setViewValue(value,eventType);// update the other radio buttons as well this.render();},getViewValue:function getViewValue(){return this._ngModelCtrl.$viewValue;},selectNext:function selectNext(){return changeSelectedButton(this.$element,1);},selectPrevious:function selectPrevious(){return changeSelectedButton(this.$element,-1);},setActiveDescendant:function setActiveDescendant(radioId){this.$element.attr('aria-activedescendant',radioId);},isDisabled:function isDisabled(){return this.$element[0].hasAttribute('disabled');}};}/** * Change the radio group's selected button by a given increment. * If no button is selected, select the first button. */function changeSelectedButton(parent,increment){// Coerce all child radio buttons into an array, then wrap then in an iterator var buttons=$mdUtil.iterator(parent[0].querySelectorAll('md-radio-button'),true);if(buttons.count()){var validate=function validate(button){// If disabled, then NOT valid return!angular.element(button).attr("disabled");};var selected=parent[0].querySelector('md-radio-button.md-checked');var target=buttons[increment<0?'previous':'next'](selected,validate)||buttons.first();// Activate radioButton's click listener (triggerHandler won't create a real click event) angular.element(target).triggerHandler('click');}}}/** * @ngdoc directive * @module material.components.radioButton * @name mdRadioButton * * @restrict E * * @description * The ``directive is the child directive required to be used within `` elements. * * While similar to the `` directive, * the `` directive provides ink effects, ARIA support, and * supports use within named radio groups. * * @param {string} ngValue AngularJS expression which sets the value to which the expression should * be set when selected. * @param {string} value The value to which the expression should be set when selected. * @param {string=} name Property name of the form under which the control is published. * @param {string=} aria-label Adds label to radio button for accessibility. * Defaults to radio button's text. If no text content is available, a warning will be logged. * * @usage * * * * Label 1 * * * * Green * * * * */function mdRadioButtonDirective($mdAria,$mdUtil,$mdTheming){var CHECKED_CSS='md-checked';return{restrict:'E',require:'^mdRadioGroup',transclude:true,template:'
      '+'
      '+'
      '+'
      '+'
      ',link:link};function link(scope,element,attr,rgCtrl){var lastChecked;$mdTheming(element);configureAria(element,scope);// ngAria overwrites the aria-checked inside a $watch for ngValue. // We should defer the initialization until all the watches have fired. // This can also be fixed by removing the `lastChecked` check, but that'll // cause more DOM manipulation on each digest. if(attr.ngValue){$mdUtil.nextTick(initialize,false);}else{initialize();}/** * Initializes the component. */function initialize(){if(!rgCtrl){throw'RadioButton: No RadioGroupController could be found.';}rgCtrl.add(render);attr.$observe('value',render);element.on('click',listener).on('$destroy',function(){rgCtrl.remove(render);});}/** * On click functionality. */function listener(ev){if(element[0].hasAttribute('disabled')||rgCtrl.isDisabled())return;scope.$apply(function(){rgCtrl.setViewValue(attr.value,ev&&ev.type);});}/** * Add or remove the `.md-checked` class from the RadioButton (and conditionally its parent). * Update the `aria-activedescendant` attribute. */function render(){var checked=rgCtrl.getViewValue()==attr.value;if(checked===lastChecked)return;if(element[0].parentNode.nodeName.toLowerCase()!=='md-radio-group'){// If the radioButton is inside a div, then add class so highlighting will work element.parent().toggleClass(CHECKED_CSS,checked);}if(checked){rgCtrl.setActiveDescendant(element.attr('id'));}lastChecked=checked;element.attr('aria-checked',checked).toggleClass(CHECKED_CSS,checked);}/** * Inject ARIA-specific attributes appropriate for each radio button */function configureAria(element,scope){element.attr({id:attr.id||'radio_'+$mdUtil.nextUid(),role:'radio','aria-checked':'false'});$mdAria.expectWithText(element,'aria-label');}}}})();(function(){"use strict";/** * @ngdoc module * @name material.components.showHide */// Add additional handlers to ng-show and ng-hide that notify directives // contained within that they should recompute their size. // These run in addition to AngularJS's built-in ng-hide and ng-show directives. angular.module('material.components.showHide',['material.core']).directive('ngShow',createDirective('ngShow',true)).directive('ngHide',createDirective('ngHide',false));function createDirective(name,targetValue){return['$mdUtil','$window',function($mdUtil,$window){return{restrict:'A',multiElement:true,link:function link($scope,$element,$attr){var unregister=$scope.$on('$md-resize-enable',function(){unregister();var node=$element[0];var cachedTransitionStyles=node.nodeType===$window.Node.ELEMENT_NODE?$window.getComputedStyle(node):{};$scope.$watch($attr[name],function(value){if(!!value===targetValue){$mdUtil.nextTick(function(){$scope.$broadcast('$md-resize');});var opts={cachedTransitionStyles:cachedTransitionStyles};$mdUtil.dom.animator.waitTransitionEnd($element,opts).then(function(){$scope.$broadcast('$md-resize');});}});});}};}];}})();(function(){"use strict";/** * @ngdoc module * @name material.components.select *//*************************************************** ### TODO - POST RC1 ### - [ ] Abstract placement logic in $mdSelect service to $mdMenu service ***************************************************/SelectProvider.$inject=['$$interimElementProvider'];OptionDirective.$inject=['$mdButtonInkRipple','$mdUtil','$mdTheming'];SelectMenuDirective.$inject=['$parse','$mdUtil','$mdConstant','$mdTheming'];SelectDirective.$inject=['$mdSelect','$mdUtil','$mdConstant','$mdTheming','$mdAria','$parse','$sce','$injector'];SelectDirective.$inject=["$mdSelect","$mdUtil","$mdConstant","$mdTheming","$mdAria","$parse","$sce","$injector"];SelectMenuDirective.$inject=["$parse","$mdUtil","$mdConstant","$mdTheming"];OptionDirective.$inject=["$mdButtonInkRipple","$mdUtil","$mdTheming"];SelectProvider.$inject=["$$interimElementProvider"];var SELECT_EDGE_MARGIN=8;var selectNextId=0;var CHECKBOX_SELECTION_INDICATOR=angular.element('
      ');angular.module('material.components.select',['material.core','material.components.backdrop']).directive('mdSelect',SelectDirective).directive('mdSelectMenu',SelectMenuDirective).directive('mdOption',OptionDirective).directive('mdOptgroup',OptgroupDirective).directive('mdSelectHeader',SelectHeaderDirective).provider('$mdSelect',SelectProvider);/** * @ngdoc directive * @name mdSelect * @restrict E * @module material.components.select * * @description Displays a select box, bound to an ng-model. * * When the select is required and uses a floating label, then the label will automatically contain * an asterisk (`*`). This behavior can be disabled by using the `md-no-asterisk` attribute. * * By default, the select will display with an underline to match other form elements. This can be * disabled by applying the `md-no-underline` CSS class. * * ### Option Params * * When applied, `md-option-empty` will mark the option as "empty" allowing the option to clear the * select and put it back in it's default state. You may supply this attribute on any option you * wish, however, it is automatically applied to an option whose `value` or `ng-value` are not * defined. * * **Automatically Applied** * * - `` * - `` * - `` * - `` * - `` * * **NOT Automatically Applied** * * - `` * - `` * - `` * - `` (this evaluates to the string `"undefined"`) * - <md-option ng-value="{{someValueThatMightBeUndefined}}"> * * **Note:** A value of `undefined` ***is considered a valid value*** (and does not auto-apply this * attribute) since you may wish this to be your "Not Available" or "None" option. * * **Note:** Using the `value` attribute (as opposed to `ng-value`) always evaluates to a string, so * `value="null"` will require the test `ng-if="myValue != 'null'"` rather than `ng-if="!myValue"`. * * @param {expression} ng-model The model! * @param {boolean=} multiple When set to true, allows for more than one option to be selected. The model is an array with the selected choices. * @param {expression=} md-on-close Expression to be evaluated when the select is closed. * @param {expression=} md-on-open Expression to be evaluated when opening the select. * Will hide the select options and show a spinner until the evaluated promise resolves. * @param {expression=} md-selected-text Expression to be evaluated that will return a string * to be displayed as a placeholder in the select input box when it is closed. The value * will be treated as *text* (not html). * @param {expression=} md-selected-html Expression to be evaluated that will return a string * to be displayed as a placeholder in the select input box when it is closed. The value * will be treated as *html*. The value must either be explicitly marked as trustedHtml or * the ngSanitize module must be loaded. * @param {string=} placeholder Placeholder hint text. * @param md-no-asterisk {boolean=} When set to true, an asterisk will not be appended to the * floating label. **Note:** This attribute is only evaluated once; it is not watched. * @param {string=} aria-label Optional label for accessibility. Only necessary if no placeholder or * explicit label is present. * @param {string=} md-container-class Class list to get applied to the `.md-select-menu-container` * element (for custom styling). * * @usage * With a placeholder (label and aria-label are added dynamically) * * * * {{ opt }} * * * * * With an explicit label * * * * * {{ opt }} * * * * * With a select-header * * When a developer needs to put more than just a text label in the * md-select-menu, they should use the md-select-header. * The user can put custom HTML inside of the header and style it to their liking. * One common use case of this would be a sticky search bar. * * When using the md-select-header the labels that would previously be added to the * OptGroupDirective are ignored. * * * * * * Neighborhoods - * * {{ opt }} * * * * * ## Selects and object equality * When using a `md-select` to pick from a list of objects, it is important to realize how javascript handles * equality. Consider the following example: * * angular.controller('MyCtrl', function($scope) { * $scope.users = [ * { id: 1, name: 'Bob' }, * { id: 2, name: 'Alice' }, * { id: 3, name: 'Steve' } * ]; * $scope.selectedUser = { id: 1, name: 'Bob' }; * }); * * *
      * * {{ user.name }} * *
      *
      * * At first one might expect that the select should be populated with "Bob" as the selected user. However, * this is not true. To determine whether something is selected, * `ngModelController` is looking at whether `$scope.selectedUser == (any user in $scope.users);`; * * Javascript's `==` operator does not check for deep equality (ie. that all properties * on the object are the same), but instead whether the objects are *the same object in memory*. * In this case, we have two instances of identical objects, but they exist in memory as unique * entities. Because of this, the select will have no value populated for a selected user. * * To get around this, `ngModelController` provides a `track by` option that allows us to specify a different * expression which will be used for the equality operator. As such, we can update our `html` to * make use of this by specifying the `ng-model-options="{trackBy: '$value.id'}"` on the `md-select` * element. This converts our equality expression to be * `$scope.selectedUser.id == (any id in $scope.users.map(function(u) { return u.id; }));` * which results in Bob being selected as desired. * * Working HTML: * *
      * * {{ user.name }} * *
      *
      */function SelectDirective($mdSelect,$mdUtil,$mdConstant,$mdTheming,$mdAria,$parse,$sce,$injector){var keyCodes=$mdConstant.KEY_CODE;var NAVIGATION_KEYS=[keyCodes.SPACE,keyCodes.ENTER,keyCodes.UP_ARROW,keyCodes.DOWN_ARROW];return{restrict:'E',require:['^?mdInputContainer','mdSelect','ngModel','?^form'],compile:compile,controller:function controller(){}// empty placeholder controller to be initialized in link };function compile(element,attr){// add the select value that will hold our placeholder or selected option value var valueEl=angular.element('');valueEl.append('');valueEl.addClass('md-select-value');if(!valueEl[0].hasAttribute('id')){valueEl.attr('id','select_value_label_'+$mdUtil.nextUid());}// There's got to be an md-content inside. If there's not one, let's add it. var mdContentEl=element.find('md-content');if(!mdContentEl.length){element.append(angular.element('').append(element.contents()));}mdContentEl.attr('role','presentation');// Add progress spinner for md-options-loading if(attr.mdOnOpen){// Show progress indicator while loading async // Use ng-hide for `display:none` so the indicator does not interfere with the options list element.find('md-content').prepend(angular.element('
      '+' '+'
      '));// Hide list [of item options] while loading async element.find('md-option').attr('ng-show','$$loadingAsyncDone');}if(attr.name){var autofillClone=angular.element('');autofillClone.attr({'name':attr.name,'aria-hidden':'true','tabindex':'-1'});var opts=element.find('md-option');angular.forEach(opts,function(el){var newEl=angular.element('');if(el.hasAttribute('ng-value'))newEl.attr('ng-value',el.getAttribute('ng-value'));else if(el.hasAttribute('value'))newEl.attr('value',el.getAttribute('value'));autofillClone.append(newEl);});// Adds an extra option that will hold the selected value for the // cases where the select is a part of a non-angular form. This can be done with a ng-model, // however if the `md-option` is being `ng-repeat`-ed, AngularJS seems to insert a similar // `option` node, but with a value of `? string: ?` which would then get submitted. // This also goes around having to prepend a dot to the name attribute. autofillClone.append('');element.parent().append(autofillClone);}var isMultiple=$mdUtil.parseAttributeBoolean(attr.multiple);// Use everything that's left inside element.contents() as the contents of the menu var multipleContent=isMultiple?'multiple':'';var selectTemplate=''+'';selectTemplate=$mdUtil.supplant(selectTemplate,[multipleContent,element.html()]);element.empty().append(valueEl);element.append(selectTemplate);if(!attr.tabindex){attr.$set('tabindex',0);}return function postLink(scope,element,attr,ctrls){var untouched=true;var isDisabled,ariaLabelBase;var containerCtrl=ctrls[0];var mdSelectCtrl=ctrls[1];var ngModelCtrl=ctrls[2];var formCtrl=ctrls[3];// grab a reference to the select menu value label var valueEl=element.find('md-select-value');var isReadonly=angular.isDefined(attr.readonly);var disableAsterisk=$mdUtil.parseAttributeBoolean(attr.mdNoAsterisk);if(disableAsterisk){element.addClass('md-no-asterisk');}if(containerCtrl){var isErrorGetter=containerCtrl.isErrorGetter||function(){return ngModelCtrl.$invalid&&(ngModelCtrl.$touched||formCtrl&&formCtrl.$submitted);};if(containerCtrl.input){// We ignore inputs that are in the md-select-header (one // case where this might be useful would be adding as searchbox) if(element.find('md-select-header').find('input')[0]!==containerCtrl.input[0]){throw new Error(" can only have *one* child ,
      Directive How Source Rendered
      ng-bind-html Automatically uses $sanitize
      <div ng-bind-html="snippet">
      </div>
      ng-bind-html Bypass $sanitize by explicitly trusting the dangerous value
      <div ng-bind-html="deliberatelyTrustDangerousSnippet()">
      </div>
      ng-bind Automatically escapes
      <div ng-bind="snippet">
      </div>
      it('should sanitize the html snippet by default', function() { expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). toBe('

      an html\nclick here\nsnippet

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

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

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

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

      * *

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

      * *
      * *
      
         *   .rootOfTheIncludedContent svg {
         *     overflow: hidden !important;
         *   }
         *   
      *
      * * @param {boolean=} flag Enable or disable SVG support in the sanitizer. * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called * without an argument or self for chaining otherwise. */this.enableSvg=function(enableSvg){if(isDefined(enableSvg)){svgEnabled=enableSvg;return this;}else{return svgEnabled;}};////////////////////////////////////////////////////////////////////////////////////////////////// // Private stuff ////////////////////////////////////////////////////////////////////////////////////////////////// bind=angular.bind;extend=angular.extend;forEach=angular.forEach;isDefined=angular.isDefined;lowercase=angular.lowercase;noop=angular.noop;htmlParser=htmlParserImpl;htmlSanitizeWriter=htmlSanitizeWriterImpl;// Regular Expressions for parsing tags and attributes var SURROGATE_PAIR_REGEXP=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,// Match everything outside of normal chars and " (quote character) NON_ALPHANUMERIC_REGEXP=/([^#-~ |!])/g;// Good source of info about elements and attributes // http://dev.w3.org/html5/spec/Overview.html#semantics // http://simon.html5.org/html-elements // Safe Void Elements - HTML5 // http://dev.w3.org/html5/spec/Overview.html#void-elements var voidElements=toMap('area,br,col,hr,img,wbr');// Elements that you can, intentionally, leave open (and which close themselves) // http://dev.w3.org/html5/spec/Overview.html#optional-tags var optionalEndTagBlockElements=toMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'),optionalEndTagInlineElements=toMap('rp,rt'),optionalEndTagElements=extend({},optionalEndTagInlineElements,optionalEndTagBlockElements);// Safe Block Elements - HTML5 var blockElements=extend({},optionalEndTagBlockElements,toMap('address,article,'+'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,'+'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul'));// Inline Elements - HTML5 var inlineElements=extend({},optionalEndTagInlineElements,toMap('a,abbr,acronym,b,'+'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,'+'samp,small,span,strike,strong,sub,sup,time,tt,u,var'));// SVG Elements // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. // They can potentially allow for arbitrary javascript to be executed. See #11290 var svgElements=toMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,'+'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,'+'radialGradient,rect,stop,svg,switch,text,title,tspan');// Blocked Elements (will be stripped) var blockedElements=toMap('script,style');var validElements=extend({},voidElements,blockElements,inlineElements,optionalEndTagElements);//Attributes that have href and hence need to be sanitized var uriAttrs=toMap('background,cite,href,longdesc,src,xlink:href');var htmlAttrs=toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,'+'valign,value,vspace,width');// SVG attributes (without "id" and "name" attributes) // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes var svgAttrs=toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,'+'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,'+'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,'+'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,'+'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,'+'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,'+'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,'+'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,'+'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,'+'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,'+'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,'+'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,'+'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,'+'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,'+'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan',true);var validAttrs=extend({},uriAttrs,svgAttrs,htmlAttrs);function toMap(str,lowercaseKeys){var obj={},items=str.split(','),i;for(i=0;i/g,'>');}/** * create an HTML/XML writer which writes to buffer * @param {Array} buf use buf.join('') to get out sanitized html string * @returns {object} in the form of { * start: function(tag, attrs) {}, * end: function(tag) {}, * chars: function(text) {}, * comment: function(text) {} * } */function htmlSanitizeWriterImpl(buf,uriValidator){var ignoreCurrentElement=false;var out=bind(buf,buf.push);return{start:function start(tag,attrs){tag=lowercase(tag);if(!ignoreCurrentElement&&blockedElements[tag]){ignoreCurrentElement=tag;}if(!ignoreCurrentElement&&validElements[tag]===true){out('<');out(tag);forEach(attrs,function(value,key){var lkey=lowercase(key);var isImage=tag==='img'&&lkey==='src'||lkey==='background';if(validAttrs[lkey]===true&&(uriAttrs[lkey]!==true||uriValidator(value,isImage))){out(' ');out(key);out('="');out(encodeEntities(value));out('"');}});out('>');}},end:function end(tag){tag=lowercase(tag);if(!ignoreCurrentElement&&validElements[tag]===true&&voidElements[tag]!==true){out('');}// eslint-disable-next-line eqeqeq if(tag==ignoreCurrentElement){ignoreCurrentElement=false;}},chars:function chars(_chars){if(!ignoreCurrentElement){out(encodeEntities(_chars));}}};}/** * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want * to allow any of these custom attributes. This method strips them all. * * @param node Root element to process */function stripCustomNsAttrs(node){while(node){if(node.nodeType===window.Node.ELEMENT_NODE){var attrs=node.attributes;for(var i=0,l=attrs.length;i * * @example
      Snippet:
      Filter Source Rendered
      linky filter
      <div ng-bind-html="snippet | linky">
      </div>
      linky target
      <div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
      </div>
      linky custom attributes
      <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
      </div>
      no filter
      <div ng-bind="snippet">
      </div>
      angular.module('linkyExample', ['ngSanitize']) .controller('ExampleController', ['$scope', function($scope) { $scope.snippet = 'Pretty text with some links:\n' + 'http://angularjs.org/,\n' + 'mailto:us@somewhere.org,\n' + 'another@somewhere.org,\n' + 'and one more: ftp://127.0.0.1/.'; $scope.snippetWithSingleURL = 'http://angularjs.org/'; }]); it('should linkify the snippet with urls', function() { expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); }); it('should not linkify snippet without the linky filter', function() { expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); }); it('should update', function() { element(by.model('snippet')).clear(); element(by.model('snippet')).sendKeys('new http://link.'); expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). toBe('new http://link.'); expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) .toBe('new http://link.'); }); it('should work with the target property', function() { expect(element(by.id('linky-target')). element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). toBe('http://angularjs.org/'); expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); }); it('should optionally add custom attributes', function() { expect(element(by.id('linky-custom-attributes')). element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). toBe('http://angularjs.org/'); expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); }); */angular.module('ngSanitize').filter('linky',['$sanitize',function($sanitize){var LINKY_URL_REGEXP=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,MAILTO_REGEXP=/^mailto:/i;var linkyMinErr=angular.$$minErr('linky');var isDefined=angular.isDefined;var isFunction=angular.isFunction;var isObject=angular.isObject;var isString=angular.isString;return function(text,target,attributes){if(text==null||text==='')return text;if(!isString(text))throw linkyMinErr('notstring','Expected string but received: {0}',text);var attributesFn=isFunction(attributes)?attributes:isObject(attributes)?function getAttributesObject(){return attributes;}:function getEmptyAttributesObject(){return{};};var match;var raw=text;var html=[];var url;var i;while(match=raw.match(LINKY_URL_REGEXP)){// We can not end in these as they are sometimes found at the end of the sentence url=match[0];// if we did not match ftp/http/www/mailto then assume mailto if(!match[2]&&!match[4]){url=(match[3]?'http://':'mailto:')+url;}i=match.index;addText(raw.substr(0,i));addLink(url,match[0].replace(MAILTO_REGEXP,''));raw=raw.substring(i+match[0].length);}addText(raw);return $sanitize(html.join(''));function addText(text){if(!text){return;}html.push(sanitizeText(text));}function addLink(url,text){var key,linkAttributes=attributesFn(url);html.push('');addText(text);html.push('');}};}]);})(window,window.angular);},{}],257:[function(require,module,exports){require('./angular-sanitize');module.exports='ngSanitize';},{"./angular-sanitize":256}],258:[function(require,module,exports){var duScrollDefaultEasing=function duScrollDefaultEasing(e){"use strict";return .5>e?Math.pow(2*e,2)/2:1-Math.pow(2*(1-e),2)/2;};angular.module("duScroll",["duScroll.scrollspy","duScroll.smoothScroll","duScroll.scrollContainer","duScroll.spyContext","duScroll.scrollHelpers"]).value("duScrollDuration",350).value("duScrollSpyWait",100).value("duScrollGreedy",!1).value("duScrollOffset",0).value("duScrollEasing",duScrollDefaultEasing).value("duScrollCancelOnEvents","scroll mousedown mousewheel touchmove keydown").value("duScrollBottomSpy",!1).value("duScrollActiveClass","active"),angular.module("duScroll.scrollHelpers",["duScroll.requestAnimation"]).run(["$window","$q","cancelAnimation","requestAnimation","duScrollEasing","duScrollDuration","duScrollOffset","duScrollCancelOnEvents",function(e,t,n,r,o,l,u,i){"use strict";var c={},a=function a(e){return"undefined"!=typeof HTMLDocument&&e instanceof HTMLDocument||e.nodeType&&e.nodeType===e.DOCUMENT_NODE;},s=function s(e){return"undefined"!=typeof HTMLElement&&e instanceof HTMLElement||e.nodeType&&e.nodeType===e.ELEMENT_NODE;},d=function d(e){return s(e)||a(e)?e:e[0];};c.duScrollTo=function(t,n,r,o){var l;if(angular.isElement(t)?l=this.duScrollToElement:angular.isDefined(r)&&(l=this.duScrollToAnimated),l)return l.apply(this,arguments);var u=d(this);return a(u)?e.scrollTo(t,n):(u.scrollLeft=t,void(u.scrollTop=n));};var f,p;c.duScrollToAnimated=function(e,l,u,c){u&&!c&&(c=o);var a=this.duScrollLeft(),s=this.duScrollTop(),d=Math.round(e-a),m=Math.round(l-s),S=null,g=0,h=this,v=function v(e){(!e||g&&e.which>0)&&(i&&h.unbind(i,v),n(f),p.reject(),f=null);};if(f&&v(),p=t.defer(),0===u||!d&&!m)return 0===u&&h.duScrollTo(e,l),p.resolve(),p.promise;var y=function y(e){null===S&&(S=e),g=e-S;var t=g>=u?1:c(g/u);h.scrollTo(a+Math.ceil(d*t),s+Math.ceil(m*t)),1>t?f=r(y):(i&&h.unbind(i,v),f=null,p.resolve());};return h.duScrollTo(a,s),i&&h.bind(i,v),f=r(y),p.promise;},c.duScrollToElement=function(e,t,n,r){var o=d(this);(!angular.isNumber(t)||isNaN(t))&&(t=u);var l=this.duScrollTop()+d(e).getBoundingClientRect().top-t;return s(o)&&(l-=o.getBoundingClientRect().top),this.duScrollTo(0,l,n,r);},c.duScrollLeft=function(t,n,r){if(angular.isNumber(t))return this.duScrollTo(t,this.duScrollTop(),n,r);var o=d(this);return a(o)?e.scrollX||document.documentElement.scrollLeft||document.body.scrollLeft:o.scrollLeft;},c.duScrollTop=function(t,n,r){if(angular.isNumber(t))return this.duScrollTo(this.duScrollLeft(),t,n,r);var o=d(this);return a(o)?e.scrollY||document.documentElement.scrollTop||document.body.scrollTop:o.scrollTop;},c.duScrollToElementAnimated=function(e,t,n,r){return this.duScrollToElement(e,t,n||l,r);},c.duScrollTopAnimated=function(e,t,n){return this.duScrollTop(e,t||l,n);},c.duScrollLeftAnimated=function(e,t,n){return this.duScrollLeft(e,t||l,n);},angular.forEach(c,function(e,t){angular.element.prototype[t]=e;var n=t.replace(/^duScroll/,"scroll");angular.isUndefined(angular.element.prototype[n])&&(angular.element.prototype[n]=e);});}]),angular.module("duScroll.polyfill",[]).factory("polyfill",["$window",function(e){"use strict";var t=["webkit","moz","o","ms"];return function(n,r){if(e[n])return e[n];for(var o,l=n.substr(0,1).toUpperCase()+n.substr(1),u=0;u=a.scrollHeight):t=Math.round(n.pageYOffset+n.innerHeight)>=r[0].body.scrollHeight;var f,p,m,S,g,h,v=i&&t?"bottom":"top";for(S=o.spies,p=o.currentlyActive,m=void 0,f=0;f>>0,from=Number(arguments[2])||0;from=from<0?Math.ceil(from):Math.floor(from);if(from<0)from+=len;for(;from=0)continue;inheritList.push(parentParams[j]);inherited[parentParams[j]]=currentParams[parentParams[j]];}}return extend({},inherited,newParams);}/** * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. * * @param {Object} a The first object. * @param {Object} b The second object. * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, * it defaults to the list of keys in `a`. * @return {Boolean} Returns `true` if the keys match, otherwise `false`. */function equalForKeys(a,b,keys){if(!keys){keys=[];for(var n in a){keys.push(n);}// Used instead of Object.keys() for IE8 compatibility }for(var i=0;i * * * * * * * * * * * * */angular.module('ui.router',['ui.router.state']);angular.module('ui.router.compat',['ui.router']);/** * @ngdoc object * @name ui.router.util.$resolve * * @requires $q * @requires $injector * * @description * Manages resolution of (acyclic) graphs of promises. */$Resolve.$inject=['$q','$injector'];function $Resolve($q,$injector){var VISIT_IN_PROGRESS=1,VISIT_DONE=2,NOTHING={},NO_DEPENDENCIES=[],NO_LOCALS=NOTHING,NO_PARENT=extend($q.when(NOTHING),{$$promises:NOTHING,$$values:NOTHING});/** * @ngdoc function * @name ui.router.util.$resolve#study * @methodOf ui.router.util.$resolve * * @description * Studies a set of invocables that are likely to be used multiple times. *
         * $resolve.study(invocables)(locals, parent, self)
         * 
      * is equivalent to *
         * $resolve.resolve(invocables, locals, parent, self)
         * 
      * but the former is more efficient (in fact `resolve` just calls `study` * internally). * * @param {object} invocables Invocable objects * @return {function} a function to pass in locals, parent and self */this.study=function(invocables){if(!isObject(invocables))throw new Error("'invocables' must be an object");var invocableKeys=objectKeys(invocables||{});// Perform a topological sort of invocables to build an ordered plan var plan=[],cycle=[],visited={};function visit(value,key){if(visited[key]===VISIT_DONE)return;cycle.push(key);if(visited[key]===VISIT_IN_PROGRESS){cycle.splice(0,indexOf(cycle,key));throw new Error("Cyclic dependency: "+cycle.join(" -> "));}visited[key]=VISIT_IN_PROGRESS;if(isString(value)){plan.push(key,[function(){return $injector.get(value);}],NO_DEPENDENCIES);}else{var params=$injector.annotate(value);forEach(params,function(param){if(param!==key&&invocables.hasOwnProperty(param))visit(invocables[param],param);});plan.push(key,value,params);}cycle.pop();visited[key]=VISIT_DONE;}forEach(invocables,visit);invocables=cycle=visited=null;// plan is all that's required function isResolve(value){return isObject(value)&&value.then&&value.$$promises;}return function(locals,parent,self){if(isResolve(locals)&&self===undefined){self=parent;parent=locals;locals=null;}if(!locals)locals=NO_LOCALS;else if(!isObject(locals)){throw new Error("'locals' must be an object");}if(!parent)parent=NO_PARENT;else if(!isResolve(parent)){throw new Error("'parent' must be a promise returned by $resolve.resolve()");}// To complete the overall resolution, we have to wait for the parent // promise and for the promise for each invokable in our plan. var resolution=$q.defer(),result=resolution.promise,promises=result.$$promises={},values=extend({},locals),wait=1+plan.length/3,merged=false;function done(){// Merge parent values we haven't got yet and publish our own $$values if(! --wait){if(!merged)merge(values,parent.$$values);result.$$values=values;result.$$promises=result.$$promises||true;// keep for isResolve() delete result.$$inheritedValues;resolution.resolve(values);}}function fail(reason){result.$$failure=reason;resolution.reject(reason);}// Short-circuit if parent has already failed if(isDefined(parent.$$failure)){fail(parent.$$failure);return result;}if(parent.$$inheritedValues){merge(values,omit(parent.$$inheritedValues,invocableKeys));}// Merge parent values if the parent has already resolved, or merge // parent promises and wait if the parent resolve is still in progress. extend(promises,parent.$$promises);if(parent.$$values){merged=merge(values,omit(parent.$$values,invocableKeys));result.$$inheritedValues=omit(parent.$$values,invocableKeys);done();}else{if(parent.$$inheritedValues){result.$$inheritedValues=omit(parent.$$inheritedValues,invocableKeys);}parent.then(done,fail);}// Process each invocable in the plan, but ignore any where a local of the same name exists. for(var i=0,ii=plan.length;i} The template html as a string, or a promise * for that string. */this.fromUrl=function(url,params){if(isFunction(url))url=url(params);if(url==null)return null;else return $http.get(url,{cache:$templateCache,headers:{Accept:'text/html'}}).then(function(response){return response.data;});};/** * @ngdoc function * @name ui.router.util.$templateFactory#fromProvider * @methodOf ui.router.util.$templateFactory * * @description * Creates a template by invoking an injectable provider function. * * @param {Function} provider Function to invoke via `$injector.invoke` * @param {Object} params Parameters for the template. * @param {Object} locals Locals to pass to `invoke`. Defaults to * `{ params: params }`. * @return {string|Promise.} The template html as a string, or a promise * for that string. */this.fromProvider=function(provider,params,locals){return $injector.invoke(provider,null,locals||{params:params});};}angular.module('ui.router.util').service('$templateFactory',$TemplateFactory);var $$UMFP;// reference to $UrlMatcherFactoryProvider /** * @ngdoc object * @name ui.router.util.type:UrlMatcher * * @description * Matches URLs against patterns and extracts named parameters from the path or the search * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list * of search parameters. Multiple search parameter names are separated by '&'. Search parameters * do not influence whether or not a URL is matched, but their values are passed through into * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}. * * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace * syntax, which optionally allows a regular expression for the parameter to be specified: * * * `':'` name - colon placeholder * * `'*'` name - catch-all placeholder * * `'{' name '}'` - curly placeholder * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash. * * Parameter names may contain only word characters (latin letters, digits, and underscore) and * must be unique within the pattern (across both path and search parameters). For colon * placeholders or curly placeholders without an explicit regexp, a path parameter matches any * number of characters other than '/'. For catch-all placeholders the path parameter matches * any number of characters. * * Examples: * * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for * trailing slashes, and patterns have to match the entire path, not just a prefix. * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or * '/user/bob/details'. The second path segment will be captured as the parameter 'id'. * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax. * * `'/user/{id:[^/]*}'` - Same as the previous example. * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id * parameter consists of 1 to 8 hex digits. * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the * path into the parameter 'path'. * * `'/files/*path'` - ditto. * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start * * @param {string} pattern The pattern to compile into a matcher. * @param {Object} config A configuration object hash: * @param {Object=} parentMatcher Used to concatenate the pattern/config onto * an existing UrlMatcher * * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`. * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`. * * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns * non-null) will start with this prefix. * * @property {string} source The pattern that was passed into the constructor * * @property {string} sourcePath The path portion of the source property * * @property {string} sourceSearch The search portion of the source property * * @property {string} regex The constructed regex that will be used to match against the url when * it is time to determine which url will match. * * @returns {Object} New `UrlMatcher` object */function UrlMatcher(pattern,config,parentMatcher){config=extend({params:{}},isObject(config)?config:{});// Find all placeholders and create a compiled pattern, using either classic or curly syntax: // '*' name // ':' name // '{' name '}' // '{' name ':' regexp '}' // The regular expression is somewhat complicated due to the need to allow curly braces // inside the regular expression. The placeholder regexp breaks down as follows: // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case) // \{([\w\[\]]+)(?:\:\s*( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either // [^{}\\]+ - anything other than curly braces or backslash // \\. - a backslash escape // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms var placeholder=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,searchPlaceholder=/([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,compiled='^',last=0,m,segments=this.segments=[],parentParams=parentMatcher?parentMatcher.params:{},params=this.params=parentMatcher?parentMatcher.params.$$new():new $$UMFP.ParamSet(),paramNames=[];function addParameter(id,type,config,location){paramNames.push(id);if(parentParams[id])return parentParams[id];if(!/^\w+([-.]+\w+)*(?:\[\])?$/.test(id))throw new Error("Invalid parameter name '"+id+"' in pattern '"+pattern+"'");if(params[id])throw new Error("Duplicate parameter name '"+id+"' in pattern '"+pattern+"'");params[id]=new $$UMFP.Param(id,type,config,location);return params[id];}function quoteRegExp(string,pattern,squash,optional){var surroundPattern=['',''],result=string.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!pattern)return result;switch(squash){case false:surroundPattern=['(',')'+(optional?"?":"")];break;case true:result=result.replace(/\/$/,'');surroundPattern=['(?:\/(',')|\/)?'];break;default:surroundPattern=['('+squash+"|",')?'];break;}return result+surroundPattern[0]+pattern+surroundPattern[1];}this.source=pattern;// Split into static segments separated by path parameter placeholders. // The number of segments is always 1 more than the number of parameters. function matchDetails(m,isSearch){var id,regexp,segment,type,cfg,arrayMode;id=m[2]||m[3];// IE[78] returns '' for unmatched groups instead of null cfg=config.params[id];segment=pattern.substring(last,m.index);regexp=isSearch?m[4]:m[4]||(m[1]=='*'?'.*':null);if(regexp){type=$$UMFP.type(regexp)||inherit($$UMFP.type("string"),{pattern:new RegExp(regexp,config.caseInsensitive?'i':undefined)});}return{id:id,regexp:regexp,segment:segment,type:type,cfg:cfg};}var p,param,segment;while(m=placeholder.exec(pattern)){p=matchDetails(m,false);if(p.segment.indexOf('?')>=0)break;// we're into the search part param=addParameter(p.id,p.type,p.cfg,"path");compiled+=quoteRegExp(p.segment,param.type.pattern.source,param.squash,param.isOptional);segments.push(p.segment);last=placeholder.lastIndex;}segment=pattern.substring(last);// Find any search parameter names and remove them from the last segment var i=segment.indexOf('?');if(i>=0){var search=this.sourceSearch=segment.substring(i);segment=segment.substring(0,i);this.sourcePath=pattern.substring(0,last+i);if(search.length>0){last=0;while(m=searchPlaceholder.exec(search)){p=matchDetails(m,true);param=addParameter(p.id,p.type,p.cfg,"search");last=placeholder.lastIndex;// check if ?& }}}else{this.sourcePath=pattern;this.sourceSearch='';}compiled+=quoteRegExp(segment)+(config.strict===false?'\/?':'')+'$';segments.push(segment);this.regexp=new RegExp(compiled,config.caseInsensitive?'i':undefined);this.prefix=segments[0];this.$$paramNames=paramNames;}/** * @ngdoc function * @name ui.router.util.type:UrlMatcher#concat * @methodOf ui.router.util.type:UrlMatcher * * @description * Returns a new matcher for a pattern constructed by appending the path part and adding the * search parameters of the specified pattern to this pattern. The current pattern is not * modified. This can be understood as creating a pattern for URLs that are relative to (or * suffixes of) the current pattern. * * @example * The following two matchers are equivalent: *
       * new UrlMatcher('/user/{id}?q').concat('/details?date');
       * new UrlMatcher('/user/{id}/details?q&date');
       * 
      * * @param {string} pattern The pattern to append. * @param {Object} config An object hash of the configuration for the matcher. * @returns {UrlMatcher} A matcher for the concatenated pattern. */UrlMatcher.prototype.concat=function(pattern,config){// Because order of search parameters is irrelevant, we can add our own search // parameters to the end of the new pattern. Parse the new pattern by itself // and then join the bits together, but it's much easier to do this on a string level. var defaultConfig={caseInsensitive:$$UMFP.caseInsensitive(),strict:$$UMFP.strictMode(),squash:$$UMFP.defaultSquashPolicy()};return new UrlMatcher(this.sourcePath+pattern+this.sourceSearch,extend(defaultConfig,config),this);};UrlMatcher.prototype.toString=function(){return this.source;};/** * @ngdoc function * @name ui.router.util.type:UrlMatcher#exec * @methodOf ui.router.util.type:UrlMatcher * * @description * Tests the specified path against this matcher, and returns an object containing the captured * parameter values, or null if the path does not match. The returned object contains the values * of any search parameters that are mentioned in the pattern, but their value may be null if * they are not present in `searchParams`. This means that search parameters are always treated * as optional. * * @example *
       * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
       *   x: '1', q: 'hello'
       * });
       * // returns { id: 'bob', q: 'hello', r: null }
       * 
      * * @param {string} path The URL path to match, e.g. `$location.path()`. * @param {Object} searchParams URL search parameters, e.g. `$location.search()`. * @returns {Object} The captured parameter values. */UrlMatcher.prototype.exec=function(path,searchParams){var m=this.regexp.exec(path);if(!m)return null;searchParams=searchParams||{};var paramNames=this.parameters(),nTotal=paramNames.length,nPath=this.segments.length-1,values={},i,j,cfg,paramName;if(nPath!==m.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");function decodePathArray(string){function reverseString(str){return str.split("").reverse().join("");}function unquoteDashes(str){return str.replace(/\\-/g,"-");}var split=reverseString(string).split(/-(?!\\)/);var allReversed=map(split,reverseString);return map(allReversed,unquoteDashes).reverse();}var param,paramVal;for(i=0;i} An array of parameter names. Must be treated as read-only. If the * pattern has no parameters, an empty array is returned. */UrlMatcher.prototype.parameters=function(param){if(!isDefined(param))return this.$$paramNames;return this.params[param]||null;};/** * @ngdoc function * @name ui.router.util.type:UrlMatcher#validates * @methodOf ui.router.util.type:UrlMatcher * * @description * Checks an object hash of parameters to validate their correctness according to the parameter * types of this `UrlMatcher`. * * @param {Object} params The object hash of parameters to validate. * @returns {boolean} Returns `true` if `params` validates, otherwise `false`. */UrlMatcher.prototype.validates=function(params){return this.params.$$validates(params);};/** * @ngdoc function * @name ui.router.util.type:UrlMatcher#format * @methodOf ui.router.util.type:UrlMatcher * * @description * Creates a URL that matches this pattern by substituting the specified values * for the path and search parameters. Null values for path parameters are * treated as empty strings. * * @example *
       * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
       * // returns '/user/bob?q=yes'
       * 
      * * @param {Object} values the values to substitute for the parameters in this pattern. * @returns {string} the formatted URL (path and optionally search part). */UrlMatcher.prototype.format=function(values){values=values||{};var segments=this.segments,params=this.parameters(),paramset=this.params;if(!this.validates(values))return null;var i,search=false,nPath=segments.length-1,nTotal=params.length,result=segments[0];function encodeDashes(str){// Replace dashes with encoded "\-" return encodeURIComponent(str).replace(/-/g,function(c){return'%5C%'+c.charCodeAt(0).toString(16).toUpperCase();});}for(i=0;i * { * decode: function(val) { return parseInt(val, 10); }, * encode: function(val) { return val && val.toString(); }, * equals: function(a, b) { return this.is(a) && a === b; }, * is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; }, * pattern: /\d+/ * } * * * @property {RegExp} pattern The regular expression pattern used to match values of this type when * coming from a substring of a URL. * * @returns {Object} Returns a new `Type` object. */function Type(config){extend(this,config);}/** * @ngdoc function * @name ui.router.util.type:Type#is * @methodOf ui.router.util.type:Type * * @description * Detects whether a value is of a particular type. Accepts a native (decoded) value * and determines whether it matches the current `Type` object. * * @param {*} val The value to check. * @param {string} key Optional. If the type check is happening in the context of a specific * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects. * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`. */Type.prototype.is=function(val,key){return true;};/** * @ngdoc function * @name ui.router.util.type:Type#encode * @methodOf ui.router.util.type:Type * * @description * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it * only needs to be a representation of `val` that has been coerced to a string. * * @param {*} val The value to encode. * @param {string} key The name of the parameter in which `val` is stored. Can be used for * meta-programming of `Type` objects. * @returns {string} Returns a string representation of `val` that can be encoded in a URL. */Type.prototype.encode=function(val,key){return val;};/** * @ngdoc function * @name ui.router.util.type:Type#decode * @methodOf ui.router.util.type:Type * * @description * Converts a parameter value (from URL string or transition param) to a custom/native value. * * @param {string} val The URL parameter value to decode. * @param {string} key The name of the parameter in which `val` is stored. Can be used for * meta-programming of `Type` objects. * @returns {*} Returns a custom representation of the URL parameter value. */Type.prototype.decode=function(val,key){return val;};/** * @ngdoc function * @name ui.router.util.type:Type#equals * @methodOf ui.router.util.type:Type * * @description * Determines whether two decoded values are equivalent. * * @param {*} a A value to compare against. * @param {*} b A value to compare against. * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`. */Type.prototype.equals=function(a,b){return a==b;};Type.prototype.$subPattern=function(){var sub=this.pattern.toString();return sub.substr(1,sub.length-2);};Type.prototype.pattern=/.*/;Type.prototype.toString=function(){return"{Type:"+this.name+"}";};/** Given an encoded string, or a decoded object, returns a decoded object */Type.prototype.$normalize=function(val){return this.is(val)?val:this.decode(val);};/* * Wraps an existing custom Type as an array of Type, depending on 'mode'. * e.g.: * - urlmatcher pattern "/path?{queryParam[]:int}" * - url: "/path?queryParam=1&queryParam=2 * - $stateParams.queryParam will be [1, 2] * if `mode` is "auto", then * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1 * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2] */Type.prototype.$asArray=function(mode,isSearch){if(!mode)return this;if(mode==="auto"&&!isSearch)throw new Error("'auto' array mode is for query parameters only");function ArrayType(type,mode){function bindTo(type,callbackName){return function(){return type[callbackName].apply(type,arguments);};}// Wrap non-array value as array function arrayWrap(val){return isArray(val)?val:isDefined(val)?[val]:[];}// Unwrap array value for "auto" mode. Return undefined for empty array. function arrayUnwrap(val){switch(val.length){case 0:return undefined;case 1:return mode==="auto"?val[0]:val;default:return val;}}function falsey(val){return!val;}// Wraps type (.is/.encode/.decode) functions to operate on each value of an array function arrayHandler(callback,allTruthyMode){return function handleArray(val){if(isArray(val)&&val.length===0)return val;val=arrayWrap(val);var result=map(val,callback);if(allTruthyMode===true)return filter(result,falsey).length===0;return arrayUnwrap(result);};}// Wraps type (.equals) functions to operate on each value of an array function arrayEqualsHandler(callback){return function handleArray(val1,val2){var left=arrayWrap(val1),right=arrayWrap(val2);if(left.length!==right.length)return false;for(var i=0;i * var list = ['John', 'Paul', 'George', 'Ringo']; * * $urlMatcherFactoryProvider.type('listItem', { * encode: function(item) { * // Represent the list item in the URL using its corresponding index * return list.indexOf(item); * }, * decode: function(item) { * // Look up the list item by index * return list[parseInt(item, 10)]; * }, * is: function(item) { * // Ensure the item is valid by checking to see that it appears * // in the list * return list.indexOf(item) > -1; * } * }); * * $stateProvider.state('list', { * url: "/list/{item:listItem}", * controller: function($scope, $stateParams) { * console.log($stateParams.item); * } * }); * * // ... * * // Changes URL to '/list/3', logs "Ringo" to the console * $state.go('list', { item: "Ringo" }); * * * This is a more complex example of a type that relies on dependency injection to * interact with services, and uses the parameter name from the URL to infer how to * handle encoding and decoding parameter values: * *
         * // Defines a custom type that gets a value from a service,
         * // where each service gets different types of values from
         * // a backend API:
         * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
         *
         *   // Matches up services to URL parameter names
         *   var services = {
         *     user: Users,
         *     post: Posts
         *   };
         *
         *   return {
         *     encode: function(object) {
         *       // Represent the object in the URL using its unique ID
         *       return object.id;
         *     },
         *     decode: function(value, key) {
         *       // Look up the object by ID, using the parameter
         *       // name (key) to call the correct service
         *       return services[key].findById(value);
         *     },
         *     is: function(object, key) {
         *       // Check that object is a valid dbObject
         *       return angular.isObject(object) && object.id && services[key];
         *     }
         *     equals: function(a, b) {
         *       // Check the equality of decoded objects by comparing
         *       // their unique IDs
         *       return a.id === b.id;
         *     }
         *   };
         * });
         *
         * // In a config() block, you can then attach URLs with
         * // type-annotated parameters:
         * $stateProvider.state('users', {
         *   url: "/users",
         *   // ...
         * }).state('users.item', {
         *   url: "/{user:dbObject}",
         *   controller: function($scope, $stateParams) {
         *     // $stateParams.user will now be an object returned from
         *     // the Users service
         *   },
         *   // ...
         * });
         * 
      */this.type=function(name,definition,definitionFn){if(!isDefined(definition))return $types[name];if($types.hasOwnProperty(name))throw new Error("A type named '"+name+"' has already been defined.");$types[name]=new Type(extend({name:name},definition));if(definitionFn){typeQueue.push({name:name,def:definitionFn});if(!enqueue)flushTypeQueue();}return this;};// `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s function flushTypeQueue(){while(typeQueue.length){var type=typeQueue.shift();if(type.pattern)throw new Error("You cannot override a type's .pattern at runtime.");angular.extend($types[type.name],injector.invoke(type.def));}}// Register default types. Store them in the prototype of $types. forEach(defaultTypes,function(type,name){$types[name]=new Type(extend({name:name},type));});$types=inherit($types,{});/* No need to document $get, since it returns this */this.$get=['$injector',function($injector){injector=$injector;enqueue=false;flushTypeQueue();forEach(defaultTypes,function(type,name){if(!$types[name])$types[name]=new Type(type);});return this;}];this.Param=function Param(id,type,config,location){var self=this;config=unwrapShorthand(config);type=getType(config,type,location);var arrayMode=getArrayMode();type=arrayMode?type.$asArray(arrayMode,location==="search"):type;if(type.name==="string"&&!arrayMode&&location==="path"&&config.value===undefined)config.value="";// for 0.2.x; in 0.3.0+ do not automatically default to "" var isOptional=config.value!==undefined;var squash=getSquashPolicy(config,isOptional);var replace=getReplace(config,arrayMode,isOptional,squash);function unwrapShorthand(config){var keys=isObject(config)?objectKeys(config):[];var isShorthand=indexOf(keys,"value")===-1&&indexOf(keys,"type")===-1&&indexOf(keys,"squash")===-1&&indexOf(keys,"array")===-1;if(isShorthand)config={value:config};config.$$fn=isInjectable(config.value)?config.value:function(){return config.value;};return config;}function getType(config,urlType,location){if(config.type&&urlType)throw new Error("Param '"+id+"' has two type configurations.");if(urlType)return urlType;if(!config.type)return location==="config"?$types.any:$types.string;if(angular.isString(config.type))return $types[config.type];if(config.type instanceof Type)return config.type;return new Type(config.type);}// array config: param name (param[]) overrides default settings. explicit config overrides param name. function getArrayMode(){var arrayDefaults={array:location==="search"?"auto":false};var arrayParamNomenclature=id.match(/\[\]$/)?{array:true}:{};return extend(arrayDefaults,arrayParamNomenclature,config).array;}/** * returns false, true, or the squash value to indicate the "default parameter url squash policy". */function getSquashPolicy(config,isOptional){var squash=config.squash;if(!isOptional||squash===false)return false;if(!isDefined(squash)||squash==null)return defaultSquashPolicy;if(squash===true||isString(squash))return squash;throw new Error("Invalid squash policy: '"+squash+"'. Valid policies: false, true, or arbitrary string");}function getReplace(config,arrayMode,isOptional,squash){var replace,configuredKeys,defaultPolicy=[{from:"",to:isOptional||arrayMode?undefined:""},{from:null,to:isOptional||arrayMode?undefined:""}];replace=isArray(config.replace)?config.replace:[];if(isString(squash))replace.push({from:squash,to:undefined});configuredKeys=map(replace,function(item){return item.from;});return filter(defaultPolicy,function(item){return indexOf(configuredKeys,item.from)===-1;}).concat(replace);}/** * [Internal] Get the default value of a parameter, which may be an injectable function. */function $$getDefaultValue(){if(!injector)throw new Error("Injectable functions cannot be called at configuration time");var defaultValue=injector.invoke(config.$$fn);if(defaultValue!==null&&defaultValue!==undefined&&!self.type.is(defaultValue))throw new Error("Default value ("+defaultValue+") for parameter '"+self.id+"' is not an instance of Type ("+self.type.name+")");return defaultValue;}/** * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the * default value, which may be the result of an injectable function. */function $value(value){function hasReplaceVal(val){return function(obj){return obj.from===val;};}function $replace(value){var replacement=map(filter(self.replace,hasReplaceVal(value)),function(obj){return obj.to;});return replacement.length?replacement[0]:value;}value=$replace(value);return!isDefined(value)?$$getDefaultValue():self.type.$normalize(value);}function toString(){return"{Param:"+id+" "+type+" squash: '"+squash+"' optional: "+isOptional+"}";}extend(this,{id:id,type:type,location:location,array:arrayMode,squash:squash,replace:replace,isOptional:isOptional,value:$value,dynamic:undefined,config:config,toString:toString});};function ParamSet(params){extend(this,params||{});}ParamSet.prototype={$$new:function $$new(){return inherit(this,extend(new ParamSet(),{$$parent:this}));},$$keys:function $$keys(){var keys=[],chain=[],parent=this,ignore=objectKeys(ParamSet.prototype);while(parent){chain.push(parent);parent=parent.$$parent;}chain.reverse();forEach(chain,function(paramset){forEach(objectKeys(paramset),function(key){if(indexOf(keys,key)===-1&&indexOf(ignore,key)===-1)keys.push(key);});});return keys;},$$values:function $$values(paramValues){var values={},self=this;forEach(self.$$keys(),function(key){values[key]=self[key].value(paramValues&¶mValues[key]);});return values;},$$equals:function $$equals(paramValues1,paramValues2){var equal=true,self=this;forEach(self.$$keys(),function(key){var left=paramValues1&¶mValues1[key],right=paramValues2&¶mValues2[key];if(!self[key].type.equals(left,right))equal=false;});return equal;},$$validates:function $$validate(paramValues){var keys=this.$$keys(),i,param,rawVal,normalized,encoded;for(i=0;i * var app = angular.module('app', ['ui.router.router']); * * app.config(function ($urlRouterProvider) { * // Here's an example of how you might allow case insensitive urls * $urlRouterProvider.rule(function ($injector, $location) { * var path = $location.path(), * normalized = path.toLowerCase(); * * if (path !== normalized) { * return normalized; * } * }); * }); * * * @param {function} rule Handler function that takes `$injector` and `$location` * services as arguments. You can use them to return a valid path as a string. * * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance */this.rule=function(rule){if(!isFunction(rule))throw new Error("'rule' must be a function");rules.push(rule);return this;};/** * @ngdoc object * @name ui.router.router.$urlRouterProvider#otherwise * @methodOf ui.router.router.$urlRouterProvider * * @description * Defines a path that is used when an invalid route is requested. * * @example *
         * var app = angular.module('app', ['ui.router.router']);
         *
         * app.config(function ($urlRouterProvider) {
         *   // if the path doesn't match any of the urls you configured
         *   // otherwise will take care of routing the user to the
         *   // specified url
         *   $urlRouterProvider.otherwise('/index');
         *
         *   // Example of using function rule as param
         *   $urlRouterProvider.otherwise(function ($injector, $location) {
         *     return '/a/valid/url';
         *   });
         * });
         * 
      * * @param {string|function} rule The url path you want to redirect to or a function * rule that returns the url path. The function version is passed two params: * `$injector` and `$location` services, and must return a url string. * * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance */this.otherwise=function(rule){if(isString(rule)){var redirect=rule;rule=function rule(){return redirect;};}else if(!isFunction(rule))throw new Error("'rule' must be a function");otherwise=rule;return this;};function handleIfMatch($injector,handler,match){if(!match)return false;var result=$injector.invoke(handler,handler,{$match:match});return isDefined(result)?result:true;}/** * @ngdoc function * @name ui.router.router.$urlRouterProvider#when * @methodOf ui.router.router.$urlRouterProvider * * @description * Registers a handler for a given url matching. * * If the handler is a string, it is * treated as a redirect, and is interpolated according to the syntax of match * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise). * * If the handler is a function, it is injectable. It gets invoked if `$location` * matches. You have the option of inject the match object as `$match`. * * The handler can return * * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` * will continue trying to find another one that matches. * - **string** which is treated as a redirect and passed to `$location.url()` * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. * * @example *
         * var app = angular.module('app', ['ui.router.router']);
         *
         * app.config(function ($urlRouterProvider) {
         *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
         *     if ($state.$current.navigable !== state ||
         *         !equalForKeys($match, $stateParams) {
         *      $state.transitionTo(state, $match, false);
         *     }
         *   });
         * });
         * 
      * * @param {string|object} what The incoming path that you want to redirect. * @param {string|function} handler The path you want to redirect your user to. */this.when=function(what,handler){var redirect,handlerIsString=isString(handler);if(isString(what))what=$urlMatcherFactory.compile(what);if(!handlerIsString&&!isFunction(handler)&&!isArray(handler))throw new Error("invalid 'handler' in when()");var strategies={matcher:function matcher(what,handler){if(handlerIsString){redirect=$urlMatcherFactory.compile(handler);handler=['$match',function($match){return redirect.format($match);}];}return extend(function($injector,$location){return handleIfMatch($injector,handler,what.exec($location.path(),$location.search()));},{prefix:isString(what.prefix)?what.prefix:''});},regex:function regex(what,handler){if(what.global||what.sticky)throw new Error("when() RegExp must not be global or sticky");if(handlerIsString){redirect=handler;handler=['$match',function($match){return interpolate(redirect,$match);}];}return extend(function($injector,$location){return handleIfMatch($injector,handler,what.exec($location.path()));},{prefix:regExpPrefix(what)});}};var check={matcher:$urlMatcherFactory.isMatcher(what),regex:what instanceof RegExp};for(var n in check){if(check[n])return this.rule(strategies[n](what,handler));}throw new Error("invalid 'what' in when()");};/** * @ngdoc function * @name ui.router.router.$urlRouterProvider#deferIntercept * @methodOf ui.router.router.$urlRouterProvider * * @description * Disables (or enables) deferring location change interception. * * If you wish to customize the behavior of syncing the URL (for example, if you wish to * defer a transition but maintain the current URL), call this method at configuration time. * Then, at run time, call `$urlRouter.listen()` after you have configured your own * `$locationChangeSuccess` event handler. * * @example *
         * var app = angular.module('app', ['ui.router.router']);
         *
         * app.config(function ($urlRouterProvider) {
         *
         *   // Prevent $urlRouter from automatically intercepting URL changes;
         *   // this allows you to configure custom behavior in between
         *   // location changes and route synchronization:
         *   $urlRouterProvider.deferIntercept();
         *
         * }).run(function ($rootScope, $urlRouter, UserService) {
         *
         *   $rootScope.$on('$locationChangeSuccess', function(e) {
         *     // UserService is an example service for managing user state
         *     if (UserService.isLoggedIn()) return;
         *
         *     // Prevent $urlRouter's default handler from firing
         *     e.preventDefault();
         *
         *     UserService.handleLogin().then(function() {
         *       // Once the user has logged in, sync the current URL
         *       // to the router:
         *       $urlRouter.sync();
         *     });
         *   });
         *
         *   // Configures $urlRouter's listener *after* your custom listener
         *   $urlRouter.listen();
         * });
         * 
      * * @param {boolean} defer Indicates whether to defer location change interception. Passing no parameter is equivalent to `true`. */this.deferIntercept=function(defer){if(defer===undefined)defer=true;interceptDeferred=defer;};/** * @ngdoc object * @name ui.router.router.$urlRouter * * @requires $location * @requires $rootScope * @requires $injector * @requires $browser * * @description * */this.$get=$get;$get.$inject=['$location','$rootScope','$injector','$browser','$sniffer'];function $get($location,$rootScope,$injector,$browser,$sniffer){var baseHref=$browser.baseHref(),location=$location.url(),lastPushedUrl;function appendBasePath(url,isHtml5,absolute){if(baseHref==='/')return url;if(isHtml5)return baseHref.slice(0,-1)+url;if(absolute)return baseHref.slice(1)+url;return url;}// TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree function update(evt){if(evt&&evt.defaultPrevented)return;var ignoreUpdate=lastPushedUrl&&$location.url()===lastPushedUrl;lastPushedUrl=undefined;// TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573 //if (ignoreUpdate) return true; function check(rule){var handled=rule($injector,$location);if(!handled)return false;if(isString(handled))$location.replace().url(handled);return true;}var n=rules.length,i;for(i=0;i * angular.module('app', ['ui.router']) * .run(function($rootScope, $urlRouter) { * $rootScope.$on('$locationChangeSuccess', function(evt) { * // Halt state change from even starting * evt.preventDefault(); * // Perform custom logic * var meetsRequirement = ... * // Continue with the update and state transition if logic allows * if (meetsRequirement) $urlRouter.sync(); * }); * }); * */sync:function sync(){update();},listen:function listen(){return _listen();},update:function update(read){if(read){location=$location.url();return;}if($location.url()===location)return;$location.url(location);$location.replace();},push:function push(urlMatcher,params,options){var url=urlMatcher.format(params||{});// Handle the special hash param, if needed if(url!==null&¶ms&¶ms['#']){url+='#'+params['#'];}$location.url(url);lastPushedUrl=options&&options.$$avoidResync?$location.url():undefined;if(options&&options.replace)$location.replace();},/** * @ngdoc function * @name ui.router.router.$urlRouter#href * @methodOf ui.router.router.$urlRouter * * @description * A URL generation method that returns the compiled URL for a given * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters. * * @example *
             * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
             *   person: "bob"
             * });
             * // $bob == "/about/bob";
             * 
      * * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate. * @param {object=} params An object of parameter values to fill the matcher's required parameters. * @param {object=} options Options object. The options are: * * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". * * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` */href:function href(urlMatcher,params,options){if(!urlMatcher.validates(params))return null;var isHtml5=$locationProvider.html5Mode();if(angular.isObject(isHtml5)){isHtml5=isHtml5.enabled;}isHtml5=isHtml5&&$sniffer.history;var url=urlMatcher.format(params);options=options||{};if(!isHtml5&&url!==null){url="#"+$locationProvider.hashPrefix()+url;}// Handle special hash param, if needed if(url!==null&¶ms&¶ms['#']){url+='#'+params['#'];}url=appendBasePath(url,isHtml5,options.absolute);if(!options.absolute||!url){return url;}var slash=!isHtml5&&url?'/':'',port=$location.port();port=port===80||port===443?'':':'+port;return[$location.protocol(),'://',$location.host(),port,slash,url].join('');}};}}angular.module('ui.router.router').provider('$urlRouter',$UrlRouterProvider);/** * @ngdoc object * @name ui.router.state.$stateProvider * * @requires ui.router.router.$urlRouterProvider * @requires ui.router.util.$urlMatcherFactoryProvider * * @description * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely * on state. * * A state corresponds to a "place" in the application in terms of the overall UI and * navigation. A state describes (via the controller / template / view properties) what * the UI looks like and does at that place. * * States often have things in common, and the primary way of factoring out these * commonalities in this model is via the state hierarchy, i.e. parent/child states aka * nested states. * * The `$stateProvider` provides interfaces to declare these states for your app. */$StateProvider.$inject=['$urlRouterProvider','$urlMatcherFactoryProvider'];function $StateProvider($urlRouterProvider,$urlMatcherFactory){$get.$inject=['$rootScope','$q','$view','$injector','$resolve','$stateParams','$urlRouter','$location','$urlMatcherFactory'];var root,states={},$state,queue={},abstractKey='abstract';// Builds state properties from definition passed to registerState() var stateBuilder={// Derive parent state from a hierarchical name only if 'parent' is not explicitly defined. // state.children = []; // if (parent) parent.children.push(state); parent:function parent(state){if(isDefined(state.parent)&&state.parent)return findState(state.parent);// regex matches any valid composite state name // would match "contact.list" but not "contacts" var compositeName=/^(.+)\.[^.]+$/.exec(state.name);return compositeName?findState(compositeName[1]):root;},// inherit 'data' from parent and override by own values (if any) data:function data(state){if(state.parent&&state.parent.data){state.data=state.self.data=inherit(state.parent.data,state.data);}return state.data;},// Build a URLMatcher if necessary, either via a relative or absolute URL url:function url(state){var url=state.url,config={params:state.params||{}};if(isString(url)){if(url.charAt(0)=='^')return $urlMatcherFactory.compile(url.substring(1),config);return(state.parent.navigable||root).url.concat(url,config);}if(!url||$urlMatcherFactory.isMatcher(url))return url;throw new Error("Invalid url '"+url+"' in state '"+state+"'");},// Keep track of the closest ancestor state that has a URL (i.e. is navigable) navigable:function navigable(state){return state.url?state:state.parent?state.parent.navigable:null;},// Own parameters for this state. state.url.params is already built at this point. Create and add non-url params ownParams:function ownParams(state){var params=state.url&&state.url.params||new $$UMFP.ParamSet();forEach(state.params||{},function(config,id){if(!params[id])params[id]=new $$UMFP.Param(id,null,config,"config");});return params;},// Derive parameters for this state and ensure they're a super-set of parent's parameters params:function params(state){var ownParams=pick(state.ownParams,state.ownParams.$$keys());return state.parent&&state.parent.params?extend(state.parent.params.$$new(),ownParams):new $$UMFP.ParamSet();},// If there is no explicit multi-view configuration, make one up so we don't have // to handle both cases in the view directive later. Note that having an explicit // 'views' property will mean the default unnamed view properties are ignored. This // is also a good time to resolve view names to absolute names, so everything is a // straight lookup at link time. views:function views(state){var views={};forEach(isDefined(state.views)?state.views:{'':state},function(view,name){if(name.indexOf('@')<0)name+='@'+state.parent.name;views[name]=view;});return views;},// Keep a full path from the root down to this state as this is needed for state activation. path:function path(state){return state.parent?state.parent.path.concat(state):[];// exclude root from path },// Speed up $state.contains() as it's used a lot includes:function includes(state){var includes=state.parent?extend({},state.parent.includes):{};includes[state.name]=true;return includes;},$delegates:{}};function isRelative(stateName){return stateName.indexOf(".")===0||stateName.indexOf("^")===0;}function findState(stateOrName,base){if(!stateOrName)return undefined;var isStr=isString(stateOrName),name=isStr?stateOrName:stateOrName.name,path=isRelative(name);if(path){if(!base)throw new Error("No reference point given for path '"+name+"'");base=findState(base);var rel=name.split("."),i=0,pathLength=rel.length,current=base;for(;i=0)throw new Error("State must have a valid name");if(states.hasOwnProperty(name))throw new Error("State '"+name+"' is already defined");// Get parent name var parentName=name.indexOf('.')!==-1?name.substring(0,name.lastIndexOf('.')):isString(state.parent)?state.parent:isObject(state.parent)&&isString(state.parent.name)?state.parent.name:'';// If parent is not registered yet, add state to queue and register later if(parentName&&!states[parentName]){return queueState(parentName,state.self);}for(var key in stateBuilder){if(isFunction(stateBuilder[key]))state[key]=stateBuilder[key](state,stateBuilder.$delegates[key]);}states[name]=state;// Register the state in the global state list and with $urlRouter if necessary. if(!state[abstractKey]&&state.url){$urlRouterProvider.when(state.url,['$match','$stateParams',function($match,$stateParams){if($state.$current.navigable!=state||!equalForKeys($match,$stateParams)){$state.transitionTo(state,$match,{inherit:true,location:false});}}]);}// Register any queued children flushQueuedChildren(name);return state;}// Checks text to see if it looks like a glob. function isGlob(text){return text.indexOf('*')>-1;}// Returns true if glob matches current $state name. function doesStateMatchGlob(glob){var globSegments=glob.split('.'),segments=$state.$current.name.split('.');//match single stars for(var i=0,l=globSegments.length;i * // Override the internal 'views' builder with a function that takes the state * // definition, and a reference to the internal function being overridden: * $stateProvider.decorator('views', function (state, parent) { * var result = {}, * views = parent(state); * * angular.forEach(views, function (config, name) { * var autoName = (state.name + '.' + name).replace('.', '/'); * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html'; * result[name] = config; * }); * return result; * }); * * $stateProvider.state('home', { * views: { * 'contact.list': { controller: 'ListController' }, * 'contact.item': { controller: 'ItemController' } * } * }); * * // ... * * $state.go('home'); * // Auto-populates list and item views with /partials/home/contact/list.html, * // and /partials/home/contact/item.html, respectively. * * * @param {string} name The name of the builder function to decorate. * @param {object} func A function that is responsible for decorating the original * builder function. The function receives two parameters: * * - `{object}` - state - The state config object. * - `{object}` - super - The original builder function. * * @return {object} $stateProvider - $stateProvider instance */this.decorator=decorator;function decorator(name,func){/*jshint validthis: true */if(isString(name)&&!isDefined(func)){return stateBuilder[name];}if(!isFunction(func)||!isString(name)){return this;}if(stateBuilder[name]&&!stateBuilder.$delegates[name]){stateBuilder.$delegates[name]=stateBuilder[name];}stateBuilder[name]=func;return this;}/** * @ngdoc function * @name ui.router.state.$stateProvider#state * @methodOf ui.router.state.$stateProvider * * @description * Registers a state configuration under a given state name. The stateConfig object * has the following acceptable properties. * * @param {string} name A unique state name, e.g. "home", "about", "contacts". * To create a parent/child state use a dot, e.g. "about.sales", "home.newest". * @param {object} stateConfig State configuration object. * @param {string|function=} stateConfig.template * * html template as a string or a function that returns * an html template as a string which should be used by the uiView directives. This property * takes precedence over templateUrl. * * If `template` is a function, it will be called with the following parameters: * * - {array.<object>} - state parameters extracted from the current $location.path() by * applying the current state * *
      template:
         *   "

      inline template definition

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

      generated template

      "; }
      *
      * * @param {string|function=} stateConfig.templateUrl * * * path or function that returns a path to an html * template that should be used by uiView. * * If `templateUrl` is a function, it will be called with the following parameters: * * - {array.<object>} - state parameters extracted from the current $location.path() by * applying the current state * *
      templateUrl: "home.html"
      *
      templateUrl: function(params) {
         *     return myTemplates[params.pageId]; }
      * * @param {function=} stateConfig.templateProvider * * Provider function that returns HTML content string. *
       templateProvider:
         *       function(MyTemplateService, params) {
         *         return MyTemplateService.getTemplate(params.pageId);
         *       }
      * * @param {string|function=} stateConfig.controller * * * Controller fn that should be associated with newly * related scope or the name of a registered controller if passed as a string. * Optionally, the ControllerAs may be declared here. *
      controller: "MyRegisteredController"
      *
      controller:
         *     "MyRegisteredController as fooCtrl"}
      *
      controller: function($scope, MyService) {
         *     $scope.data = MyService.getData(); }
      * * @param {function=} stateConfig.controllerProvider * * * Injectable provider function that returns the actual controller or string. *
      controllerProvider:
         *   function(MyResolveData) {
         *     if (MyResolveData.foo)
         *       return "FooCtrl"
         *     else if (MyResolveData.bar)
         *       return "BarCtrl";
         *     else return function($scope) {
         *       $scope.baz = "Qux";
         *     }
         *   }
      * * @param {string=} stateConfig.controllerAs * * * A controller alias name. If present the controller will be * published to scope under the controllerAs name. *
      controllerAs: "myCtrl"
      * * @param {string|object=} stateConfig.parent * * Optionally specifies the parent state of this state. * *
      parent: 'parentState'
      *
      parent: parentState // JS variable
      * * @param {object=} stateConfig.resolve * * * An optional map<string, function> of dependencies which * should be injected into the controller. If any of these dependencies are promises, * the router will wait for them all to be resolved before the controller is instantiated. * If all the promises are resolved successfully, the $stateChangeSuccess event is fired * and the values of the resolved promises are injected into any controllers that reference them. * If any of the promises are rejected the $stateChangeError event is fired. * * The map object is: * * - key - {string}: name of dependency to be injected into controller * - factory - {string|function}: If string then it is alias for service. Otherwise if function, * it is injected and return value it treated as dependency. If result is a promise, it is * resolved before its value is injected into controller. * *
      resolve: {
         *     myResolve1:
         *       function($http, $stateParams) {
         *         return $http.get("/api/foos/"+stateParams.fooID);
         *       }
         *     }
      * * @param {string=} stateConfig.url * * * A url fragment with optional parameters. When a state is navigated or * transitioned to, the `$stateParams` service will be populated with any * parameters that were passed. * * (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for * more details on acceptable patterns ) * * examples: *
      url: "/home"
         * url: "/users/:userid"
         * url: "/books/{bookid:[a-zA-Z_-]}"
         * url: "/books/{categoryid:int}"
         * url: "/books/{publishername:string}/{categoryid:int}"
         * url: "/messages?before&after"
         * url: "/messages?{before:date}&{after:date}"
         * url: "/messages/:mailboxid?{before:date}&{after:date}"
         * 
      * * @param {object=} stateConfig.views * * an optional map<string, object> which defined multiple views, or targets views * manually/explicitly. * * Examples: * * Targets three named `ui-view`s in the parent state's template *
      views: {
         *     header: {
         *       controller: "headerCtrl",
         *       templateUrl: "header.html"
         *     }, body: {
         *       controller: "bodyCtrl",
         *       templateUrl: "body.html"
         *     }, footer: {
         *       controller: "footCtrl",
         *       templateUrl: "footer.html"
         *     }
         *   }
      * * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template. *
      views: {
         *     'header@top': {
         *       controller: "msgHeaderCtrl",
         *       templateUrl: "msgHeader.html"
         *     }, 'body': {
         *       controller: "messagesCtrl",
         *       templateUrl: "messages.html"
         *     }
         *   }
      * * @param {boolean=} [stateConfig.abstract=false] * * An abstract state will never be directly activated, * but can provide inherited properties to its common children states. *
      abstract: true
      * * @param {function=} stateConfig.onEnter * * * Callback function for when a state is entered. Good way * to trigger an action or dispatch an event, such as opening a dialog. * If minifying your scripts, make sure to explicitly annotate this function, * because it won't be automatically annotated by your build tools. * *
      onEnter: function(MyService, $stateParams) {
         *     MyService.foo($stateParams.myParam);
         * }
      * * @param {function=} stateConfig.onExit * * * Callback function for when a state is exited. Good way to * trigger an action or dispatch an event, such as opening a dialog. * If minifying your scripts, make sure to explicitly annotate this function, * because it won't be automatically annotated by your build tools. * *
      onExit: function(MyService, $stateParams) {
         *     MyService.cleanup($stateParams.myParam);
         * }
      * * @param {boolean=} [stateConfig.reloadOnSearch=true] * * * If `false`, will not retrigger the same state * just because a search/query parameter has changed (via $location.search() or $location.hash()). * Useful for when you'd like to modify $location.search() without triggering a reload. *
      reloadOnSearch: false
      * * @param {object=} stateConfig.data * * * Arbitrary data object, useful for custom configuration. The parent state's `data` is * prototypally inherited. In other words, adding a data property to a state adds it to * the entire subtree via prototypal inheritance. * *
      data: {
         *     requiredRole: 'foo'
         * } 
      * * @param {object=} stateConfig.params * * * A map which optionally configures parameters declared in the `url`, or * defines additional non-url parameters. For each parameter being * configured, add a configuration object keyed to the name of the parameter. * * Each parameter configuration object may contain the following properties: * * - ** value ** - {object|function=}: specifies the default value for this * parameter. This implicitly sets this parameter as optional. * * When UI-Router routes to a state and no value is * specified for this parameter in the URL or transition, the * default value will be used instead. If `value` is a function, * it will be injected and invoked, and the return value used. * * *Note*: `undefined` is treated as "no default value" while `null` * is treated as "the default value is `null`". * * *Shorthand*: If you only need to configure the default value of the * parameter, you may use a shorthand syntax. In the **`params`** * map, instead mapping the param name to a full parameter configuration * object, simply set map it to the default parameter value, e.g.: * *
      // define a parameter's default value
         * params: {
         *     param1: { value: "defaultValue" }
         * }
         * // shorthand default values
         * params: {
         *     param1: "defaultValue",
         *     param2: "param2Default"
         * }
      * * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be * treated as an array of values. If you specified a Type, the value will be * treated as an array of the specified Type. Note: query parameter values * default to a special `"auto"` mode. * * For query parameters in `"auto"` mode, if multiple values for a single parameter * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single * value (e.g.: `{ foo: '1' }`). * *
      params: {
         *     param1: { array: true }
         * }
      * * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when * the current parameter value is the same as the default value. If `squash` is not set, it uses the * configured default squash policy. * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`}) * * There are three squash settings: * * - false: The parameter's default value is not squashed. It is encoded and included in the URL * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed * by slashes in the state's `url` declaration, then one of those slashes are omitted. * This can allow for cleaner looking URLs. * - `""`: The parameter's default value is replaced with an arbitrary placeholder of your choice. * *
      params: {
         *     param1: {
         *       value: "defaultId",
         *       squash: true
         * } }
         * // squash "defaultValue" to "~"
         * params: {
         *     param1: {
         *       value: "defaultValue",
         *       squash: "~"
         * } }
         * 
      * * * @example *
         * // Some state name examples
         *
         * // stateName can be a single top-level name (must be unique).
         * $stateProvider.state("home", {});
         *
         * // Or it can be a nested state name. This state is a child of the
         * // above "home" state.
         * $stateProvider.state("home.newest", {});
         *
         * // Nest states as deeply as needed.
         * $stateProvider.state("home.newest.abc.xyz.inception", {});
         *
         * // state() returns $stateProvider, so you can chain state declarations.
         * $stateProvider
         *   .state("home", {})
         *   .state("about", {})
         *   .state("contacts", {});
         * 
      * */this.state=state;function state(name,definition){/*jshint validthis: true */if(isObject(name))definition=name;else definition.name=name;registerState(definition);return this;}/** * @ngdoc object * @name ui.router.state.$state * * @requires $rootScope * @requires $q * @requires ui.router.state.$view * @requires $injector * @requires ui.router.util.$resolve * @requires ui.router.state.$stateParams * @requires ui.router.router.$urlRouter * * @property {object} params A param object, e.g. {sectionId: section.id)}, that * you'd like to test against the current active state. * @property {object} current A reference to the state's config object. However * you passed it in. Useful for accessing custom data. * @property {object} transition Currently pending transition. A promise that'll * resolve or reject. * * @description * `$state` service is responsible for representing states as well as transitioning * between them. It also provides interfaces to ask for current state or even states * you're coming from. */this.$get=$get;$get.$inject=['$rootScope','$q','$view','$injector','$resolve','$stateParams','$urlRouter','$location','$urlMatcherFactory'];function $get($rootScope,$q,$view,$injector,$resolve,$stateParams,$urlRouter,$location,$urlMatcherFactory){var TransitionSuperseded=$q.reject(new Error('transition superseded'));var TransitionPrevented=$q.reject(new Error('transition prevented'));var TransitionAborted=$q.reject(new Error('transition aborted'));var TransitionFailed=$q.reject(new Error('transition failed'));// Handles the case where a state which is the target of a transition is not found, and the user // can optionally retry or defer the transition function handleRedirect(redirect,state,params,options){/** * @ngdoc event * @name ui.router.state.$state#$stateNotFound * @eventOf ui.router.state.$state * @eventType broadcast on root scope * @description * Fired when a requested state **cannot be found** using the provided state name during transition. * The event is broadcast allowing any handlers a single chance to deal with the error (usually by * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler, * you can see its three properties in the example. You can use `event.preventDefault()` to abort the * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value. * * @param {Object} event Event object. * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties. * @param {State} fromState Current state object. * @param {Object} fromParams Current state params. * * @example * *
             * // somewhere, assume lazy.state has not been defined
             * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
             *
             * // somewhere else
             * $scope.$on('$stateNotFound',
             * function(event, unfoundState, fromState, fromParams){
             *     console.log(unfoundState.to); // "lazy.state"
             *     console.log(unfoundState.toParams); // {a:1, b:2}
             *     console.log(unfoundState.options); // {inherit:false} + default options
             * })
             * 
      */var evt=$rootScope.$broadcast('$stateNotFound',redirect,state,params);if(evt.defaultPrevented){$urlRouter.update();return TransitionAborted;}if(!evt.retry){return null;}// Allow the handler to return a promise to defer state lookup retry if(options.$retry){$urlRouter.update();return TransitionFailed;}var retryTransition=$state.transition=$q.when(evt.retry);retryTransition.then(function(){if(retryTransition!==$state.transition)return TransitionSuperseded;redirect.options.$retry=true;return $state.transitionTo(redirect.to,redirect.toParams,redirect.options);},function(){return TransitionAborted;});$urlRouter.update();return retryTransition;}root.locals={resolve:null,globals:{$stateParams:{}}};$state={params:{},current:root.self,$current:root,transition:null};/** * @ngdoc function * @name ui.router.state.$state#reload * @methodOf ui.router.state.$state * * @description * A method that force reloads the current state. All resolves are re-resolved, * controllers reinstantiated, and events re-fired. * * @example *
           * var app angular.module('app', ['ui.router']);
           *
           * app.controller('ctrl', function ($scope, $state) {
           *   $scope.reload = function(){
           *     $state.reload();
           *   }
           * });
           * 
      * * `reload()` is just an alias for: *
           * $state.transitionTo($state.current, $stateParams, { 
           *   reload: true, inherit: false, notify: true
           * });
           * 
      * * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved. * @example *
           * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
           * //and current state is 'contacts.detail.item'
           * var app angular.module('app', ['ui.router']);
           *
           * app.controller('ctrl', function ($scope, $state) {
           *   $scope.reload = function(){
           *     //will reload 'contact.detail' and 'contact.detail.item' states
           *     $state.reload('contact.detail');
           *   }
           * });
           * 
      * * `reload()` is just an alias for: *
           * $state.transitionTo($state.current, $stateParams, { 
           *   reload: true, inherit: false, notify: true
           * });
           * 
      * @returns {promise} A promise representing the state of the new transition. See * {@link ui.router.state.$state#methods_go $state.go}. */$state.reload=function reload(state){return $state.transitionTo($state.current,$stateParams,{reload:state||true,inherit:false,notify:true});};/** * @ngdoc function * @name ui.router.state.$state#go * @methodOf ui.router.state.$state * * @description * Convenience method for transitioning to a new state. `$state.go` calls * `$state.transitionTo` internally but automatically sets options to * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. * This allows you to easily use an absolute or relative to path and specify * only the parameters you'd like to update (while letting unspecified parameters * inherit from the currently active ancestor states). * * @example *
           * var app = angular.module('app', ['ui.router']);
           *
           * app.controller('ctrl', function ($scope, $state) {
           *   $scope.changeState = function () {
           *     $state.go('contact.detail');
           *   };
           * });
           * 
      * * * @param {string} to Absolute state name or relative state path. Some examples: * * - `$state.go('contact.detail')` - will go to the `contact.detail` state * - `$state.go('^')` - will go to a parent state * - `$state.go('^.sibling')` - will go to a sibling state * - `$state.go('.child.grandchild')` - will go to grandchild state * * @param {object=} params A map of the parameters that will be sent to the state, * will populate $stateParams. Any parameters that are not specified will be inherited from currently * defined parameters. Only parameters specified in the state definition can be overridden, new * parameters will be ignored. This allows, for example, going to a sibling state that shares parameters * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e. * transitioning to a sibling will get you the parameters for all parents, transitioning to a child * will get you all current parameters, etc. * @param {object=} options Options object. The options are: * * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` * will not. If string, must be `"replace"`, which will update url and also replace last history record. * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), * defines which state to be relative from. * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. * - **`reload`** (v0.2.5) - {boolean=false|string|object}, If `true` will force transition even if no state or params * have changed. It will reload the resolves and views of the current state and parent states. * If `reload` is a string (or state object), the state object is fetched (by name, or object reference); and \ * the transition reloads the resolves and views for that matched state, and all its children states. * * @returns {promise} A promise representing the state of the new transition. * * Possible success values: * * - $state.current * *
      Possible rejection values: * * - 'transition superseded' - when a newer transition has been started after this one * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or * when a `$stateNotFound` `event.retry` promise errors. * - 'transition failed' - when a state has been unsuccessfully found after 2 tries. * - *resolve error* - when an error has occurred with a `resolve` * */$state.go=function go(to,params,options){return $state.transitionTo(to,params,extend({inherit:true,relative:$state.$current},options));};/** * @ngdoc function * @name ui.router.state.$state#transitionTo * @methodOf ui.router.state.$state * * @description * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go} * uses `transitionTo` internally. `$state.go` is recommended in most situations. * * @example *
           * var app = angular.module('app', ['ui.router']);
           *
           * app.controller('ctrl', function ($scope, $state) {
           *   $scope.changeState = function () {
           *     $state.transitionTo('contact.detail');
           *   };
           * });
           * 
      * * @param {string} to State name. * @param {object=} toParams A map of the parameters that will be sent to the state, * will populate $stateParams. * @param {object=} options Options object. The options are: * * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` * will not. If string, must be `"replace"`, which will update url and also replace last history record. * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url. * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), * defines which state to be relative from. * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd * use this when you want to force a reload when *everything* is the same, including search params. * if String, then will reload the state with the name given in reload, and any children. * if Object, then a stateObj is expected, will reload the state found in stateObj, and any children. * * @returns {promise} A promise representing the state of the new transition. See * {@link ui.router.state.$state#methods_go $state.go}. */$state.transitionTo=function transitionTo(to,toParams,options){toParams=toParams||{};options=extend({location:true,inherit:false,relative:null,notify:true,reload:false,$retry:false},options||{});var from=$state.$current,fromParams=$state.params,fromPath=from.path;var evt,toState=findState(to,options.relative);// Store the hash param for later (since it will be stripped out by various methods) var hash=toParams['#'];if(!isDefined(toState)){var redirect={to:to,toParams:toParams,options:options};var redirectResult=handleRedirect(redirect,from.self,fromParams,options);if(redirectResult){return redirectResult;}// Always retry once if the $stateNotFound was not prevented // (handles either redirect changed or state lazy-definition) to=redirect.to;toParams=redirect.toParams;options=redirect.options;toState=findState(to,options.relative);if(!isDefined(toState)){if(!options.relative)throw new Error("No such state '"+to+"'");throw new Error("Could not resolve '"+to+"' from state '"+options.relative+"'");}}if(toState[abstractKey])throw new Error("Cannot transition to abstract state '"+to+"'");if(options.inherit)toParams=inheritParams($stateParams,toParams||{},$state.$current,toState);if(!toState.params.$$validates(toParams))return TransitionFailed;toParams=toState.params.$$values(toParams);to=toState;var toPath=to.path;// Starting from the root of the path, keep all levels that haven't changed var keep=0,state=toPath[keep],locals=root.locals,toLocals=[];if(!options.reload){while(state&&state===fromPath[keep]&&state.ownParams.$$equals(toParams,fromParams)){locals=toLocals[keep]=state.locals;keep++;state=toPath[keep];}}else if(isString(options.reload)||isObject(options.reload)){if(isObject(options.reload)&&!options.reload.name){throw new Error('Invalid reload state object');}var reloadState=options.reload===true?fromPath[0]:findState(options.reload);if(options.reload&&!reloadState){throw new Error("No such reload state '"+(isString(options.reload)?options.reload:options.reload.name)+"'");}while(state&&state===fromPath[keep]&&state!==reloadState){locals=toLocals[keep]=state.locals;keep++;state=toPath[keep];}}// If we're going to the same state and all locals are kept, we've got nothing to do. // But clear 'transition', as we still want to cancel any other pending transitions. // TODO: We may not want to bump 'transition' if we're called from a location change // that we've initiated ourselves, because we might accidentally abort a legitimate // transition initiated from code? if(shouldSkipReload(to,toParams,from,fromParams,locals,options)){if(hash)toParams['#']=hash;$state.params=toParams;copy($state.params,$stateParams);copy(filterByKeys(to.params.$$keys(),$stateParams),to.locals.globals.$stateParams);if(options.location&&to.navigable&&to.navigable.url){$urlRouter.push(to.navigable.url,toParams,{$$avoidResync:true,replace:options.location==='replace'});$urlRouter.update(true);}$state.transition=null;return $q.when($state.current);}// Filter parameters before we pass them to event handlers etc. toParams=filterByKeys(to.params.$$keys(),toParams||{});// Re-add the saved hash before we start returning things or broadcasting $stateChangeStart if(hash)toParams['#']=hash;// Broadcast start event and cancel the transition if requested if(options.notify){/** * @ngdoc event * @name ui.router.state.$state#$stateChangeStart * @eventOf ui.router.state.$state * @eventType broadcast on root scope * @description * Fired when the state transition **begins**. You can use `event.preventDefault()` * to prevent the transition from happening and then the transition promise will be * rejected with a `'transition prevented'` value. * * @param {Object} event Event object. * @param {State} toState The state being transitioned to. * @param {Object} toParams The params supplied to the `toState`. * @param {State} fromState The current state, pre-transition. * @param {Object} fromParams The params supplied to the `fromState`. * * @example * *
               * $rootScope.$on('$stateChangeStart',
               * function(event, toState, toParams, fromState, fromParams){
               *     event.preventDefault();
               *     // transitionTo() promise will be rejected with
               *     // a 'transition prevented' error
               * })
               * 
      */if($rootScope.$broadcast('$stateChangeStart',to.self,toParams,from.self,fromParams,options).defaultPrevented){$rootScope.$broadcast('$stateChangeCancel',to.self,toParams,from.self,fromParams);//Don't update and resync url if there's been a new transition started. see issue #2238, #600 if($state.transition==null)$urlRouter.update();return TransitionPrevented;}}// Resolve locals for the remaining states, but don't update any global state just // yet -- if anything fails to resolve the current state needs to remain untouched. // We also set up an inheritance chain for the locals here. This allows the view directive // to quickly look up the correct definition for each view in the current state. Even // though we create the locals object itself outside resolveState(), it is initially // empty and gets filled asynchronously. We need to keep track of the promise for the // (fully resolved) current locals, and pass this down the chain. var resolved=$q.when(locals);for(var l=keep;l=keep;l--){exiting=fromPath[l];if(exiting.self.onExit){$injector.invoke(exiting.self.onExit,exiting.self,exiting.locals.globals);}exiting.locals=null;}// Enter 'to' states not kept for(l=keep;l * $state.$current.name = 'contacts.details.item'; * * // absolute name * $state.is('contact.details.item'); // returns true * $state.is(contactDetailItemStateObject); // returns true * * // relative name (. and ^), typically from a template * // E.g. from the 'contacts.details' template *
      Item
      * * * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check. * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like * to test against the current active state. * @param {object=} options An options object. The options are: * * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will * test relative to `options.relative` state (or name). * * @returns {boolean} Returns true if it is the state. */$state.is=function is(stateOrName,params,options){options=extend({relative:$state.$current},options||{});var state=findState(stateOrName,options.relative);if(!isDefined(state)){return undefined;}if($state.$current!==state){return false;}return params?equalForKeys(state.params.$$values(params),$stateParams):true;};/** * @ngdoc function * @name ui.router.state.$state#includes * @methodOf ui.router.state.$state * * @description * A method to determine if the current active state is equal to or is the child of the * state stateName. If any params are passed then they will be tested for a match as well. * Not all the parameters need to be passed, just the ones you'd like to test for equality. * * @example * Partial and relative names *
           * $state.$current.name = 'contacts.details.item';
           *
           * // Using partial names
           * $state.includes("contacts"); // returns true
           * $state.includes("contacts.details"); // returns true
           * $state.includes("contacts.details.item"); // returns true
           * $state.includes("contacts.list"); // returns false
           * $state.includes("about"); // returns false
           *
           * // Using relative names (. and ^), typically from a template
           * // E.g. from the 'contacts.details' template
           * 
      Item
      *
      * * Basic globbing patterns *
           * $state.$current.name = 'contacts.details.item.url';
           *
           * $state.includes("*.details.*.*"); // returns true
           * $state.includes("*.details.**"); // returns true
           * $state.includes("**.item.**"); // returns true
           * $state.includes("*.details.item.url"); // returns true
           * $state.includes("*.details.*.url"); // returns true
           * $state.includes("*.details.*"); // returns false
           * $state.includes("item.**"); // returns false
           * 
      * * @param {string} stateOrName A partial name, relative name, or glob pattern * to be searched for within the current state name. * @param {object=} params A param object, e.g. `{sectionId: section.id}`, * that you'd like to test against the current active state. * @param {object=} options An options object. The options are: * * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set, * .includes will test relative to `options.relative` state (or name). * * @returns {boolean} Returns true if it does include the state */$state.includes=function includes(stateOrName,params,options){options=extend({relative:$state.$current},options||{});if(isString(stateOrName)&&isGlob(stateOrName)){if(!doesStateMatchGlob(stateOrName)){return false;}stateOrName=$state.$current.name;}var state=findState(stateOrName,options.relative);if(!isDefined(state)){return undefined;}if(!isDefined($state.$current.includes[state.name])){return false;}return params?equalForKeys(state.params.$$values(params),$stateParams,objectKeys(params)):true;};/** * @ngdoc function * @name ui.router.state.$state#href * @methodOf ui.router.state.$state * * @description * A url generation method that returns the compiled url for the given state populated with the given params. * * @example *
           * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
           * 
      * * @param {string|object} stateOrName The state name or state object you'd like to generate a url from. * @param {object=} params An object of parameter values to fill the state's required parameters. * @param {object=} options Options object. The options are: * * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the * first parameter, then the constructed href url will be built from the first navigable ancestor (aka * ancestor with a valid url). * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), * defines which state to be relative from. * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". * * @returns {string} compiled state url */$state.href=function href(stateOrName,params,options){options=extend({lossy:true,inherit:true,absolute:false,relative:$state.$current},options||{});var state=findState(stateOrName,options.relative);if(!isDefined(state))return null;if(options.inherit)params=inheritParams($stateParams,params||{},$state.$current,state);var nav=state&&options.lossy?state.navigable:state;if(!nav||nav.url===undefined||nav.url===null){return null;}return $urlRouter.href(nav.url,filterByKeys(state.params.$$keys().concat('#'),params||{}),{absolute:options.absolute});};/** * @ngdoc function * @name ui.router.state.$state#get * @methodOf ui.router.state.$state * * @description * Returns the state configuration object for any specific state or all states. * * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for * the requested state. If not provided, returns an array of ALL state configs. * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context. * @returns {Object|Array} State configuration object or array of all objects. */$state.get=function(stateOrName,context){if(arguments.length===0)return map(objectKeys(states),function(name){return states[name].self;});var state=findState(stateOrName,context||$state.$current);return state&&state.self?state.self:null;};function resolveState(state,params,paramsAreFiltered,inherited,dst,options){// Make a restricted $stateParams with only the parameters that apply to this state if // necessary. In addition to being available to the controller and onEnter/onExit callbacks, // we also need $stateParams to be available for any $injector calls we make during the // dependency resolution process. var $stateParams=paramsAreFiltered?params:filterByKeys(state.params.$$keys(),params);var locals={$stateParams:$stateParams};// Resolve 'global' dependencies for the state, i.e. those not specific to a view. // We're also including $stateParams in this; that way the parameters are restricted // to the set that should be visible to the state, and are independent of when we update // the global $state and $stateParams values. dst.resolve=$resolve.resolve(state.resolve,locals,dst.resolve,state);var promises=[dst.resolve.then(function(globals){dst.globals=globals;})];if(inherited)promises.push(inherited);function resolveViews(){var viewsPromises=[];// Resolve template and dependencies for all views. forEach(state.views,function(view,name){var injectables=view.resolve&&view.resolve!==state.resolve?view.resolve:{};injectables.$template=[function(){return $view.load(name,{view:view,locals:dst.globals,params:$stateParams,notify:options.notify})||'';}];viewsPromises.push($resolve.resolve(injectables,dst.globals,dst.resolve,state).then(function(result){// References to the controller (only instantiated at link time) if(isFunction(view.controllerProvider)||isArray(view.controllerProvider)){var injectLocals=angular.extend({},injectables,dst.globals);result.$$controller=$injector.invoke(view.controllerProvider,null,injectLocals);}else{result.$$controller=view.controller;}// Provide access to the state itself for internal use result.$$state=state;result.$$controllerAs=view.controllerAs;dst[name]=result;}));});return $q.all(viewsPromises).then(function(){return dst.globals;});}// Wait for all the promises and then return the activation object return $q.all(promises).then(resolveViews).then(function(values){return dst;});}return $state;}function shouldSkipReload(to,toParams,from,fromParams,locals,options){// Return true if there are no differences in non-search (path/object) params, false if there are differences function nonSearchParamsEqual(fromAndToState,fromParams,toParams){// Identify whether all the parameters that differ between `fromParams` and `toParams` were search params. function notSearchParam(key){return fromAndToState.params[key].location!="search";}var nonQueryParamKeys=fromAndToState.params.$$keys().filter(notSearchParam);var nonQueryParams=pick.apply({},[fromAndToState.params].concat(nonQueryParamKeys));var nonQueryParamSet=new $$UMFP.ParamSet(nonQueryParams);return nonQueryParamSet.$$equals(fromParams,toParams);}// If reload was not explicitly requested // and we're transitioning to the same state we're already in // and the locals didn't change // or they changed in a way that doesn't merit reloading // (reloadOnParams:false, or reloadOnSearch.false and only search params changed) // Then return true. if(!options.reload&&to===from&&(locals===from.locals||to.self.reloadOnSearch===false&&nonSearchParamsEqual(from,fromParams,toParams))){return true;}}}angular.module('ui.router.state').factory('$stateParams',function(){return{};}).provider('$state',$StateProvider);$ViewProvider.$inject=[];function $ViewProvider(){$get.$inject=['$rootScope','$templateFactory'];this.$get=$get;/** * @ngdoc object * @name ui.router.state.$view * * @requires ui.router.util.$templateFactory * @requires $rootScope * * @description * */$get.$inject=['$rootScope','$templateFactory'];function $get($rootScope,$templateFactory){return{// $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) /** * @ngdoc function * @name ui.router.state.$view#load * @methodOf ui.router.state.$view * * @description * * @param {string} name name * @param {object} options option object. */load:function load(name,options){var result,defaults={template:null,controller:null,view:null,locals:null,notify:true,async:true,params:{}};options=extend(defaults,options);if(options.view){result=$templateFactory.fromConfig(options.view,options.params,options.locals);}return result;}};}}angular.module('ui.router.state').provider('$view',$ViewProvider);/** * @ngdoc object * @name ui.router.state.$uiViewScrollProvider * * @description * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. */function $ViewScrollProvider(){var useAnchorScroll=false;/** * @ngdoc function * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll * @methodOf ui.router.state.$uiViewScrollProvider * * @description * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for * scrolling based on the url anchor. */this.useAnchorScroll=function(){useAnchorScroll=true;};/** * @ngdoc object * @name ui.router.state.$uiViewScroll * * @requires $anchorScroll * @requires $timeout * * @description * When called with a jqLite element, it scrolls the element into view (after a * `$timeout` so the DOM has time to refresh). * * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. */this.$get=['$anchorScroll','$timeout',function($anchorScroll,$timeout){if(useAnchorScroll){return $anchorScroll;}return function($element){return $timeout(function(){$element[0].scrollIntoView();},0,false);};}];}angular.module('ui.router.state').provider('$uiViewScroll',$ViewScrollProvider);var ngMajorVer=angular.version.major;var ngMinorVer=angular.version.minor;/** * @ngdoc directive * @name ui.router.state.directive:ui-view * * @requires ui.router.state.$state * @requires $compile * @requires $controller * @requires $injector * @requires ui.router.state.$uiViewScroll * @requires $document * * @restrict ECA * * @description * The ui-view directive tells $state where to place your templates. * * @param {string=} name A view name. The name should be unique amongst the other views in the * same state. You can have views of the same name that live in different states. * * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you * scroll ui-view elements into view when they are populated during a state activation. * * @param {string=} noanimation If truthy, the non-animated renderer will be selected (no animations * will be applied to the ui-view) * * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.* * * @param {string=} onload Expression to evaluate whenever the view updates. * * @example * A view can be unnamed or named. *
       * 
       * 
      * * *
      *
      * * You can only have one unnamed view within any template (or root html). If you are only using a * single view and it is unnamed then you can populate it like so: *
       * 
      * $stateProvider.state("home", { * template: "

      HELLO!

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

      HELLO!

      " * } * } * }) *
      * * But typically you'll only use the views property if you name your view or have more than one view * in the same template. There's not really a compelling reason to name a view if its the only one, * but you could if you wanted, like so: *
       * 
      *
      *
       * $stateProvider.state("home", {
       *   views: {
       *     "main": {
       *       template: "

      HELLO!

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

      HELLO!

      " * }, * "chart": { * template: "" * }, * "data": { * template: "" * } * } * }) *
      * * Examples for `autoscroll`: * *
       * 
       * 
       *
       * 
       * 
       * 
       * 
       * 
      */$ViewDirective.$inject=['$state','$injector','$uiViewScroll','$interpolate'];function $ViewDirective($state,$injector,$uiViewScroll,$interpolate){function getService(){return $injector.has?function(service){return $injector.has(service)?$injector.get(service):null;}:function(service){try{return $injector.get(service);}catch(e){return null;}};}var service=getService(),$animator=service('$animator'),$animate=service('$animate');// Returns a set of DOM manipulation functions based on which Angular version // it should use function getRenderer(attrs,scope){var statics={enter:function enter(element,target,cb){target.after(element);cb();},leave:function leave(element,cb){element.remove();cb();}};if(!!attrs.noanimation)return statics;function animEnabled(element){if(ngMajorVer===1&&ngMinorVer>=4)return!!$animate.enabled(element);if(ngMajorVer===1&&ngMinorVer>=2)return!!$animate.enabled();return!!$animator;}// ng 1.2+ if($animate){return{enter:function enter(element,target,cb){if(!animEnabled(element)){statics.enter(element,target,cb);}else if(angular.version.minor>2){$animate.enter(element,null,target).then(cb);}else{$animate.enter(element,null,target,cb);}},leave:function leave(element,cb){if(!animEnabled(element)){statics.leave(element,cb);}else if(angular.version.minor>2){$animate.leave(element).then(cb);}else{$animate.leave(element,cb);}}};}// ng 1.1.5 if($animator){var animate=$animator&&$animator(scope,attrs);return{enter:function enter(element,target,cb){animate.enter(element,null,target);cb();},leave:function leave(element,cb){animate.leave(element);cb();}};}return statics;}var directive={restrict:'ECA',terminal:true,priority:400,transclude:'element',compile:function compile(tElement,tAttrs,$transclude){return function(scope,$element,attrs){var previousEl,currentEl,currentScope,latestLocals,onloadExp=attrs.onload||'',autoScrollExp=attrs.autoscroll,renderer=getRenderer(attrs,scope);scope.$on('$stateChangeSuccess',function(){updateView(false);});updateView(true);function cleanupLastView(){var _previousEl=previousEl;var _currentScope=currentScope;if(_currentScope){_currentScope._willBeDestroyed=true;}function cleanOld(){if(_previousEl){_previousEl.remove();}if(_currentScope){_currentScope.$destroy();}}if(currentEl){renderer.leave(currentEl,function(){cleanOld();previousEl=null;});previousEl=currentEl;}else{cleanOld();previousEl=null;}currentEl=null;currentScope=null;}function updateView(firstTime){var newScope,name=getUiViewName(scope,attrs,$element,$interpolate),previousLocals=name&&$state.$current&&$state.$current.locals[name];if(!firstTime&&previousLocals===latestLocals||scope._willBeDestroyed)return;// nothing to do newScope=scope.$new();latestLocals=$state.$current.locals[name];/** * @ngdoc event * @name ui.router.state.directive:ui-view#$viewContentLoading * @eventOf ui.router.state.directive:ui-view * @eventType emits on ui-view directive scope * @description * * Fired once the view **begins loading**, *before* the DOM is rendered. * * @param {Object} event Event object. * @param {string} viewName Name of the view. */newScope.$emit('$viewContentLoading',name);var clone=$transclude(newScope,function(clone){renderer.enter(clone,$element,function onUiViewEnter(){if(currentScope){currentScope.$emit('$viewContentAnimationEnded');}if(angular.isDefined(autoScrollExp)&&!autoScrollExp||scope.$eval(autoScrollExp)){$uiViewScroll(clone);}});cleanupLastView();});currentEl=clone;currentScope=newScope;/** * @ngdoc event * @name ui.router.state.directive:ui-view#$viewContentLoaded * @eventOf ui.router.state.directive:ui-view * @eventType emits on ui-view directive scope * @description * Fired once the view is **loaded**, *after* the DOM is rendered. * * @param {Object} event Event object. * @param {string} viewName Name of the view. */currentScope.$emit('$viewContentLoaded',name);currentScope.$eval(onloadExp);}};}};return directive;}$ViewDirectiveFill.$inject=['$compile','$controller','$state','$interpolate'];function $ViewDirectiveFill($compile,$controller,$state,$interpolate){return{restrict:'ECA',priority:-400,compile:function compile(tElement){var initial=tElement.html();return function(scope,$element,attrs){var current=$state.$current,name=getUiViewName(scope,attrs,$element,$interpolate),locals=current&¤t.locals[name];if(!locals){return;}$element.data('$uiView',{name:name,state:locals.$$state});$element.html(locals.$template?locals.$template:initial);var link=$compile($element.contents());if(locals.$$controller){locals.$scope=scope;locals.$element=$element;var controller=$controller(locals.$$controller,locals);if(locals.$$controllerAs){scope[locals.$$controllerAs]=controller;}$element.data('$ngControllerController',controller);$element.children().data('$ngControllerController',controller);}link(scope);};}};}/** * Shared ui-view code for both directives: * Given scope, element, and its attributes, return the view's name */function getUiViewName(scope,attrs,element,$interpolate){var name=$interpolate(attrs.uiView||attrs.name||'')(scope);var inherited=element.inheritedData('$uiView');return name.indexOf('@')>=0?name:name+'@'+(inherited?inherited.state.name:'');}angular.module('ui.router.state').directive('uiView',$ViewDirective);angular.module('ui.router.state').directive('uiView',$ViewDirectiveFill);function parseStateRef(ref,current){var preparsed=ref.match(/^\s*({[^}]*})\s*$/),parsed;if(preparsed)ref=current+'('+preparsed[1]+')';parsed=ref.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/);if(!parsed||parsed.length!==4)throw new Error("Invalid state ref '"+ref+"'");return{state:parsed[1],paramExpr:parsed[3]||null};}function stateContext(el){var stateData=el.parent().inheritedData('$uiView');if(stateData&&stateData.state&&stateData.state.name){return stateData.state;}}function getTypeInfo(el){// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. var isSvg=Object.prototype.toString.call(el.prop('href'))==='[object SVGAnimatedString]';var isForm=el[0].nodeName==="FORM";return{attr:isForm?"action":isSvg?'xlink:href':'href',isAnchor:el.prop("tagName").toUpperCase()==="A",clickable:!isForm};}function clickHook(el,$state,$timeout,type,current){return function(e){var button=e.which||e.button,target=current();if(!(button>1||e.ctrlKey||e.metaKey||e.shiftKey||el.attr('target'))){// HACK: This is to allow ng-clicks to be processed before the transition is initiated: var transition=$timeout(function(){$state.go(target.state,target.params,target.options);});e.preventDefault();// if the state has no URL, ignore one preventDefault from the directive. var ignorePreventDefaultCount=type.isAnchor&&!target.href?1:0;e.preventDefault=function(){if(ignorePreventDefaultCount--<=0)$timeout.cancel(transition);};}};}function defaultOpts(el,$state){return{relative:stateContext(el)||$state.$current,inherit:true};}/** * @ngdoc directive * @name ui.router.state.directive:ui-sref * * @requires ui.router.state.$state * @requires $timeout * * @restrict A * * @description * A directive that binds a link (`` tag) to a state. If the state has an associated * URL, the directive will automatically generate & update the `href` attribute via * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking * the link will trigger a state transition with optional parameters. * * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be * handled natively by the browser. * * You can also use relative state paths within ui-sref, just like the relative * paths passed to `$state.go()`. You just need to be aware that the path is relative * to the state that the link lives in, in other words the state that loaded the * template containing the link. * * You can specify options to pass to {@link ui.router.state.$state#go $state.go()} * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`, * and `reload`. * * @example * Here's an example of how you'd use ui-sref and how it would compile. If you have the * following template: *
       * Home | About | Next page
       *
       * 
       * 
      * * Then the compiled html would be (assuming Html5Mode is off and current state is contacts): *
       * Home | About | Next page
       *
       * 
       *
       * Home
       * 
      * * @param {string} ui-sref 'stateName' can be any valid absolute or relative state * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()} */$StateRefDirective.$inject=['$state','$timeout'];function $StateRefDirective($state,$timeout){return{restrict:'A',require:['?^uiSrefActive','?^uiSrefActiveEq'],link:function link(scope,element,attrs,uiSrefActive){var ref=parseStateRef(attrs.uiSref,$state.current.name);var def={state:ref.state,href:null,params:null};var type=getTypeInfo(element);var active=uiSrefActive[1]||uiSrefActive[0];def.options=extend(defaultOpts(element,$state),attrs.uiSrefOpts?scope.$eval(attrs.uiSrefOpts):{});var update=function update(val){if(val)def.params=angular.copy(val);def.href=$state.href(ref.state,def.params,def.options);if(active)active.$$addStateInfo(ref.state,def.params);if(def.href!==null)attrs.$set(type.attr,def.href);};if(ref.paramExpr){scope.$watch(ref.paramExpr,function(val){if(val!==def.params)update(val);},true);def.params=angular.copy(scope.$eval(ref.paramExpr));}update();if(!type.clickable)return;element.bind("click",clickHook(element,$state,$timeout,type,function(){return def;}));}};}/** * @ngdoc directive * @name ui.router.state.directive:ui-state * * @requires ui.router.state.uiSref * * @restrict A * * @description * Much like ui-sref, but will accept named $scope properties to evaluate for a state definition, * params and override options. * * @param {string} ui-state 'stateName' can be any valid absolute or relative state * @param {Object} ui-state-params params to pass to {@link ui.router.state.$state#href $state.href()} * @param {Object} ui-state-opts options to pass to {@link ui.router.state.$state#go $state.go()} */$StateRefDynamicDirective.$inject=['$state','$timeout'];function $StateRefDynamicDirective($state,$timeout){return{restrict:'A',require:['?^uiSrefActive','?^uiSrefActiveEq'],link:function link(scope,element,attrs,uiSrefActive){var type=getTypeInfo(element);var active=uiSrefActive[1]||uiSrefActive[0];var group=[attrs.uiState,attrs.uiStateParams||null,attrs.uiStateOpts||null];var watch='['+group.map(function(val){return val||'null';}).join(', ')+']';var def={state:null,params:null,options:null,href:null};function runStateRefLink(group){def.state=group[0];def.params=group[1];def.options=group[2];def.href=$state.href(def.state,def.params,def.options);if(active)active.$$addStateInfo(def.state,def.params);if(def.href)attrs.$set(type.attr,def.href);}scope.$watch(watch,runStateRefLink,true);runStateRefLink(scope.$eval(watch));if(!type.clickable)return;element.bind("click",clickHook(element,$state,$timeout,type,function(){return def;}));}};}/** * @ngdoc directive * @name ui.router.state.directive:ui-sref-active * * @requires ui.router.state.$state * @requires ui.router.state.$stateParams * @requires $interpolate * * @restrict A * * @description * A directive working alongside ui-sref to add classes to an element when the * related ui-sref directive's state is active, and removing them when it is inactive. * The primary use-case is to simplify the special appearance of navigation menus * relying on `ui-sref`, by having the "active" state's menu button appear different, * distinguishing it from the inactive menu items. * * ui-sref-active can live on the same element as ui-sref or on a parent element. The first * ui-sref-active found at the same level or above the ui-sref will be used. * * Will activate when the ui-sref's target state or any child state is active. If you * need to activate only when the ui-sref target state is active and *not* any of * it's children, then you will use * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq} * * @example * Given the following template: *
       * 
       * 
      * * * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins", * the resulting HTML will appear as (note the 'active' class): *
       * 
       * 
      * * The class name is interpolated **once** during the directives link time (any further changes to the * interpolated value are ignored). * * Multiple classes may be specified in a space-separated format: *
       * 
       * 
      * * It is also possible to pass ui-sref-active an expression that evaluates * to an object hash, whose keys represent active class names and whose * values represent the respective state names/globs. * ui-sref-active will match if the current active state **includes** any of * the specified state names/globs, even the abstract ones. * * @Example * Given the following template, with "admin" being an abstract state: *
       * 
      * Roles *
      *
      * * When the current state is "admin.roles" the "active" class will be applied * to both the
      and elements. It is important to note that the state * names/globs passed to ui-sref-active shadow the state provided by ui-sref. *//** * @ngdoc directive * @name ui.router.state.directive:ui-sref-active-eq * * @requires ui.router.state.$state * @requires ui.router.state.$stateParams * @requires $interpolate * * @restrict A * * @description * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate * when the exact target state used in the `ui-sref` is active; no child states. * */$StateRefActiveDirective.$inject=['$state','$stateParams','$interpolate'];function $StateRefActiveDirective($state,$stateParams,$interpolate){return{restrict:"A",controller:['$scope','$element','$attrs','$timeout',function($scope,$element,$attrs,$timeout){var states=[],activeClasses={},activeEqClass,uiSrefActive;// There probably isn't much point in $observing this // uiSrefActive and uiSrefActiveEq share the same directive object with some // slight difference in logic routing activeEqClass=$interpolate($attrs.uiSrefActiveEq||'',false)($scope);try{uiSrefActive=$scope.$eval($attrs.uiSrefActive);}catch(e){// Do nothing. uiSrefActive is not a valid expression. // Fall back to using $interpolate below }uiSrefActive=uiSrefActive||$interpolate($attrs.uiSrefActive||'',false)($scope);if(isObject(uiSrefActive)){forEach(uiSrefActive,function(stateOrName,activeClass){if(isString(stateOrName)){var ref=parseStateRef(stateOrName,$state.current.name);addState(ref.state,$scope.$eval(ref.paramExpr),activeClass);}});}// Allow uiSref to communicate with uiSrefActive[Equals] this.$$addStateInfo=function(newState,newParams){// we already got an explicit state provided by ui-sref-active, so we // shadow the one that comes from ui-sref if(isObject(uiSrefActive)&&states.length>0){return;}addState(newState,newParams,uiSrefActive);update();};$scope.$on('$stateChangeSuccess',update);function addState(stateName,stateParams,activeClass){var state=$state.get(stateName,stateContext($element));var stateHash=createStateHash(stateName,stateParams);states.push({state:state||{name:stateName},params:stateParams,hash:stateHash});activeClasses[stateHash]=activeClass;}/** * @param {string} state * @param {Object|string} [params] * @return {string} */function createStateHash(state,params){if(!isString(state)){throw new Error('state should be a string');}if(isObject(params)){return state+toJson(params);}params=$scope.$eval(params);if(isObject(params)){return state+toJson(params);}return state;}// Update route state function update(){for(var i=0;i
      */var REGEX_STRING_REGEXP=/^\/(.+)\/([a-z]*)$/;// The name of a form control's ValidityState property. // This is used so that it's possible for internal tests to create mock ValidityStates. var VALIDITY_STATE_PROPERTY='validity';var hasOwnProperty=Object.prototype.hasOwnProperty;var lowercase=function lowercase(string){return isString(string)?string.toLowerCase():string;};var uppercase=function uppercase(string){return isString(string)?string.toUpperCase():string;};var manualLowercase=function manualLowercase(s){/* eslint-disable no-bitwise */return isString(s)?s.replace(/[A-Z]/g,function(ch){return String.fromCharCode(ch.charCodeAt(0)|32);}):s;/* eslint-enable */};var manualUppercase=function manualUppercase(s){/* eslint-disable no-bitwise */return isString(s)?s.replace(/[a-z]/g,function(ch){return String.fromCharCode(ch.charCodeAt(0)&~32);}):s;/* eslint-enable */};// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387 if('i'!=='I'.toLowerCase()){lowercase=manualLowercase;uppercase=manualUppercase;}var msie,// holds major version number for IE, or NaN if UA is not IE. jqLite,// delay binding since jQuery could be loaded after us. jQuery,// delay binding slice=[].slice,splice=[].splice,push=[].push,toString=Object.prototype.toString,getPrototypeOf=Object.getPrototypeOf,ngMinErr=minErr('ng'),/** @name angular */angular=window.angular||(window.angular={}),angularModule,uid=0;/** * documentMode is an IE-only property * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx */msie=window.document.documentMode;/** * @private * @param {*} obj * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, * String ...) */function isArrayLike(obj){// `null`, `undefined` and `window` are not array-like if(obj==null||isWindow(obj))return false;// arrays, strings and jQuery/jqLite objects are array like // * jqLite is either the jQuery or jqLite constructor function // * we have to check the existence of jqLite first as this method is called // via the forEach method when constructing the jqLite object in the first place if(isArray(obj)||isString(obj)||jqLite&&obj instanceof jqLite)return true;// Support: iOS 8.2 (not reproducible in simulator) // "length" in obj used to prevent JIT error (gh-11508) var length='length'in Object(obj)&&obj.length;// NodeList objects (with `item` method) and // other objects with suitable length characteristics are array-like return isNumber(length)&&(length>=0&&(length-1 in obj||obj instanceof Array)||typeof obj.item==='function');}/** * @ngdoc function * @name angular.forEach * @module ng * @kind function * * @description * Invokes the `iterator` function once for each item in `obj` collection, which can be either an * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` * is the value of an object property or an array element, `key` is the object property key or * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. * * It is worth noting that `.forEach` does not iterate over inherited properties because it filters * using the `hasOwnProperty` method. * * Unlike ES262's * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just * return the value provided. * ```js var values = {name: 'misko', gender: 'male'}; var log = []; angular.forEach(values, function(value, key) { this.push(key + ': ' + value); }, log); expect(log).toEqual(['name: misko', 'gender: male']); ``` * * @param {Object|Array} obj Object to iterate over. * @param {Function} iterator Iterator function. * @param {Object=} context Object to become context (`this`) for the iterator function. * @returns {Object|Array} Reference to `obj`. */function forEach(obj,iterator,context){var key,length;if(obj){if(isFunction(obj)){for(key in obj){// Need to check if hasOwnProperty exists, // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function if(key!=='prototype'&&key!=='length'&&key!=='name'&&(!obj.hasOwnProperty||obj.hasOwnProperty(key))){iterator.call(context,obj[key],key,obj);}}}else if(isArray(obj)||isArrayLike(obj)){var isPrimitive=(typeof obj==='undefined'?'undefined':_typeof2(obj))!=='object';for(key=0,length=obj.length;key=0){array.splice(index,1);}return index;}/** * @ngdoc function * @name angular.copy * @module ng * @kind function * * @description * Creates a deep copy of `source`, which should be an object or an array. * * * If no destination is supplied, a copy of the object or array is created. * * If a destination is provided, all of its elements (for arrays) or properties (for objects) * are deleted and then all elements/properties from the source are copied to it. * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. * * If `source` is identical to `destination` an exception will be thrown. * *
      *
      * Only enumerable properties are taken into account. Non-enumerable properties (both on `source` * and on `destination`) will be ignored. *
      * * @param {*} source The source that will be used to make a copy. * Can be any type, including primitives, `null`, and `undefined`. * @param {(Object|Array)=} destination Destination into which the source is copied. If * provided, must be of the same type as `source`. * @returns {*} The copy or updated `destination`, if `destination` was specified. * * @example


      Gender:
      form = {{user | json}}
      master = {{master | json}}
      // Module: copyExample angular. module('copyExample', []). controller('ExampleController', ['$scope', function($scope) { $scope.master = {}; $scope.reset = function() { // Example with 1 argument $scope.user = angular.copy($scope.master); }; $scope.update = function(user) { // Example with 2 arguments angular.copy(user, $scope.master); }; $scope.reset(); }]);
      */function copy(source,destination){var stackSource=[];var stackDest=[];if(destination){if(isTypedArray(destination)||isArrayBuffer(destination)){throw ngMinErr('cpta','Can\'t copy! TypedArray destination cannot be mutated.');}if(source===destination){throw ngMinErr('cpi','Can\'t copy! Source and destination are identical.');}// Empty the destination object if(isArray(destination)){destination.length=0;}else{forEach(destination,function(value,key){if(key!=='$$hashKey'){delete destination[key];}});}stackSource.push(source);stackDest.push(destination);return copyRecurse(source,destination);}return copyElement(source);function copyRecurse(source,destination){var h=destination.$$hashKey;var key;if(isArray(source)){for(var i=0,ii=source.length;i false. But we consider two NaN as equal) * * Both values represent the same regular expression (In JavaScript, * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual * representation matches). * * During a property comparison, properties of `function` type and properties with names * that begin with `$` are ignored. * * Scope and DOMWindow objects are being compared only by identify (`===`). * * @param {*} o1 Object or value to compare. * @param {*} o2 Object or value to compare. * @returns {boolean} True if arguments are equal. * * @example

      User 1

      Name: Age:

      User 2

      Name: Age:

      User 1:
      {{user1 | json}}
      User 2:
      {{user2 | json}}
      Equal:
      {{result}}
      angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) { $scope.user1 = {}; $scope.user2 = {}; $scope.compare = function() { $scope.result = angular.equals($scope.user1, $scope.user2); }; }]);
      */function equals(o1,o2){if(o1===o2)return true;if(o1===null||o2===null)return false;// eslint-disable-next-line no-self-compare if(o1!==o1&&o2!==o2)return true;// NaN === NaN var t1=typeof o1==='undefined'?'undefined':_typeof2(o1),t2=typeof o2==='undefined'?'undefined':_typeof2(o2),length,key,keySet;if(t1===t2&&t1==='object'){if(isArray(o1)){if(!isArray(o2))return false;if((length=o1.length)===o2.length){for(key=0;key ... ... ``` * @example * This example shows how to use a jQuery based library of a different name. * The library name must be available at the top most 'window'. ```html ... ... ``` */var jq=function jq(){if(isDefined(jq.name_))return jq.name_;var el;var i,ii=ngAttrPrefixes.length,prefix,name;for(i=0;i2?sliceArgs(arguments,2):[];if(isFunction(fn)&&!(fn instanceof RegExp)){return curryArgs.length?function(){return arguments.length?fn.apply(self,concat(curryArgs,arguments,0)):fn.apply(self,curryArgs);}:function(){return arguments.length?fn.apply(self,arguments):fn.call(self);};}else{// In IE, native methods are not functions so they cannot be bound (note: they don't need to be). return fn;}}function toJsonReplacer(key,value){var val=value;if(typeof key==='string'&&key.charAt(0)==='$'&&key.charAt(1)==='$'){val=undefined;}else if(isWindow(value)){val='$WINDOW';}else if(value&&window.document===value){val='$DOCUMENT';}else if(isScope(value)){val='$SCOPE';}return val;}/** * @ngdoc function * @name angular.toJson * @module ng * @kind function * * @description * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be * stripped since angular uses this notation internally. * * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON. * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. * If set to an integer, the JSON output will contain that many spaces per indentation. * @returns {string|undefined} JSON-ified string representing `obj`. * @knownIssue * * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date` * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the * `Date.prototype.toJSON` method as follows: * * ``` * var _DatetoJSON = Date.prototype.toJSON; * Date.prototype.toJSON = function() { * try { * return _DatetoJSON.call(this); * } catch(e) { * if (e instanceof RangeError) { * return null; * } * throw e; * } * }; * ``` * * See https://github.com/angular/angular.js/pull/14221 for more information. */function toJson(obj,pretty){if(isUndefined(obj))return undefined;if(!isNumber(pretty)){pretty=pretty?2:null;}return JSON.stringify(obj,toJsonReplacer,pretty);}/** * @ngdoc function * @name angular.fromJson * @module ng * @kind function * * @description * Deserializes a JSON string. * * @param {string} json JSON string to deserialize. * @returns {Object|Array|string|number} Deserialized JSON string. */function fromJson(json){return isString(json)?JSON.parse(json):json;}var ALL_COLONS=/:/g;function timezoneToOffset(timezone,fallback){// IE/Edge do not "understand" colon (`:`) in timezone timezone=timezone.replace(ALL_COLONS,'');var requestedTimezoneOffset=Date.parse('Jan 01, 1970 00:00:00 '+timezone)/60000;return isNumberNaN(requestedTimezoneOffset)?fallback:requestedTimezoneOffset;}function addDateMinutes(date,minutes){date=new Date(date.getTime());date.setMinutes(date.getMinutes()+minutes);return date;}function convertTimezoneToLocal(date,timezone,reverse){reverse=reverse?-1:1;var dateTimezoneOffset=date.getTimezoneOffset();var timezoneOffset=timezoneToOffset(timezone,dateTimezoneOffset);return addDateMinutes(date,reverse*(timezoneOffset-dateTimezoneOffset));}/** * @returns {string} Returns the string representation of the element. */function startingTag(element){element=jqLite(element).clone();try{// turns out IE does not let you set .html() on elements which // are not allowed to have children. So we just ignore it. element.empty();}catch(e){/* empty */}var elemHtml=jqLite('
      ').append(element).html();try{return element[0].nodeType===NODE_TYPE_TEXT?lowercase(elemHtml):elemHtml.match(/^(<[^>]+>)/)[1].replace(/^<([\w-]+)/,function(match,nodeName){return'<'+lowercase(nodeName);});}catch(e){return lowercase(elemHtml);}}///////////////////////////////////////////////// /** * Tries to decode the URI component without throwing an exception. * * @private * @param str value potential URI component to check. * @returns {boolean} True if `value` can be decoded * with the decodeURIComponent function. */function tryDecodeURIComponent(value){try{return decodeURIComponent(value);}catch(e){// Ignore any invalid uri component. }}/** * Parses an escaped url query string into key-value pairs. * @returns {Object.} */function parseKeyValue(/**string*/keyValue){var obj={};forEach((keyValue||'').split('&'),function(keyValue){var splitPoint,key,val;if(keyValue){key=keyValue=keyValue.replace(/\+/g,'%20');splitPoint=keyValue.indexOf('=');if(splitPoint!==-1){key=keyValue.substring(0,splitPoint);val=keyValue.substring(splitPoint+1);}key=tryDecodeURIComponent(key);if(isDefined(key)){val=isDefined(val)?tryDecodeURIComponent(val):true;if(!hasOwnProperty.call(obj,key)){obj[key]=val;}else if(isArray(obj[key])){obj[key].push(val);}else{obj[key]=[obj[key],val];}}}});return obj;}function toKeyValue(obj){var parts=[];forEach(obj,function(value,key){if(isArray(value)){forEach(value,function(arrayValue){parts.push(encodeUriQuery(key,true)+(arrayValue===true?'':'='+encodeUriQuery(arrayValue,true)));});}else{parts.push(encodeUriQuery(key,true)+(value===true?'':'='+encodeUriQuery(value,true)));}});return parts.length?parts.join('&'):'';}/** * We need our custom method because encodeURIComponent is too aggressive and doesn't follow * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path * segments: * segment = *pchar * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * pct-encoded = "%" HEXDIG HEXDIG * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" */function encodeUriSegment(val){return encodeUriQuery(val,true).replace(/%26/gi,'&').replace(/%3D/gi,'=').replace(/%2B/gi,'+');}/** * This method is intended for encoding *key* or *value* parts of query component. We need a custom * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be * encoded per http://tools.ietf.org/html/rfc3986: * query = *( pchar / "/" / "?" ) * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * pct-encoded = "%" HEXDIG HEXDIG * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" */function encodeUriQuery(val,pctEncodeSpaces){return encodeURIComponent(val).replace(/%40/gi,'@').replace(/%3A/gi,':').replace(/%24/g,'$').replace(/%2C/gi,',').replace(/%3B/gi,';').replace(/%20/g,pctEncodeSpaces?'%20':'+');}var ngAttrPrefixes=['ng-','data-ng-','ng:','x-ng-'];function getNgAttribute(element,ngAttr){var attr,i,ii=ngAttrPrefixes.length;for(i=0;i` or `` tags. * * There are a few things to keep in mind when using `ngApp`: * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` * found in the document will be used to define the root element to auto-bootstrap as an * application. To run multiple applications in an HTML document you must manually bootstrap them using * {@link angular.bootstrap} instead. * - AngularJS applications cannot be nested within each other. * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`. * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and * {@link ngRoute.ngView `ngView`}. * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, * causing animations to stop working and making the injector inaccessible from outside the app. * * You can specify an **AngularJS module** to be used as the root module for the application. This * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It * should contain the application code needed or have dependencies on other modules that will * contain the code. See {@link angular.module} for more information. * * In the example below if the `ngApp` directive were not placed on the `html` element then the * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` * would not be resolved to `3`. * * `ngApp` is the easiest, and most common way to bootstrap an application. *
      I can add: {{a}} + {{b}} = {{ a+b }}
      angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { $scope.a = 1; $scope.b = 2; });
      * * Using `ngStrictDi`, you would see something like this: *
      I can add: {{a}} + {{b}} = {{ a+b }}

      This renders because the controller does not fail to instantiate, by using explicit annotation style (see script.js for details)

      Name:
      Hello, {{name}}!

      This renders because the controller does not fail to instantiate, by using explicit annotation style (see script.js for details)

      I can add: {{a}} + {{b}} = {{ a+b }}

      The controller could not be instantiated, due to relying on automatic function annotations (which are disabled in strict mode). As such, the content of this section is not interpolated, and there should be an error in your web console.

      angular.module('ngAppStrictDemo', []) // BadController will fail to instantiate, due to relying on automatic function annotation, // rather than an explicit annotation .controller('BadController', function($scope) { $scope.a = 1; $scope.b = 2; }) // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, // due to using explicit annotations using the array style and $inject property, respectively. .controller('GoodController1', ['$scope', function($scope) { $scope.a = 1; $scope.b = 2; }]) .controller('GoodController2', GoodController2); function GoodController2($scope) { $scope.name = 'World'; } GoodController2.$inject = ['$scope']; div[ng-controller] { margin-bottom: 1em; -webkit-border-radius: 4px; border-radius: 4px; border: 1px solid; padding: .5em; } div[ng-controller^=Good] { border-color: #d6e9c6; background-color: #dff0d8; color: #3c763d; } div[ng-controller^=Bad] { border-color: #ebccd1; background-color: #f2dede; color: #a94442; margin-bottom: 0; }
      */function angularInit(element,bootstrap){var appElement,module,config={};// The element `element` has priority over any other element. forEach(ngAttrPrefixes,function(prefix){var name=prefix+'app';if(!appElement&&element.hasAttribute&&element.hasAttribute(name)){appElement=element;module=element.getAttribute(name);}});forEach(ngAttrPrefixes,function(prefix){var name=prefix+'app';var candidate;if(!appElement&&(candidate=element.querySelector('['+name.replace(':','\\:')+']'))){appElement=candidate;module=candidate.getAttribute(name);}});if(appElement){if(!isAutoBootstrapAllowed){window.console.error('Angular: disabling automatic bootstrap. * * * * ``` * * @param {DOMElement} element DOM element which is the root of angular application. * @param {Array=} modules an array of modules to load into the application. * Each item in the array should be the name of a predefined module or a (DI annotated) * function that will be invoked by the injector as a `config` block. * See: {@link angular.module modules} * @param {Object=} config an object for defining configuration options for the application. The * following keys are supported: * * * `strictDi` - disable automatic function annotation for the application. This is meant to * assist in finding bugs which break minified code. Defaults to `false`. * * @returns {auto.$injector} Returns the newly created injector for this app. */function bootstrap(element,modules,config){if(!isObject(config))config={};var defaultConfig={strictDi:false};config=extend(defaultConfig,config);var doBootstrap=function doBootstrap(){element=jqLite(element);if(element.injector()){var tag=element[0]===window.document?'document':startingTag(element);// Encode angle brackets to prevent input from being sanitized to empty string #8683. throw ngMinErr('btstrpd','App already bootstrapped with this element \'{0}\'',tag.replace(//,'>'));}modules=modules||[];modules.unshift(['$provide',function($provide){$provide.value('$rootElement',element);}]);if(config.debugInfoEnabled){// Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. modules.push(['$compileProvider',function($compileProvider){$compileProvider.debugInfoEnabled(true);}]);}modules.unshift('ng');var injector=createInjector(modules,config.strictDi);injector.invoke(['$rootScope','$rootElement','$compile','$injector',function bootstrapApply(scope,element,compile,injector){scope.$apply(function(){element.data('$injector',injector);compile(element)(scope);});}]);return injector;};var NG_ENABLE_DEBUG_INFO=/^NG_ENABLE_DEBUG_INFO!/;var NG_DEFER_BOOTSTRAP=/^NG_DEFER_BOOTSTRAP!/;if(window&&NG_ENABLE_DEBUG_INFO.test(window.name)){config.debugInfoEnabled=true;window.name=window.name.replace(NG_ENABLE_DEBUG_INFO,'');}if(window&&!NG_DEFER_BOOTSTRAP.test(window.name)){return doBootstrap();}window.name=window.name.replace(NG_DEFER_BOOTSTRAP,'');angular.resumeBootstrap=function(extraModules){forEach(extraModules,function(module){modules.push(module);});return doBootstrap();};if(isFunction(angular.resumeDeferredBootstrap)){angular.resumeDeferredBootstrap();}}/** * @ngdoc function * @name angular.reloadWithDebugInfo * @module ng * @description * Use this function to reload the current application with debug information turned on. * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. * * See {@link ng.$compileProvider#debugInfoEnabled} for more. */function reloadWithDebugInfo(){window.name='NG_ENABLE_DEBUG_INFO!'+window.name;window.location.reload();}/** * @name angular.getTestability * @module ng * @description * Get the testability service for the instance of Angular on the given * element. * @param {DOMElement} element DOM element which is the root of angular application. */function getTestability(rootElement){var injector=angular.element(rootElement).injector();if(!injector){throw ngMinErr('test','no injector found for element argument to getTestability');}return injector.get('$$testability');}var SNAKE_CASE_REGEXP=/[A-Z]/g;function snake_case(name,separator){separator=separator||'_';return name.replace(SNAKE_CASE_REGEXP,function(letter,pos){return(pos?separator:'')+letter.toLowerCase();});}var bindJQueryFired=false;function bindJQuery(){var originalCleanData;if(bindJQueryFired){return;}// bind to jQuery if present; var jqName=jq();jQuery=isUndefined(jqName)?window.jQuery:// use jQuery (if present) !jqName?undefined:// use jqLite window[jqName];// use jQuery specified by `ngJq` // Use jQuery if it exists with proper functionality, otherwise default to us. // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older // versions. It will not work for sure with jQuery <1.7, though. if(jQuery&&jQuery.fn.on){jqLite=jQuery;extend(jQuery.fn,{scope:JQLitePrototype.scope,isolateScope:JQLitePrototype.isolateScope,controller:JQLitePrototype.controller,injector:JQLitePrototype.injector,inheritedData:JQLitePrototype.inheritedData});// All nodes removed from the DOM via various jQuery APIs like .remove() // are passed through jQuery.cleanData. Monkey-patch this method to fire // the $destroy event on all removed nodes. originalCleanData=jQuery.cleanData;jQuery.cleanData=function(elems){var events;for(var i=0,elem;(elem=elems[i])!=null;i++){events=jQuery._data(elem,'events');if(events&&events.$destroy){jQuery(elem).triggerHandler('$destroy');}}originalCleanData(elems);};}else{jqLite=JQLite;}angular.element=jqLite;// Prevent double-proxying. bindJQueryFired=true;}/** * throw error if the argument is falsy. */function assertArg(arg,name,reason){if(!arg){throw ngMinErr('areq','Argument \'{0}\' is {1}',name||'?',reason||'required');}return arg;}function assertArgFn(arg,name,acceptArrayAnnotation){if(acceptArrayAnnotation&&isArray(arg)){arg=arg[arg.length-1];}assertArg(isFunction(arg),name,'not a function, got '+(arg&&(typeof arg==='undefined'?'undefined':_typeof2(arg))==='object'?arg.constructor.name||'Object':typeof arg==='undefined'?'undefined':_typeof2(arg)));return arg;}/** * throw error if the name given is hasOwnProperty * @param {String} name the name to test * @param {String} context the context in which the name is used, such as module or directive */function assertNotHasOwnProperty(name,context){if(name==='hasOwnProperty'){throw ngMinErr('badname','hasOwnProperty is not a valid {0} name',context);}}/** * Return the value accessible from the object by path. Any undefined traversals are ignored * @param {Object} obj starting object * @param {String} path path to traverse * @param {boolean} [bindFnToScope=true] * @returns {Object} value as accessible by path *///TODO(misko): this function needs to be removed function getter(obj,path,bindFnToScope){if(!path)return obj;var keys=path.split('.');var key;var lastInstance=obj;var len=keys.length;for(var i=0;i} */var modules={};/** * @ngdoc function * @name angular.module * @module ng * @description * * The `angular.module` is a global place for creating, registering and retrieving Angular * modules. * All modules (angular core or 3rd party) that should be available to an application must be * registered using this mechanism. * * Passing one argument retrieves an existing {@link angular.Module}, * whereas passing more than one argument creates a new {@link angular.Module} * * * # Module * * A module is a collection of services, directives, controllers, filters, and configuration information. * `angular.module` is used to configure the {@link auto.$injector $injector}. * * ```js * // Create a new module * var myModule = angular.module('myModule', []); * * // register a new service * myModule.value('appName', 'MyCoolApp'); * * // configure existing services inside initialization blocks. * myModule.config(['$locationProvider', function($locationProvider) { * // Configure existing providers * $locationProvider.hashPrefix('!'); * }]); * ``` * * Then you can create an injector and load your modules like this: * * ```js * var injector = angular.injector(['ng', 'myModule']) * ``` * * However it's more likely that you'll just use * {@link ng.directive:ngApp ngApp} or * {@link angular.bootstrap} to simplify this process for you. * * @param {!string} name The name of the module to create or retrieve. * @param {!Array.=} requires If specified then new module is being created. If * unspecified then the module is being retrieved for further configuration. * @param {Function=} configFn Optional configuration function for the module. Same as * {@link angular.Module#config Module#config()}. * @returns {angular.Module} new module with the {@link angular.Module} api. */return function module(name,requires,configFn){var assertNotHasOwnProperty=function assertNotHasOwnProperty(name,context){if(name==='hasOwnProperty'){throw ngMinErr('badname','hasOwnProperty is not a valid {0} name',context);}};assertNotHasOwnProperty(name,'module');if(requires&&modules.hasOwnProperty(name)){modules[name]=null;}return ensure(modules,name,function(){if(!requires){throw $injectorMinErr('nomod','Module \'{0}\' is not available! You either misspelled '+'the module name or forgot to load it. If registering a module ensure that you '+'specify the dependencies as the second argument.',name);}/** @type {!Array.>} */var invokeQueue=[];/** @type {!Array.} */var configBlocks=[];/** @type {!Array.} */var runBlocks=[];var config=invokeLater('$injector','invoke','push',configBlocks);/** @type {angular.Module} */var moduleInstance={// Private state _invokeQueue:invokeQueue,_configBlocks:configBlocks,_runBlocks:runBlocks,/** * @ngdoc property * @name angular.Module#requires * @module ng * * @description * Holds the list of modules which the injector will load before the current module is * loaded. */requires:requires,/** * @ngdoc property * @name angular.Module#name * @module ng * * @description * Name of the module. */name:name,/** * @ngdoc method * @name angular.Module#provider * @module ng * @param {string} name service name * @param {Function} providerType Construction function for creating new instance of the * service. * @description * See {@link auto.$provide#provider $provide.provider()}. */provider:invokeLaterAndSetModuleName('$provide','provider'),/** * @ngdoc method * @name angular.Module#factory * @module ng * @param {string} name service name * @param {Function} providerFunction Function for creating new instance of the service. * @description * See {@link auto.$provide#factory $provide.factory()}. */factory:invokeLaterAndSetModuleName('$provide','factory'),/** * @ngdoc method * @name angular.Module#service * @module ng * @param {string} name service name * @param {Function} constructor A constructor function that will be instantiated. * @description * See {@link auto.$provide#service $provide.service()}. */service:invokeLaterAndSetModuleName('$provide','service'),/** * @ngdoc method * @name angular.Module#value * @module ng * @param {string} name service name * @param {*} object Service instance object. * @description * See {@link auto.$provide#value $provide.value()}. */value:invokeLater('$provide','value'),/** * @ngdoc method * @name angular.Module#constant * @module ng * @param {string} name constant name * @param {*} object Constant value. * @description * Because the constants are fixed, they get applied before other provide methods. * See {@link auto.$provide#constant $provide.constant()}. */constant:invokeLater('$provide','constant','unshift'),/** * @ngdoc method * @name angular.Module#decorator * @module ng * @param {string} name The name of the service to decorate. * @param {Function} decorFn This function will be invoked when the service needs to be * instantiated and should return the decorated service instance. * @description * See {@link auto.$provide#decorator $provide.decorator()}. */decorator:invokeLaterAndSetModuleName('$provide','decorator'),/** * @ngdoc method * @name angular.Module#animation * @module ng * @param {string} name animation name * @param {Function} animationFactory Factory function for creating new instance of an * animation. * @description * * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. * * * Defines an animation hook that can be later used with * {@link $animate $animate} service and directives that use this service. * * ```js * module.animation('.animation-name', function($inject1, $inject2) { * return { * eventName : function(element, done) { * //code to run the animation * //once complete, then run done() * return function cancellationFunction(element) { * //code to cancel the animation * } * } * } * }) * ``` * * See {@link ng.$animateProvider#register $animateProvider.register()} and * {@link ngAnimate ngAnimate module} for more information. */animation:invokeLaterAndSetModuleName('$animateProvider','register'),/** * @ngdoc method * @name angular.Module#filter * @module ng * @param {string} name Filter name - this must be a valid angular expression identifier * @param {Function} filterFactory Factory function for creating new instance of filter. * @description * See {@link ng.$filterProvider#register $filterProvider.register()}. * *
      * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores * (`myapp_subsection_filterx`). *
      */filter:invokeLaterAndSetModuleName('$filterProvider','register'),/** * @ngdoc method * @name angular.Module#controller * @module ng * @param {string|Object} name Controller name, or an object map of controllers where the * keys are the names and the values are the constructors. * @param {Function} constructor Controller constructor function. * @description * See {@link ng.$controllerProvider#register $controllerProvider.register()}. */controller:invokeLaterAndSetModuleName('$controllerProvider','register'),/** * @ngdoc method * @name angular.Module#directive * @module ng * @param {string|Object} name Directive name, or an object map of directives where the * keys are the names and the values are the factories. * @param {Function} directiveFactory Factory function for creating new instance of * directives. * @description * See {@link ng.$compileProvider#directive $compileProvider.directive()}. */directive:invokeLaterAndSetModuleName('$compileProvider','directive'),/** * @ngdoc method * @name angular.Module#component * @module ng * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp) * @param {Object} options Component definition object (a simplified * {@link ng.$compile#directive-definition-object directive definition object}) * * @description * See {@link ng.$compileProvider#component $compileProvider.component()}. */component:invokeLaterAndSetModuleName('$compileProvider','component'),/** * @ngdoc method * @name angular.Module#config * @module ng * @param {Function} configFn Execute this function on module load. Useful for service * configuration. * @description * Use this method to register work which needs to be performed on module loading. * For more about how to configure services, see * {@link providers#provider-recipe Provider Recipe}. */config:config,/** * @ngdoc method * @name angular.Module#run * @module ng * @param {Function} initializationFn Execute this function after injector creation. * Useful for application initialization. * @description * Use this method to register work which should be performed when the injector is done * loading all modules. */run:function run(block){runBlocks.push(block);return this;}};if(configFn){config(configFn);}return moduleInstance;/** * @param {string} provider * @param {string} method * @param {String=} insertMethod * @returns {angular.Module} */function invokeLater(provider,method,insertMethod,queue){if(!queue)queue=invokeQueue;return function(){queue[insertMethod||'push']([provider,method,arguments]);return moduleInstance;};}/** * @param {string} provider * @param {string} method * @returns {angular.Module} */function invokeLaterAndSetModuleName(provider,method){return function(recipeName,factoryFunction){if(factoryFunction&&isFunction(factoryFunction))factoryFunction.$$moduleName=name;invokeQueue.push([provider,method,arguments]);return moduleInstance;};}});};});}/* global shallowCopy: true *//** * Creates a shallow copy of an object, an array or a primitive. * * Assumes that there are no proto properties for objects. */function shallowCopy(src,dst){if(isArray(src)){dst=dst||[];for(var i=0,ii=src.length;i=0)return'...';seen.push(val);}return val;});}function toDebugString(obj){if(typeof obj==='function'){return obj.toString().replace(/ \{[\s\S]*$/,'');}else if(isUndefined(obj)){return'undefined';}else if(typeof obj!=='string'){return serializeObject(obj);}return obj;}/* global angularModule: true, version: true, $CompileProvider, htmlAnchorDirective, inputDirective, inputDirective, formDirective, scriptDirective, selectDirective, optionDirective, ngBindDirective, ngBindHtmlDirective, ngBindTemplateDirective, ngClassDirective, ngClassEvenDirective, ngClassOddDirective, ngCloakDirective, ngControllerDirective, ngFormDirective, ngHideDirective, ngIfDirective, ngIncludeDirective, ngIncludeFillContentDirective, ngInitDirective, ngNonBindableDirective, ngPluralizeDirective, ngRepeatDirective, ngShowDirective, ngStyleDirective, ngSwitchDirective, ngSwitchWhenDirective, ngSwitchDefaultDirective, ngOptionsDirective, ngTranscludeDirective, ngModelDirective, ngListDirective, ngChangeDirective, patternDirective, patternDirective, requiredDirective, requiredDirective, minlengthDirective, minlengthDirective, maxlengthDirective, maxlengthDirective, ngValueDirective, ngModelOptionsDirective, ngAttributeAliasDirectives, ngEventDirectives, $AnchorScrollProvider, $AnimateProvider, $CoreAnimateCssProvider, $$CoreAnimateJsProvider, $$CoreAnimateQueueProvider, $$AnimateRunnerFactoryProvider, $$AnimateAsyncRunFactoryProvider, $BrowserProvider, $CacheFactoryProvider, $ControllerProvider, $DateProvider, $DocumentProvider, $ExceptionHandlerProvider, $FilterProvider, $$ForceReflowProvider, $InterpolateProvider, $IntervalProvider, $$HashMapProvider, $HttpProvider, $HttpParamSerializerProvider, $HttpParamSerializerJQLikeProvider, $HttpBackendProvider, $xhrFactoryProvider, $jsonpCallbacksProvider, $LocationProvider, $LogProvider, $ParseProvider, $RootScopeProvider, $QProvider, $$QProvider, $$SanitizeUriProvider, $SceProvider, $SceDelegateProvider, $SnifferProvider, $TemplateCacheProvider, $TemplateRequestProvider, $$TestabilityProvider, $TimeoutProvider, $$RAFProvider, $WindowProvider, $$jqLiteProvider, $$CookieReaderProvider *//** * @ngdoc object * @name angular.version * @module ng * @description * An object that contains information about the current AngularJS version. * * This object has the following properties: * * - `full` – `{string}` – Full version string, such as "0.9.18". * - `major` – `{number}` – Major version number, such as "0". * - `minor` – `{number}` – Minor version number, such as "9". * - `dot` – `{number}` – Dot version number, such as "18". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */var version={// These placeholder strings will be replaced by grunt's `build` task. // They need to be double- or single-quoted. full:'1.5.11',major:1,minor:5,dot:11,codeName:'princely-quest'};function publishExternalAPI(angular){extend(angular,{'bootstrap':bootstrap,'copy':copy,'extend':extend,'merge':merge,'equals':equals,'element':jqLite,'forEach':forEach,'injector':createInjector,'noop':noop,'bind':bind,'toJson':toJson,'fromJson':fromJson,'identity':identity,'isUndefined':isUndefined,'isDefined':isDefined,'isString':isString,'isFunction':isFunction,'isObject':isObject,'isNumber':isNumber,'isElement':isElement,'isArray':isArray,'version':version,'isDate':isDate,'lowercase':lowercase,'uppercase':uppercase,'callbacks':{$$counter:0},'getTestability':getTestability,'$$minErr':minErr,'$$csp':csp,'reloadWithDebugInfo':reloadWithDebugInfo});angularModule=setupModuleLoader(window);angularModule('ng',['ngLocale'],['$provide',function ngModule($provide){// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. $provide.provider({$$sanitizeUri:$$SanitizeUriProvider});$provide.provider('$compile',$CompileProvider).directive({a:htmlAnchorDirective,input:inputDirective,textarea:inputDirective,form:formDirective,script:scriptDirective,select:selectDirective,option:optionDirective,ngBind:ngBindDirective,ngBindHtml:ngBindHtmlDirective,ngBindTemplate:ngBindTemplateDirective,ngClass:ngClassDirective,ngClassEven:ngClassEvenDirective,ngClassOdd:ngClassOddDirective,ngCloak:ngCloakDirective,ngController:ngControllerDirective,ngForm:ngFormDirective,ngHide:ngHideDirective,ngIf:ngIfDirective,ngInclude:ngIncludeDirective,ngInit:ngInitDirective,ngNonBindable:ngNonBindableDirective,ngPluralize:ngPluralizeDirective,ngRepeat:ngRepeatDirective,ngShow:ngShowDirective,ngStyle:ngStyleDirective,ngSwitch:ngSwitchDirective,ngSwitchWhen:ngSwitchWhenDirective,ngSwitchDefault:ngSwitchDefaultDirective,ngOptions:ngOptionsDirective,ngTransclude:ngTranscludeDirective,ngModel:ngModelDirective,ngList:ngListDirective,ngChange:ngChangeDirective,pattern:patternDirective,ngPattern:patternDirective,required:requiredDirective,ngRequired:requiredDirective,minlength:minlengthDirective,ngMinlength:minlengthDirective,maxlength:maxlengthDirective,ngMaxlength:maxlengthDirective,ngValue:ngValueDirective,ngModelOptions:ngModelOptionsDirective}).directive({ngInclude:ngIncludeFillContentDirective}).directive(ngAttributeAliasDirectives).directive(ngEventDirectives);$provide.provider({$anchorScroll:$AnchorScrollProvider,$animate:$AnimateProvider,$animateCss:$CoreAnimateCssProvider,$$animateJs:$$CoreAnimateJsProvider,$$animateQueue:$$CoreAnimateQueueProvider,$$AnimateRunner:$$AnimateRunnerFactoryProvider,$$animateAsyncRun:$$AnimateAsyncRunFactoryProvider,$browser:$BrowserProvider,$cacheFactory:$CacheFactoryProvider,$controller:$ControllerProvider,$document:$DocumentProvider,$exceptionHandler:$ExceptionHandlerProvider,$filter:$FilterProvider,$$forceReflow:$$ForceReflowProvider,$interpolate:$InterpolateProvider,$interval:$IntervalProvider,$http:$HttpProvider,$httpParamSerializer:$HttpParamSerializerProvider,$httpParamSerializerJQLike:$HttpParamSerializerJQLikeProvider,$httpBackend:$HttpBackendProvider,$xhrFactory:$xhrFactoryProvider,$jsonpCallbacks:$jsonpCallbacksProvider,$location:$LocationProvider,$log:$LogProvider,$parse:$ParseProvider,$rootScope:$RootScopeProvider,$q:$QProvider,$$q:$$QProvider,$sce:$SceProvider,$sceDelegate:$SceDelegateProvider,$sniffer:$SnifferProvider,$templateCache:$TemplateCacheProvider,$templateRequest:$TemplateRequestProvider,$$testability:$$TestabilityProvider,$timeout:$TimeoutProvider,$window:$WindowProvider,$$rAF:$$RAFProvider,$$jqLite:$$jqLiteProvider,$$HashMap:$$HashMapProvider,$$cookieReader:$$CookieReaderProvider});}]);}/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Any commits to this file should be reviewed with security in mind. * * Changes to this file can potentially create security vulnerabilities. * * An approval from 2 Core members with history of modifying * * this file is required. * * * * Does the change somehow allow for arbitrary javascript to be executed? * * Or allows for someone to change the prototype of built-in objects? * * Or gives undesired access to variables likes document or window? * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//* global JQLitePrototype: true, addEventListenerFn: true, removeEventListenerFn: true, BOOLEAN_ATTR: true, ALIASED_ATTR: true */////////////////////////////////// //JQLite ////////////////////////////////// /** * @ngdoc function * @name angular.element * @module ng * @kind function * * @description * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. * * If jQuery is available, `angular.element` is an alias for the * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**. * * jqLite is a tiny, API-compatible subset of jQuery that allows * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most * commonly needed functionality with the goal of having a very small footprint. * * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a * specific version of jQuery if multiple versions exist on the page. * *
      **Note:** All element references in Angular are always wrapped with jQuery or * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.
      * *
      **Note:** Keep in mind that this function will not find elements * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)` * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.
      * * ## Angular's jqLite * jqLite provides only the following jQuery methods: * * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument * - [`after()`](http://api.jquery.com/after/) * - [`append()`](http://api.jquery.com/append/) * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData * - [`children()`](http://api.jquery.com/children/) - Does not support selectors * - [`clone()`](http://api.jquery.com/clone/) * - [`contents()`](http://api.jquery.com/contents/) * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing. * - [`data()`](http://api.jquery.com/data/) * - [`detach()`](http://api.jquery.com/detach/) * - [`empty()`](http://api.jquery.com/empty/) * - [`eq()`](http://api.jquery.com/eq/) * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name * - [`hasClass()`](http://api.jquery.com/hasClass/) * - [`html()`](http://api.jquery.com/html/) * - [`next()`](http://api.jquery.com/next/) - Does not support selectors * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors * - [`prepend()`](http://api.jquery.com/prepend/) * - [`prop()`](http://api.jquery.com/prop/) * - [`ready()`](http://api.jquery.com/ready/) * - [`remove()`](http://api.jquery.com/remove/) * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument * - [`removeData()`](http://api.jquery.com/removeData/) * - [`replaceWith()`](http://api.jquery.com/replaceWith/) * - [`text()`](http://api.jquery.com/text/) * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter * - [`val()`](http://api.jquery.com/val/) * - [`wrap()`](http://api.jquery.com/wrap/) * * ## jQuery/jqLite Extras * Angular also provides the following additional methods and events to both jQuery and jqLite: * * ### Events * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM * element before it is removed. * * ### Methods * - `controller(name)` - retrieves the controller of the current element or its parent. By default * retrieves controller associated with the `ngController` directive. If `name` is provided as * camelCase directive name, then the controller for this directive will be retrieved (e.g. * `'ngModel'`). * - `injector()` - retrieves the injector of the current element or its parent. * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to * be enabled. * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the * current element. This getter should be used only on elements that contain a directive which starts a new isolate * scope. Calling `scope()` on this element always returns the original non-isolate scope. * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top * parent element is reached. * * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See * https://github.com/angular/angular.js/issues/14251 for more information. * * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. * @returns {Object} jQuery object. */JQLite.expando='ng339';var jqCache=JQLite.cache={},jqId=1,addEventListenerFn=function addEventListenerFn(element,type,fn){element.addEventListener(type,fn,false);},removeEventListenerFn=function removeEventListenerFn(element,type,fn){element.removeEventListener(type,fn,false);};/* * !!! This is an undocumented "private" function !!! */JQLite._data=function(node){//jQuery always returns an object on cache miss return this.cache[node[this.expando]]||{};};function jqNextId(){return++jqId;}var SPECIAL_CHARS_REGEXP=/([:\-_]+(.))/g;var MOZ_HACK_REGEXP=/^moz([A-Z])/;var MOUSE_EVENT_MAP={mouseleave:'mouseout',mouseenter:'mouseover'};var jqLiteMinErr=minErr('jqLite');/** * Converts snake_case to camelCase. * Also there is special case for Moz prefix starting with upper case letter. * @param name Name to normalize */function camelCase(name){return name.replace(SPECIAL_CHARS_REGEXP,function(_,separator,letter,offset){return offset?letter.toUpperCase():letter;}).replace(MOZ_HACK_REGEXP,'Moz$1');}var SINGLE_TAG_REGEXP=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;var HTML_REGEXP=/<|&#?\w+;/;var TAG_NAME_REGEXP=/<([\w:-]+)/;var XHTML_TAG_REGEXP=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;var wrapMap={'option':[1,''],'thead':[1,'','
      '],'col':[2,'','
      '],'tr':[2,'','
      '],'td':[3,'','
      '],'_default':[0,'','']};wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;function jqLiteIsTextNode(html){return!HTML_REGEXP.test(html);}function jqLiteAcceptsData(node){// The window object can accept data but has no nodeType // Otherwise we are only interested in elements (1) and documents (9) var nodeType=node.nodeType;return nodeType===NODE_TYPE_ELEMENT||!nodeType||nodeType===NODE_TYPE_DOCUMENT;}function jqLiteHasData(node){for(var key in jqCache[node.ng339]){return true;}return false;}function jqLiteCleanData(nodes){for(var i=0,ii=nodes.length;i')+wrap[2];// Descend through wrappers to the right content i=wrap[0];while(i--){tmp=tmp.lastChild;}nodes=concat(nodes,tmp.childNodes);tmp=fragment.firstChild;tmp.textContent='';}// Remove wrapper from fragment fragment.textContent='';fragment.innerHTML='';// Clear inner HTML forEach(nodes,function(node){fragment.appendChild(node);});return fragment;}function jqLiteParseHTML(html,context){context=context||window.document;var parsed;if(parsed=SINGLE_TAG_REGEXP.exec(html)){return[context.createElement(parsed[1])];}if(parsed=jqLiteBuildFragment(html,context)){return parsed.childNodes;}return[];}function jqLiteWrapNode(node,wrapper){var parent=node.parentNode;if(parent){parent.replaceChild(wrapper,node);}wrapper.appendChild(node);}// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. var jqLiteContains=window.Node.prototype.contains||/** @this */function(arg){// eslint-disable-next-line no-bitwise return!!(this.compareDocumentPosition(arg)&16);};///////////////////////////////////////////// function JQLite(element){if(element instanceof JQLite){return element;}var argIsString;if(isString(element)){element=trim(element);argIsString=true;}if(!(this instanceof JQLite)){if(argIsString&&element.charAt(0)!=='<'){throw jqLiteMinErr('nosel','Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');}return new JQLite(element);}if(argIsString){jqLiteAddNodes(this,jqLiteParseHTML(element));}else{jqLiteAddNodes(this,element);}}function jqLiteClone(element){return element.cloneNode(true);}function jqLiteDealoc(element,onlyDescendants){if(!onlyDescendants)jqLiteRemoveData(element);if(element.querySelectorAll){var descendants=element.querySelectorAll('*');for(var i=0,l=descendants.length;i0)){removeEventListenerFn(element,type,handle);delete events[type];}};forEach(type.split(' '),function(type){removeHandler(type);if(MOUSE_EVENT_MAP[type]){removeHandler(MOUSE_EVENT_MAP[type]);}});}}function jqLiteRemoveData(element,name){var expandoId=element.ng339;var expandoStore=expandoId&&jqCache[expandoId];if(expandoStore){if(name){delete expandoStore.data[name];return;}if(expandoStore.handle){if(expandoStore.events.$destroy){expandoStore.handle({},'$destroy');}jqLiteOff(element);}delete jqCache[expandoId];element.ng339=undefined;// don't delete DOM expandos. IE and Chrome don't like it }}function jqLiteExpandoStore(element,createIfNecessary){var expandoId=element.ng339,expandoStore=expandoId&&jqCache[expandoId];if(createIfNecessary&&!expandoStore){element.ng339=expandoId=jqNextId();expandoStore=jqCache[expandoId]={events:{},data:{},handle:undefined};}return expandoStore;}function jqLiteData(element,key,value){if(jqLiteAcceptsData(element)){var isSimpleSetter=isDefined(value);var isSimpleGetter=!isSimpleSetter&&key&&!isObject(key);var massGetter=!key;var expandoStore=jqLiteExpandoStore(element,!isSimpleGetter);var data=expandoStore&&expandoStore.data;if(isSimpleSetter){// data('key', value) data[key]=value;}else{if(massGetter){// data() return data;}else{if(isSimpleGetter){// data('key') // don't force creation of expandoStore if it doesn't exist yet return data&&data[key];}else{// mass-setter: data({key1: val1, key2: val2}) extend(data,key);}}}}}function jqLiteHasClass(element,selector){if(!element.getAttribute)return false;return(' '+(element.getAttribute('class')||'')+' ').replace(/[\n\t]/g,' ').indexOf(' '+selector+' ')>-1;}function jqLiteRemoveClass(element,cssClasses){if(cssClasses&&element.setAttribute){forEach(cssClasses.split(' '),function(cssClass){element.setAttribute('class',trim((' '+(element.getAttribute('class')||'')+' ').replace(/[\n\t]/g,' ').replace(' '+trim(cssClass)+' ',' ')));});}}function jqLiteAddClass(element,cssClasses){if(cssClasses&&element.setAttribute){var existingClasses=(' '+(element.getAttribute('class')||'')+' ').replace(/[\n\t]/g,' ');forEach(cssClasses.split(' '),function(cssClass){cssClass=trim(cssClass);if(existingClasses.indexOf(' '+cssClass+' ')===-1){existingClasses+=cssClass+' ';}});element.setAttribute('class',trim(existingClasses));}}function jqLiteAddNodes(root,elements){// THIS CODE IS VERY HOT. Don't make changes without benchmarking. if(elements){// if a Node (the most common case) if(elements.nodeType){root[root.length++]=elements;}else{var length=elements.length;// if an Array or NodeList and not a Window if(typeof length==='number'&&elements.window!==elements){if(length){for(var i=0;i=0?jqLite(this[index]):jqLite(this[this.length+index]);},length:0,push:push,sort:[].sort,splice:[].splice};////////////////////////////////////////// // Functions iterating getter/setters. // these functions return self on setter and // value on get. ////////////////////////////////////////// var BOOLEAN_ATTR={};forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','),function(value){BOOLEAN_ATTR[lowercase(value)]=value;});var BOOLEAN_ELEMENTS={};forEach('input,select,option,textarea,button,form,details'.split(','),function(value){BOOLEAN_ELEMENTS[value]=true;});var ALIASED_ATTR={'ngMinlength':'minlength','ngMaxlength':'maxlength','ngMin':'min','ngMax':'max','ngPattern':'pattern'};function getBooleanAttrName(element,name){// check dom last since we will most likely fail on name var booleanAttr=BOOLEAN_ATTR[name.toLowerCase()];// booleanAttr is here twice to minimize DOM access return booleanAttr&&BOOLEAN_ELEMENTS[nodeName_(element)]&&booleanAttr;}function getAliasedAttrName(name){return ALIASED_ATTR[name];}forEach({data:jqLiteData,removeData:jqLiteRemoveData,hasData:jqLiteHasData,cleanData:jqLiteCleanData},function(fn,name){JQLite[name]=fn;});forEach({data:jqLiteData,inheritedData:jqLiteInheritedData,scope:function scope(element){// Can't use jqLiteData here directly so we stay compatible with jQuery! return jqLite.data(element,'$scope')||jqLiteInheritedData(element.parentNode||element,['$isolateScope','$scope']);},isolateScope:function isolateScope(element){// Can't use jqLiteData here directly so we stay compatible with jQuery! return jqLite.data(element,'$isolateScope')||jqLite.data(element,'$isolateScopeNoTemplate');},controller:jqLiteController,injector:function injector(element){return jqLiteInheritedData(element,'$injector');},removeAttr:function removeAttr(element,name){element.removeAttribute(name);},hasClass:jqLiteHasClass,css:function css(element,name,value){name=camelCase(name);if(isDefined(value)){element.style[name]=value;}else{return element.style[name];}},attr:function attr(element,name,value){var nodeType=element.nodeType;if(nodeType===NODE_TYPE_TEXT||nodeType===NODE_TYPE_ATTRIBUTE||nodeType===NODE_TYPE_COMMENT){return;}var lowercasedName=lowercase(name);if(BOOLEAN_ATTR[lowercasedName]){if(isDefined(value)){if(value){element[name]=true;element.setAttribute(name,lowercasedName);}else{element[name]=false;element.removeAttribute(lowercasedName);}}else{return element[name]||(element.attributes.getNamedItem(name)||noop).specified?lowercasedName:undefined;}}else if(isDefined(value)){element.setAttribute(name,value);}else if(element.getAttribute){// the extra argument "2" is to get the right thing for a.href in IE, see jQuery code // some elements (e.g. Document) don't have get attribute, so return undefined var ret=element.getAttribute(name,2);// normalize non-existing attributes to undefined (as jQuery) return ret===null?undefined:ret;}},prop:function prop(element,name,value){if(isDefined(value)){element[name]=value;}else{return element[name];}},text:function(){getText.$dv='';return getText;function getText(element,value){if(isUndefined(value)){var nodeType=element.nodeType;return nodeType===NODE_TYPE_ELEMENT||nodeType===NODE_TYPE_TEXT?element.textContent:'';}element.textContent=value;}}(),val:function val(element,value){if(isUndefined(value)){if(element.multiple&&nodeName_(element)==='select'){var result=[];forEach(element.options,function(option){if(option.selected){result.push(option.value||option.text);}});return result.length===0?null:result;}return element.value;}element.value=value;},html:function html(element,value){if(isUndefined(value)){return element.innerHTML;}jqLiteDealoc(element,true);element.innerHTML=value;},empty:jqLiteEmpty},function(fn,name){/** * Properties: writes return selection, reads return first value */JQLite.prototype[name]=function(arg1,arg2){var i,key;var nodeCount=this.length;// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it // in a way that survives minification. // jqLiteEmpty takes no arguments but is a setter. if(fn!==jqLiteEmpty&&isUndefined(fn.length===2&&fn!==jqLiteHasClass&&fn!==jqLiteController?arg1:arg2)){if(isObject(arg1)){// we are a write, but the object properties are the key/values for(i=0;i1){eventFns=shallowCopy(eventFns);}for(var i=0;i=0?type.split(' '):[type];var i=types.length;var addHandler=function addHandler(type,specialHandlerWrapper,noEventListener){var eventFns=events[type];if(!eventFns){eventFns=events[type]=[];eventFns.specialHandlerWrapper=specialHandlerWrapper;if(type!=='$destroy'&&!noEventListener){addEventListenerFn(element,type,handle);}}eventFns.push(fn);};while(i--){type=types[i];if(MOUSE_EVENT_MAP[type]){addHandler(MOUSE_EVENT_MAP[type],specialMouseHandlerWrapper);addHandler(type,undefined,true);}else{addHandler(type);}}},off:jqLiteOff,one:function one(element,type,fn){element=jqLite(element);//add the listener twice so that when it is called //you can remove the original function and still be //able to call element.off(ev, fn) normally element.on(type,function onFn(){element.off(type,fn);element.off(type,onFn);});element.on(type,fn);},replaceWith:function replaceWith(element,replaceNode){var index,parent=element.parentNode;jqLiteDealoc(element);forEach(new JQLite(replaceNode),function(node){if(index){parent.insertBefore(node,index.nextSibling);}else{parent.replaceChild(node,element);}index=node;});},children:function children(element){var children=[];forEach(element.childNodes,function(element){if(element.nodeType===NODE_TYPE_ELEMENT){children.push(element);}});return children;},contents:function contents(element){return element.contentDocument||element.childNodes||[];},append:function append(element,node){var nodeType=element.nodeType;if(nodeType!==NODE_TYPE_ELEMENT&&nodeType!==NODE_TYPE_DOCUMENT_FRAGMENT)return;node=new JQLite(node);for(var i=0,ii=node.length;i} modules A list of module functions or their aliases. See * {@link angular.module}. The `ng` module must be explicitly added. * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which * disallows argument name annotation inference. * @returns {injector} Injector object. See {@link auto.$injector $injector}. * * @example * Typical usage * ```js * // create an injector * var $injector = angular.injector(['ng']); * * // use the injector to kick off your application * // use the type inference to auto inject arguments, or use implicit injection * $injector.invoke(function($rootScope, $compile, $document) { * $compile($document)($rootScope); * $rootScope.$digest(); * }); * ``` * * Sometimes you want to get access to the injector of a currently running Angular app * from outside Angular. Perhaps, you want to inject and compile some markup after the * application has been bootstrapped. You can do this using the extra `injector()` added * to JQuery/jqLite elements. See {@link angular.element}. * * *This is fairly rare but could be the case if a third party library is injecting the * markup.* * * In the following example a new block of HTML containing a `ng-controller` * directive is added to the end of the document body by JQuery. We then compile and link * it into the current AngularJS scope. * * ```js * var $div = $('
      {{content.label}}
      '); * $(document.body).append($div); * * angular.element(document).injector().invoke(function($compile) { * var scope = angular.element($div).scope(); * $compile($div)(scope); * }); * ``` *//** * @ngdoc module * @name auto * @installation * @description * * Implicit module which gets automatically added to each {@link auto.$injector $injector}. */var ARROW_ARG=/^([^(]+?)=>/;var FN_ARGS=/^[^(]*\(\s*([^)]*)\)/m;var FN_ARG_SPLIT=/,/;var FN_ARG=/^\s*(_?)(\S+?)\1\s*$/;var STRIP_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;var $injectorMinErr=minErr('$injector');function stringifyFn(fn){// Support: Chrome 50-51 only // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51 // (See https://github.com/angular/angular.js/issues/14487.) // TODO (gkalpak): Remove workaround when Chrome v52 is released return Function.prototype.toString.call(fn)+' ';}function extractArgs(fn){var fnText=stringifyFn(fn).replace(STRIP_COMMENTS,''),args=fnText.match(ARROW_ARG)||fnText.match(FN_ARGS);return args;}function anonFn(fn){// For anonymous functions, showing at the very least the function signature can help in // debugging. var args=extractArgs(fn);if(args){return'function('+(args[1]||'').replace(/[\s\r\n]+/,' ')+')';}return'fn';}function annotate(fn,strictDi,name){var $inject,argDecl,last;if(typeof fn==='function'){if(!($inject=fn.$inject)){$inject=[];if(fn.length){if(strictDi){if(!isString(name)||!name){name=fn.name||anonFn(fn);}throw $injectorMinErr('strictdi','{0} is not using explicit annotation and cannot be invoked in strict mode',name);}argDecl=extractArgs(fn);forEach(argDecl[1].split(FN_ARG_SPLIT),function(arg){arg.replace(FN_ARG,function(all,underscore,name){$inject.push(name);});});}fn.$inject=$inject;}}else if(isArray(fn)){last=fn.length-1;assertArgFn(fn[last],'fn');$inject=fn.slice(0,last);}else{assertArgFn(fn,'fn',true);}return $inject;}/////////////////////////////////////// /** * @ngdoc service * @name $injector * * @description * * `$injector` is used to retrieve object instances as defined by * {@link auto.$provide provider}, instantiate types, invoke methods, * and load modules. * * The following always holds true: * * ```js * var $injector = angular.injector(); * expect($injector.get('$injector')).toBe($injector); * expect($injector.invoke(function($injector) { * return $injector; * })).toBe($injector); * ``` * * # Injection Function Annotation * * JavaScript does not have annotations, and annotations are needed for dependency injection. The * following are all valid ways of annotating function with injection arguments and are equivalent. * * ```js * // inferred (only works if code not minified/obfuscated) * $injector.invoke(function(serviceA){}); * * // annotated * function explicit(serviceA) {}; * explicit.$inject = ['serviceA']; * $injector.invoke(explicit); * * // inline * $injector.invoke(['serviceA', function(serviceA){}]); * ``` * * ## Inference * * In JavaScript calling `toString()` on a function returns the function definition. The definition * can then be parsed and the function arguments can be extracted. This method of discovering * annotations is disallowed when the injector is in strict mode. * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the * argument names. * * ## `$inject` Annotation * By adding an `$inject` property onto a function the injection parameters can be specified. * * ## Inline * As an array of injection names, where the last item in the array is the function to call. *//** * @ngdoc method * @name $injector#get * * @description * Return an instance of the service. * * @param {string} name The name of the instance to retrieve. * @param {string=} caller An optional string to provide the origin of the function call for error messages. * @return {*} The instance. *//** * @ngdoc method * @name $injector#invoke * * @description * Invoke the method and supply the method arguments from the `$injector`. * * @param {Function|Array.} fn The injectable function to invoke. Function parameters are * injected according to the {@link guide/di $inject Annotation} rules. * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this * object first, before the `$injector` is consulted. * @returns {*} the value returned by the invoked `fn` function. *//** * @ngdoc method * @name $injector#has * * @description * Allows the user to query if the particular service exists. * * @param {string} name Name of the service to query. * @returns {boolean} `true` if injector has given service. *//** * @ngdoc method * @name $injector#instantiate * @description * Create a new instance of JS type. The method takes a constructor function, invokes the new * operator, and supplies all of the arguments to the constructor function as specified by the * constructor annotation. * * @param {Function} Type Annotated constructor function. * @param {Object=} locals Optional object. If preset then any argument names are read from this * object first, before the `$injector` is consulted. * @returns {Object} new instance of `Type`. *//** * @ngdoc method * @name $injector#annotate * * @description * Returns an array of service names which the function is requesting for injection. This API is * used by the injector to determine which services need to be injected into the function when the * function is invoked. There are three ways in which the function can be annotated with the needed * dependencies. * * # Argument names * * The simplest form is to extract the dependencies from the arguments of the function. This is done * by converting the function into a string using `toString()` method and extracting the argument * names. * ```js * // Given * function MyController($scope, $route) { * // ... * } * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * ``` * * You can disallow this method by using strict injection mode. * * This method does not work with code minification / obfuscation. For this reason the following * annotation strategies are supported. * * # The `$inject` property * * If a function has an `$inject` property and its value is an array of strings, then the strings * represent names of services to be injected into the function. * ```js * // Given * var MyController = function(obfuscatedScope, obfuscatedRoute) { * // ... * } * // Define function dependencies * MyController['$inject'] = ['$scope', '$route']; * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * ``` * * # The array notation * * It is often desirable to inline Injected functions and that's when setting the `$inject` property * is very inconvenient. In these situations using the array notation to specify the dependencies in * a way that survives minification is a better choice: * * ```js * // We wish to write this (not minification / obfuscation safe) * injector.invoke(function($compile, $rootScope) { * // ... * }); * * // We are forced to write break inlining * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { * // ... * }; * tmpFn.$inject = ['$compile', '$rootScope']; * injector.invoke(tmpFn); * * // To better support inline function the inline annotation is supported * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { * // ... * }]); * * // Therefore * expect(injector.annotate( * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) * ).toEqual(['$compile', '$rootScope']); * ``` * * @param {Function|Array.} fn Function for which dependent service names need to * be retrieved as described above. * * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. * * @returns {Array.} The names of the services which the function requires. *//** * @ngdoc service * @name $provide * * @description * * The {@link auto.$provide $provide} service has a number of methods for registering components * with the {@link auto.$injector $injector}. Many of these functions are also exposed on * {@link angular.Module}. * * An Angular **service** is a singleton object created by a **service factory**. These **service * factories** are functions which, in turn, are created by a **service provider**. * The **service providers** are constructor functions. When instantiated they must contain a * property called `$get`, which holds the **service factory** function. * * When you request a service, the {@link auto.$injector $injector} is responsible for finding the * correct **service provider**, instantiating it and then calling its `$get` **service factory** * function to get the instance of the **service**. * * Often services have no configuration options and there is no need to add methods to the service * provider. The provider will be no more than a constructor function with a `$get` property. For * these cases the {@link auto.$provide $provide} service has additional helper methods to register * services without specifying a provider. * * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the * {@link auto.$injector $injector} * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by * providers and services. * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by * services, not providers. * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function** * that will be wrapped in a **service provider** object, whose `$get` property will contain the * given factory function. * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function** * that will be wrapped in a **service provider** object, whose `$get` property will instantiate * a new object using the given constructor function. * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that * will be able to modify or replace the implementation of another service. * * See the individual methods for more information and examples. *//** * @ngdoc method * @name $provide#provider * @description * * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions * are constructor functions, whose instances are responsible for "providing" a factory for a * service. * * Service provider names start with the name of the service they provide followed by `Provider`. * For example, the {@link ng.$log $log} service has a provider called * {@link ng.$logProvider $logProvider}. * * Service provider objects can have additional methods which allow configuration of the provider * and its service. Importantly, you can configure what kind of service is created by the `$get` * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a * method {@link ng.$logProvider#debugEnabled debugEnabled} * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the * console or not. * * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. * @param {(Object|function())} provider If the provider is: * * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. * - `Constructor`: a new instance of the provider will be created using * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. * * @returns {Object} registered provider instance * @example * * The following example shows how to create a simple event tracking service and register it using * {@link auto.$provide#provider $provide.provider()}. * * ```js * // Define the eventTracker provider * function EventTrackerProvider() { * var trackingUrl = '/track'; * * // A provider method for configuring where the tracked events should been saved * this.setTrackingUrl = function(url) { * trackingUrl = url; * }; * * // The service factory function * this.$get = ['$http', function($http) { * var trackedEvents = {}; * return { * // Call this to track an event * event: function(event) { * var count = trackedEvents[event] || 0; * count += 1; * trackedEvents[event] = count; * return count; * }, * // Call this to save the tracked events to the trackingUrl * save: function() { * $http.post(trackingUrl, trackedEvents); * } * }; * }]; * } * * describe('eventTracker', function() { * var postSpy; * * beforeEach(module(function($provide) { * // Register the eventTracker provider * $provide.provider('eventTracker', EventTrackerProvider); * })); * * beforeEach(module(function(eventTrackerProvider) { * // Configure eventTracker provider * eventTrackerProvider.setTrackingUrl('/custom-track'); * })); * * it('tracks events', inject(function(eventTracker) { * expect(eventTracker.event('login')).toEqual(1); * expect(eventTracker.event('login')).toEqual(2); * })); * * it('saves to the tracking url', inject(function(eventTracker, $http) { * postSpy = spyOn($http, 'post'); * eventTracker.event('login'); * eventTracker.save(); * expect(postSpy).toHaveBeenCalled(); * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); * })); * }); * ``` *//** * @ngdoc method * @name $provide#factory * @description * * Register a **service factory**, which will be called to return the service instance. * This is short for registering a service where its provider consists of only a `$get` property, * which is the given service factory function. * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to * configure your service in a provider. * * @param {string} name The name of the instance. * @param {Function|Array.} $getFn The injectable $getFn for the instance creation. * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. * @returns {Object} registered provider instance * * @example * Here is an example of registering a service * ```js * $provide.factory('ping', ['$http', function($http) { * return function ping() { * return $http.send('/ping'); * }; * }]); * ``` * You would then inject and use this service like this: * ```js * someModule.controller('Ctrl', ['ping', function(ping) { * ping(); * }]); * ``` *//** * @ngdoc method * @name $provide#service * @description * * Register a **service constructor**, which will be invoked with `new` to create the service * instance. * This is short for registering a service where its provider's `$get` property is a factory * function that returns an instance instantiated by the injector from the service constructor * function. * * Internally it looks a bit like this: * * ``` * { * $get: function() { * return $injector.instantiate(constructor); * } * } * ``` * * * You should use {@link auto.$provide#service $provide.service(class)} if you define your service * as a type/class. * * @param {string} name The name of the instance. * @param {Function|Array.} constructor An injectable class (constructor function) * that will be instantiated. * @returns {Object} registered provider instance * * @example * Here is an example of registering a service using * {@link auto.$provide#service $provide.service(class)}. * ```js * var Ping = function($http) { * this.$http = $http; * }; * * Ping.$inject = ['$http']; * * Ping.prototype.send = function() { * return this.$http.get('/ping'); * }; * $provide.service('ping', Ping); * ``` * You would then inject and use this service like this: * ```js * someModule.controller('Ctrl', ['ping', function(ping) { * ping.send(); * }]); * ``` *//** * @ngdoc method * @name $provide#value * @description * * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a * number, an array, an object or a function. This is short for registering a service where its * provider's `$get` property is a factory function that takes no arguments and returns the **value * service**. That also means it is not possible to inject other services into a value service. * * Value services are similar to constant services, except that they cannot be injected into a * module configuration function (see {@link angular.Module#config}) but they can be overridden by * an Angular {@link auto.$provide#decorator decorator}. * * @param {string} name The name of the instance. * @param {*} value The value. * @returns {Object} registered provider instance * * @example * Here are some examples of creating value services. * ```js * $provide.value('ADMIN_USER', 'admin'); * * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); * * $provide.value('halfOf', function(value) { * return value / 2; * }); * ``` *//** * @ngdoc method * @name $provide#constant * @description * * Register a **constant service** with the {@link auto.$injector $injector}, such as a string, * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not * possible to inject other services into a constant. * * But unlike {@link auto.$provide#value value}, a constant can be * injected into a module configuration function (see {@link angular.Module#config}) and it cannot * be overridden by an Angular {@link auto.$provide#decorator decorator}. * * @param {string} name The name of the constant. * @param {*} value The constant value. * @returns {Object} registered instance * * @example * Here a some examples of creating constants: * ```js * $provide.constant('SHARD_HEIGHT', 306); * * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); * * $provide.constant('double', function(value) { * return value * 2; * }); * ``` *//** * @ngdoc method * @name $provide#decorator * @description * * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function * intercepts the creation of a service, allowing it to override or modify the behavior of the * service. The return value of the decorator function may be the original service, or a new service * that replaces (or wraps and delegates to) the original service. * * You can find out more about using decorators in the {@link guide/decorators} guide. * * @param {string} name The name of the service to decorate. * @param {Function|Array.} decorator This function will be invoked when the service needs to be * provided and should return the decorated service instance. The function is called using * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. * Local injection arguments: * * * `$delegate` - The original service instance, which can be replaced, monkey patched, configured, * decorated or delegated to. * * @example * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting * calls to {@link ng.$log#error $log.warn()}. * ```js * $provide.decorator('$log', ['$delegate', function($delegate) { * $delegate.warn = $delegate.error; * return $delegate; * }]); * ``` */function createInjector(modulesToLoad,strictDi){strictDi=strictDi===true;var INSTANTIATING={},providerSuffix='Provider',path=[],loadedModules=new HashMap([],true),providerCache={$provide:{provider:supportObject(provider),factory:supportObject(factory),service:supportObject(service),value:supportObject(value),constant:supportObject(constant),decorator:decorator}},providerInjector=providerCache.$injector=createInternalInjector(providerCache,function(serviceName,caller){if(angular.isString(caller)){path.push(caller);}throw $injectorMinErr('unpr','Unknown provider: {0}',path.join(' <- '));}),instanceCache={},protoInstanceInjector=createInternalInjector(instanceCache,function(serviceName,caller){var provider=providerInjector.get(serviceName+providerSuffix,caller);return instanceInjector.invoke(provider.$get,provider,undefined,serviceName);}),instanceInjector=protoInstanceInjector;providerCache['$injector'+providerSuffix]={$get:valueFn(protoInstanceInjector)};var runBlocks=loadModules(modulesToLoad);instanceInjector=protoInstanceInjector.get('$injector');instanceInjector.strictDi=strictDi;forEach(runBlocks,function(fn){if(fn)instanceInjector.invoke(fn);});return instanceInjector;//////////////////////////////////// // $provider //////////////////////////////////// function supportObject(delegate){return function(key,value){if(isObject(key)){forEach(key,reverseParams(delegate));}else{return delegate(key,value);}};}function provider(name,provider_){assertNotHasOwnProperty(name,'service');if(isFunction(provider_)||isArray(provider_)){provider_=providerInjector.instantiate(provider_);}if(!provider_.$get){throw $injectorMinErr('pget','Provider \'{0}\' must define $get factory method.',name);}return providerCache[name+providerSuffix]=provider_;}function enforceReturnValue(name,factory){return(/** @this */function enforcedReturnValue(){var result=instanceInjector.invoke(factory,this);if(isUndefined(result)){throw $injectorMinErr('undef','Provider \'{0}\' must return a value from $get factory method.',name);}return result;});}function factory(name,factoryFn,enforce){return provider(name,{$get:enforce!==false?enforceReturnValue(name,factoryFn):factoryFn});}function service(name,constructor){return factory(name,['$injector',function($injector){return $injector.instantiate(constructor);}]);}function value(name,val){return factory(name,valueFn(val),false);}function constant(name,value){assertNotHasOwnProperty(name,'constant');providerCache[name]=value;instanceCache[name]=value;}function decorator(serviceName,decorFn){var origProvider=providerInjector.get(serviceName+providerSuffix),orig$get=origProvider.$get;origProvider.$get=function(){var origInstance=instanceInjector.invoke(orig$get,origProvider);return instanceInjector.invoke(decorFn,null,{$delegate:origInstance});};}//////////////////////////////////// // Module Loading //////////////////////////////////// function loadModules(modulesToLoad){assertArg(isUndefined(modulesToLoad)||isArray(modulesToLoad),'modulesToLoad','not an array');var runBlocks=[],moduleFn;forEach(modulesToLoad,function(module){if(loadedModules.get(module))return;loadedModules.put(module,true);function runInvokeQueue(queue){var i,ii;for(i=0,ii=queue.length;i * Use this method to disable automatic scrolling. * * If automatic scrolling is disabled, one must explicitly call * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the * current hash. */this.disableAutoScrolling=function(){autoScrollingEnabled=false;};/** * @ngdoc service * @name $anchorScroll * @kind function * @requires $window * @requires $location * @requires $rootScope * * @description * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified * in the * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document). * * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to * match any anchor whenever it changes. This can be disabled by calling * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. * * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a * vertical scroll-offset (either fixed or dynamic). * * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of * {@link ng.$location#hash $location.hash()} will be used. * * @property {(number|function|jqLite)} yOffset * If set, specifies a vertical scroll-offset. This is often useful when there are fixed * positioned elements at the top of the page, such as navbars, headers etc. * * `yOffset` can be specified in various ways: * - **number**: A fixed number of pixels to be used as offset.

      * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return * a number representing the offset (in pixels).

      * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from * the top of the page to the element's bottom will be used as offset.
      * **Note**: The element will be taken into account only as long as its `position` is set to * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust * their height and/or positioning according to the viewport's size. * *
      *
      * In order for `yOffset` to work properly, scrolling should take place on the document's root and * not some child element. *
      * * @example
      Go to bottom You're at the bottom!
      angular.module('anchorScrollExample', []) .controller('ScrollController', ['$scope', '$location', '$anchorScroll', function($scope, $location, $anchorScroll) { $scope.gotoBottom = function() { // set the location.hash to the id of // the element you wish to scroll to. $location.hash('bottom'); // call $anchorScroll() $anchorScroll(); }; }]); #scrollArea { height: 280px; overflow: auto; } #bottom { display: block; margin-top: 2000px; } * *
      * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. * * @example
      Anchor {{x}} of 5
      angular.module('anchorScrollOffsetExample', []) .run(['$anchorScroll', function($anchorScroll) { $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels }]) .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', function($anchorScroll, $location, $scope) { $scope.gotoAnchor = function(x) { var newHash = 'anchor' + x; if ($location.hash() !== newHash) { // set the $location.hash to `newHash` and // $anchorScroll will automatically scroll to it $location.hash('anchor' + x); } else { // call $anchorScroll() explicitly, // since $location.hash hasn't changed $anchorScroll(); } }; } ]); body { padding-top: 50px; } .anchor { border: 2px dashed DarkOrchid; padding: 10px 10px 200px 10px; } .fixed-header { background-color: rgba(0, 0, 0, 0.2); height: 50px; position: fixed; top: 0; left: 0; right: 0; } .fixed-header > a { display: inline-block; margin: 5px 15px; }
      */this.$get=['$window','$location','$rootScope',function($window,$location,$rootScope){var document=$window.document;// Helper function to get first anchor from a NodeList // (using `Array#some()` instead of `angular#forEach()` since it's more performant // and working in all supported browsers.) function getFirstAnchor(list){var result=null;Array.prototype.some.call(list,function(element){if(nodeName_(element)==='a'){result=element;return true;}});return result;}function getYOffset(){var offset=scroll.yOffset;if(isFunction(offset)){offset=offset();}else if(isElement(offset)){var elem=offset[0];var style=$window.getComputedStyle(elem);if(style.position!=='fixed'){offset=0;}else{offset=elem.getBoundingClientRect().bottom;}}else if(!isNumber(offset)){offset=0;}return offset;}function scrollTo(elem){if(elem){elem.scrollIntoView();var offset=getYOffset();if(offset){// `offset` is the number of pixels we should scroll UP in order to align `elem` properly. // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the // top of the viewport. // // IF the number of pixels from the top of `elem` to the end of the page's content is less // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some // way down the page. // // This is often the case for elements near the bottom of the page. // // In such cases we do not need to scroll the whole `offset` up, just the difference between // the top of the element and the offset, which is enough to align the top of `elem` at the // desired position. var elemTop=elem.getBoundingClientRect().top;$window.scrollBy(0,elemTop-offset);}}else{$window.scrollTo(0,0);}}function scroll(hash){// Allow numeric hashes hash=isString(hash)?hash:isNumber(hash)?hash.toString():$location.hash();var elm;// empty hash, scroll to the top of the page if(!hash)scrollTo(null);// element with given id else if(elm=document.getElementById(hash))scrollTo(elm);// first anchor with given name :-D else if(elm=getFirstAnchor(document.getElementsByName(hash)))scrollTo(elm);// no element and hash === 'top', scroll to the top of the page else if(hash==='top')scrollTo(null);}// does not scroll when user clicks on anchor link that is currently on // (no url change, no $location.hash() change), browser native does scroll if(autoScrollingEnabled){$rootScope.$watch(function autoScrollWatch(){return $location.hash();},function autoScrollWatchAction(newVal,oldVal){// skip the initial scroll if $location.hash is empty if(newVal===oldVal&&newVal==='')return;jqLiteDocumentLoaded(function(){$rootScope.$evalAsync(scroll);});});}return scroll;}];}var $animateMinErr=minErr('$animate');var ELEMENT_NODE=1;var NG_ANIMATE_CLASSNAME='ng-animate';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 extractElementNode(element){for(var i=0;i` tag, but we wanted to allow for an element to be situated * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. * * Note that this feature is only active when the `ngAnimate` module is used. * * @param {DOMElement} element the external element that will be pinned * @param {DOMElement} parentElement the host parent element that will be associated with the external element */pin:$$animateQueue.pin,/** * * @ngdoc method * @name $animate#enabled * @kind function * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This * function can be called in four ways: * * ```js * // returns true or false * $animate.enabled(); * * // changes the enabled state for all animations * $animate.enabled(false); * $animate.enabled(true); * * // returns true or false if animations are enabled for an element * $animate.enabled(element); * * // changes the enabled state for an element and its children * $animate.enabled(element, true); * $animate.enabled(element, false); * ``` * * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state * @param {boolean=} enabled whether or not the animations will be enabled for the element * * @return {boolean} whether or not animations are enabled */enabled:$$animateQueue.enabled,/** * @ngdoc method * @name $animate#cancel * @kind function * @description Cancels the provided animation. * * @param {Promise} animationPromise The animation promise that is returned when an animation is started. */cancel:function cancel(runner){if(runner.end){runner.end();}},/** * * @ngdoc method * @name $animate#enter * @kind function * @description Inserts the element into the DOM either after the `after` element (if provided) or * as the first child within the `parent` element and then triggers an animation. * A promise is returned that will be resolved during the next digest once the animation * has completed. * * @param {DOMElement} element the element which will be inserted into the DOM * @param {DOMElement} parent the parent element which will append the element as * a child (so long as the after element is not present) * @param {DOMElement=} after the sibling element after which the element will be appended * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */enter:function enter(element,parent,after,options){parent=parent&&jqLite(parent);after=after&&jqLite(after);parent=parent||after.parent();domInsert(element,parent,after);return $$animateQueue.push(element,'enter',prepareAnimateOptions(options));},/** * * @ngdoc method * @name $animate#move * @kind function * @description Inserts (moves) the element into its new position in the DOM either after * the `after` element (if provided) or as the first child within the `parent` element * and then triggers an animation. A promise is returned that will be resolved * during the next digest once the animation has completed. * * @param {DOMElement} element the element which will be moved into the new DOM position * @param {DOMElement} parent the parent element which will append the element as * a child (so long as the after element is not present) * @param {DOMElement=} after the sibling element after which the element will be appended * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */move:function move(element,parent,after,options){parent=parent&&jqLite(parent);after=after&&jqLite(after);parent=parent||after.parent();domInsert(element,parent,after);return $$animateQueue.push(element,'move',prepareAnimateOptions(options));},/** * @ngdoc method * @name $animate#leave * @kind function * @description Triggers an animation and then removes the element from the DOM. * When the function is called a promise is returned that will be resolved during the next * digest once the animation has completed. * * @param {DOMElement} element the element which will be removed from the DOM * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */leave:function leave(element,options){return $$animateQueue.push(element,'leave',prepareAnimateOptions(options),function(){element.remove();});},/** * @ngdoc method * @name $animate#addClass * @kind function * * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon * execution, the addClass operation will only be handled after the next digest and it will not trigger an * animation if element already contains the CSS class or if the class is removed at a later step. * Note that class-based animations are treated differently compared to structural animations * (like enter, move and leave) since the CSS classes may be added/removed at different points * depending if CSS or JavaScript animations are used. * * @param {DOMElement} element the element which the CSS classes will be applied to * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */addClass:function addClass(element,className,options){options=prepareAnimateOptions(options);options.addClass=mergeClasses(options.addclass,className);return $$animateQueue.push(element,'addClass',options);},/** * @ngdoc method * @name $animate#removeClass * @kind function * * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon * execution, the removeClass operation will only be handled after the next digest and it will not trigger an * animation if element does not contain the CSS class or if the class is added at a later step. * Note that class-based animations are treated differently compared to structural animations * (like enter, move and leave) since the CSS classes may be added/removed at different points * depending if CSS or JavaScript animations are used. * * @param {DOMElement} element the element which the CSS classes will be applied to * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */removeClass:function removeClass(element,className,options){options=prepareAnimateOptions(options);options.removeClass=mergeClasses(options.removeClass,className);return $$animateQueue.push(element,'removeClass',options);},/** * @ngdoc method * @name $animate#setClass * @kind function * * @description Performs both the addition and removal of a CSS classes on an element and (during the process) * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has * passed. Note that class-based animations are treated differently compared to structural animations * (like enter, move and leave) since the CSS classes may be added/removed at different points * depending if CSS or JavaScript animations are used. * * @param {DOMElement} element the element which the CSS classes will be applied to * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces) * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces) * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */setClass:function setClass(element,add,remove,options){options=prepareAnimateOptions(options);options.addClass=mergeClasses(options.addClass,add);options.removeClass=mergeClasses(options.removeClass,remove);return $$animateQueue.push(element,'setClass',options);},/** * @ngdoc method * @name $animate#animate * @kind function * * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element. * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding * style in `to`, the style in `from` is applied immediately, and no animation is run. * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate` * method (or as part of the `options` parameter): * * ```js * ngModule.animation('.my-inline-animation', function() { * return { * animate : function(element, from, to, done, options) { * //animation * done(); * } * } * }); * ``` * * @param {DOMElement} element the element which the CSS styles will be applied to * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation. * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation. * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element. * (Note that if no animation is detected then this value will not be applied to the element.) * @param {object=} options an optional collection of options/styles that will be applied to the element. * The object can have the following properties: * * - **addClass** - `{string}` - space-separated CSS classes to add to element * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` * - **removeClass** - `{string}` - space-separated CSS classes to remove from element * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` * * @return {Promise} the animation callback promise */animate:function animate(element,from,to,className,options){options=prepareAnimateOptions(options);options.from=options.from?extend(options.from,from):from;options.to=options.to?extend(options.to,to):to;className=className||'ng-inline-animate';options.tempClasses=mergeClasses(options.tempClasses,className);return $$animateQueue.push(element,'animate',options);}};}];}];var $$AnimateAsyncRunFactoryProvider=/** @this */function $$AnimateAsyncRunFactoryProvider(){this.$get=['$$rAF',function($$rAF){var waitQueue=[];function waitForTick(fn){waitQueue.push(fn);if(waitQueue.length>1)return;$$rAF(function(){for(var i=0;i * (always relative - without domain) * * @returns {string} The current base href */self.baseHref=function(){var href=baseElement.attr('href');return href?href.replace(/^(https?:)?\/\/[^/]*/,''):'';};/** * @name $browser#defer * @param {function()} fn A function, who's execution should be deferred. * @param {number=} [delay=0] of milliseconds to defer the function execution. * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. * * @description * Executes a fn asynchronously via `setTimeout(fn, delay)`. * * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed * via `$browser.defer.flush()`. * */self.defer=function(fn,delay){var timeoutId;outstandingRequestCount++;timeoutId=setTimeout(function(){delete pendingDeferIds[timeoutId];completeOutstandingRequest(fn);},delay||0);pendingDeferIds[timeoutId]=true;return timeoutId;};/** * @name $browser#defer.cancel * * @description * Cancels a deferred task identified with `deferId`. * * @param {*} deferId Token returned by the `$browser.defer` function. * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully * canceled. */self.defer.cancel=function(deferId){if(pendingDeferIds[deferId]){delete pendingDeferIds[deferId];clearTimeout(deferId);completeOutstandingRequest(noop);return true;}return false;};}/** @this */function $BrowserProvider(){this.$get=['$window','$log','$sniffer','$document',function($window,$log,$sniffer,$document){return new Browser($window,$document,$log,$sniffer);}];}/** * @ngdoc service * @name $cacheFactory * @this * * @description * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to * them. * * ```js * * var cache = $cacheFactory('cacheId'); * expect($cacheFactory.get('cacheId')).toBe(cache); * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); * * cache.put("key", "value"); * cache.put("another key", "another value"); * * // We've specified no options on creation * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); * * ``` * * * @param {string} cacheId Name or id of the newly created cache. * @param {object=} options Options object that specifies the cache behavior. Properties: * * - `{number=}` `capacity` — turns the cache into LRU cache. * * @returns {object} Newly created cache object with the following set of methods: * * - `{object}` `info()` — Returns id, size, and options of cache. * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns * it. * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. * - `{void}` `removeAll()` — Removes all cached values. * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. * * @example

      Cached Values

      :

      Cache Info

      :
      angular.module('cacheExampleApp', []). controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { $scope.keys = []; $scope.cache = $cacheFactory('cacheId'); $scope.put = function(key, value) { if (angular.isUndefined($scope.cache.get(key))) { $scope.keys.push(key); } $scope.cache.put(key, angular.isUndefined(value) ? null : value); }; }]); p { margin: 10px 0 3px; }
      */function $CacheFactoryProvider(){this.$get=function(){var caches={};function cacheFactory(cacheId,options){if(cacheId in caches){throw minErr('$cacheFactory')('iid','CacheId \'{0}\' is already taken!',cacheId);}var size=0,stats=extend({},options,{id:cacheId}),data=createMap(),capacity=options&&options.capacity||Number.MAX_VALUE,lruHash=createMap(),freshEnd=null,staleEnd=null;/** * @ngdoc type * @name $cacheFactory.Cache * * @description * A cache object used to store and retrieve data, primarily used by * {@link $http $http} and the {@link ng.directive:script script} directive to cache * templates and other data. * * ```js * angular.module('superCache') * .factory('superCache', ['$cacheFactory', function($cacheFactory) { * return $cacheFactory('super-cache'); * }]); * ``` * * Example test: * * ```js * it('should behave like a cache', inject(function(superCache) { * superCache.put('key', 'value'); * superCache.put('another key', 'another value'); * * expect(superCache.info()).toEqual({ * id: 'super-cache', * size: 2 * }); * * superCache.remove('another key'); * expect(superCache.get('another key')).toBeUndefined(); * * superCache.removeAll(); * expect(superCache.info()).toEqual({ * id: 'super-cache', * size: 0 * }); * })); * ``` */return caches[cacheId]={/** * @ngdoc method * @name $cacheFactory.Cache#put * @kind function * * @description * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be * retrieved later, and incrementing the size of the cache if the key was not already * present in the cache. If behaving like an LRU cache, it will also remove stale * entries from the set. * * It will not insert undefined values into the cache. * * @param {string} key the key under which the cached data is stored. * @param {*} value the value to store alongside the key. If it is undefined, the key * will not be stored. * @returns {*} the value stored. */put:function put(key,value){if(isUndefined(value))return;if(capacitycapacity){this.remove(staleEnd.key);}return value;},/** * @ngdoc method * @name $cacheFactory.Cache#get * @kind function * * @description * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. * * @param {string} key the key of the data to be retrieved * @returns {*} the value stored. */get:function get(key){if(capacity *
    • **id**: the id of the cache instance
    • *
    • **size**: the number of entries kept in the cache instance
    • *
    • **...**: any additional properties from the options object when creating the * cache.
    • * */info:function info(){return extend({},stats,{size:size});}};/** * makes the `entry` the freshEnd of the LRU linked list */function refresh(entry){if(entry!==freshEnd){if(!staleEnd){staleEnd=entry;}else if(staleEnd===entry){staleEnd=entry.n;}link(entry.n,entry.p);link(entry,freshEnd);freshEnd=entry;freshEnd.n=null;}}/** * bidirectionally links two entries of the LRU linked list */function link(nextEntry,prevEntry){if(nextEntry!==prevEntry){if(nextEntry)nextEntry.p=prevEntry;//p stands for previous, 'prev' didn't minify if(prevEntry)prevEntry.n=nextEntry;//n stands for next, 'next' didn't minify }}}/** * @ngdoc method * @name $cacheFactory#info * * @description * Get information about all the caches that have been created * * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` */cacheFactory.info=function(){var info={};forEach(caches,function(cache,cacheId){info[cacheId]=cache.info();});return info;};/** * @ngdoc method * @name $cacheFactory#get * * @description * Get access to a cache object by the `cacheId` used when it was created. * * @param {string} cacheId Name or id of a cache to access. * @returns {object} Cache object identified by the cacheId or undefined if no such cache. */cacheFactory.get=function(cacheId){return caches[cacheId];};return cacheFactory;};}/** * @ngdoc service * @name $templateCache * @this * * @description * The first time a template is used, it is loaded in the template cache for quick retrieval. You * can load templates directly into the cache in a `script` tag, or by consuming the * `$templateCache` service directly. * * Adding via the `script` tag: * * ```html * * ``` * * **Note:** the `script` tag containing the template does not need to be included in the `head` of * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, * element with ng-app attribute), otherwise the template will be ignored. * * Adding via the `$templateCache` service: * * ```js * var myApp = angular.module('myApp', []); * myApp.run(function($templateCache) { * $templateCache.put('templateId.html', 'This is the content of the template'); * }); * ``` * * To retrieve the template later, simply use it in your component: * ```js * myApp.component('myComponent', { * templateUrl: 'templateId.html' * }); * ``` * * or get it via the `$templateCache` service: * ```js * $templateCache.get('templateId.html') * ``` * * See {@link ng.$cacheFactory $cacheFactory}. * */function $TemplateCacheProvider(){this.$get=['$cacheFactory',function($cacheFactory){return $cacheFactory('templates');}];}/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Any commits to this file should be reviewed with security in mind. * * Changes to this file can potentially create security vulnerabilities. * * An approval from 2 Core members with history of modifying * * this file is required. * * * * Does the change somehow allow for arbitrary javascript to be executed? * * Or allows for someone to change the prototype of built-in objects? * * Or gives undesired access to variables like document or window? * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! * * DOM-related variables: * * - "node" - DOM Node * - "element" - DOM Element or Node * - "$node" or "$element" - jqLite-wrapped node or element * * * Compiler related stuff: * * - "linkFn" - linking fn of a single directive * - "nodeLinkFn" - function that aggregates all linking fns for a particular node * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) *//** * @ngdoc service * @name $compile * @kind function * * @description * Compiles an HTML string or DOM into a template and produces a template function, which * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. * * The compilation is a process of walking the DOM tree and matching DOM elements to * {@link ng.$compileProvider#directive directives}. * *
      * **Note:** This document is an in-depth reference of all directive options. * For a gentle introduction to directives with examples of common use cases, * see the {@link guide/directive directive guide}. *
      * * ## Comprehensive Directive API * * There are many different options for a directive. * * The difference resides in the return value of the factory function. * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)} * that defines the directive properties, or just the `postLink` function (all other properties will have * the default values). * *
      * **Best Practice:** It's recommended to use the "directive definition object" form. *
      * * Here's an example directive declared with a Directive Definition Object: * * ```js * var myModule = angular.module(...); * * myModule.directive('directiveName', function factory(injectables) { * var directiveDefinitionObject = { * {@link $compile#-priority- priority}: 0, * {@link $compile#-template- template}: '
      ', // or // function(tElement, tAttrs) { ... }, * // or * // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... }, * {@link $compile#-transclude- transclude}: false, * {@link $compile#-restrict- restrict}: 'A', * {@link $compile#-templatenamespace- templateNamespace}: 'html', * {@link $compile#-scope- scope}: false, * {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, * {@link $compile#-controlleras- controllerAs}: 'stringIdentifier', * {@link $compile#-bindtocontroller- bindToController}: false, * {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], * {@link $compile#-multielement- multiElement}: false, * {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) { * return { * {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... }, * {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... } * } * // or * // return function postLink( ... ) { ... } * }, * // or * // {@link $compile#-link- link}: { * // {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... }, * // {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... } * // } * // or * // {@link $compile#-link- link}: function postLink( ... ) { ... } * }; * return directiveDefinitionObject; * }); * ``` * *
      * **Note:** Any unspecified options will use the default value. You can see the default values below. *
      * * Therefore the above can be simplified as: * * ```js * var myModule = angular.module(...); * * myModule.directive('directiveName', function factory(injectables) { * var directiveDefinitionObject = { * link: function postLink(scope, iElement, iAttrs) { ... } * }; * return directiveDefinitionObject; * // or * // return function postLink(scope, iElement, iAttrs) { ... } * }); * ``` * * ### Life-cycle hooks * Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the * directive: * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and * had their bindings initialized (and before the pre & post linking functions for the directives on * this element). This is a good place to put initialization code for your controller. * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a * component such as cloning the bound value to prevent accidental mutation of the outer value. * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on * changes. Any actions that you wish to take in response to the changes that you detect must be * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments; * if detecting changes, you must store the previous value(s) for comparison to the current values. * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent * components will have their `$onDestroy()` hook called before child components. * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link * function this hook can be used to set up DOM event handlers and do direct DOM manipulation. * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since * they are waiting for their template to load asynchronously and their own compilation and linking has been * suspended until that occurs. * * #### Comparison with Angular 2 life-cycle hooks * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are * some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2: * * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`. * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor. * In Angular 2 you can only define hooks on the prototype of the Component class. * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to * `ngDoCheck` in Angular 2 * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be * propagated throughout the application. * Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an * error or do nothing depending upon the state of `enableProdMode()`. * * #### Life-cycle hook examples * * This example shows how you can check for mutations to a Date object even though the identity of the object * has not changed. * * * * angular.module('do-check-module', []) * .component('app', { * template: * 'Month: ' + * 'Date: {{ $ctrl.date }}' + * '', * controller: function() { * this.date = new Date(); * this.month = this.date.getMonth(); * this.updateDate = function() { * this.date.setMonth(this.month); * }; * } * }) * .component('test', { * bindings: { date: '<' }, * template: * '
      {{ $ctrl.log | json }}
      ', * controller: function() { * var previousValue; * this.log = []; * this.$doCheck = function() { * var currentValue = this.date && this.date.valueOf(); * if (previousValue !== currentValue) { * this.log.push('doCheck: date mutated: ' + this.date); * previousValue = currentValue; * } * }; * } * }); *
      * * * *
      * * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large * arrays or objects can have a negative impact on your application performance) * * * *
      * * *
      {{ items }}
      * *
      *
      * * angular.module('do-check-module', []) * .component('test', { * bindings: { items: '<' }, * template: * '
      {{ $ctrl.log | json }}
      ', * controller: function() { * this.log = []; * * this.$doCheck = function() { * if (this.items_ref !== this.items) { * this.log.push('doCheck: items changed'); * this.items_ref = this.items; * } * if (!angular.equals(this.items_clone, this.items)) { * this.log.push('doCheck: items mutated'); * this.items_clone = angular.copy(this.items); * } * }; * } * }); *
      *
      * * * ### Directive Definition Object * * The directive definition object provides instructions to the {@link ng.$compile * compiler}. The attributes are: * * #### `multiElement` * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them * together as the directive elements. It is recommended that this feature be used on directives * which are not strictly behavioral (such as {@link ngClick}), and which * do not manipulate or replace child nodes (such as {@link ngInclude}). * * #### `priority` * When there are multiple directives defined on a single DOM element, sometimes it * is necessary to specify the order in which the directives are applied. The `priority` is used * to sort the directives before their `compile` functions get called. Priority is defined as a * number. Directives with greater numerical `priority` are compiled first. Pre-link functions * are also run in priority order, but post-link functions are run in reverse order. The order * of directives with the same priority is undefined. The default priority is `0`. * * #### `terminal` * If set to true then the current `priority` will be the last set of directives * which will execute (any directives at the current priority will still execute * as the order of execution on same `priority` is undefined). Note that expressions * and other directives used in the directive's template will also be excluded from execution. * * #### `scope` * The scope property can be `false`, `true`, or an object: * * * **`false` (default):** No scope will be created for the directive. The directive will use its * parent's scope. * * * **`true`:** A new child scope that prototypically inherits from its parent will be created for * the directive's element. If multiple directives on the same element request a new scope, * only one new scope is created. * * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent * scope. This is useful when creating reusable components, which should not accidentally read or modify * data in the parent scope. * * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the * directive's element. These local properties are useful for aliasing values for templates. The keys in * the object hash map to the name of the property on the isolate scope; the values define how the property * is bound to the parent scope, via matching attributes on the directive's element: * * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is * always a string since DOM attributes are strings. If no `attr` name is specified then the * attribute name is assumed to be the same as the local name. Given `` and the isolate scope definition `scope: { localName:'@myAttr' }`, * the directive's scope property `localName` will reflect the interpolated value of `hello * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's * scope. The `name` is read from the parent scope (not the directive's scope). * * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope. * If no `attr` name is specified then the attribute name is assumed to be the same as the local * name. Given `` and the isolate scope definition `scope: { * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in * `localModel` and vice versa. Optional attributes should be marked as such with a question mark: * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`}) * will be thrown upon discovering changes to the local value, since it will be impossible to sync * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`} * method is used for tracking changes, and the equality check is based on object identity. * However, if an object literal or an array literal is passed as the binding expression, the * equality check is done by value (using the {@link angular.equals} function). It's also possible * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional). * * * `<` or `` and directive definition of * `scope: { localModel:'` and the isolate scope definition `scope: { * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope * via an expression to the parent scope. This can be done by passing a map of local variable names * and values into the expression wrapper fn. For example, if the expression is `increment(amount)` * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`. * * In general it's possible to apply more than one directive to one element, but there might be limitations * depending on the type of scope required by the directives. The following points will help explain these limitations. * For simplicity only two directives are taken into account, but it is also applicable for several directives: * * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope * * **child scope** + **no scope** => Both directives will share one single child scope * * **child scope** + **child scope** => Both directives will share one single child scope * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use * its parent's scope * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot * be applied to the same element. * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives * cannot be applied to the same element. * * * #### `bindToController` * This property is used to bind scope properties directly to the controller. It can be either * `true` or an object hash with the same format as the `scope` property. * * When an isolate scope is used for a directive (see above), `bindToController: true` will * allow a component to have its properties bound to the controller, rather than to scope. * * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller * properties. You can access these bindings once they have been initialized by providing a controller method called * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings * initialized. * *
      * **Deprecation warning:** although bindings for non-ES6 class controllers are currently * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization * code that relies upon bindings inside a `$onInit` method on the controller, instead. *
      * * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property. * This will set up the scope bindings to the controller directly. Note that `scope` can still be used * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate * scope (useful for component directives). * * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`. * * * #### `controller` * Controller constructor function. The controller is instantiated before the * pre-linking phase and can be accessed by other directives (see * `require` attribute). This allows the directives to communicate with each other and augment * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: * * * `$scope` - Current scope associated with the element * * `$element` - Current element * * `$attrs` - Current attributes object for the element * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: * `function([scope], cloneLinkingFn, futureParentElement, slotName)`: * * `scope`: (optional) override the scope. * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content. * * `futureParentElement` (optional): * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) * and when the `cloneLinkingFn` is passed, * as those elements need to created and cloned in a special way when they are defined outside their * usual containers (e.g. like ``). * * See also the `directive.templateNamespace` property. * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`) * then the default transclusion is provided. * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns * `true` if the specified slot contains content (i.e. one or more DOM nodes). * * #### `require` * Require another directive and inject its controller as the fourth argument to the linking function. The * `require` property can be a string, an array or an object: * * a **string** containing the name of the directive to pass to the linking function * * an **array** containing the names of directives to pass to the linking function. The argument passed to the * linking function will be an array of controllers in the same order as the names in the `require` property * * an **object** whose property values are the names of the directives to pass to the linking function. The argument * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding * controllers. * * If the `require` property is an object and `bindToController` is truthy, then the required controllers are * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers * have been constructed but before `$onInit` is called. * If the name of the required controller is the same as the local name (the key), the name can be * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`. * See the {@link $compileProvider#component} helper for an example of how this can be used. * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is * raised (unless no link function is specified and the required controllers are not being bound to the directive * controller, in which case error checking is skipped). The name can be prefixed with: * * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass * `null` to the `link` fn if not found. * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass * `null` to the `link` fn if not found. * * * #### `controllerAs` * Identifier name for a reference to the controller in the directive's scope. * This allows the controller to be referenced from the directive template. This is especially * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the * `controllerAs` reference might overwrite a property that already exists on the parent scope. * * * #### `restrict` * String of subset of `EACM` which restricts the directive to a specific directive * declaration style. If omitted, the defaults (elements and attributes) are used. * * * `E` - Element name (default): `` * * `A` - Attribute (default): `
      ` * * `C` - Class: `
      ` * * `M` - Comment: `` * * * #### `templateNamespace` * String representing the document type used by the markup in the template. * AngularJS needs this information as those elements need to be created and cloned * in a special way when they are defined outside their usual containers like `` and ``. * * * `html` - All root nodes in the template are HTML. Root nodes may also be * top-level elements such as `` or ``. * * `svg` - The root nodes in the template are SVG elements (excluding ``). * * `math` - The root nodes in the template are MathML elements (excluding ``). * * If no `templateNamespace` is specified, then the namespace is considered to be `html`. * * #### `template` * HTML markup that may: * * Replace the contents of the directive's element (default). * * Replace the directive's element itself (if `replace` is true - DEPRECATED). * * Wrap the contents of the directive's element (if `transclude` is true). * * Value may be: * * * A string. For example `
      {{delete_str}}
      `. * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` * function api below) and returns a string value. * * * #### `templateUrl` * This is similar to `template` but the template is loaded from the specified URL, asynchronously. * * Because template loading is asynchronous the compiler will suspend compilation of directives on that element * for later when the template has been resolved. In the meantime it will continue to compile and link * sibling and parent elements as though this element had not contained any directives. * * The compiler does not suspend the entire compilation to wait for templates to be loaded because this * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the * case when only one deeply nested directive has `templateUrl`. * * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} * * You can specify `templateUrl` as a string representing the URL or as a function which takes two * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns * a string value representing the url. In either case, the template URL is passed through {@link * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. * * * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) * specify what the template should replace. Defaults to `false`. * * * `true` - the template will replace the directive's element. * * `false` - the template will replace the contents of the directive's element. * * The replacement process migrates all of the attributes / classes from the old element to the new * one. See the {@link guide/directive#template-expanding-directive * Directives Guide} for an example. * * There are very few scenarios where element replacement is required for the application function, * the main one being reusable custom components that are used within SVG contexts * (because SVG doesn't work with custom elements in the DOM tree). * * #### `transclude` * Extract the contents of the element where the directive appears and make it available to the directive. * The contents are compiled and provided to the directive as a **transclusion function**. See the * {@link $compile#transclusion Transclusion} section below. * * * #### `compile` * * ```js * function compile(tElement, tAttrs, transclude) { ... } * ``` * * The compile function deals with transforming the template DOM. Since most directives do not do * template transformation, it is not used often. The compile function takes the following arguments: * * * `tElement` - template element - The element where the directive has been declared. It is * safe to do template transformation on the element and child elements only. * * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared * between all directive compile functions. * * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` * *
      * **Note:** The template instance and the link instance may be different objects if the template has * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration * should be done in a linking function rather than in a compile function. *
      *
      * **Note:** The compile function cannot handle directives that recursively use themselves in their * own templates or compile functions. Compiling these directives results in an infinite loop and * stack overflow errors. * * This can be avoided by manually using $compile in the postLink function to imperatively compile * a directive's template instead of relying on automatic template compilation via `template` or * `templateUrl` declaration or manual compilation inside the compile function. *
      * *
      * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it * e.g. does not know about the right outer scope. Please use the transclude function that is passed * to the link function instead. *
      * A compile function can have a return value which can be either a function or an object. * * * returning a (post-link) function - is equivalent to registering the linking function via the * `link` property of the config object when the compile function is empty. * * * returning an object with function(s) registered via `pre` and `post` properties - allows you to * control when a linking function should be called during the linking phase. See info about * pre-linking and post-linking functions below. * * * #### `link` * This property is used only if the `compile` property is not defined. * * ```js * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } * ``` * * The link function is responsible for registering DOM listeners as well as updating the DOM. It is * executed after the template has been cloned. This is where most of the directive logic will be * put. * * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the * directive for registering {@link ng.$rootScope.Scope#$watch watches}. * * * `iElement` - instance element - The element where the directive is to be used. It is safe to * manipulate the children of the element only in `postLink` function since the children have * already been linked. * * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared * between all directive linking functions. * * * `controller` - the directive's required controller instance(s) - Instances are shared * among all directives, which allows the directives to use the controllers as a communication * channel. The exact value depends on the directive's `require` property: * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one * * `string`: the controller instance * * `array`: array of controller instances * * If a required controller cannot be found, and it is optional, the instance is `null`, * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. * * Note that you can also require the directive's own controller - it will be made available like * any other controller. * * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. * This is the same as the `$transclude` parameter of directive controllers, * see {@link ng.$compile#-controller- the controller section for details}. * `function([scope], cloneLinkingFn, futureParentElement)`. * * #### Pre-linking function * * Executed before the child elements are linked. Not safe to do DOM transformation since the * compiler linking function will fail to locate the correct elements for linking. * * #### Post-linking function * * Executed after the child elements are linked. * * Note that child elements that contain `templateUrl` directives will not have been compiled * and linked since they are waiting for their template to load asynchronously and their own * compilation and linking has been suspended until that occurs. * * It is safe to do DOM transformation in the post-linking function on elements that are not waiting * for their async templates to be resolved. * * * ### Transclusion * * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and * copying them to another part of the DOM, while maintaining their connection to the original AngularJS * scope from where they were taken. * * Transclusion is used (often with {@link ngTransclude}) to insert the * original contents of a directive's element into a specified place in the template of the directive. * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded * content has access to the properties on the scope from which it was taken, even if the directive * has isolated scope. * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. * * This makes it possible for the widget to have private state for its template, while the transcluded * content has access to its originating scope. * *
      * **Note:** When testing an element transclude directive you must not place the directive at the root of the * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives * Testing Transclusion Directives}. *
      * * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the * directive's element, the entire element or multiple parts of the element contents: * * * `true` - transclude the content (i.e. the child nodes) of the directive's element. * * `'element'` - transclude the whole of the directive's element including any directives on this * element that defined at a lower priority than this directive. When used, the `template` * property is ignored. * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template. * * **Mult-slot transclusion** is declared by providing an object for the `transclude` property. * * This object is a map where the keys are the name of the slot to fill and the value is an element selector * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`) * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc). * * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} * * If the element selector is prefixed with a `?` then that slot is optional. * * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `` elements to * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive. * * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and * injectable into the directive's controller. * * * #### Transclusion Functions * * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion * function** to the directive's `link` function and `controller`. This transclusion function is a special * **linking function** that will return the compiled contents linked to a new transclusion scope. * *
      * If you are just using {@link ngTransclude} then you don't need to worry about this function, since * ngTransclude will deal with it for us. *
      * * If you want to manually control the insertion and removal of the transcluded content in your directive * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery * object that contains the compiled DOM, which is linked to the correct transclusion scope. * * When you call a transclusion function you can pass in a **clone attach function**. This function accepts * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded * content and the `scope` is the newly created transclusion scope, which the clone will be linked to. * *
      * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. *
      * * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone * attach function**: * * ```js * var transcludedContent, transclusionScope; * * $transclude(function(clone, scope) { * element.append(clone); * transcludedContent = clone; * transclusionScope = scope; * }); * ``` * * Later, if you want to remove the transcluded content from your DOM then you should also destroy the * associated transclusion scope: * * ```js * transcludedContent.remove(); * transclusionScope.$destroy(); * ``` * *
      * **Best Practice**: if you intend to add and remove transcluded content manually in your directive * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it), * then you are also responsible for calling `$destroy` on the transclusion scope. *
      * * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} * automatically destroy their transcluded clones as necessary so you do not need to worry about this if * you are simply using {@link ngTransclude} to inject the transclusion into your directive. * * * #### Transclusion Scopes * * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it * was taken. * * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look * like this: * * ```html *
      *
      *
      *
      *
      *
      * ``` * * The `$parent` scope hierarchy will look like this: * ``` - $rootScope - isolate - transclusion ``` * * but the scopes will inherit prototypically from different scopes to their `$parent`. * ``` - $rootScope - transclusion - isolate ``` * * * ### Attributes * * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the * `link()` or `compile()` functions. It has a variety of uses. * * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways: * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access * to the attributes. * * * *Directive inter-communication:* All directives share the same instance of the attributes * object which allows the directives to use the attributes object as inter directive * communication. * * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object * allowing other directives to read the interpolated value. * * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also * the only way to easily get the actual value because during the linking phase the interpolation * hasn't been evaluated yet and so the value is at this time set to `undefined`. * * ```js * function linkingFn(scope, elm, attrs, ctrl) { * // get the attribute value * console.log(attrs.ngModel); * * // change the attribute * attrs.$set('ngModel', 'new value'); * * // observe changes to interpolated attribute * attrs.$observe('ngModel', function(value) { * console.log('ngModel has changed value to ' + value); * }); * } * ``` * * ## Example * *
      * **Note**: Typically directives are registered with `module.directive`. The example below is * to illustrate how `$compile` works. *
      *


      it('should auto compile', function() { var textarea = $('textarea'); var output = $('div[compile]'); // The initial state reads 'Hello Angular'. expect(output.getText()).toBe('Hello Angular'); textarea.clear(); textarea.sendKeys('{{name}}!'); expect(output.getText()).toBe('Angular!'); });
      * * * @param {string|DOMElement} element Element or HTML string to compile into a template function. * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. * *
      * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it * e.g. will not use the right outer scope. Please pass the transclude function as a * `parentBoundTranscludeFn` to the link function instead. *
      * * @param {number} maxPriority only apply directives lower than given priority (Only effects the * root element(s), not their children) * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template * (a DOM element/tree) to a scope. Where: * * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the * `template` and call the `cloneAttachFn` function allowing the caller to attach the * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is * called as:
      `cloneAttachFn(clonedElement, scope)` where: * * * `clonedElement` - is a clone of the original `element` passed into the compiler. * * `scope` - is the current scope with which the linking function is working with. * * * `options` - An optional object hash with linking options. If `options` is provided, then the following * keys may be used to control linking behavior: * * * `parentBoundTranscludeFn` - the transclude function made available to * directives; if given, it will be passed through to the link functions of * directives found in `element` during compilation. * * `transcludeControllers` - an object hash with keys that map controller names * to a hash with the key `instance`, which maps to the controller instance; * if given, it will make the controllers available to directives on the compileNode: * ``` * { * parent: { * instance: parentControllerInstance * } * } * ``` * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add * the cloned elements; only needed for transcludes that are allowed to contain non html * elements (e.g. SVG elements). See also the directive.controller property. * * Calling the linking function returns the element of the template. It is either the original * element passed in, or the clone of the element if the `cloneAttachFn` is provided. * * After linking the view is not updated until after a call to $digest which typically is done by * Angular automatically. * * If you need access to the bound view, there are two ways to do it: * * - If you are not asking the linking function to clone the template, create the DOM element(s) * before you send them to the compiler and keep this reference around. * ```js * var element = $compile('

      {{total}}

      ')(scope); * ``` * * - if on the other hand, you need the element to be cloned, the view reference from the original * example would not point to the clone, but rather to the original template that was cloned. In * this case, you can access the clone via the cloneAttachFn: * ```js * var templateElement = angular.element('

      {{total}}

      '), * scope = ....; * * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { * //attach the clone to DOM document at the right place * }); * * //now we have reference to the cloned DOM via `clonedElement` * ``` * * * For information on how the compiler works, see the * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. * * @knownIssue * * ### Double Compilation * Double compilation occurs when an already compiled part of the DOM gets compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues, and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it section on double compilation} for an in-depth explanation and ways to avoid it. * */var $compileMinErr=minErr('$compile');function UNINITIALIZED_VALUE(){}var _UNINITIALIZED_VALUE=new UNINITIALIZED_VALUE();/** * @ngdoc provider * @name $compileProvider * * @description */$CompileProvider.$inject=['$provide','$$sanitizeUriProvider'];/** @this */function $CompileProvider($provide,$$sanitizeUriProvider){var hasDirectives={},Suffix='Directive',COMMENT_DIRECTIVE_REGEXP=/^\s*directive:\s*([\w-]+)\s+(.*)$/,CLASS_DIRECTIVE_REGEXP=/(([\w-]+)(?::([^;]+))?;?)/,ALL_OR_NOTHING_ATTRS=makeMap('ngSrc,ngSrcset,src,srcset'),REQUIRE_PREFIX_REGEXP=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/;// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes // The assumption is that future DOM event attribute names will begin with // 'on' and be composed of only English letters. var EVENT_HANDLER_ATTR_REGEXP=/^(on[a-z]+|formaction)$/;var bindingCache=createMap();function parseIsolateBindings(scope,directiveName,isController){var LOCAL_REGEXP=/^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;var bindings=createMap();forEach(scope,function(definition,scopeName){if(definition in bindingCache){bindings[scopeName]=bindingCache[definition];return;}var match=definition.match(LOCAL_REGEXP);if(!match){throw $compileMinErr('iscp','Invalid {3} for directive \'{0}\'.'+' Definition: {... {1}: \'{2}\' ...}',directiveName,scopeName,definition,isController?'controller bindings definition':'isolate scope definition');}bindings[scopeName]={mode:match[1][0],collection:match[2]==='*',optional:match[3]==='?',attrName:match[4]||scopeName};if(match[4]){bindingCache[definition]=bindings[scopeName];}});return bindings;}function parseDirectiveBindings(directive,directiveName){var bindings={isolateScope:null,bindToController:null};if(isObject(directive.scope)){if(directive.bindToController===true){bindings.bindToController=parseIsolateBindings(directive.scope,directiveName,true);bindings.isolateScope={};}else{bindings.isolateScope=parseIsolateBindings(directive.scope,directiveName,false);}}if(isObject(directive.bindToController)){bindings.bindToController=parseIsolateBindings(directive.bindToController,directiveName,true);}if(bindings.bindToController&&!directive.controller){// There is no controller throw $compileMinErr('noctrl','Cannot bind to controller without directive \'{0}\'s controller.',directiveName);}return bindings;}function assertValidDirectiveName(name){var letter=name.charAt(0);if(!letter||letter!==lowercase(letter)){throw $compileMinErr('baddir','Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter',name);}if(name!==name.trim()){throw $compileMinErr('baddir','Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces',name);}}function getDirectiveRequire(directive){var require=directive.require||directive.controller&&directive.name;if(!isArray(require)&&isObject(require)){forEach(require,function(value,key){var match=value.match(REQUIRE_PREFIX_REGEXP);var name=value.substring(match[0].length);if(!name)require[key]=match[0]+key;});}return require;}function getDirectiveRestrict(restrict,name){if(restrict&&!(isString(restrict)&&/[EACM]/.test(restrict))){throw $compileMinErr('badrestrict','Restrict property \'{0}\' of directive \'{1}\' is invalid',restrict,name);}return restrict||'EA';}/** * @ngdoc method * @name $compileProvider#directive * @kind function * * @description * Register a new directive with the compiler. * * @param {string|Object} name Name of the directive in camel-case (i.e. ngBind which * will match as ng-bind), or an object map of directives where the keys are the * names and the values are the factories. * @param {Function|Array} directiveFactory An injectable directive factory function. See the * {@link guide/directive directive guide} and the {@link $compile compile API} for more info. * @returns {ng.$compileProvider} Self for chaining. */this.directive=function registerDirective(name,directiveFactory){assertArg(name,'name');assertNotHasOwnProperty(name,'directive');if(isString(name)){assertValidDirectiveName(name);assertArg(directiveFactory,'directiveFactory');if(!hasDirectives.hasOwnProperty(name)){hasDirectives[name]=[];$provide.factory(name+Suffix,['$injector','$exceptionHandler',function($injector,$exceptionHandler){var directives=[];forEach(hasDirectives[name],function(directiveFactory,index){try{var directive=$injector.invoke(directiveFactory);if(isFunction(directive)){directive={compile:valueFn(directive)};}else if(!directive.compile&&directive.link){directive.compile=valueFn(directive.link);}directive.priority=directive.priority||0;directive.index=index;directive.name=directive.name||name;directive.require=getDirectiveRequire(directive);directive.restrict=getDirectiveRestrict(directive.restrict,name);directive.$$moduleName=directiveFactory.$$moduleName;directives.push(directive);}catch(e){$exceptionHandler(e);}});return directives;}]);}hasDirectives[name].push(directiveFactory);}else{forEach(name,reverseParams(registerDirective));}return this;};/** * @ngdoc method * @name $compileProvider#component * @module ng * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match ``) * @param {Object} options Component definition object (a simplified * {@link ng.$compile#directive-definition-object directive definition object}), * with the following properties (all optional): * * - `controller` – `{(string|function()=}` – controller constructor function that should be * associated with newly created scope or the name of a {@link ng.$compile#-controller- * registered controller} if passed as a string. An empty `noop` function by default. * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope. * If present, the controller will be published to scope under the `controllerAs` name. * If not present, this will default to be `$ctrl`. * - `template` – `{string=|function()=}` – html template as a string or a function that * returns an html template as a string which should be used as the contents of this component. * Empty string by default. * * If `template` is a function, then it is {@link auto.$injector#invoke injected} with * the following locals: * * - `$element` - Current element * - `$attrs` - Current attributes object for the element * * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html * template that should be used as the contents of this component. * * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with * the following locals: * * - `$element` - Current element * - `$attrs` - Current attributes object for the element * * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties. * Component properties are always bound to the component controller and not to the scope. * See {@link ng.$compile#-bindtocontroller- `bindToController`}. * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled. * Disabled by default. * - `require` - `{Object=}` - requires the controllers of other directives and binds them to * this component's controller. The object keys specify the property names under which the required * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}. * - `$...` – additional properties to attach to the directive factory function and the controller * constructor function. (This is used by the component router to annotate) * * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls. * @description * Register a **component definition** with the compiler. This is a shorthand for registering a special * type of directive, which represents a self-contained UI component in your application. Such components * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`). * * Component definitions are very simple and do not require as much configuration as defining general * directives. Component definitions usually consist only of a template and a controller backing it. * * In order to make the definition easier, components enforce best practices like use of `controllerAs`, * `bindToController`. They always have **isolate scope** and are restricted to elements. * * Here are a few examples of how you would usually define components: * * ```js * var myMod = angular.module(...); * myMod.component('myComp', { * template: '
      My name is {{$ctrl.name}}
      ', * controller: function() { * this.name = 'shahar'; * } * }); * * myMod.component('myComp', { * template: '
      My name is {{$ctrl.name}}
      ', * bindings: {name: '@'} * }); * * myMod.component('myComp', { * templateUrl: 'views/my-comp.html', * controller: 'MyCtrl', * controllerAs: 'ctrl', * bindings: {name: '@'} * }); * * ``` * For more examples, and an in-depth guide, see the {@link guide/component component guide}. * *
      * See also {@link ng.$compileProvider#directive $compileProvider.directive()}. */this.component=function registerComponent(name,options){var controller=options.controller||function(){};function factory($injector){function makeInjectable(fn){if(isFunction(fn)||isArray(fn)){return(/** @this */function(tElement,tAttrs){return $injector.invoke(fn,this,{$element:tElement,$attrs:tAttrs});});}else{return fn;}}var template=!options.template&&!options.templateUrl?'':options.template;var ddo={controller:controller,controllerAs:identifierForController(options.controller)||options.controllerAs||'$ctrl',template:makeInjectable(template),templateUrl:makeInjectable(options.templateUrl),transclude:options.transclude,scope:{},bindToController:options.bindings||{},restrict:'E',require:options.require};// Copy annotations (starting with $) over to the DDO forEach(options,function(val,key){if(key.charAt(0)==='$')ddo[key]=val;});return ddo;}// TODO(pete) remove the following `forEach` before we release 1.6.0 // The component-router@0.2.0 looks for the annotations on the controller constructor // Nothing in Angular looks for annotations on the factory function but we can't remove // it from 1.5.x yet. // Copy any annotation properties (starting with $) over to the factory and controller constructor functions // These could be used by libraries such as the new component router forEach(options,function(val,key){if(key.charAt(0)==='$'){factory[key]=val;// Don't try to copy over annotations to named controller if(isFunction(controller))controller[key]=val;}});factory.$inject=['$injector'];return this.directive(name,factory);};/** * @ngdoc method * @name $compileProvider#aHrefSanitizationWhitelist * @kind function * * @description * Retrieves or overrides the default regular expression that is used for whitelisting of safe * urls during a[href] sanitization. * * The sanitization is a security measure aimed at preventing XSS attacks via html links. * * Any url about to be assigned to a[href] via data-binding is first normalized and turned into * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` * regular expression. If a match is found, the original url is written into the dom. Otherwise, * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. * * @param {RegExp=} regexp New regexp to whitelist urls with. * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for * chaining otherwise. */this.aHrefSanitizationWhitelist=function(regexp){if(isDefined(regexp)){$$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);return this;}else{return $$sanitizeUriProvider.aHrefSanitizationWhitelist();}};/** * @ngdoc method * @name $compileProvider#imgSrcSanitizationWhitelist * @kind function * * @description * Retrieves or overrides the default regular expression that is used for whitelisting of safe * urls during img[src] sanitization. * * The sanitization is a security measure aimed at prevent XSS attacks via html links. * * Any url about to be assigned to img[src] via data-binding is first normalized and turned into * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` * regular expression. If a match is found, the original url is written into the dom. Otherwise, * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. * * @param {RegExp=} regexp New regexp to whitelist urls with. * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for * chaining otherwise. */this.imgSrcSanitizationWhitelist=function(regexp){if(isDefined(regexp)){$$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);return this;}else{return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();}};/** * @ngdoc method * @name $compileProvider#debugInfoEnabled * * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the * current debugInfoEnabled state * @returns {*} current value if used as getter or itself (chaining) if used as setter * * @kind function * * @description * Call this method to enable/disable various debug runtime information in the compiler such as adding * binding information and a reference to the current scope on to DOM elements. * If enabled, the compiler will add the following to DOM elements that have been bound to the scope * * `ng-binding` CSS class * * `$binding` data property containing an array of the binding expressions * * You may want to disable this in production for a significant performance boost. See * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. * * The default value is true. */var debugInfoEnabled=true;this.debugInfoEnabled=function(enabled){if(isDefined(enabled)){debugInfoEnabled=enabled;return this;}return debugInfoEnabled;};/** * @ngdoc method * @name $compileProvider#preAssignBindingsEnabled * * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the * current preAssignBindingsEnabled state * @returns {*} current value if used as getter or itself (chaining) if used as setter * * @kind function * * @description * Call this method to enable/disable whether directive controllers are assigned bindings before * calling the controller's constructor. * If enabled (true), the compiler assigns the value of each of the bindings to the * properties of the controller object before the constructor of this object is called. * * If disabled (false), the compiler calls the constructor first before assigning bindings. * * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x. */var preAssignBindingsEnabled=true;this.preAssignBindingsEnabled=function(enabled){if(isDefined(enabled)){preAssignBindingsEnabled=enabled;return this;}return preAssignBindingsEnabled;};var TTL=10;/** * @ngdoc method * @name $compileProvider#onChangesTtl * @description * * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and * assuming that the model is unstable. * * The current default is 10 iterations. * * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result * in several iterations of calls to these hooks. However if an application needs more than the default 10 * iterations to stabilize then you should investigate what is causing the model to continuously change during * the `$onChanges` hook execution. * * Increasing the TTL could have performance implications, so you should not change it without proper justification. * * @param {number} limit The number of `$onChanges` hook iterations. * @returns {number|object} the current limit (or `this` if called as a setter for chaining) */this.onChangesTtl=function(value){if(arguments.length){TTL=value;return this;}return TTL;};var commentDirectivesEnabledConfig=true;/** * @ngdoc method * @name $compileProvider#commentDirectivesEnabled * @description * * It indicates to the compiler * whether or not directives on comments should be compiled. * Defaults to `true`. * * Calling this function with false disables the compilation of directives * on comments for the whole application. * This results in a compilation performance gain, * as the compiler doesn't have to check comments when looking for directives. * This should however only be used if you are sure that no comment directives are used in * the application (including any 3rd party directives). * * @param {boolean} enabled `false` if the compiler may ignore directives on comments * @returns {boolean|object} the current value (or `this` if called as a setter for chaining) */this.commentDirectivesEnabled=function(value){if(arguments.length){commentDirectivesEnabledConfig=value;return this;}return commentDirectivesEnabledConfig;};var cssClassDirectivesEnabledConfig=true;/** * @ngdoc method * @name $compileProvider#cssClassDirectivesEnabled * @description * * It indicates to the compiler * whether or not directives on element classes should be compiled. * Defaults to `true`. * * Calling this function with false disables the compilation of directives * on element classes for the whole application. * This results in a compilation performance gain, * as the compiler doesn't have to check element classes when looking for directives. * This should however only be used if you are sure that no class directives are used in * the application (including any 3rd party directives). * * @param {boolean} enabled `false` if the compiler may ignore directives on element classes * @returns {boolean|object} the current value (or `this` if called as a setter for chaining) */this.cssClassDirectivesEnabled=function(value){if(arguments.length){cssClassDirectivesEnabledConfig=value;return this;}return cssClassDirectivesEnabledConfig;};this.$get=['$injector','$interpolate','$exceptionHandler','$templateRequest','$parse','$controller','$rootScope','$sce','$animate','$$sanitizeUri',function($injector,$interpolate,$exceptionHandler,$templateRequest,$parse,$controller,$rootScope,$sce,$animate,$$sanitizeUri){var SIMPLE_ATTR_NAME=/^\w/;var specialAttrHolder=window.document.createElement('div');var commentDirectivesEnabled=commentDirectivesEnabledConfig;var cssClassDirectivesEnabled=cssClassDirectivesEnabledConfig;var onChangesTtl=TTL;// The onChanges hooks should all be run together in a single digest // When changes occur, the call to trigger their hooks will be added to this queue var onChangesQueue;// This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest function flushOnChangesQueue(){try{if(! --onChangesTtl){// We have hit the TTL limit so reset everything onChangesQueue=undefined;throw $compileMinErr('infchng','{0} $onChanges() iterations reached. Aborting!\n',TTL);}// We must run this hook in an apply since the $$postDigest runs outside apply $rootScope.$apply(function(){var errors=[];for(var i=0,ii=onChangesQueue.length;i0){$animate.addClass(this.$$element,classVal);}},/** * @ngdoc method * @name $compile.directive.Attributes#$removeClass * @kind function * * @description * Removes the CSS class value specified by the classVal parameter from the element. If * animations are enabled then an animation will be triggered for the class removal. * * @param {string} classVal The className value that will be removed from the element */$removeClass:function $removeClass(classVal){if(classVal&&classVal.length>0){$animate.removeClass(this.$$element,classVal);}},/** * @ngdoc method * @name $compile.directive.Attributes#$updateClass * @kind function * * @description * Adds and removes the appropriate CSS class values to the element based on the difference * between the new and old CSS class values (specified as newClasses and oldClasses). * * @param {string} newClasses The current CSS className value * @param {string} oldClasses The former CSS className value */$updateClass:function $updateClass(newClasses,oldClasses){var toAdd=tokenDifference(newClasses,oldClasses);if(toAdd&&toAdd.length){$animate.addClass(this.$$element,toAdd);}var toRemove=tokenDifference(oldClasses,newClasses);if(toRemove&&toRemove.length){$animate.removeClass(this.$$element,toRemove);}},/** * Set a normalized attribute on the element in a way such that all directives * can share the attribute. This function properly handles boolean attributes. * @param {string} key Normalized key. (ie ngAttribute) * @param {string|boolean} value The value to set. If `null` attribute will be deleted. * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. * Defaults to true. * @param {string=} attrName Optional none normalized name. Defaults to key. */$set:function $set(key,value,writeAttr,attrName){// TODO: decide whether or not to throw an error if "class" //is set through this function since it may cause $updateClass to //become unstable. var node=this.$$element[0],booleanKey=getBooleanAttrName(node,key),aliasedKey=getAliasedAttrName(key),observer=key,nodeName;if(booleanKey){this.$$element.prop(key,value);attrName=booleanKey;}else if(aliasedKey){this[aliasedKey]=value;observer=aliasedKey;}this[key]=value;// translate normalized key to actual key if(attrName){this.$attr[key]=attrName;}else{attrName=this.$attr[key];if(!attrName){this.$attr[key]=attrName=snake_case(key,'-');}}nodeName=nodeName_(this.$$element);if(nodeName==='a'&&(key==='href'||key==='xlinkHref')||nodeName==='img'&&key==='src'){// sanitize a[href] and img[src] values this[key]=value=$$sanitizeUri(value,key==='src');}else if(nodeName==='img'&&key==='srcset'&&isDefined(value)){// sanitize img[srcset] values var result='';// first check if there are spaces because it's not the same pattern var trimmedSrcset=trim(value);// ( 999x ,| 999w ,| ,|, ) var srcPattern=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;var pattern=/\s/.test(trimmedSrcset)?srcPattern:/(,)/;// split srcset into tuple of uri and descriptor except for the last item var rawUris=trimmedSrcset.split(pattern);// for each tuples var nbrUrisWith2parts=Math.floor(rawUris.length/2);for(var i=0;i';var attributes=specialAttrHolder.firstChild.attributes;var attribute=attributes[0];// We have to remove the attribute from its container element before we can add it to the destination element attributes.removeNamedItem(attribute.name);attribute.value=value;element.attributes.setNamedItem(attribute);}function safeAddClass($element,className){try{$element.addClass(className);}catch(e){// ignore, since it means that we are trying to set class on // SVG element, where class name is read-only. }}var startSymbol=$interpolate.startSymbol(),endSymbol=$interpolate.endSymbol(),denormalizeTemplate=startSymbol==='{{'&&endSymbol==='}}'?identity:function denormalizeTemplate(template){return template.replace(/\{\{/g,startSymbol).replace(/}}/g,endSymbol);},NG_ATTR_BINDING=/^ngAttr[A-Z]/;var MULTI_ELEMENT_DIR_RE=/^(.+)Start$/;compile.$$addBindingInfo=debugInfoEnabled?function $$addBindingInfo($element,binding){var bindings=$element.data('$binding')||[];if(isArray(binding)){bindings=bindings.concat(binding);}else{bindings.push(binding);}$element.data('$binding',bindings);}:noop;compile.$$addBindingClass=debugInfoEnabled?function $$addBindingClass($element){safeAddClass($element,'ng-binding');}:noop;compile.$$addScopeInfo=debugInfoEnabled?function $$addScopeInfo($element,scope,isolated,noTemplate){var dataName=isolated?noTemplate?'$isolateScopeNoTemplate':'$isolateScope':'$scope';$element.data(dataName,scope);}:noop;compile.$$addScopeClass=debugInfoEnabled?function $$addScopeClass($element,isolated){safeAddClass($element,isolated?'ng-isolate-scope':'ng-scope');}:noop;compile.$$createComment=function(directiveName,comment){var content='';if(debugInfoEnabled){content=' '+(directiveName||'')+': ';if(comment)content+=comment+' ';}return window.document.createComment(content);};return compile;//================================ function compile($compileNodes,transcludeFn,maxPriority,ignoreDirective,previousCompileContext){if(!($compileNodes instanceof jqLite)){// jquery always rewraps, whereas we need to preserve the original selector so that we can // modify it. $compileNodes=jqLite($compileNodes);}var NOT_EMPTY=/\S+/;// We can not compile top level text elements since text nodes can be merged and we will // not be able to attach scope data to them, so we will wrap them in for(var i=0,len=$compileNodes.length;i').append($compileNodes).html()));}else if(cloneConnectFn){// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart // and sometimes changes the structure of the DOM. $linkNode=JQLitePrototype.clone.call($compileNodes);}else{$linkNode=$compileNodes;}if(transcludeControllers){for(var controllerName in transcludeControllers){$linkNode.data('$'+controllerName+'Controller',transcludeControllers[controllerName].instance);}}compile.$$addScopeInfo($linkNode,scope);if(cloneConnectFn)cloneConnectFn($linkNode,scope);if(compositeLinkFn)compositeLinkFn(scope,$linkNode,$linkNode,parentBoundTranscludeFn);return $linkNode;};}function detectNamespaceForChildElements(parentElement){// TODO: Make this detect MathML as well... var node=parentElement&&parentElement[0];if(!node){return'html';}else{return nodeName_(node)!=='foreignobject'&&toString.call(node).match(/SVG/)?'svg':'html';}}/** * Compile function matches each node in nodeList against the directives. Once all directives * for a particular node are collected their compile functions are executed. The compile * functions return values - the linking functions - are combined into a composite linking * function, which is the a linking function for the node. * * @param {NodeList} nodeList an array of nodes or NodeList to compile * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the * scope argument is auto-generated to the new child of the transcluded parent scope. * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then * the rootElement must be set the jqLite collection of the compile root. This is * needed so that the jqLite collection items can be replaced with widgets. * @param {number=} maxPriority Max directive priority. * @returns {Function} A composite linking function of all of the matched directives or null. */function compileNodes(nodeList,transcludeFn,$rootElement,maxPriority,ignoreDirective,previousCompileContext){var linkFns=[],attrs,directives,nodeLinkFn,childNodes,childLinkFn,linkFnFound,nodeLinkFnFound;for(var i=0;i addDirective(directives,directiveNormalize(nodeName),'E',maxPriority,ignoreDirective);// iterate over the attributes for(var attr,name,nName,ngAttrName,value,isNgAttr,nAttrs=node.attributes,j=0,jj=nAttrs&&nAttrs.length;j