UNPKG

domotz-remote-pawn

Version:

Domotz Agent

4 lines 113 kB
!function(){"use strict";angular.module("domotzEntities",[])}(),function(){"use strict";angular.module("googlechart",[]),angular.module("ionic-datepicker",[]),angular.module("domotzAngularWidgets",["domotzAngularWidgets.utils","domotzAngularWidgets.template","googlechart","ionic-datepicker"]).constant("ANGULAR_WIDGETS_PATH","../").factory("netToolsService",[function(){var e=function(){throw new Error("netToolsService method not implemented")};return{fetchNetworkInfo:e,fetchNetworkHistory:e,startMtrTest:e,startSpeedTest:e,fetchSpeedTestServer:e}}]).factory("signupService",[function(){var e=function(){throw new Error("signupService method not implemented")};return{getUserCell:e}}]).run([function(){"ontouchstart"in document.documentElement||(document.documentElement.className+=(document.documentElement.className.length>0?" ":"")+"dmt-no-touch")}]),angular.module("domotzAngularWidgets.template",[])}(),function(){"use strict";angular.module("domotzAngularWidgets.utils",["ngLodash"]).directive("dmtBindHtmlCompile",["$compile",function(e){return{restrict:"A",link:function(t,n,a){t.$watch(function(){return t.$eval(a.dmtBindHtmlCompile)},function(a){n.html(a),e(n.contents())(t)})}}}]).factory("XORCipher",["$window","lodash",function(e,t){function n(e){var t,n,a,s,o,i,l,r,c,p=0,m="";if(!e)return e;do t=e[p++],n=e[p++],a=e[p++],r=t<<16|n<<8|a,s=r>>18&63,o=r>>12&63,i=r>>6&63,l=63&r,m+=d.charAt(s)+d.charAt(o)+d.charAt(i)+d.charAt(l);while(p<e.length);return c=e.length%3,(c?m.slice(0,c-3):m)+"===".slice(c||3)}function a(e){var t,n,a,s,o,i,l,r,c=0,p=[];if(!e)return e;e+="";do s=d.indexOf(e.charAt(c++)),o=d.indexOf(e.charAt(c++)),i=d.indexOf(e.charAt(c++)),l=d.indexOf(e.charAt(c++)),r=s<<18|o<<12|i<<6|l,t=r>>16&255,n=r>>8&255,a=255&r,p.push(t),64!==i&&(p.push(n),64!==l&&p.push(a));while(c<e.length);return p}function s(e,t){return t=l(e,t),n(t)}function o(e,t){return t=a(t),r(e,t)}function i(e,t){return e.charCodeAt(Math.floor(t%e.length))}function l(e,n){return t.map(n,function(t,n){return t.charCodeAt(0)^i(e,n)})}function r(e,n){return t.map(n,function(t,n){return String.fromCharCode(t^i(e,n))}).join("")}var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:s,decode:o}}])}(),function(){"use strict";function e(e){for(var t in e)e.hasOwnProperty(t)&&(this[t]=e[t]);this.logged=void 0!==e}function t(e){var t={available:[],activated:[]};if(void 0!==e)for(var n=0;n<e.length;n++)null===e[n].bound_mac_address?t.available.push(e[n]):void 0!==e[n].agent&&null!=e[n].agent&&t.activated.push(e[n]);return t}angular.module("domotzEntities").factory("userFactory",[function(){function t(t){return new e(t)}return{createUser:t}}]),e.prototype.TYPE_STANDARD="STANDARD",e.prototype.TYPE_REFERRED="REFERRED",e.prototype.TYPE_PARTNER="PARTNER",e.prototype.TYPE_DEPUTY="DEPUTY",e.prototype.TYPE_MINION="MINION",e.prototype.TYPE_DIRECTOR="DIRECTOR",e.prototype.TYPE_MANAGER="MANAGER",e.prototype.TYPE_TEAM_LEADER="TEAM_LEADER",e.prototype.getId=function(){return this.id},e.prototype.getUsername=function(){return this.name&&this.name.toLowerCase()},e.prototype.getType=function(){return void 0===this.type||this.type===this.TYPE_REFERRED?this.TYPE_STANDARD:this.type},e.prototype.isLogged=function(){return this.logged},e.prototype.isPartner=function(){return this.type===this.TYPE_PARTNER},e.prototype.isDeputy=function(){return this.type===this.TYPE_DEPUTY},e.prototype.isMinion=function(){return this.type===this.TYPE_MINION},e.prototype.isDirector=function(){return this.type===this.TYPE_DIRECTOR},e.prototype.isManager=function(){return this.type===this.TYPE_MANAGER},e.prototype.isTeamLeader=function(){return this.type===this.TYPE_TEAM_LEADER},e.prototype.getBadge=function(){return this.isDirector()?"SUPER ADMIN":this.isManager()?"REGIONAL MANAGER":this.isPro()?"PRO DEALER":"PRO"},e.prototype.isStandard=function(){if(void 0===this.type)return!0;var e=[this.TYPE_STANDARD,this.TYPE_REFERRED];return e.indexOf(this.type)>-1},e.prototype.isPro=function(){if(void 0===this.type)return!1;var e=[this.TYPE_PARTNER,this.TYPE_DEPUTY,this.TYPE_MINION,this.TYPE_TEAM_LEADER];return e.indexOf(this.type)>-1},e.prototype.getRequestId=function(){return this.isDeputy()?this.partner_id:this.id},e.prototype.getAvailableLicences=function(){return void 0===this.availableLicences?[]:this.availableLicences},e.prototype.getActivatedLicences=function(){return void 0===this.activatedLicences?[]:this.activatedLicences},e.prototype.setLicences=function(e){if(this.id){var n=t(e);this.availableLicences=n.available,this.activatedLicences=n.activated}},e.prototype.setCustomizations=function(e){this.id&&(this.customizations=e||{})},e.prototype.getCustomizations=function(){return void 0===this.customizations?{}:this.customizations},e.prototype.getAlertLevel=function(){return void 0===this.alert_level?{}:this.alert_level},e.prototype.setAlertLevel=function(e){this.id&&(this.alert_level=e)}}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("CellOptionsController",["$scope","signupService",function(e,t){var n=e;n.directiveScope=n,n.cells=["eu"],n.cellLabels={eu:"Europe",us:"North America",rotw:"Rest of the World"};var a=t.getUserCell();a.then(function(e){n.cells=e,1==n.cells.length?n.ngModel=n.cells[0]:n.cells.push("rotw")})["catch"](function(){n.ngModel=n.cells[0]})}]).directive("dmtCellOptions",[function(){return{restrict:"E",scope:{ngModel:"="},controller:"CellOptionsController",template:'<div class=dmt-cell-options><div ng-if="cells.length > 1"><span>My Region:</span><ul class=dmt-radio-list><li ng-repeat="cell in cells"><div class=dmt-radio-button><input id={{cell}} type=radio name=selected_cell value={{cell}} ng-model=directiveScope.ngModel required> <label for={{cell}}></label></div><label for={{cell}}>{{cellLabels[cell]}}</label></li></ul></div></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("CheckboxController",["$scope",function(e){var t=e;t.directiveScope=t,t.parentScope=e.$parent,t.formSubmitted=function(){return t.parentScope.formSubmitted},t.toggleCheckbox=function(){t.ngModel=!t.ngModel}}]).directive("dmtCheckbox",["$compile",function(e){return{restrict:"E",scope:{ngModel:"=",checkboxLabel:"@?"},controller:"CheckboxController",link:function(e,t,n){},template:"<div class=dmt-checkbox-wrapper ng-form=fieldForm><div class=dmt-checkbox><input type=checkbox name=checkbox-item ng-model=ngModel ng-required=true id=agreement-checkbox> <i class=icon ng-class=\"(ngModel ? 'icon-checkbox-checked' : 'icon-checkbox-stroke')\" ng-click=toggleCheckbox()></i> <label class=dmt-checkbox-label for=agreement-checkbox>{{checkboxLabel}}</label></div><span ng-show=\"!fieldForm['checkbox-item'].$valid && formSubmitted()\" class=dmt-field-error>Selection is required</span></div>",replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("AgreementController",["$scope",function(e){var t=e;t.parentScope=e.$parent,t.formSubmitted=function(){return t.parentScope.formSubmitted}}]).directive("dmtAgreementCheckbox",[function(){return{scope:{ngModel:"=",fieldId:"@",fieldName:"@",fieldValue:"@",labelText:"@",hasLabelLink:"=",labelLink:"@",ngRequired:"=",ngDisabled:"=",fieldError:"@",onClickFn:"&"},controller:"AgreementController",restrict:"E",template:'<div class=dmt-agreement-wrapper ng-class="{\'dmt-disabled\': ngDisabled, \'dmt-error\': !fieldForm[fieldName].$valid && (fieldForm[fieldName].$dirty || formSubmitted()) && !ngDisabled}" ng-form=fieldForm><input id={{fieldId}} type=checkbox name={{fieldName}} ng-model=ngModel ng-required=ngRequired ng-disabled=ngDisabled><div class=dmt-label><span class=dmt-agreement-checkbox><i class="icon icon-tick"></i></span> <span ng-if=hasLabelLink class=dmt-agreement-label>{{labelText}} <span class=dmt-link ng-click="ngDisabled || onClickFn()">{{labelLink}}</span></span> <label ng-if=!hasLabelLink for={{fieldId}} class=dmt-agreement-label>{{labelText}}</label></div><span ng-show="!fieldForm[fieldName].$valid && (fieldForm[fieldName].$dirty || formSubmitted()) && !ngDisabled" class=dmt-error>{{fieldError}}</span></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("CountrySelectController",["$scope","signupService","ngDialog","$window",function(e,t,n,a){var s=e;s.directiveScope=s,s.parentScope=e.$parent,s.selected_country="",s.button_text=s.buttonLabel||"Please Select Your Country",s.formSubmitted=function(){return s.parentScope.formSubmitted};var o=t.getUserCountries();o.then(function(e){s.countries=e.countries,s.countries.length>0&&s.countries.unshift({full_name:"United States",iso_code:"US",has_vat:!1,region:"us"},{full_name:"United Kingdom",iso_code:"GB",has_vat:!0,region:"eu"});var t,n=Object.keys(s.countries).filter(function(e){if(s.countries[e].iso_code===s.ngModel)return e});void 0!==s.countries[n[0]]&&(t=s.countries[n[0]].full_name,s.selected_country=t,s.button_text=t,s.ngModel=s.countries[n[0]])})["catch"](function(){console.log("Something gone wrong retrieving the list of Countries")}),s.openCountryListDialog=function(e){n.openConfirm({template:'<div class="dmt-country-list"><h3>Select your Country</h3><p ng-show="ctrl_countries.displayImportanceMsg">Set up your country is important to get better <b><em>performance</em></b> and better <b><em>experience</em></b>.</p><div ng-if="ctrl_countries.countries.length > 1"><ul><li ng-repeat="country in ctrl_countries.countries" ng-click="closeThisDialog(country)"><span id="{{country.iso_code}}">{{country.full_name}}</span></li></ul></div><hr /><div class="dmt-btn-container"><div class="dmt-btn-back dmt-btn-big" ng-click="confirm()">Cancel</div></div></div>',plain:!0,controllerAs:"ctrl_countries",controller:[function(){var t=this;t.countries=e,t.displayImportanceMsg=s.displayImportanceMsg}],className:"ngdialog-theme-default dmt-country-list-dialog",showClose:!1,closeByEscape:!0,closeByDocument:!0,preCloseCallback:function(e){return void 0!=e.full_name&&(console.log("Country selected, I'm going to close the dialog"),s.button_text=e.full_name,s.selected_country=e.full_name,s.ngModel=e),!0}}).then(function(e){})},s.openCountryDialog=function(e){var t=a.navigator.language;if(void 0!=t&&""===s.selected_country){var o;o=t.indexOf("-")>-1?t.split("-")[1]:t;var i=Object.keys(e).filter(function(t){if(e[t].iso_code==o.toUpperCase())return t});if(i.length>0)var l=e[i[0]];n.openConfirm({template:'<div class="dmt-hint-country"><h3>Are you from<br /><b>{{ctrl_hint_country.hint_country.full_name}} ?</b></h3><div ng-if="ctrl_hint_country.displayImportanceMsg"><hr /><p>Set up your country to get better <b><em>performance</em></b> and better <b><em>experience</em></b>.</p><hr /></div><div class="dmt-btn-container"><div class="dmt-btn-proceed"  ng-click="closeThisDialog(ctrl_hint_country.hint_country)">Yes</div><div class="dmt-btn-back"  ng-click="closeThisDialog(\'NO\')">NO</div></div"></div>',plain:!0,controllerAs:"ctrl_hint_country",controller:[function(){var e=this;e.hint_country=l,e.displayImportanceMsg=s.displayImportanceMsg}],className:"ngdialog-theme-default dmt-hint-country-dialog",showClose:!1,closeByEscape:!0,closeByDocument:!0,preCloseCallback:function(t){return void 0!==t.full_name?(console.log("Country selected, I'm going to close the dialog"),s.button_text=t.full_name,s.selected_country=t.full_name,s.ngModel=t,!0):"NO"!=t||(s.openCountryListDialog(e),!0)}}).then(function(e){})}else s.openCountryListDialog(e)}}]).directive("dmtCountrySelect",[function(){return{restrict:"E",scope:{ngModel:"=",displayImportanceMsg:"=?",buttonLabel:"@"},controller:"CountrySelectController",template:'<div class=dmt-country-select ng-form=countryForm ng-class="{\'dmt-country-select-error\': !countryForm[\'selectedCountry\'].$valid && (fieldForm[\'selectedCountry\'].$dirty || formSubmitted())}"><div class="dmt-btn-control dmt-btn-icon dmt-btn-full dmt-country-button" ng-click=openCountryDialog(countries)><span>{{button_text}}</span> <i class="icon icon-sort"></i></div><input style="display: none;" type=text name=selectedCountry ng-model=selected_country required> <span class=dmt-field-error>Country required</span></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("FieldController",["$scope",function(e){var t=e;t.directiveScope=t,t.parentScope=e.$parent,t.fieldFocus=!1,t.formSubmitted=function(){return t.parentScope.formSubmitted},void 0===t.onFieldFocus&&(t.onFieldFocus=angular.noop);var n=34,a=25;t.zxcvbn={},t.getPwdStrength=function(){var e=t.zxcvbn.entropy,s="";return e&&void 0!==t.ngModel&&t.ngModel.length>0&&(s=e>n?"strong":e>a?"medium":"weak"),s},t.showPassword=function(n){t.fieldType=n?"text":"password",e.$digest()}}]).directive("dmtField",["$compile",function(e){return{restrict:"E",scope:{ngModel:"=",fieldType:"@",fieldLabel:"@",fieldRequired:"=",fieldErrorMessage:"@",fieldDisabled:"=",onFieldFocus:"&?",displayPwdStrength:"=?",displayPwdEye:"=?",tabIndex:"@"},controller:"FieldController",link:function(t,n,a){if(t.fieldName=a.fieldName||"fieldName",t.fieldRegex=a.fieldRegex,t.displayPwdStrength=!!angular.isDefined(t.displayPwdStrength)&&t.displayPwdStrength,t.displayPwdEye=!!angular.isDefined(t.displayPwdEye)&&t.displayPwdEye,"password"==t.fieldType&&t.displayPwdEye===!0){var s=angular.element('<span class="dmt-show-password"><i class="icon-visible"></i></span>');s.on("mousedown touchstart",function(){t.showPassword(!0)}),s.on("mouseup touchend mouseout",function(){t.showPassword(!1)}),e(s)(t),angular.element(n[0].querySelector(".dmt-field-input")).append(s)}},template:'<div class=dmt-field-wrapper ng-form=fieldForm><div class=dmt-field-input ng-class="{\'dmt-active\': fieldFocus || ngModel.length > 0 || (!fieldForm.$valid && (fieldForm[fieldName].$dirty || formSubmitted())), \'dmt-disabled\': fieldDisabled}"><input class=dmt-input name={{fieldName}} type={{fieldType}} ng-model=ngModel ng-focus="fieldFocus = true; onFieldFocus()" ng-blur="fieldFocus = false" ng-required=fieldRequired ng-maxlength=255 ng-disabled=fieldDisabled ng-pattern=fieldRegex tabindex={{tabIndex}}> <span class=dmt-input-label>{{fieldLabel}}</span></div><div class=dmt-password-bar ng-class="\'dmt-\' + directiveScope.getPwdStrength()" ng-if=displayPwdStrength><zxcvbn password=directiveScope.ngModel full=directiveScope.zxcvbn show=false></zxcvbn></div><span ng-show="!fieldForm[fieldName].$valid && (fieldForm[fieldName].$dirty || formSubmitted())" class=dmt-field-error>{{fieldErrorMessage}}</span></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("TrafficRateController",["$timeout","$scope","$rootScope","$filter","googleChartApiPromise","deviceService","lodash",function(e,t,n,a,s,o,i){function l(){t.chartObject={},t.chartObject.type="AreaChart",t.chartObject.options={chartArea:{width:"100%",height:"100%",left:"70",right:"26",top:"10",bottom:"40"},vAxis:{format:"0.0 Mb/s",viewWindow:{min:0,max:null},maxValue:20,textStyle:{color:"#7186A3"},baselineColor:"#ccc"},hAxis:{format:v,gridlines:{count:-1,units:{months:{format:[v]},days:{format:[v]},hours:{format:["MM/dd hh a"]}}},textStyle:{color:"#7186A3"},baselineColor:"#ccc"},explorer:{actions:["dragToZoom","rightClickToReset"],axis:"horizontal",keepInBounds:!0,maxZoomIn:20,maxZoomOut:2},legend:{position:"none"},selectionMode:"single",aggregationTarget:"category",tooltip:{trigger:"focus"},focusTarget:"category",fontName:"Helvetica Neue",interpolateNulls:!1,series:{0:{color:c.seriesFilter.Download.color},1:{color:c.seriesFilter.Upload.color},2:{color:"#e4a232"},3:{color:"#b27000"}}},t.downloadPieObj={},t.downloadPieObj.type="PieChart",t.uploadPieObj={},t.uploadPieObj.type="PieChart";var e={chartArea:{width:"100%",height:"100%",left:"10",right:"10",top:"10",bottom:"10"},legend:"none",pieSliceText:"none",pieResidueSliceColor:"#ecf0f1",colors:["#54b2ff","#007ac3","#2a598f","#3e80c9","#009efa","#a1d2ff"],tooltip:{trigger:"selection",isHtml:!0}},n={chartArea:{width:"100%",height:"100%",left:"10",right:"10",top:"10",bottom:"10"},legend:"none",pieSliceText:"none",pieResidueSliceColor:"#ecf0f1",colors:["#94afd6","#778eb0","#5c708c","#455467","#b2d2ff","#afc7eb"],tooltip:{trigger:"selection",isHtml:!0}};t.downloadPieObj.options=e,t.uploadPieObj.options=n}var r,d=1e6,c=t,p="",m=o.getGSHistory();m.then(function(e){p=o.data.gsRouterMac,r=e.result,b(r,p)});var u="yyyy MMM dd HH:mm",v="yyyy MMM dd";c.loaded=!1;var g={cols:[{id:"day",label:"Day",type:"date"},{id:"download",label:"Total Download",type:"number"},{id:"upload",label:"Total Upload",type:"number"},{id:"device_download",label:"",type:"number"},{id:"device_upload",label:"",type:"number"}],rows:[]},f=!1,h=!1;c.updateChartWithDetail=function(e,t){"download"==t?void 0===e?(f=!1,g.cols[3].label=""):(f=c.topDownloads[e.row].mac,g.cols[3].label=c.topDownloads[e.row].name+" Download"):void 0===e?(h=!1,g.cols[4].label=""):(h=c.topUploads[e.row].mac,g.cols[4].label=c.topUploads[e.row].name+" Upload"),b(r,p)},c.openDeviceDetail=function(e){if(void 0!==e){for(var a,s=t.$parent;void 0===a&&void 0!==s;)void 0!==s._hidePopup?a=s._hidePopup:s=s.$parent;a(),n.$broadcast("showDeviceTraffic",e)}},c.timeFrame={from:new Date,to:new Date},c.seriesFilter={Download:{index:0,color:"#54B2FF",name:"Download",cssClass:"dmt-filter-download",selected:!0},Upload:{index:1,color:"#7186A3",name:"Upload",cssClass:"dmt-filter-upload",selected:!0}},c.disableFilter=!1,c.zoomSelector={"1D":{name:"1D",extended_name:"1 Day",selected:!1,offset:1},"7D":{name:"7D",extended_name:"1 Week",selected:!1,offset:7},"1M":{name:"1M",extended_name:"1 Month",selected:!1,offset:30},"3M":{name:"3M",extended_name:"3 Months",selected:!1,offset:90},"6M":{name:"6M",extended_name:"6 Months",selected:!1,offset:180},"1Y":{name:"1Y",extended_name:"1 Year",selected:!1,offset:360}};var w=null,b=function(n,o){var i=[];if(w=n,w.length>0){for(var l={},r=0;r<w.length;r++){var p=w[r].ts;void 0!==w[r][o]?(l={},l.c=[{v:new Date(p),f:a("date")(Math.round(1*new Date(p)),u)},{v:parseFloat((parseInt(w[r][o][0])/d).toFixed(2)),f:parseFloat((parseInt(w[r][o][0])/d).toFixed(2))+" Mbps"},{v:parseFloat((parseInt(w[r][o][2])/d).toFixed(2)),f:parseFloat((parseInt(w[r][o][2])/d).toFixed(2))+" Mbps"}],f&&void 0!==w[r][f]?l.c.push({v:parseFloat((parseInt(w[r][f][0])/d).toFixed(2)),f:parseFloat((parseInt(w[r][f][0])/d).toFixed(2))+" Mbps"}):l.c.push({}),h&&void 0!==w[r][h]?l.c.push({v:parseFloat((parseInt(w[r][h][2])/d).toFixed(2)),f:parseFloat((parseInt(w[r][h][2])/d).toFixed(2))+" Mbps"}):l.c.push({}),i.push(l)):console.log("IS UNDEFINED")}g.rows=i,s.then(function(){e(function(){t.chartObject.data=new google.visualization.DataTable(g),c.loaded=!0},1)})}};c.updateChart=b,c.selectedPoint={dr:!1,ur:!1},c.selectHandler=function(e){if(void 0!==e){var n=[],a=[],s=o.data.devices_assoc_mac;angular.forEach(w[e.row],function(e,t){if("ts"!=t&&(void 0!==e[0]||void 0!==e[2])){var i=parseFloat((parseInt(e[0])/d).toFixed(2)),l=parseFloat((parseInt(e[2])/d).toFixed(2));if(t==p)c.selectedPoint.dr=i,c.selectedPoint.ur=l;else{var r=o.data.devices[s[t]],m=r&&r.computed_name||t,u=r&&r.id;i>0&&n.push({name:m,id:u,mac:t,value:i,label:i+" Mbps"}),l>0&&a.push({name:m,id:u,mac:t,value:l,label:l+" Mbps"})}}}),n=i.sortByOrder(n,"value",!1),a=i.sortByOrder(a,"value",!1);var l=[],r=[];angular.forEach(n,function(e){l.push({c:[{v:e.name},{v:e.value,f:e.label},{v:'<div class="dmt-download-tooltip"><i class="icon icon-chart-single"></i><div class="dmt-tt-text"><div><b>'+e.name+"</b></div><div>"+e.label+"</div></div></div>",p:{}}]})}),angular.forEach(a,function(e){r.push({c:[{v:e.name},{v:e.value,f:e.label},{v:'<div class="dmt-upload-tooltip"><i class="icon icon-chart-single"></i><div class="dmt-tt-text"><div><b>'+e.name+"</b></div><div>"+e.label+"</div></div></div>",p:{}}]})}),t.downloadPieObj.data={cols:[{id:"d",label:"Devices",type:"string"},{id:"s",label:"Slices",type:"number"},{id:"",role:"tooltip",type:"string",p:{role:"tooltip",html:!0}}],rows:l},t.uploadPieObj.data={cols:[{id:"d",label:"Devices",type:"string"},{id:"s",label:"Slices",type:"number"},{id:"",role:"tooltip",type:"string",p:{role:"tooltip",html:!0}}],rows:r},c.topDownloads=n.slice(0,5),c.topUploads=a.slice(0,5)}},c.chartErrorHandler=function(e){google.visualization.errors.removeError(e.id)},s.then(l)}]).directive("dmtTrafficRate",[function(){return{restrict:"E",scope:{agentName:"=?"},controller:"TrafficRateController",template:'<div class=dmt-trafficrate-wrapper><div class=dmt-pies-wrapper><div class=dmt-download-pie><div class=dmt-graph-placeholder><i class="icon icon-chart-hint"></i><span class=dmt-graph-placeholder-label>Select a sample on the graph</span></div><div google-chart id=dmt-download-pie chart=downloadPieObj agc-on-select="updateChartWithDetail(selectedItem, \'download\')" agc-on-error=chartErrorHandler(error)></div><div class=dmt-graph-label><icon class="icon icon-single-arrow-down"></icon>Download <span ng-show=selectedPoint.dr>({{selectedPoint.dr}} Mbps)</span></div></div><div class=dmt-upload-pie><div class=dmt-graph-placeholder><i class="icon icon-chart-hint"></i><span class=dmt-graph-placeholder-label>Select a sample on the graph</span></div><div google-chart id=dmt-upload-pie chart=uploadPieObj agc-on-select="updateChartWithDetail(selectedItem, \'upload\')" agc-on-error=chartErrorHandler(error)></div><div class=dmt-graph-label><icon class="icon icon-single-arrow-up"></icon>Upload <span ng-show=selectedPoint.ur>({{selectedPoint.ur}} Mbps)</span></div></div></div><div class=dmt-network-graph google-chart id=dmt-google-chart chart=chartObject agc-on-select=selectHandler(selectedItem) agc-on-error=chartErrorHandler(error)></div><dmt-spinner display=!loaded message="\'Loading...\'"></dmt-spinner><div class=dmt-graph-lower-wrapper><div class=dmt-top-title ng-show="topDownloads.length > 0 || topUploads.length > 0">Top 5 Consumers</div><div class=dmt-top-downloads ng-if="topDownloads.length > 0"><icon class="icon icon-single-arrow-down"></icon><div class=dmt-top-device ng-repeat="device in topDownloads" ng-click=openDeviceDetail(device.id) ng-class="{\'dmt-unknown-device\': device.id === undefined}"><div class=dmt-top-name>{{device.name}}</div><div class=dmt-top-value>{{device.label}}</div><i class="icon icon-arrow-right"></i></div></div><div class=dmt-top-uploads ng-if="topUploads.length > 0"><icon class="icon icon-single-arrow-up"></icon><div class=dmt-top-device ng-repeat="device in topUploads" ng-click=openDeviceDetail(device.id) ng-class="{\'dmt-unknown-device\': device.id === undefined}"><div class=dmt-top-name>{{device.name}}</div><div class=dmt-top-value>{{device.label}}</div><i class="icon icon-arrow-right"></i></div></div></div></div>',replace:!0}}]).directive("ngMousedrag",["$document",function(e){return{restrict:"A",link:function(t,n,a){var s="mouseup",o="mousemove",i="mousedown",l=0;n.bind(i,function(n){n.preventDefault(),e.bind(o,function(){l++}),e.bind(s,function(n){e.unbind(o),e.unbind(s),0!==l&&(t.$event=n,t.$eval(a.ngMousedrag),l=0)})})}}}]).directive("ngRightClick",function(){return document.oncontextmenu=function(e){if(e.target.hasAttribute("ng-right-click"))return!1},function(e,t,n){t.bind("contextmenu",function(t){e.$event=t,e.$eval(n.ngRightClick)})}})}(),function(){angular.module("domotzAngularWidgets").directive("dmtPlugIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-plug-icon dmt-icon-multilayer"><span class="icon-plug-top"></span><span class="icon-plug-front"></span><span class="icon-plug-stroke"></span><span class="icon-plug-pins"></span></div>'}}]).directive("dmtFavouriteIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-favourites-icon dmt-icon-multilayer"><span class="icon-fav-filled"></span><span class="icon-fav"></span></div>'}}]).directive("dmtDevicesIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-devices-icon dmt-icon-multilayer"><span class="icon-devices-bg-laptop"></span><span class="icon-devices-bg-phone"></span><span class="icon-devices-stroke"></span></div>'}}]).directive("dmtNetworkIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-network-icon dmt-icon-multilayer"><span class="icon-network-bg"></span><span class="icon-network-stroke"></span></div>'}}]).directive("dmtAlertsIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-alerts-icon dmt-icon-multilayer"><span class="icon-alerts-bg"></span><span class="icon-alerts-stroke"></span><span class="icon-bar-right"></span></div>'}}]).directive("dmtCollaborationIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-collaboration-icon dmt-icon-multilayer"><span class="icon-collaboration-bg"></span><span class="icon-collaboration-stroke"></span><span class="icon-collaboration-tick"></span></div>'}}]).directive("dmtGuestIconX",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-collaboration-guest-icon-x dmt-icon-multilayer"><span class="icon-guest-back-stroke"></span><span class="icon-guest-front-fill"></span><span class="icon-guest-front-stroke-x"></span><span class="icon-guest-cross"></span></div>'}}]).directive("dmtGuestIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-collaboration-guest-icon dmt-icon-multilayer"><span class="icon-guest-back-stroke"></span><span class="icon-guest-back-fill"></span><span class="icon-guest-front-stroke"></span></div>'}}]).directive("dmtHelpIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-help-icon dmt-icon-multilayer"><span class="icon-help-bg"></span><span class="icon-help-stroke"></span><span class="icon-help-question-mark"></span></div>'}}]).directive("dmtDudesIcon",[function(){return{restrict:"E",replace:!0,template:'<span class="dmt-dudes-icon dmt-icon-multilayer"><span class="icon-dudes-half-stroke"></span><span class="icon-dudes-full-stroke"></span><span class="icon-dudes-fill"></span></span>'}}]).directive("dmtDiscoverIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-discover-icon dmt-icon-multilayer"><span class="icon-discover-solid"></span><span class="icon-discover-magnify"></span></div>'}}]).directive("dmtGiftIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-gift-icon dmt-icon-multilayer"><span class="icon-gift-fill"></span><span class="icon-gift-stroke"></span><span class="icon-gift-ribbon"></span></div>'}}]).directive("dmtLockIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-lock-icon dmt-icon-multilayer"><span class="icon-fill-upper-lock"></span><span class="icon-fill-body-lock"></span><span class="icon-stroke-lock"></span></div>'}}]).directive("dmtCustomerDetails",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-customer-details-icon dmt-icon-multilayer"><span class="icon-guest-device"></span><span class="icon-guest-device-bg"></span><span class="icon-circle"></span></div>'}}]).directive("dmtPlaceMark",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-place-mark-icon dmt-icon-multilayer"><span class="icon-geomarker-outter-stroke"></span><span class="icon-geomarker-inner-stroke"></span></div>'}}]).directive("dmtChartFilter",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-chart-filter-icon dmt-icon-multilayer"><span class="icon-radio-chart"></span><span class="icon-thick-circle"></span></div>'}}]).directive("dmtWebIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-web-icon dmt-icon-multilayer"><span class="icon-web-fill"></span><span class="icon-web-stroke"></span></div>'}}]).directive("dmtRdpIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-rdp-icon dmt-icon-multilayer"><span class="icon-rdp-fill"></span><span class="icon-rdp-stroke"></span></div>'}}]).directive("dmtSshIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-ssh-icon dmt-icon-multilayer"><span class="icon-ssh-fill"></span><span class="icon-ssh-stroke"></span></div>'}}]).directive("dmtSnmpAdminDownIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-icon-snmp-admin-down dmt-icon-multilayer"><span class="icon-admindown-stroke"></span></div>'}}]).directive("dmtSnmpOperationDownIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-icon-snmp-operation-down dmt-icon-multilayer"><span class="icon-opdown-fill-plug"></span><span class="icon-opdown-stroke"></span></div>'}}]).directive("dmtSnmpOperationUpIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-icon-snmp-operation-up dmt-icon-multilayer"><span class="icon-opup-fill-plug"></span><span class="icon-opup-fill-cable"></span><span class="icon-opup-stroke"></span></div>'}}]).directive("dmtEthernetUnplugIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-icon-ethernet-unplug dmt-icon-multilayer"><span class="icon-eth-unplug-pins"></span><span class="icon-eth-unplug-fill"></span><span class="icon-eth-unplug-stroke"></span></div>'}}]).directive("dmtEthernetPlugIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-icon-ethernet-plug dmt-icon-multilayer"><span class="icon-eth-plug-fill"></span><span class="icon-eth-plug-stroke"></span></div>'}}]).directive("dmtEthernetIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-icon-ethernet dmt-icon-multilayer"><span class="icon-eth-fill"></span><span class="icon-eth-stroke"></span></div>'}}]).directive("dmtAlertIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-alert-icon dmt-icon-multilayer"><span class="icon-circle-filled"></span><span class="icon-alert-stroke"></span></div>'}}]).directive("dmtZigbeeLockedIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-zigbee-locked-icon dmt-icon-multilayer"><span class="icon-zigbee"></span><span class="icon-fill-upper-lock"></span><span class="icon-fill-body-lock"></span><span class="icon-stroke-lock"></span></div>'}}]).directive("dmtLogoutIcon",[function(){return{restrict:"E",replace:!0,template:'<div class="dmt-logout-icon dmt-icon-multilayer"><span class="icon-logout-stroke"></span><span class="icon-logout-fill"></span></div>'}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("LatencyController",["$scope","deviceService",function(e,t){function n(){var n=t.getDevice(e.deviceId);a.avg=n.avg,a.stddev=n.stddev,a.last_avg=n.last_avg,a.avg_point=100*s(Math.max(a.avg,1))/o,a.avg_prev_dev=100*s(Math.max(a.avg-a.stddev,1))/o,a.avg_next_dev=100*s(Math.max(a.avg+a.stddev,1))/o,a.avg_width_left=a.avg_point-a.avg_prev_dev,a.avg_width_right=a.avg_next_dev-a.avg_point,a.avg_label=Math.max(a.avg-a.stddev,1)+"ms - "+Math.max(a.avg+a.stddev,1)+"ms",a.avg_label_position=a.avg_prev_dev+(a.avg_next_dev-a.avg_prev_dev)/2,a.avg_visible=n.available&&void 0!==a.avg&&void 0!==a.stddev&&a.avg>0&&a.stddev>0,a.last_point=100*s(Math.max(a.last_avg,1))/o,a.last_point_label="Current ping "+(a.avg_visible&&a.last_point>=a.avg_prev_dev?"("+(a.last_point>a.avg_next_dev?"out of usual":"in")+" range)":""),a.last_point_unavailable=!n.available||void 0===a.last_avg||a.last_avg<=0,a.last_point_value=a.last_point_unavailable?"N/A":a.last_avg+"ms",a.avg_out_of_expected=a.avg>i,a.last_out_of_range=a.avg_visible&&(a.last_point>a.avg_next_dev||a.avg_out_of_expected&&a.last_point>a.avg_prev_dev)}var a=e;a.directiveScope=a,a.parentScope=e.$parent,Math.log=function(){var e=Math.log;return function(t,n){return e(t)/(n?e(n):1)}}();var s=function(e){return Math.log(e,1.01)},o=s(5e3),i=260;e.$on("dmtGSUpdated",n),n()}]).directive("dmtLatency",["$compile",function(e){return{restrict:"E",scope:{deviceId:"="},controller:"LatencyController",link:function(e,t,n){},template:"<div class=dmt-latency-wrapper><div class=dmt-latency-bar-wrapper><div class=dmt-latency-bar ng-class=\"{'dmt-out-range': last_out_of_range}\"><div class=dmt-expected-range ng-class=\"{'dmt-out-expected': avg_out_of_expected}\" ng-show=avg_visible><div class=dmt-left-range ng-style=\"{'width': avg_width_left + '%', 'left': avg_prev_dev + '%'}\"></div><div class=dmt-right-range ng-style=\"{'width': avg_width_right + '%', 'left': avg_point + '%'}\"></div><div class=dmt-range-label ng-style=\"{'left': avg_label_position + '%'}\"><span>Usual range</span> <span class=dmt-range-values>{{avg_label}}</span></div></div><div class=dmt-value-arrow ng-class=\"{'dmt-value-unavailable': last_point_unavailable}\" ng-style=\"{'left': last_point + '%'}\"><span class=dmt-ping-value>{{last_point_value}}</span> <i class=\"icon icon-sort-down\"></i> <span class=dmt-ping-label>{{last_point_label}}</span></div></div></div></div>",
replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("NetHistoryController",["$scope","netToolsService","$timeout",function(e,t,n){var a=e;a.networkHistory={loading:!1,error:!1,data:{},online_from:""},a.fetchNetworkHistory=function(){a.networkHistory.loading||(a.networkHistory.loading=!0,a.networkHistory.error=!1,t.fetchNetworkHistory().then(function(e){a.networkHistory.data=e},function(){a.networkHistory.data={},a.networkHistory.error=!0})["finally"](function(){a.networkHistory.loading=!1}))}}]).directive("dmtNetHistoryView",[function(){return{restrict:"E",scope:{},controller:"NetHistoryController",template:'<div ng-init=fetchNetworkHistory() ng-class="{\'dmt-error\': networkHistory.error}"><ion-content class="has-header dmt-content-wrapper dmt-netHistory" ng-show=!networkHistory.loading><h3>Network Log</h3><div class=dmt-error-wrapper><span class=dmt-error-msg>Oops, something went wrong.</span></div><div ng-if=networkHistory.data.events.length ng-repeat="event in networkHistory.data.events"><div class="dmt-rows dmt-connection-history-up"><i class="icon dmt-log-icon icon-online"></i><div class=dmt-connection-history-col-status>Connection<br>Up</div><div class=dmt-connection-history-col-time><div>{{::event.to_timestamp | amDateFormat:\'YYYY MMM DD\'}}</div><div>{{::event.to_timestamp | amDateFormat:\'hh:mm A (z)\'}}</div></div></div><div class="dmt-rows dmt-connection-history-down"><i class="icon dmt-log-icon icon-offline"></i><div class=dmt-connection-history-col-status>Connection<br>Down</div><div class=dmt-connection-history-col-time><div>{{::event.from_timestamp | amDateFormat:\'YYYY MMM DD\'}}</div><div>{{::event.from_timestamp | amDateFormat:\'hh:mm A (z)\'}}</div></div></div></div><div class=dmt-connection-history-no-events ng-if=!networkHistory.data.events.length>No recent disconnection events</div></ion-content><ion-content class="has-header dmt-spinner-wrapper" ng-show=networkHistory.loading><ion-spinner icon=android class=dmt-spinner></ion-spinner><div class=dmt-notice>Fetching your network log.</div></ion-content></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("NetInfoController",["$scope","netToolsService","$timeout",function(e,t,n){var a=e;a.networkInformation={loading:!1,error:!1,data:{}},a.fetchNetworkInfo=function(){a.networkInformation.loading||(a.networkInformation.loading=!0,a.networkInformation.error=!1,n(function(){t.fetchNetworkInfo().then(function(e){a.networkInformation.data=e},function(){a.networkInformation.data={},a.networkInformation.error=!0})["finally"](function(){a.networkInformation.loading=!1})},1e3))}}]).directive("dmtNetInfoView",[function(){return{restrict:"E",scope:{},controller:"NetInfoController",template:'<div ng-init=fetchNetworkInfo() ng-class="{\'dmt-error\': networkInformation.error}"><ion-content class="has-header dmt-content-wrapper dmt-netinfo" ng-show=!networkInformation.loading><h3>Network Info</h3><div class=dmt-error-wrapper><span class=dmt-error-msg>Oops, something went wrong.</span></div><div class=dmt-rows><div class=dmt-row-data><span class=dmt-left>Public IP Address</span> <span class=dmt-right>{{networkInformation.data.internet_access.ip.address || \'N/A\'}}</span></div><div class=dmt-row-data><span class=dmt-left>Public Name</span> <span class=dmt-right>{{networkInformation.data.internet_access.ip.name || \'N/A\'}}</span></div><div class=dmt-row-data><span class=dmt-left>Provider</span> <span class=dmt-right>{{networkInformation.data.internet_access.provider.netname}}</span> <span class=dmt-right>{{networkInformation.data.internet_access.provider.descr}}</span> <span class=dmt-right>{{networkInformation.data.internet_access.provider.inetnum}}</span> <span class=dmt-right ng-if=!networkInformation.data.internet_access.provider>N/A</span></div><div class=dmt-row-data><span class=dmt-left>Gateway</span> <span class=dmt-right>{{networkInformation.data.local_network.servers.gateway || \'N/A\'}}</span></div><div class=dmt-row-data><span class=dmt-left>DNS</span> <span class=dmt-right ng-repeat="dns in networkInformation.data.local_network.servers.dns">{{dns}}</span> <span class=dmt-right ng-if=!networkInformation.data.local_network.servers.dns>N/A</span></div><div class=dmt-row-data><span class=dmt-left>DHCP</span> <span class=dmt-right ng-repeat="dhcp in networkInformation.data.local_network.servers.dhcp">{{dhcp}}</span> <span class=dmt-right ng-if=!networkInformation.data.local_network.servers.dhcp>N/A</span></div><div class=dmt-row-data><span class=dmt-left>Interface</span> <span class=dmt-right ng-repeat="interface in networkInformation.data.local_network.interfaces">{{interface.address}} ({{interface.name}})</span> <span class=dmt-right ng-if=!networkInformation.data.local_network.interfaces>N/A</span></div></div><div class=dmt-action-button ng-click=fetchNetworkInfo() ng-if=networkInformation.error>Retry</div></ion-content><ion-content class="has-header dmt-spinner-wrapper" ng-show=networkInformation.loading><ion-spinner icon=android class=dmt-spinner></ion-spinner><div class=dmt-notice>Fetching your network information.</div></ion-content></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("NetRouteController",["$scope","netToolsService","$ionicScrollDelegate","ANGULAR_WIDGETS_PATH",function(e,t,n,a){function s(){e.$broadcast("scroll-resize",{handler:"netroute-scroll"})}var o=e;o.imagesPath=a+"images/",o.mtrService={selected:"play.spotify.com"},o.mtrCustomService={value:"",focus:!1},o.mtrTest={loading:!1,error:!1,packets:10,raw:!1,data:{}},o.startMtrTest=function(){if(!o.mtrTest.loading){if(o.mtrTest.data.mtr)return o.mtrTest.data={},o.mtrTest.raw=!1,o.mtrTest.error=!1,void s();o.mtrTest.loading=!0,o.mtrTest.error=!1,o.mtrTest.data={};var e=o.mtrService.selected;"custom"==e&&(e=o.mtrCustomService.value||"google.com");var n=o.mtrTest.packets||10;t.startMtrTest(e,n).then(function(e){o.mtrTest.data=e},function(){o.mtrTest.error=!0})["finally"](function(){o.mtrTest.loading=!1,s()})}},o.showMtrRawData=function(){o.mtrTest.raw=!0,s(),n.$getByHandle("netroute-scroll").scrollBottom()}}]).directive("dmtNetRouteView",[function(){return{restrict:"E",scope:{},controller:"NetRouteController",template:'<div ng-init=fetchNetworkInfo() ng-class="{\'dmt-error\': mtrTest.error}"><ion-content class="has-header dmt-content-wrapper dmt-netroute" delegate-handle=netroute-scroll scroll-resize><h3>Route Analysis</h3><div class=dmt-error-wrapper><span class=dmt-error-msg>Oops, something went wrong.</span></div><div class=dmt-animation-wrapper><img ng-src={{imagesPath}}mtr-test-results.gif ng-show=mtrTest.data.mtr> <img ng-src="{{imagesPath}}{{mtrTest.loading ? \'mtr-test-animation\' : \'mtr-test-static\'}}.gif" ng-show=!mtrTest.data.mtr><div class=dmt-netroute-results ng-class="\'dmt-netroute-results-\' + res" ng-repeat="res in [\'internal\', \'isp\', \'destination\']" ng-show=mtrTest.data.mtr><div class="dmt-netroute-labels row"><div class=col-50>Loss%</div><div class=col-50>Avg</div></div><div class="dmt-netroute-values row"><div class=col-50>{{mtrTest.data.mtr[res].loss_ratio || \'N/A\'}}</div><div class=col-50>{{(mtrTest.data.mtr[res].ping.average ? mtrTest.data.mtr[res].ping.average + \'ms\' : \'N/A\')}}</div></div></div></div><div class=dmt-netroute-config ng-class="{\'dmt-disabled\' : mtrTest.loading}" ng-show=!mtrTest.data.mtr><div class=dmt-label>Select a service:</div><select class=dmt-select ng-model=mtrService.selected><option value=play.spotify.com>Spotify</option><option value=facebook.com>Facebook</option><option value=google.com>Google</option><option value=custom>Custom</option></select><div class=dmt-input ng-show="mtrService.selected == \'custom\'"><div class=dmt-field-wrapper><div class=dmt-field-input ng-class="{\'dmt-active\': mtrCustomService.focus || mtrCustomService.value.length > 0}"><input class=dmt-input name=custom_service type=text ng-model=mtrCustomService.value ng-focus="mtrCustomService.focus = true" ng-blur="mtrCustomService.focus = false"> <span class=dmt-input-label>Insert a hostname</span></div></div></div><div class=dmt-label>Packets number</div><div class="dmt-options-group row"><div class="dmt-option col-33" ng-repeat="val in [10, 20, 50]" ng-class="{\'dmt-active\': mtrTest.packets == val}" ng-click="mtrTest.packets = val">{{val}}</div></div></div><div class=dmt-notice ng-show=!mtrTest.data.mtr>This test will last approximately {{mtrTest.packets}} seconds.</div><div class=dmt-netroute-resume ng-show=mtrTest.data.mtr><div class=dmt-highlight>Sent {{mtrTest.packets}} packets to {{mtrTest.target}}</div><div class=dmt-more ng-click=showMtrRawData() ng-show=!mtrTest.raw>See raw data</div><ion-scroll direction=x class=dmt-netroute-raw ng-show=mtrTest.raw><pre ng-repeat="line in mtrTest.data.mtr.raw">{{line}}</pre></ion-scroll></div><div class=dmt-action-button ng-class="{\'dmt-disabled\' : mtrTest.loading}" ng-click=startMtrTest()>{{mtrTest.loading ? \'Testing...\' : (mtrTest.data.mtr ? \'Done\' : \'Start\')}}</div></ion-content></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("NetSpeedController",["$scope","netToolsService","$timeout","ANGULAR_WIDGETS_PATH",function(e,t,n,a){function s(){l.speedTest.data.ping.value="",l.speedTest.data.download.value="",l.speedTest.data.upload.value=""}var o="speed-test-starter",i=1e6,l=e;l.imagesPath=a+"images/",l.speedTest={loading:!1,error:!1,server:{loading:!1,error:!1,location:""},data:{ping:{gif:o},download:{gif:o},upload:{gif:o}}},l.startSpeedTest=function(){l.speedTest.loading||(l.speedTest.loading=!0,l.speedTest.error=!1,l.speedTest.server.error=!1,s(),l.speedTest.server.loading=!0,t.fetchSpeedTestServer().then(l.executeSpeedTest,function(e){l.speedTest.server.location="N/A",l.speedTest.server.error=!0,l.speedTest.loading=!1})["finally"](function(){l.speedTest.server.loading=!1}))},l.executeSpeedTest=function(e){l.speedTest.server.location=e.location&&e.location.siteName?e.location.siteName+(e.location.state?" ("+e.location.state+")":""):"N/A";var a=e.id;if(a){l.speedTest.data.ping.gif="ping-animation",l.speedTest.data.download.gif="download-animation",l.speedTest.data.upload.gif="upload-animation";var s=t.startSpeedTest(a);n(function(){s.then(function(e){l.speedTest.data.ping.value=e.speedTest.speed&&e.speedTest.speed.rtt?parseInt(e.speedTest.speed.rtt)+"ms":"N/A",l.speedTest.data.download.value=e.speedTest.speed&&e.speedTest.speed.download?parseFloat(e.speedTest.speed.download/i).toFixed(2)+" Mbps":"N/A",l.speedTest.data.upload.value=e.speedTest.speed&&e.speedTest.speed.upload?parseFloat(e.speedTest.speed.upload/i).toFixed(2)+" Mbps":"N/A"},function(){l.speedTest.data.ping.value="N/A",l.speedTest.data.download.value="N/A",l.speedTest.data.upload.value="N/A",l.speedTest.error=!0})["finally"](function(){l.speedTest.loading=!1,l.speedTest.data.ping.gif=o,l.speedTest.data.download.gif=o,l.speedTest.data.upload.gif=o})},3e4)}else l.speedTest.server.location="N/A",l.speedTest.server.error=!0,l.speedTest.loading=!1}}]).directive("dmtNetSpeedView",[function(){return{restrict:"E",scope:{agentName:"=?"},controller:"NetSpeedController",template:'<div ng-init=fetchNetworkInfo() ng-class="{\'dmt-error\': speedTest.error || speedTest.server.error}"><ion-content class="has-header dmt-content-wrapper dmt-netspeed dmt-netspeed-new"><h3>Speed Test</h3><div class=dmt-error-wrapper><span class=dmt-error-msg>{{speedTest.server.error ? \'Cannot find an available server right now, please try again.\' : \'Oops, something went wrong.\'}}</span></div><div class=dmt-rows><div class="dmt-row-data dmt-netspeed-location"><span class=dmt-left>From</span> <span class=dmt-right>To</span> <span class="dmt-left dmt-netspeed-source">{{agentName || \'Domotz Agent\'}}</span> <span class="dmt-right dmt-netspeed-server-spinner" ng-show="speedTest.loading && speedTest.server.loading"><ion-spinner icon=android class=dmt-spinner></ion-spinner></span> <span class="dmt-right dmt-netspeed-server"><span ng-show=!speedTest.server.loading ng-class="{\'dmt-error\': speedTest.server.error, \'dmt-italic\': !speedTest.server.location}">{{speedTest.server.location || \'Nearest Server\'}}</span></span></div></div><div class=dmt-rows><div class=dmt-row-data><span class="dmt-left dmt-netspeed-label">Ping</span> <span class="dmt-right dmt-netspeed-value">{{speedTest.data.ping.value}}</span> <span class="dmt-right dmt-netspeed-gif" ng-show=!speedTest.data.ping.value><img ng-src={{imagesPath}}{{speedTest.data.ping.gif}}.gif></span></div><div class=dmt-row-data><span class="dmt-left dmt-netspeed-label">Download Speed</span> <span class="dmt-right dmt-netspeed-value">{{speedTest.data.download.value}}</span> <span class="dmt-right dmt-netspeed-gif" ng-show=!speedTest.data.download.value><img ng-src={{imagesPath}}{{speedTest.data.download.gif}}.gif></span></div><div class=dmt-row-data><span class="dmt-left dmt-netspeed-label">Upload Speed</span> <span class="dmt-right dmt-netspeed-value">{{speedTest.data.upload.value}}</span> <span class="dmt-right dmt-netspeed-gif" ng-show=!speedTest.data.upload.value><img ng-src={{imagesPath}}{{speedTest.data.upload.gif}}.gif></span></div></div><div class=dmt-action-button ng-class="{\'dmt-disabled\' : speedTest.loading}" ng-click=startSpeedTest()>{{speedTest.loading ? \'Testing...\' : (speedTest.data.ping.value ? \'Test Again\' : \'Start\')}}</div></ion-content></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("PlacesController",["$scope","uiGmapIsReady",function(e,t){var n=e;n.directiveScope=n,n.predictions=[];var a={street_number:{extractor:"short_name",key:"street"},route:{extractor:"long_name",key:"street"},locality:{extractor:"long_name",key:"city"},administrative_area_level_1:{extractor:"short_name",key:"state"},country:{extractor:"short_name",key:"country"},postal_code:{extractor:"short_name",key:"zip"}};n.selectPlace=function(t){var n=new google.maps.places.PlacesService(document.getElementById("place-detail-hidden"));n.getDetails({placeId:t.placeId},function(t,n){if(n===google.maps.places.PlacesServiceStatus.OK){e.ngModel={};for(var s=0;s<t.address_components.length;s++){var o=t.address_components[s].types[0];if(a[o]){var i=t.address_components[s][a[o].extractor];["street_number","route"].indexOf(o)>-1?void 0===e.ngModel[a[o].key]||0==e.ngModel[a[o].key].length?e.ngModel[a[o].key]=i:e.ngModel[a[o].key]+=" "+i:"country"==o?e.ngModel[a[o].key]={full_name:t.address_components[s].long_name,iso_code:i}:e.ngModel[a[o].key]=i}}t.geometry&&(t.geometry.location?"function"!=typeof t.geometry.location.lat?(e.ngModel.latitude=t.geometry.location.lat,e.ngModel.longitude=t.geometry.location.lng):(e.ngModel.latitude=t.geometry.location.lat(),e.ngModel.longitude=t.geometry.location.lng()):t.geometry.access_points&&t.geometry.access_points.length>0&&t.geometry.access_points[0].location&&(e.ngModel.latitude=t.geometry.access_points[0].location.lat,e.ngModel.longitude=t.geometry.access_points[0].location.lng)),e.$apply(),void 0!==e.selectionCallback&&e.selectionCallback()}})},t.promise(1).then(function(){n.displaySuggestions=function(t,a){a!=google.maps.places.PlacesServiceStatus.OK&&a!=google.maps.places.PlacesServiceStatus.ZERO_RESULTS||(n.predictions=[],a==google.maps.places.PlacesServiceStatus.OK&&t.forEach(function(e){if(0!=e.terms.length){var t=0,a="",s="";if(1==e.terms.length)a=e.terms[0].value;else{for(!(e.terms.length>2)||isNaN(e.terms[0].value)&&isNaN(e.terms[1].value)?(a=e.terms[0].value,t=1):(a=e.terms[0].value+" "+e.terms[1].value,t=2);t<e.terms.length-1;)s+=e.terms[t].value+", ",t++;s+=e.terms[t].value}0==s.length&&(s="-"),n.predictions.push({main:a,additional:s,placeId:e.place_id})}}),e.$digest())};var t=new google.maps.places.AutocompleteService(null,{types:["geocode"]});e.$watch("inputPlace",function(e){void 0!==e&&e.length>0?t.getPlacePredictions({input:e,types:["geocode"]},n.displaySuggestions):n.predictions=[]})})}]).directive("dmtPlacesAutocomplete",[function(){return{restrict:"E",scope:{ngModel:"=",selectionCallback:"&?"},controller:"PlacesController",template:'<div class=dmt-places-autocomplete><div class=dmt-places-search><input type=text ng-model=inputPlace ng-model-options="{debounce: 600}" placeholder="Enter your address"> <i class="icon icon-search"></i></div><ul class=dmt-places-list><li class=dmt-places-item ng-repeat="place in predictions" ng-click=selectPlace(place)><div class=dmt-place-details><div class=dmt-place-main>{{place.main}}</div><div class=dmt-place-additional>{{place.additional}}</div></div><div class=dmt-place-icon><i class="icon icon-geomarker-fill"></i></div></li></ul><div class=dmt-places-placeholder ng-show="predictions.length === 0"><i class="icon icon-search"></i><div class=dmt-placeholder-title>Search Tip</div><div class=dmt-placeholder-tip>Type the name of a place to set your agent address</div></div><div id=place-detail-hidden></div></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("SpeedTestController",["$scope","$rootScope","$filter","netToolsService","$timeout","dmtPopup","googleChartApiPromise","ionicDatePicker",function(e,t,n,a,s,o,i,l){function r(){g.chartLoader=!1,g.lockInterface=!1,s.cancel(f)}function d(){Object.keys(g.zoomSelector).forEach(function(e){g.zoomSelector[e].selected=!1})}function c(){Object.keys(g.seriesFilter).forEach(function(e){g.seriesFilter[e].selected=!0});var e=new google.visualization.DataView(I);z=e.getViewColumns()}function p(){g.speedTest.data.download.label="Download • Mb/s",g.speedTest.data.upload.label="Upload • Mb/s"}function m(){if(null!==P){var e=P.getChart();if(null!==e){d();var t=e.getChartLayoutInterface(),n=t.getChartAreaBoundingBox();g.timeFrame.from=t.getHAxisValue(n.left),g.timeFrame.to=t.getHAxisValue(n.left+n.width),g.timeFrameAvg=(g.timeFrame.to-g.timeFrame.from)/864e5,g.downloadAvg=parseFloat($(A,g.timeFrame.from,g.timeFrame.to)/v).toFixed(1),0==g.downloadAvg&&(g.downloadAvg="--"),g.uploadAvg=parseFloat($(M,g.timeFrame.from,g.timeFrame.to)/v).toFixed(1),0==g.uploadAvg&&(g.uploadAvg="--")}}}function u(){e.chartObject={},e.chartObject.type="LineChart",e.chartObject.options={chartArea:{width:g.chartWidth,height:"90%",left:g.chartLeft},vAxis:{format:"0 Mb/s",viewWindow:{min:0,max:null},textStyle:{color:"#7186A3"},baselineColor:"#ccc"},hAxis:{format:w,gridlines:{count:-1,units:{months:{format:[w]},days:{format:[w]},hours:{format:["MM/dd hh a"]}}},textStyle:{color:"#7186A3"},baselineColor:"#ccc"},height:"400",explorer:{actions:["dragToZoom","rightClickToReset"],axis:"horizontal",keepInBounds:!0,maxZoomIn:20,maxZoomOut:2},legend:{position:"none"},selectionMode:"single",aggregationTarget:"category",tooltip:{trigger:"focus"},focusTarget:"category",fontName:"Helvetica Neue",interpolateNulls:!1,series:{0:{color:g.seriesFilter.Upload.color},1:{color:g.seriesFilter.Download.color}}}}var v=1e6,g=e;g.testButtonLabel="Test Now",g.downloadAnimationClass="",g.uploadAnimationClass="",g.chartLoader=!1;var f=null;g.lockInterface=!1;var h="yyyy MMM dd HH:mm",w="yyyy MMM dd",b=a.getLastSpeedTest();g.down=function(){return void 0!=b.download&&void 0!=b.download.value?parseFloat(b.download.value/v).toFixed(1):"--"},g.up=function(){return void 0!=b.upload&&void 0!=b.upload.value?parseFloat(b.upload.value/v).toFixed(1):"--"},g.isValidDate=function(e){return 0!==Date.parse(e)},g.speedTest={loading:!1,error:!1,server:{loading:!1,error:!1,location:"",hint:"Please Perform a Speed Test",timestamp:void 0!=b.timestamp?b.timestamp.value:""},data:{download:{label:"Download • Mb/s",value:g.down()},upload:{label:"Upload • Mb/s",value:g.up()}}},g.timeFrame={from:new Date,to:new Date},g.seriesFilter={Download:{index:0,color:"#54B2FF",name:"Download",cssClass:"dmt-filter-download",selected:!0},Upload:{index:1,color:"#7186A3",name:"Upload",cssClass:"dmt-filter-upload",selected:!0}},g.disableFilter=!1,g.zoomSelector={"1D":{name:"1D",extended_name:"1 Day",selected:!1,offset:1},"7D":{name:"7D",extended_name:"1 Week",selected:!1,offset:7},"1M":{name:"1M",extended_name:"1 Month",selected:!1,offset:30},"3M":{name:"3M",extended_name:"3 Months",selected:!1,offset:90},"6M":{name:"6M",extended_name:"6 Months",selected:!1,offset:180},"1Y":{name:"1Y",extended_name:"1 Year",selected:!1,offset:360}};var y=function(){return g.lockInterface=!0,s(function(){g.chartLoader=!0},200)};g.downloadAvg="--",g.uploadAvg="--",g.timeFrameAvg="",g.chartEmpty={shown:!1,text:"",arrow:!1},g.openFromDatePicker=function(e){var t={callback:function(e){g.timeFrame.from=new Date(e),g.timeFrame.from>g.timeFrame.to&&(g.timeFrame.to=new Date(e)),k(),d()},weeksList:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],to:function(e){return e.setDate(e.getDate()-1),e}(new Date),inputDate:e,mondayFirst:!1,closeOnSelect:!1,setLabel:"OK",closeLabel:"Cancel",templateType:"popup"};l.openDatePicker(t)},g.openToDatePicker=function(e){var t={callback:function(e){if(g.timeFrame.to=new Date(e),g.timeFrame.to<=g.timeFrame.from){var t=new Date(e);t.setDate(t.getDate()-1),g.timeFrame.from=t}k(),d()},weeksList:["Sun","Mon","Tue","Wed","thu","Fri","Sat"],to:new Date,inputDate:e,mondayFirst:!1,closeOnSelect:!1,setLabel:"OK",closeLabel:"Cancel",templateType:"popup"};l.openDatePicker(t)};var T=function(e,t){if(null!==A&&null!==M){for(var n=!1,a=!1,s=0;s<A.length;s++)if(n){if(Math.round(t/1e3)-21600<A[s].time){a=!0;break}}else Math.round(e/1e3)+21600>A[s].time&&(n=!0);var o=function(n){return Math.round(e/1e3)<n.time&&Math.round(t/1e3)>n.time};if(a){var i={speedtest:{download:{series:[]},upload:{series:[]}}};return i.speedtest.download.series=A.filter(o),i.speedtest.upload.series=M.filter(o),E(i),!0}}return!1},k=function(){var e=g.timeFrame.from,t=g.timeFrame.to;T(e,t)||(f=y(),a.getSpeedTestHistory(Math.round(e/1e3),Math.round(t/1e3)).then(E)["catch"](function(){o.alert({subTitle:"Something went wrong, please try again"})})["finally"](r))};g.dateFilterPreset=function(e){if(!g.lockInterface){d(),g.zoomSelector[e.name].selected=!0;var t=new Date,n=new Date;if(n.setDate(n.getDate()-g.zoomSelector[e.name].offset),g.timeFrame.from=n,g.timeFrame.to=t,g.timeFrameAvg=g.zoomSelector[e.name].offset,!T(n,t)){f=y();var s=a.getSpeedTestHistory(Math.round(n/1e3),Math.round(t/1e3));s.then(E)["catch"](function(){o.alert({subTitle:"Something went wrong, please try again"})})["finally"](r)}}},g.startSpeedTest=function(){g.speedTest.loading||(g.speedTest.loading=!0,p(),g.speedTest.server.hint="Testing Now",g.speedTest.data.download.label="Download Speed",g.speedTest.data.upload.label="Upload Speed",g.testButtonLabel="Testing...",g.speedTest.server.loading=!0,a.fetchSpeedTestServer().then(g.executeSpeedTest,function(e){o.alert({subTitle:"Cannot find an available server right now, please try again"}),g.speedTest.loading=!1,g.speedTest.data.download.label="Download • Mb/s",g.speedTest.data.upload.label="Upload • Mb/s",g.testButtonLabel="Test Now"})["finally"](function(){g.speedTest.server.loading=!1}))},g.executeSpeedTest=function(l){g.speedTest.server.location=l.location&&l.location.siteName?"on "+(l.location.siteName+(l.location.state?" ("+l.location.state+")":"")):"N/A";var r=l.id;if(r){var d=a.startSpeedTest(r);g.downloadAnimationClass="dmt-animation-active",g.speedTest.data.download.label="Performing Download Speed",g.speedTest.data.upload.label="Upload Speed",s(function(){g.downloadAnimationClass="dmt-animation-play"},200),s(function(){g.downloadAnimationClass="dmt-animation-complete",g.speedTest.data.download.label="Download Speed"},12500),s(function(){g.uploadAnimationClass="dmt-animation-active",g.speedTest.data.upload.label="Performing Upload Speed"},12700),s(function(){g.uploadAnimationClass="dmt-animation-play"},13200),s(function(){g.uploadAnimationClass="dmt-animation-complete",g.speedTest.data.upload.label="Upload Speed"},25700),s(function(){d.then(function(e){g.speedTest.server.timestamp=e.stats.end,g.speedTest.data.download.value=e.speedTest.speed&&e.speedTest.speed.download?parseFloat(e.speedTest.speed.download/v).toFixed(1):"N/A",g.speedTest.data.download.label="Download • Mb/s",g.speedTest.data.upload.value=e.speedTest.speed&&e.speedTest.speed.upload?parseFloat(e.speedTest.speed.upload/v).toFixed(1):"N/A",g.speedTest.data.upload.label="Upload • Mb/s"},function(){g.speedTest.data.download.value="N/A",g.speedTest.data.upload.value="N/A",o.alert({subTitle:"Oops, something went wrong."})})["finally"](function(){g.downloadAnimationClass="",g.uploadAnimationClass="",g.testButtonLabel="Test Now",g.speedTest.loading=!1,g.chartEmpty.shown=!0;var a={};a.c=[{v:new Date,f:n("date")(Math.round(1*new Date),h)},{v:parseFloat(b.download.value/v).toFixed(2),f:parseFloat(b.download.value/v).toFixed(2)+" Mbps"},{v:parseFloat(b.upload.value/v).toFixed(2),f:parseFloat(b.upload.value/v).toFixed(2)+" Mbps"}],N.rows.push(a),i.then(function(){I=new google.visualization.DataTable(N),e.chartObject.data=I,t.$emit("resizeMsg")})})},26700)}else o.alert({subTitle:"Cannot find an available server right now, please try again"}),g.speedTest.loading=!1};var _=new Date,S=new Date;_.setDate(_.getDate()-7),S.setDate(S.getDate()-30);var D=Math.round(new Date/1e3);ionic.Platform.isIOS()||ionic.Platform.isAndroid()?(D=Math.round(_/1e3),g.zoomSelector["7D"].selected=!0,g.timeFrameAvg=g.zoomSelector["7D"].offset,g.timeFrame.from=new Date(_)):(D=Math.round(S/1e3),g.zoomSelector["1M"].selected=!0,g.timeFrameAvg=g.zoomSelector["1M"].offset,g.timeFrame.from=new Date(S));var F=Math.round(new Date/1e3),C=864e5,A=null,M=null,E=function(t){g.seriesFilter.Upload.selected&&g.seriesFilter.Download.selected||c();var a=[];if(A=t.speedtest.download.series,M=t.speedtest.upload.series,A.length>0){var s=Math.round(Math.abs((g.timeFrame.from.getTime()-1e3*A[0].time)/C));s>0&&(A.unshift({time:Math.round(A[0].time-1),value:0}),M.unshift({time:Math.round(A[0].time-1),value:0}),A.unshift({time:Math.round(g.timeFrame.from/1e3),value:0}),M.unshift({time:Math.round(g.timeFrame.from/1e3),value:0})),g.chartEmpty.shown=!0,g.downloadAvg=parseFloat($(A,g.timeFrame.from,g.timeFrame.to)/v).toFixed(1),0==g.downloadAvg&&(g.downloadAvg="--"),g.uploadAvg=parseFloat($(M,g.timeFrame.from,g.timeFrame.to)/v).toFixed(1),0==g.uploadAvg&&(g.uploadAvg="--"),g.timeFrameAvg=(g.timeFrame.to-g.timeFrame.from)/864e5;for(var o={},l=0;l<A.length;l++)A[l]&&A[l].value&&M[l]&&M[l].value&&(o={},o.c=[{v:new Date(1e3*A[l].time),f:n("date")(new Date(1e3*A[l].time),h)},{v:parseFloat(A[l].value/v).toFixed(2),f:parseFloat(A[l].value/v).toFixed(2)+" Mbps"},{v:parseFloat(M[l].value/v).toFixed(2),f:parseFloat(M[l].value/v).toFixed(2)+" Mbps"}],a.push(o));N.rows=a,i.then(function(){I=new google.visualization.DataTable(N),e.chartObject.data=I,L()})}else g.chartEmpty.shown=!1,g.chartEmpty.text="No past data available, please perform a Speed Test.",g.chartEmpty.arrow=!1};f=y();var x=a.getSpeedTestHistory(D,F);x.then(E)["catch"](function(){o.alert({subTitle:"Something went wrong, please try again"})})["finally"](r);var I=null,P=null;g.drawHandler=function(e){P=e},g.chartReset=function(){s(function(){m()},1e3)},g.chartDragEnd=function(e){s(function(){m()},1e3)},g.selectHandler=function(e,t){e.setSelection([])},g.chartErrorHandler=function(e){google.visualization.errors.removeError(e.id)};var N={cols:[{id:"day",label:"Day",type:"date"},{id:"download",label:"Download",type:"number"},{id:"upload",label:"Upload",type:"number"}],rows:[]};g.chartWidth="85%",g.chartLeft="auto",(ionic.Platform.isIOS()||ionic.Platform.isAndroid())&&(g.chartWidth="80%",g.chartLeft=50,ionic.Platform.isIPad()&&(g.chartWidth="90%",g.chartLeft=75));var L=function(){var t=new google.visualization.DataView(e.chartObject.data),n=t.getViewColumns();for(var a in n)if(n.hasOwnProperty(a)){var s=parseInt(a);if(0!=s){var o=t.getColumnLabel(s);e.chartObject.options.series[s-1].color=O(o)}}},R=function(e,t){var n=new google.visualization.DataView(e),a=n.getViewColumns();for(var s in a)if(a.hasOwnProperty(s)){var o=parseInt(s);if(0!=o){var i=n.getColumnLabel(o);if(i==t)return o}}return-1},$=function(e,t,n){var a=0,s=0,o=Math.round(t/1e3),i=Math.round(n/1e3);return angular.forEach(e,function(e){e.time<=i&&e.time>=o&&0!==e.value&&(a+=parseFloat(e.value),s++)}),0!=s?parseFloat(a/s).toFixed(2):0},O=function(e){return g.seriesFilter[e].color},z=null;g.toggleSeries=function(t){g.lockInterface||i.then(function(){var n=new google.visualization.DataView(I),a=n.getViewColumns();null===z&&(z=a);var s=R(I,t);z.indexOf(s)>-1?z.length>2?(z.splice(z.indexOf(s),1),g.seriesFilter[t].selected=!1,g.chartEmpty.shown=!0):g.chartEmpty.shown?(g.chartEmpty.shown=!1,g.chartEmpty.text="You haven't selected any filter.",g.chartEmpty.arrow=!0,g.seriesFilter[t].selected=!1,z=[0]):(g.chartEmpty.shown=!0,g.seriesFilter[t].selected=!0):(g.chartEmpty.shown=!0,z.push(s),g.seriesFilter[t].selected=!0),n.hideColumns(a),n.setColumns(z),e.chartObject.data=n,L()})},i.then(u)}]).directive("dmtSpeedTestView",[function(){return{restrict:"E",scope:{agentName:"=?"},controller:"SpeedTestController",template:'<div ng-init=fetchNetworkInfo()><ion-content class="has-header dmt-content-wrapper dmt-speedtest"><h3>Speed Test</h3><div class=dmt-chart-wrapper ng-class="{\'dmt-no-past-chart\': !chartEmpty.shown && !chartEmpty.arrow}"><div class=dmt-chart-settings><div class=dmt-chart-filters><h4 ng-show="chartEmpty.shown || chartEmpty.arrow">Show</h4><span ng-show="chartEmpty.shown || chartEmpty.arrow" ng-repeat="filter in seriesFilter" ng-click=toggleSeries(filter.name) class={{filter.cssClass}} ng-class="{\'dmt-filter-off\': !filter.selected}"><dmt-chart-filter></dmt-chart-filter>{{filter.name}}</span></div><div class=dmt-chart-time><div class=dmt-chart-time-frame><h4>Custom Time Frame</h4><button class=dmt-btn-from-time ng-click=openFromDatePicker(timeFrame.from) ng-disabled=lockInterface><span>{{timeFrame.from | date: "yyyy MMM dd"}}</span> <i class="icon icon-sort"></i></button> <span>To</span> <button class=dmt-btn-to-time ng-click=openToDatePicker(timeFrame.to) ng-disabled=lockInterface><span>{{timeFrame.to | date: "yyyy MMM dd"}}</span> <i class="icon icon-sort"></i></button></div><div class=dmt-chart-zoom><h4>Zoom to</h4><span ng-repeat="selector in zoomSelector" ng-class="{\'dmt-current\': selector.selected}" ng-click=dateFilterPreset(selector)>{{::selector.name}}</span></div></div></div><div class=dmt-chart><span class=dmt-chart-empty-text ng-show=!chartEmpty.shown><i ng-show=chartEmpty.arrow class="icon icon-curved-arrow"></i> <span>{{chartEmpty.text}}</span></span><dmt-spinner class=dmt-content-spinner display=chartLoader message="\'Loading chart...\'"></dmt-spinner><div google-chart id=dmt-google-chart style="position:relative; width: 100%; height: 100%;" chart=chartObject agc-before-draw=drawHandler(chartWrapper) agc-on-select="selectHandler(chart, selectedItem)" agc-on-error=chartErrorHandler(error) ng-show=chartEmpty.shown ng-mousedrag=chartDragEnd($event) ng-right-click=chartReset($event)></div></div></div><div class=dmt-clear></div><div class=dmt-speedtest-info-wrapper><div class=dmt-speedtest-average><h3>Average of: <span>{{timeFrameAvg | amDurationFormat : \'days\' | uppercase }}</span></h3><div class="dmt-row dmt-desktop"><span class=dmt-label>Download</span> <span class=dmt-value>{{downloadAvg}}</span> <span class=dmt-unit>Mb/s</span></div><div class="dmt-row dmt-desktop"><span class=dmt-label>Upload</span> <span class=dmt-value>{{uploadAvg}}</span> <span class=dmt-unit>Mb/s</span></div><div class=dmt-speedtest-average-mobile><div class=dmt-row><span class=dmt-value>{{downloadAvg}}</span> <span>Download &bull; Mb/s</span></div><div class=dmt-row><span class=dmt-value>{{uploadAvg}}</span> <span>Upload &bull; Mb/s</span></div></div></div><div class=dmt-speedtest-last><div class=dmt-container><h3><b>Last Speed Test:</b> <span ng-show="isValidDate(speedTest.server.timestamp) && speedTest.server.timestamp && !speedTest.loading"><span am-time-ago=speedTest.server.timestamp></span> ago</span> <span ng-show="!isValidDate(speedTest.server.timestamp) || !speedTest.server.timestamp || speedTest.loading">{{speedTest.server.hint}}</span> <span class=dmt-speed-server-spinner ng-show="speedTest.loading && speedTest.server.loading"><ion-spinner icon=android class=dmt-spinner></ion-spinner></span> <span ng-show=!speedTest.server.loading>{{speedTest.server.location}}</span></h3><div class=dmt-last-values-box><div class=dmt-last-download><div class="dmt-animation {{downloadAnimationClass}}" ng-show=speedTest.loading><div class=dmt-animation-border></div><div class=dmt-animated-block></div><div class=dmt-animation-check><i class="icon icon-thick-stroke"></i></div></div><span class=dmt-value ng-show=!speedTest.loading><span><i class="icon icon-double-arrow-down"></i> {{speedTest.data.download.value}}</span></span> <span class="dmt-label {{downloadAnimationClass}}">{{speedTest.data.download.label}}</span></div><div class=dmt-last-upload><div class="dmt-animation {{uploadAnimationClass}}" ng-show=speedTest.loading><div class=dmt-animation-border></div><div class=dmt-animated-block></div><div class=dmt-animation-check><i class="icon icon-thick-stroke"></i></div></div><span class=dmt-value ng-show=!speedTest.loading><span>{{speedTest.data.upload.value}} <i class="icon icon-double-arrow-up"></i></span></span> <span class="dmt-label {{uploadAnimationClass}}">{{speedTest.data.upload.label}}</span></div></div><button class=dmt-action-button ng-disabled=speedTest.loading ng-class="{\'dmt-disabled\': speedTest.loading}" ng-click=startSpeedTest()>{{testButtonLabel}}</button></div></div><div class=dmt-speedtest-settings></div></div></ion-content></div>',
replace:!0}}]).directive("ngMousedrag",["$document",function(e){return{restrict:"A",link:function(t,n,a){var s="mouseup",o="mousemove",i="mousedown",l=0;n.bind(i,function(n){n.preventDefault(),e.bind(o,function(){l++}),e.bind(s,function(n){e.unbind(o),e.unbind(s),0!==l&&(t.$event=n,t.$eval(a.ngMousedrag),l=0)})})}}}]).directive("ngRightClick",function(){return document.oncontextmenu=function(e){if(e.target.hasAttribute("ng-right-click"))return!1},function(e,t,n){t.bind("contextmenu",function(t){e.$event=t,e.$eval(n.ngRightClick)})}})}(),function(){"use strict";angular.module("domotzAngularWidgets").factory("dmtPopup",["$ionicModal","$q","$rootScope","$document",function(e,t,n,a){function s(e){var n=t.defer();return c.then(function(){i(e).then(function(e){n.resolve(e)})}),c=n.promise,n.promise}function o(e){13===e.which&&e.preventDefault()}function i(s){s=angular.extend({scope:null,title:"",buttons:[]},s||{});var i={};return i.scope=(s.scope||n).$new(),i.responseDeferred=t.defer(),c=i.responseDeferred.promise,e.fromTemplateUrl(s.templateUrl,{scope:i.scope,animation:s.animation}).then(function(e){angular.extend(i.scope,{cssClass:s.cssClass,iconMultilayer:s.iconMultilayer,iconClass:s.iconClass,title:s.title,subTitle:s.subTitle,closeIcon:s.closeIcon,contentTemplate:s.contentTemplate,buttons:s.buttons,_onButtonTapped:function(t,n){var a=(t.onTap||angular.noop)(n);n=n.originalEvent||n,n.defaultPrevented||l(e,function(){i.responseDeferred.resolve(a)})},_hidePopup:function(){s.preventClosing!==!0&&l(e,function(){i.responseDeferred.resolve()})}}),s.scope&&(s.scope.closeCurrentPopup=function(){l(e,function(){i.responseDeferred.resolve()})}),e.show(),a.on("keydown keypress",o)}),i.responseDeferred.promise}function l(e,t){e.hide().then(function(){e.remove(),a.off("keydown keypress",o),t()})}var r="/angular-widgets/templates/popup.template.html",d=t.defer();d.resolve();var c=d.promise,p={alert:function(e){e=e||{};var t={animation:"dmt-scale-in",templateUrl:r,iconClass:"icon-ooops",title:"Ooops!",buttons:[{text:e.okText||"OK",type:e.okType||"dmt-neutral",onTap:function(){return!0}}]};return p._showPopup(angular.extend(t,e||{}))},confirm:function(e){e=e||{};var t={animation:"slide-in-up",templateUrl:r,iconClass:"icon-notice2",buttons:[{text:e.okText||"OK",type:e.okType||"dmt-positive",onTap:function(){return!0}},{text:e.cancelText||"Cancel",type:e.cancelType||"dmt-neutral",onTap:function(){return!1}}]};return p._showPopup(angular.extend(t,e||{}))},custom:function(e){var t={animation:"slide-in-up",templateUrl:r};return p._showPopup(angular.extend(t,e||{}))},_showPopup:s};return p._closePopup=l,p._createPopup=i,p}])}(),function(){"use strict";angular.module("domotzAngularWidgets").factory("flurryService",[function(){function e(e){var t="";switch(e){case"ios":t="6WP9ST8VP7DTCNVSNQ2G";break;case"android":t="X25MZNBH3F4GH3CBDV8X";break;case"web":t="TKYMM56BFT7NZFFFFB2J";break;case"portal":t="C69ZN59CHZTH4DYJBHPF";break;case"agent":t="2F7PZH86CPJKTWTYY93Q"}return t}function t(){window.plugins&&window.plugins.flurry?m=window.plugins.flurry:void 0!==window.FlurryAgent&&(console.log("FLURRY - INIT WEB"),m=window.FlurryAgent)}function n(t){if(console.log("FLURRY - START SESSION"),void 0!==m){var n=e(t);m.startSession(n)}}function a(){console.log("FLURRY - END SESSION"),void 0!==m&&m.endSession()}function s(){void 0!==m&&m.setAppVersion()}function o(e){void 0!==m&&m.setSessionReportsOnCloseEnabled(e)}function i(e){void 0!==m&&m.setSessionReportsOnPauseEnabled(e)}function l(e){void 0!==m&&(console.log("FLURRY - LOG EVENT"),console.log(e),m.logEvent(e))}function r(e,t){void 0!==m&&m.logEvent(e,t)}function d(e,t,n){void 0!==m&&m.logEvent(e,t,n)}function c(e){void 0!==m&&m.endTimedEvent(e)}function p(e,t){void 0!==m&&m.endTimedEvent(e,t)}var m;return{init:t,startSession:n,endSession:a,setAppVersion:s,setSessionReportsOnCloseEnabled:o,setSessionReportsOnPauseEnabled:i,logEvent:l,logEventWithParameters:r,startTimedEvent:d,endTimedEvent:c,endTimedEventWithParameters:p}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").factory("dmtTermsConditionsFactory",["agreementService","ngDialog","$q","userService","flurryService",function(e,t,n,a,s){function o(){var t=n.defer(),s=e.getUserAgreements();return s.then(function(e){var n=a.getUser(),s=n.country;return a.getUserAgreementVersion(s).then(function(n){if(!n)return void t.resolve();if(0!=e.length){var a=e[0].version;a.split("-")[0]<n.number?i(!1,n.full,t):t.resolve()}else t.resolve()})})["catch"](function(){t.resolve()}),t.promise}function i(t,n,a){e.getAgreementHTML(n).then(function(e){d=angular.copy(e.html),c=n,p._showDialog(t,a)})["catch"](function(e){a.resolve(),console.log(e)})}function l(n,o){var i=function(){o&&o.resolve()},l=function(){o&&o.reject()},r={agreed:!1,isFromLink:n},p=null;t.open({template:'<div class="dmt-terms-conditions-header"><div class="dmt-login-icon"></div><span ng-if="dialogCtrl.dialogData.isFromLink" class="dmt-dialog-title">Domotz Terms and Conditions</span><span ng-if="!dialogCtrl.dialogData.isFromLink" class="dmt-dialog-title">Hi! We have updated our Terms.</span></div><div class="dmt-terms-conditions-content" id="tc-agreement-content">'+d+'</div><div class="dmt-terms-conditions-footer"><div ng-if="!dialogCtrl.dialogData.isFromLink" class="dmt-checkbox-agreement-wrapper"><dmt-agreement-checkbox field-id="tc-agreement" field-name="tc-agreement" ng-model="dialogCtrl.dialogData.agreed" label-text="I agree with Terms and Conditions" has-label-link="false" ng-required="true" ng-disabled="false" field-error="Please accept Terms and Conditions" ></div><div class="dmt-buttons-wrapper"><div class="dmt-print-button-wrapper"><div class="dmt-print-button dmt-btn-control" ng-click="dialogCtrl.printAgreement()">Print <i class="icon icon-printer-fax"></i></div></div><div ng-if="dialogCtrl.dialogData.isFromLink" class="dmt-cancel-button dmt-btn-back dmt-btn-big" ng-click="closeThisDialog()">OK</div><div ng-if="!dialogCtrl.dialogData.isFromLink" class="dmt-cancel-button dmt-btn-back dmt-btn-big" ng-click="closeThisDialog(false)">Decline</div><div ng-if="!dialogCtrl.dialogData.isFromLink" class="dmt-continue-button dmt-btn-proceed dmt-btn-big" ng-click="!dialogCtrl.dialogData.agreed || closeThisDialog(true)">Agree</div></div></div>',plain:!0,controllerAs:"dialogCtrl",controller:["$scope","$window",function(e,t){var n=this;n.agreementHtml=d,n.dialogData=r,n.printAgreement=function(){var e=t.open("","_blank"),n=document.getElementById("tc-agreement-content").innerHTML;e.document.write(n),e.print(),e.close()},p=e}],className:"ngdialog-theme-default dmt-terms-conditions-dialog"+(n?" dmt-terms-condition-read":""),showClose:!1,closeByEscape:!1,closeByDocument:!1,preCloseCallback:function(t){return p.formSubmitted=!0,!!n||(t?r.agreed?(s.logEventWithParameters("Terms and Condition - accepted",{userId:a.getUser().getId()}),e.setUserAgreement(c),i(),!0):(l(),s.logEventWithParameters("Terms and Condition - refused",{userId:a.getUser().getId()}),!1):(l(),s.logEventWithParameters("Terms and Condition - refused",{userId:a.getUser().getId()}),!0))}})}function r(e,t){try{var n={};return n.full=t[e].tc_version+"-"+e.toUpperCase(),n.number=t[e].tc_version.split("-")[0],n}catch(a){console.log("Error: Country not available in regions")}}var d="",c="",p={checkAgreement:o,showAgreement:i,getCurrentVersion:r,_showDialog:l};return p}])}(),function(){"use strict";angular.module("domotzAngularWidgets.utils").factory("dmtBase64",["$window",function(e){function t(t){return e.btoa(encodeURIComponent(t).replace(/%([0-9A-F]{2})/g,function(e,t){return String.fromCharCode("0x"+t)}))}function n(t){return decodeURIComponent(Array.prototype.map.call(e.atob(t),function(e){return"%"+e.charCodeAt(0).toString(16)}).join(""))}return{encode:t,decode:n}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").directive("dmtAddButton",[function(){return{restrict:"E",transclude:!0,template:"<div class=dmt-add-button><div class=dmt-button-icon><span>+</span></div><div class=dmt-button-label><span ng-transclude></span></div></div>",replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").directive("dmtCycleButton",[function(){return{scope:{isDisabled:"=",isLocked:"=",onClickFn:"&"},restrict:"E",template:'<div class=dmt-cycle-button ng-class="{\'dmt-disabled\': isDisabled, \'dmt-locked\': isLocked}" ng-click="!isDisabled && !isLocked && onClickFn()"><div class=dmt-cycle-icons><i class="icon icon-reboot-arrow-down"></i> <i class="icon icon-reboot-arrow-up"></i> <i class="icon icon-bolt-filled"></i></div></div>',replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").controller("GlobalLoaderController",["$scope","globalLoaderService",function(e,t){e.loader=t.loader}]).directive("dmtGlobalLoader",[function(){return{restrict:"E",transclude:!0,template:['<div class="dmt-global-loader" ng-class="{\'dmt-loader-visible\': loader.display || loader.displayConfirmed}">','<dmt-spinner display="loader.display" message="loader.message" toggle="loader.toggle" is-global="true"></dmt-spinner>','<div class="dmt-confirmed-loader"  ng-show="loader.displayConfirmed">','<dmt-alert-icon ng-if="loader.displayError" class="dmt-error-loader-icon"> </dmt-alert-icon>','<div ng-if="!loader.displayError" class="dmt-confirmed-loader-icon icon icon-{{loader.icon}}"></div>','<div class="dmt-confirmed-msg" >{{loader.message}}</div>',"</div>","</div>"].join(""),controller:"GlobalLoaderController",replace:!0}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").factory("globalLoaderService",["$timeout","$rootScope",function(e,t){function n(e,n){t.$broadcast("hideLocalSpinner"),r.message=e,r.display=!0,void 0!==n&&(r.toggle=n)}function a(){r.display=!1,r.displayConfirmed=!1,r.displayError=!1,e(function(){r.message=""},300)}function s(t,n){l(t),r.displayError=!1,r.icon=n||"thick-stroke",e(function(){a()},1800)}function o(t,n){l(t),r.displayError=!0,r.icon=n||"alert-stroke",e(function(){a()},3e3)}function i(e){r.message=e}function l(e){t.$broadcast("hideLocalSpinner"),r.message=e,r.display=!1,r.displayConfirmed=!0}var r={display:!1,message:"",toggle:!0,displayConfirmed:!1,displayError:!1,icon:""};return{loader:r,showLoader:n,hideLoader:a,setMessage:i,showSuccess:s,showError:o}}])}(),function(){"use strict";angular.module("domotzAngularWidgets").directive("dmtSpinner",["$timeout",function(e){return{restrict:"E",scope:{display:"=",message:"=?",isGlobal:"=?",toggle:"=?",hideMsg:"=?"},link:function(t,n,a){function s(n){var a=n||0;t.dynamicMsg=i[a],l=e(function(){s((a+1)%i.length)},2e3)}function o(){return t.toggle?[t.message?t.message:"Retrieving info...","Please wait..."]:[t.message?t.message:"Retrieving info..."]}var i=[],l=null;t.$watch("message",function(){i=o()}),t.$watch("display",function(t){t?s():e.cancel(l)}),t.isGlobal||t.$on("hideLocalSpinner",function(){t.display=!1}),t.$on("$destroy",function(){e.cancel(l)})},template:'<div class=dmt-spinner-wrapper ng-show=display><div class=dmt-spinner-loader><svg class=dmt-spinner-circular viewbox="25 25 50 50"><circle class=dmt-spinner-path cx=50 cy=50 r=20 fill=none stroke-width=3 stroke-miterlimit=10></circle></svg></div><div class=dmt-spinner-msg ng-if=!hideMsg>{{dynamicMsg}}</div></div>',replace:!0}}])}(),angular.module("domotzAngularWidgets.template").run(["$templateCache",function(e){e.put("/angular-widgets/templates/addbutton.template.html",'<div class="dmt-add-button">\n    <div class="dmt-button-icon">\n        <span>+</span>\n    </div>\n    <div class="dmt-button-label">\n        <span ng-transclude></span>\n    </div>\n</div>'),e.put("/angular-widgets/templates/cells.template.html",'<div class="dmt-cell-options">\n    <div ng-if="cells.length > 1">\n        <span>My Region:</span>\n        <ul class="dmt-radio-list">\n            <li ng-repeat="cell in cells">\n                <div class="dmt-radio-button">\n                    <input id="{{cell}}" type="radio" name="selected_cell" value="{{cell}}" ng-model="directiveScope.ngModel" required>\n                    <label for="{{cell}}"></label>\n                </div>\n                <label for="{{cell}}">{{cellLabels[cell]}}</label>\n            </li>\n        </ul>\n    </div>\n</div>'),e.put("/angular-widgets/templates/checkbox.agreement.template.html",'<div class="dmt-agreement-wrapper" ng-class="{\'dmt-disabled\': ngDisabled, \'dmt-error\': !fieldForm[fieldName].$valid && (fieldForm[fieldName].$dirty || formSubmitted()) && !ngDisabled}" ng-form="fieldForm">\n    <input id="{{fieldId}}" type="checkbox" name="{{fieldName}}" ng-model="ngModel" ng-required="ngRequired" ng-disabled="ngDisabled"/>\n    <div class="dmt-label">\n        <span class="dmt-agreement-checkbox">\n                        <i class="icon icon-tick"></i>\n                    </span>\n        <span ng-if="hasLabelLink" class="dmt-agreement-label">{{labelText}} <span class="dmt-link" ng-click="ngDisabled || onClickFn()">{{labelLink}}</span></span>\n        <label ng-if="!hasLabelLink" for="{{fieldId}}" class="dmt-agreement-label">{{labelText}}</label>\n        </div>\n    <span ng-show="!fieldForm[fieldName].$valid && (fieldForm[fieldName].$dirty || formSubmitted()) && !ngDisabled" class="dmt-error">{{fieldError}}</span>\n    </div>'),e.put("/angular-widgets/templates/checkbox.template.html",'<div class="dmt-checkbox-wrapper" ng-form="fieldForm">\n    <div class="dmt-checkbox">\n        <input type="checkbox" name="checkbox-item" ng-model="ngModel" ng-required="true" id="agreement-checkbox">\n        <i class="icon" ng-class="(ngModel ? \'icon-checkbox-checked\' : \'icon-checkbox-stroke\')" ng-click="toggleCheckbox()"></i>\n        <label class="dmt-checkbox-label" for="agreement-checkbox">{{checkboxLabel}}</label>\n    </div>\n    <span ng-show="!fieldForm[\'checkbox-item\'].$valid && formSubmitted()" class="dmt-field-error">Selection is required</span>\n</div>'),e.put("/angular-widgets/templates/countries.template.html",'<div class="dmt-country-select" ng-form="countryForm" ng-class="{\'dmt-country-select-error\': !countryForm[\'selectedCountry\'].$valid && (fieldForm[\'selectedCountry\'].$dirty || formSubmitted())}">\n    <div class="dmt-btn-control dmt-btn-icon dmt-btn-full dmt-country-button" ng-click="openCountryDialog(countries)">\n        <span>{{button_text}}</span>\n        <i class="icon icon-sort"></i>\n    </div>\n    <input style="display: none;" type="text" name="selectedCountry" ng-model="selected_country" required="" />\n    <span class="dmt-field-error">Country required</span>\n</div>'),e.put("/angular-widgets/templates/field.template.html",'<div class="dmt-field-wrapper" ng-form="fieldForm">\n    <div class="dmt-field-input" ng-class="{\'dmt-active\': fieldFocus || ngModel.length > 0 || (!fieldForm.$valid && (fieldForm[fieldName].$dirty || formSubmitted())), \'dmt-disabled\': fieldDisabled}">\n        <input class="dmt-input" name="{{fieldName}}" type="{{fieldType}}" ng-model="ngModel"\n               ng-focus="fieldFocus = true; onFieldFocus()"\n               ng-blur="fieldFocus = false"\n               ng-required="fieldRequired"\n               ng-maxlength="255"\n               ng-disabled="fieldDisabled"\n               ng-pattern="fieldRegex"\n               tabindex="{{tabIndex}}"\n        />\n        <span class="dmt-input-label">{{fieldLabel}}</span>\n    </div>\n    <div class="dmt-password-bar" ng-class="\'dmt-\' + directiveScope.getPwdStrength()" ng-if="displayPwdStrength">\n        <zxcvbn password="directiveScope.ngModel" full="directiveScope.zxcvbn" show="false"></zxcvbn>\n    </div>\n    <span ng-show="!fieldForm[fieldName].$valid && (fieldForm[fieldName].$dirty || formSubmitted())" class="dmt-field-error">{{fieldErrorMessage}}</span>\n</div>'),e.put("/angular-widgets/templates/gs.trafficrate.template.html",'<div class="dmt-trafficrate-wrapper">\n\n    <div class="dmt-pies-wrapper">\n\n        <div class="dmt-download-pie">\n            <div class="dmt-graph-placeholder"><i class="icon icon-chart-hint"></i><span class="dmt-graph-placeholder-label">Select a sample on the graph</span></div>\n            <div google-chart\n                 id="dmt-download-pie"\n                 chart="downloadPieObj"\n                 agc-on-select="updateChartWithDetail(selectedItem, \'download\')"\n                 agc-on-error="chartErrorHandler(error)"></div>\n            <div class="dmt-graph-label"><icon class="icon icon-single-arrow-down"></icon> Download <span ng-show="selectedPoint.dr"> ({{selectedPoint.dr}} Mbps)</span></div>\n        </div>\n\n        <div class="dmt-upload-pie">\n            <div class="dmt-graph-placeholder"><i class="icon icon-chart-hint"></i><span class="dmt-graph-placeholder-label">Select a sample on the graph</span></div>\n            <div google-chart\n                 id="dmt-upload-pie"\n                 chart="uploadPieObj"\n                 agc-on-select="updateChartWithDetail(selectedItem, \'upload\')"\n                 agc-on-error="chartErrorHandler(error)"></div>\n            <div class="dmt-graph-label"><icon class="icon icon-single-arrow-up"></icon> Upload <span ng-show="selectedPoint.ur"> ({{selectedPoint.ur}} Mbps)</span></div>\n        </div>\n\n    </div>\n\n    <div class="dmt-network-graph"\n         google-chart\n         id="dmt-google-chart"\n         chart="chartObject"\n         agc-on-select="selectHandler(selectedItem)"\n         agc-on-error="chartErrorHandler(error)"></div>\n\n    <dmt-spinner display="!loaded" message="\'Loading...\'"></dmt-spinner>\n\n    <div class="dmt-graph-lower-wrapper">\n        <div class="dmt-top-title" ng-show="topDownloads.length > 0 || topUploads.length > 0">Top 5 Consumers</div>\n        <div class="dmt-top-downloads" ng-if="topDownloads.length > 0">\n            <icon class="icon icon-single-arrow-down"></icon>\n            <div class="dmt-top-device" ng-repeat="device in topDownloads" ng-click="openDeviceDetail(device.id)" ng-class="{\'dmt-unknown-device\': device.id === undefined}">\n                <div class="dmt-top-name">{{device.name}}</div>\n                <div class="dmt-top-value">{{device.label}}</div>\n                <i class="icon icon-arrow-right"></i>\n            </div>\n        </div>\n        <div class="dmt-top-uploads" ng-if="topUploads.length > 0">\n            <icon class="icon icon-single-arrow-up"></icon>\n            <div class="dmt-top-device" ng-repeat="device in topUploads" ng-click="openDeviceDetail(device.id)" ng-class="{\'dmt-unknown-device\': device.id === undefined}">\n                <div class="dmt-top-name">{{device.name}}</div>\n                <div class="dmt-top-value">{{device.label}}</div>\n                <i class="icon icon-arrow-right"></i>\n            </div>\n        </div>\n    </div>\n\n</div>'),e.put("/angular-widgets/templates/latency.template.html",'<div class="dmt-latency-wrapper">\n\n    <div class="dmt-latency-bar-wrapper">\n\n        <div class="dmt-latency-bar" ng-class="{\'dmt-out-range\': last_out_of_range}">\n\n            <div class="dmt-expected-range"\n                 ng-class="{\'dmt-out-expected\': avg_out_of_expected}"\n                 ng-show="avg_visible">\n                <div class="dmt-left-range"\n                     ng-style="{\'width\': avg_width_left + \'%\', \'left\': avg_prev_dev + \'%\'}"></div>\n                <div class="dmt-right-range"\n                     ng-style="{\'width\': avg_width_right + \'%\', \'left\': avg_point + \'%\'}"></div>\n\n                <div class="dmt-range-label"\n                     ng-style="{\'left\': avg_label_position + \'%\'}">\n                    <span>Usual range</span>\n                    <span class="dmt-range-values">{{avg_label}}</span>\n                </div>\n            </div>\n\n            <div class="dmt-value-arrow"\n                 ng-class="{\'dmt-value-unavailable\': last_point_unavailable}"\n                 ng-style="{\'left\': last_point + \'%\'}">\n                <span class="dmt-ping-value">{{last_point_value}}</span>\n                <i class="icon icon-sort-down"></i>\n\n                <span class="dmt-ping-label">\n                    {{last_point_label}}\n                </span>\n            </div>\n\n        </div>\n\n    </div>\n\n</div>'),e.put("/angular-widgets/templates/nethistory.template.html",'<div ng-init="fetchNetworkHistory()" ng-class="{\'dmt-error\': networkHistory.error}">\n    <ion-content class="has-header dmt-content-wrapper dmt-netHistory" ng-show="!networkHistory.loading">\n        <h3>Network Log</h3>\n\n        <div class="dmt-error-wrapper">\n            <span class="dmt-error-msg">Oops, something went wrong.</span>\n        </div>\n        <div ng-if="networkHistory.data.events.length" ng-repeat="event in networkHistory.data.events">\n            <div class="dmt-rows dmt-connection-history-up">\n                <i class="icon dmt-log-icon icon-online"></i>\n\n                <div class="dmt-connection-history-col-status">Connection<br/>Up</div>\n                <div class="dmt-connection-history-col-time">\n                    <div>\n                        {{::event.to_timestamp | amDateFormat:\'YYYY MMM DD\'}}\n                    </div>\n                    <div>\n                        {{::event.to_timestamp | amDateFormat:\'hh:mm A (z)\'}}\n                    </div>\n                </div>\n            </div>\n            <div class="dmt-rows dmt-connection-history-down">\n                <i class="icon dmt-log-icon icon-offline"></i>\n\n                <div class="dmt-connection-history-col-status">Connection<br/>Down</div>\n                <div class="dmt-connection-history-col-time">\n                    <div>\n                        {{::event.from_timestamp | amDateFormat:\'YYYY MMM DD\'}}\n                    </div>\n                    <div>\n                        {{::event.from_timestamp | amDateFormat:\'hh:mm A (z)\'}}\n                    </div>\n                </div>\n            </div>\n        </div>\n\n        <div class="dmt-connection-history-no-events" ng-if="!networkHistory.data.events.length">\n            No recent disconnection events\n        </div>\n\n    </ion-content>\n    <ion-content class="has-header dmt-spinner-wrapper" ng-show="networkHistory.loading">\n        <ion-spinner icon="android" class="dmt-spinner"></ion-spinner>\n        <div class="dmt-notice">Fetching your network log.</div>\n    </ion-content>\n</div>'),e.put("/angular-widgets/templates/netinfo.template.html",'<div ng-init="fetchNetworkInfo()" ng-class="{\'dmt-error\': networkInformation.error}">\n    <ion-content class="has-header dmt-content-wrapper dmt-netinfo" ng-show="!networkInformation.loading">\n        <h3>Network Info</h3>\n        <div class="dmt-error-wrapper">\n            <span class="dmt-error-msg">Oops, something went wrong.</span>\n        </div>\n        <div class="dmt-rows">\n            <div class="dmt-row-data">\n                <span class="dmt-left">Public IP Address</span>\n                <span class="dmt-right">{{networkInformation.data.internet_access.ip.address || \'N/A\'}}</span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left">Public Name</span>\n                <span class="dmt-right">{{networkInformation.data.internet_access.ip.name || \'N/A\'}}</span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left">Provider</span>\n                <span class="dmt-right">{{networkInformation.data.internet_access.provider.netname}}</span>\n                <span class="dmt-right">{{networkInformation.data.internet_access.provider.descr}}</span>\n                <span class="dmt-right">{{networkInformation.data.internet_access.provider.inetnum}}</span>\n                <span class="dmt-right" ng-if="!networkInformation.data.internet_access.provider">N/A</span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left">Gateway</span>\n                <span class="dmt-right">{{networkInformation.data.local_network.servers.gateway || \'N/A\'}}</span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left">DNS</span>\n                <span class="dmt-right" ng-repeat="dns in networkInformation.data.local_network.servers.dns">{{dns}}</span>\n                <span class="dmt-right" ng-if="!networkInformation.data.local_network.servers.dns">N/A</span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left">DHCP</span>\n                <span class="dmt-right" ng-repeat="dhcp in networkInformation.data.local_network.servers.dhcp">{{dhcp}}</span>\n                <span class="dmt-right" ng-if="!networkInformation.data.local_network.servers.dhcp">N/A</span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left">Interface</span>\n                <span class="dmt-right" ng-repeat="interface in networkInformation.data.local_network.interfaces">{{interface.address}} ({{interface.name}})</span>\n                <span class="dmt-right" ng-if="!networkInformation.data.local_network.interfaces">N/A</span>\n            </div>\n        </div>\n\n        <div class="dmt-action-button" ng-click="fetchNetworkInfo()" ng-if="networkInformation.error">Retry</div>\n    </ion-content>\n    <ion-content class="has-header dmt-spinner-wrapper" ng-show="networkInformation.loading">\n        <ion-spinner icon="android" class="dmt-spinner"></ion-spinner>\n        <div class="dmt-notice">Fetching your network information.</div>\n    </ion-content>\n</div>'),e.put("/angular-widgets/templates/netroute.template.html",'<div ng-init="fetchNetworkInfo()" ng-class="{\'dmt-error\': mtrTest.error}">\n    <ion-content class="has-header dmt-content-wrapper dmt-netroute" delegate-handle="netroute-scroll" scroll-resize>\n        <h3>Route Analysis</h3>\n\n        <div class="dmt-error-wrapper">\n            <span class="dmt-error-msg">Oops, something went wrong.</span>\n        </div>\n\n        <div class="dmt-animation-wrapper">\n            <!--\n            <div class="dmt-image" ng-class="{\'dmt-image-results\': mtrTest.data.mtr, \'dmt-image-static\': !mtrTest.data.mtr && !mtrTest.loading, \'dmt-image-loading\': !mtrTest.data.mtr && mtrTest.loading}"></div>\n            -->\n            <img ng-src="{{imagesPath}}mtr-test-results.gif" ng-show="mtrTest.data.mtr" />\n            <img ng-src="{{imagesPath}}{{mtrTest.loading ? \'mtr-test-animation\' : \'mtr-test-static\'}}.gif" ng-show="!mtrTest.data.mtr" />\n\n            <div class="dmt-netroute-results" ng-class="\'dmt-netroute-results-\' + res" ng-repeat="res in [\'internal\', \'isp\', \'destination\']" ng-show="mtrTest.data.mtr">\n                <div class="dmt-netroute-labels row">\n                    <div class="col-50">Loss%</div>\n                    <div class="col-50">Avg</div>\n                </div>\n                <div class="dmt-netroute-values row">\n                    <div class="col-50">{{mtrTest.data.mtr[res].loss_ratio || \'N/A\'}}</div>\n                    <div class="col-50">{{(mtrTest.data.mtr[res].ping.average ? mtrTest.data.mtr[res].ping.average + \'ms\' : \'N/A\')}}</div>\n                </div>\n            </div>\n        </div>\n\n        <div class="dmt-netroute-config" ng-class="{\'dmt-disabled\' : mtrTest.loading}" ng-show="!mtrTest.data.mtr">\n            <div class="dmt-label">Select a service:</div>\n\n            <select class="dmt-select" ng-model="mtrService.selected">\n                <option value="play.spotify.com">Spotify</option>\n                <option value="facebook.com">Facebook</option>\n                <option value="google.com">Google</option>\n                <option value="custom">Custom</option>\n            </select>\n\n            <div class="dmt-input" ng-show="mtrService.selected == \'custom\'">\n                <div class="dmt-field-wrapper">\n                    <div class="dmt-field-input" ng-class="{\'dmt-active\': mtrCustomService.focus || mtrCustomService.value.length > 0}">\n                        <input class="dmt-input" name="custom_service" type="text" ng-model="mtrCustomService.value" ng-focus="mtrCustomService.focus = true" ng-blur="mtrCustomService.focus = false" />\n                        <span class="dmt-input-label">Insert a hostname</span>\n                    </div>\n                </div>\n            </div>\n\n            <div class="dmt-label">Packets number</div>\n            <div class="dmt-options-group row">\n                <div class="dmt-option col-33" ng-repeat="val in [10, 20, 50]" ng-class="{\'dmt-active\': mtrTest.packets == val}" ng-click="mtrTest.packets = val">{{val}}</div>\n            </div>\n        </div>\n\n        <div class="dmt-notice" ng-show="!mtrTest.data.mtr">\n            This test will last approximately {{mtrTest.packets}} seconds.\n        </div>\n\n        <div class="dmt-netroute-resume" ng-show="mtrTest.data.mtr">\n            <div class="dmt-highlight">Sent {{mtrTest.packets}} packets to {{mtrTest.target}}</div>\n            <div class="dmt-more" ng-click="showMtrRawData()" ng-show="!mtrTest.raw">See raw data</div>\n            <ion-scroll direction="x" class="dmt-netroute-raw" ng-show="mtrTest.raw">\n                <pre ng-repeat="line in mtrTest.data.mtr.raw">{{line}}</pre>\n            </ion-scroll>\n        </div>\n\n        <div class="dmt-action-button" ng-class="{\'dmt-disabled\' : mtrTest.loading}" ng-click="startMtrTest()">{{mtrTest.loading ? \'Testing...\' : (mtrTest.data.mtr ? \'Done\' : \'Start\')}}</div>\n\n    </ion-content>\n</div>'),e.put("/angular-widgets/templates/netspeed.template.html",'<div ng-init="fetchNetworkInfo()" ng-class="{\'dmt-error\': speedTest.error || speedTest.server.error}">\n    <ion-content class="has-header dmt-content-wrapper dmt-netspeed dmt-netspeed-new">\n        <h3>Speed Test</h3>\n\n        <div class="dmt-error-wrapper">\n            <span class="dmt-error-msg">{{speedTest.server.error ? \'Cannot find an available server right now, please try again.\' : \'Oops, something went wrong.\'}}</span>\n        </div>\n\n        <div class="dmt-rows">\n            <div class="dmt-row-data dmt-netspeed-location">\n                <span class="dmt-left">From</span>\n                <span class="dmt-right">To</span>\n                <span class="dmt-left dmt-netspeed-source">{{agentName || \'Domotz Agent\'}}</span>\n                <span class="dmt-right dmt-netspeed-server-spinner" ng-show="speedTest.loading && speedTest.server.loading">\n                    <ion-spinner icon="android" class="dmt-spinner"></ion-spinner>\n                </span>\n                <span class="dmt-right dmt-netspeed-server">\n                    <span ng-show="!speedTest.server.loading" ng-class="{\'dmt-error\': speedTest.server.error, \'dmt-italic\': !speedTest.server.location}">{{speedTest.server.location || \'Nearest Server\'}}</span>\n                </span>\n            </div>\n        </div>\n\n        <div class="dmt-rows">\n            <div class="dmt-row-data">\n                <span class="dmt-left dmt-netspeed-label">Ping</span>\n                <span class="dmt-right dmt-netspeed-value">{{speedTest.data.ping.value}}</span>\n                <span class="dmt-right dmt-netspeed-gif" ng-show="!speedTest.data.ping.value">\n                    <!--\n                    <div class="dmt-image" ng-class="\'dmt-image-\' + speedTest.data.ping.gif"></div>\n                    -->\n                    <img ng-src="{{imagesPath}}{{speedTest.data.ping.gif}}.gif"/>\n                </span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left dmt-netspeed-label">Download Speed</span>\n                <span class="dmt-right dmt-netspeed-value">{{speedTest.data.download.value}}</span>\n                <span class="dmt-right dmt-netspeed-gif" ng-show="!speedTest.data.download.value">\n                    <!--\n                    <div class="dmt-image" ng-class="\'dmt-image-\' + speedTest.data.download.gif"></div>\n                    -->\n                    <img ng-src="{{imagesPath}}{{speedTest.data.download.gif}}.gif"/>\n                </span>\n            </div>\n            <div class="dmt-row-data">\n                <span class="dmt-left dmt-netspeed-label">Upload Speed</span>\n                <span class="dmt-right dmt-netspeed-value">{{speedTest.data.upload.value}}</span>\n                <span class="dmt-right dmt-netspeed-gif" ng-show="!speedTest.data.upload.value">\n                    <!--\n                    <div class="dmt-image" ng-class="\'dmt-image-\' + speedTest.data.upload.gif"></div>\n                    -->\n                    <img ng-src="{{imagesPath}}{{speedTest.data.upload.gif}}.gif"/>\n                </span>\n            </div>\n        </div>\n\n        <div class="dmt-action-button" ng-class="{\'dmt-disabled\' : speedTest.loading}" ng-click="startSpeedTest()">{{speedTest.loading ? \'Testing...\' : (speedTest.data.ping.value ? \'Test Again\' : \'Start\')}}</div>\n\n    </ion-content>\n</div>'),
e.put("/angular-widgets/templates/places.template.html",'<div class="dmt-places-autocomplete">\n    <div class="dmt-places-search">\n        <input type="text" ng-model="inputPlace" ng-model-options="{debounce: 600}" placeholder="Enter your address" />\n        <i class="icon icon-search"></i>\n    </div>\n\n    <ul class="dmt-places-list">\n        <li class="dmt-places-item" ng-repeat="place in predictions" ng-click="selectPlace(place)">\n            <div class="dmt-place-details">\n                <div class="dmt-place-main">{{place.main}}</div>\n                <div class="dmt-place-additional">{{place.additional}}</div>\n            </div>\n            <div class="dmt-place-icon">\n                <i class="icon icon-geomarker-fill"></i>\n            </div>\n        </li>\n    </ul>\n\n    <div class="dmt-places-placeholder" ng-show="predictions.length === 0">\n        <i class="icon icon-search"></i>\n        <div class="dmt-placeholder-title">Search Tip</div>\n        <div class="dmt-placeholder-tip">Type the name of a place to set your agent address</div>\n    </div>\n\n    <div id="place-detail-hidden"></div>\n</div>'),e.put("/angular-widgets/templates/popup.template.html",'<ion-modal-view class="dmt-overlay-modal dmt-modal-popup" ng-class="cssClass" ng-click="_hidePopup()">\n    <div class="dmt-inner-content" ng-click="$event.stopPropagation()">\n        <div class="dmt-content-block">\n            <div class="dmt-popup-heading">\n                <div class="dmt-popup-icon-multilayer" dmt-bind-html-compile="iconMultilayer" ng-if="iconMultilayer"></div>\n                <div class="dmt-popup-icon" ng-if="iconClass && !iconMultilayer">\n                    <i class="icon" ng-class="iconClass"></i>\n                </div>\n                <div class="dmt-popup-title" ng-bind-html="title"></div>\n                <div class="dmt-popup-close-icon" ng-show="closeIcon" ng-click="_hidePopup()"><i class="icon icon-close"></i></div>\n                <div class="dmt-popup-subtitle" ng-bind-html="subTitle" ng-if="subTitle"></div>\n            </div>\n\n            <div class="dmt-popup-content" dmt-bind-html-compile="contentTemplate" ng-if="contentTemplate"></div>\n\n        </div>\n        <div class="dmt-buttons-block" ng-if="buttons.length > 0">\n            <div class="dmt-popup-buttons">\n                <button class="dmt-action-button {{button.type}}" ng-repeat="button in buttons" ng-click="_onButtonTapped(button, $event)" ng-class="{\'dmt-disabled\': button.onlyIfValid && !popupFormIsValid}" ng-bind-html="button.text"></button>\n            </div>\n        </div>\n    </div>\n</ion-modal-view>'),e.put("/angular-widgets/templates/speedtest.template.html",'<div ng-init="fetchNetworkInfo()">\n    <ion-content class="has-header dmt-content-wrapper dmt-speedtest">\n        <h3>Speed Test</h3>\n\n        <div class="dmt-chart-wrapper" ng-class="{\'dmt-no-past-chart\': !chartEmpty.shown && !chartEmpty.arrow}">\n            <div class="dmt-chart-settings">\n                <div class="dmt-chart-filters">\n                    <h4 ng-show="chartEmpty.shown || chartEmpty.arrow">Show</h4>\n                    <span ng-show="chartEmpty.shown || chartEmpty.arrow" ng-repeat="filter in seriesFilter" ng-click="toggleSeries(filter.name)" class="{{filter.cssClass}}" ng-class="{\'dmt-filter-off\': !filter.selected}">\n                        <dmt-chart-filter></dmt-chart-filter>\n                        {{filter.name}}\n                    </span>\n                </div>\n                <div class="dmt-chart-time">\n                    <div class="dmt-chart-time-frame">\n                        <h4>Custom Time Frame</h4>\n                        <button class="dmt-btn-from-time" ng-click="openFromDatePicker(timeFrame.from)" ng-disabled="lockInterface">\n                            <span>{{timeFrame.from | date: "yyyy MMM dd"}}</span>\n                            <i class="icon icon-sort"></i>\n                        </button>\n                        <span>To</span>\n                        <button class="dmt-btn-to-time" ng-click="openToDatePicker(timeFrame.to)" ng-disabled="lockInterface">\n                            <span>{{timeFrame.to | date: "yyyy MMM dd"}}</span>\n                            <i class="icon icon-sort"></i>\n                        </button>\n                    </div>\n                    <div class="dmt-chart-zoom">\n                        <h4>Zoom to</h4>\n                        <span ng-repeat="selector in zoomSelector"\n                              ng-class="{\'dmt-current\': selector.selected}"\n                              ng-click="dateFilterPreset(selector)"  >\n                            {{::selector.name}}\n                        </span>\n                    </div>\n\n                </div>\n            </div>\n            <div class="dmt-chart">\n                <span class="dmt-chart-empty-text" ng-show="!chartEmpty.shown">\n                    <i ng-show="chartEmpty.arrow" class="icon icon-curved-arrow"></i>\n                    <span>{{chartEmpty.text}}</span>\n                </span>\n                <dmt-spinner class="dmt-content-spinner" display="chartLoader" message="\'Loading chart...\'"></dmt-spinner>\n                <div google-chart\n                     id="dmt-google-chart"\n                     style="position:relative; width: 100%; height: 100%;"\n                     chart="chartObject"\n                     agc-before-draw="drawHandler(chartWrapper)"\n                     agc-on-select="selectHandler(chart, selectedItem)"\n                     agc-on-error="chartErrorHandler(error)"\n                     ng-show="chartEmpty.shown"\n                     ng-mousedrag="chartDragEnd($event)"\n                     ng-right-click="chartReset($event)"></div>\n            </div>\n        </div>\n        <div class="dmt-clear"></div>\n        <div class="dmt-speedtest-info-wrapper">\n            <div class="dmt-speedtest-average">\n                <h3>Average of: <span>{{timeFrameAvg | amDurationFormat : \'days\' | uppercase }} </span></h3>\n                <div class="dmt-row dmt-desktop">\n                    <span class="dmt-label">Download</span>\n                    <span class="dmt-value">{{downloadAvg}}</span>\n                    <span class="dmt-unit">Mb/s</span>\n                </div>\n                <div class="dmt-row dmt-desktop">\n                    <span class="dmt-label">Upload</span>\n                    <span class="dmt-value">{{uploadAvg}}</span>\n                    <span class="dmt-unit">Mb/s</span>\n                </div>\n\n                <div class="dmt-speedtest-average-mobile">\n                    <div class="dmt-row">\n                        <span class="dmt-value">{{downloadAvg}}</span>\n                        <span>Download &bull; Mb/s</span>\n                    </div>\n                    <div class="dmt-row">\n                        <span class="dmt-value">{{uploadAvg}}</span>\n                        <span>Upload &bull; Mb/s</span>\n                    </div>\n                </div>\n                <!--<div class="dmt-row">-->\n                    <!--<span class="dmt-label">Ping (avg)</span>-->\n                    <!--<span class="dmt-value">19</span>-->\n                    <!--<span class="dmt-unit">ms</span>-->\n                <!--</div>-->\n                <!--<div class="dmt-row">-->\n                    <!--<span class="dmt-label">Ping (range)</span>-->\n                    <!--<span class="dmt-value">19-174</span>-->\n                    <!--<span class="dmt-unit">ms</span>-->\n                <!--</div>-->\n            </div>\n            <div class="dmt-speedtest-last">\n                <div class="dmt-container">\n                    <h3>\n                        <b>Last Speed Test:</b>\n                        <span ng-show="isValidDate(speedTest.server.timestamp) && speedTest.server.timestamp && !speedTest.loading">\n                            <span am-time-ago="speedTest.server.timestamp"></span> ago\n                        </span>\n\n                        <span ng-show="!isValidDate(speedTest.server.timestamp) || !speedTest.server.timestamp || speedTest.loading">{{speedTest.server.hint}}</span>\n                        <span class="dmt-speed-server-spinner" ng-show="speedTest.loading && speedTest.server.loading">\n                            <ion-spinner icon="android" class="dmt-spinner"></ion-spinner>\n                        </span>\n                        <span ng-show="!speedTest.server.loading">{{speedTest.server.location}}</span>\n                    </h3>\n                    <div class="dmt-last-values-box">\n                        <div class="dmt-last-download">\n                            <div class="dmt-animation {{downloadAnimationClass}}"  ng-show="speedTest.loading">\n                                <div class="dmt-animation-border"></div>\n                                <div class="dmt-animated-block"></div>\n                                <div class="dmt-animation-check">\n                                    <i class="icon icon-thick-stroke"></i>\n                                </div>\n                            </div>\n                            <span class="dmt-value" ng-show="!speedTest.loading">\n                                <span>\n                                    <i class="icon icon-double-arrow-down"></i>\n                                    {{speedTest.data.download.value}}\n                                </span>\n                            </span>\n                            <span class="dmt-label {{downloadAnimationClass}}">{{speedTest.data.download.label}}</span>\n                        </div>\n                        <!--<div class="dmt-last-ping">-->\n                            <!--<div class="dmt-last-ping-min">-->\n                                <!--<span>Min.</span>-->\n                                <!--<span class="dmt-value">9</span>-->\n                                <!--<span>ms</span>-->\n                            <!--</div>-->\n                            <!--<div class="dmt-last-ping-avg">-->\n                                <!--<span>Ping</span>-->\n                                <!--<span class="dmt-value">26</span>-->\n                                <!--<span>ms</span>-->\n                            <!--</div>-->\n                            <!--<div class="dmt-last-ping-max">-->\n                                <!--<span>Max.</span>-->\n                                <!--<span class="dmt-value">30</span>-->\n                                <!--<span>ms</span>-->\n                            <!--</div>-->\n                        <!--</div>-->\n                        <div class="dmt-last-upload">\n                            <div class="dmt-animation {{uploadAnimationClass}}" ng-show="speedTest.loading">\n                                <div class="dmt-animation-border"></div>\n                                <div class="dmt-animated-block"></div>\n                                <div class="dmt-animation-check">\n                                    <i class="icon icon-thick-stroke"></i>\n                                </div>\n                            </div>\n                            <span class="dmt-value" ng-show="!speedTest.loading">\n                                <span>\n                                    {{speedTest.data.upload.value}}\n                                    <i class="icon icon-double-arrow-up"></i>\n                                </span>\n                            </span>\n                            <span class="dmt-label {{uploadAnimationClass}}">{{speedTest.data.upload.label}}</span>\n                        </div>\n                    </div>\n                    <button class="dmt-action-button" ng-disabled="speedTest.loading" ng-class="{\'dmt-disabled\': speedTest.loading}" ng-click="startSpeedTest()">\n                        {{testButtonLabel}}\n                    </button>\n                </div>\n            </div>\n            <div class="dmt-speedtest-settings"></div>\n        </div>\n\n    </ion-content>\n</div>'),e.put("/angular-widgets/templates/spinner.template.html",'<div class="dmt-spinner-wrapper" ng-show="display">\n    <div class="dmt-spinner-loader">\n        <svg class="dmt-spinner-circular" viewBox="25 25 50 50">\n            <circle class="dmt-spinner-path" cx="50" cy="50" r="20" fill="none" stroke-width="3" stroke-miterlimit="10"/>\n        </svg>\n    </div>\n    <div class="dmt-spinner-msg" ng-if="!hideMsg">{{dynamicMsg}}</div>\n</div>'),e.put("/angular-widgets/templates/buttons/cycle.template.html",'<div class="dmt-cycle-button" ng-class="{\'dmt-disabled\': isDisabled, \'dmt-locked\': isLocked}" ng-click="!isDisabled && !isLocked && onClickFn()">\n\n    <div class="dmt-cycle-icons">\n        <i class="icon icon-reboot-arrow-down"></i>\n        <i class="icon icon-reboot-arrow-up"></i>\n        <i class="icon icon-bolt-filled"></i>\n    </div>\n\n</div>')}]);