6767 .ui-content { padding-top : 0 ; }
6868 .btn-text , # loadingsocket { font-size : 32px ; }
6969 # loadingsocket , # loadinggraph {padding : 15px ;}
70- .labelbold { font-weight : bold !important ; }
70+ .labelbold { font-weight : bold !important ; overflow : hidden; text-overflow : ellipsis; white-space : nowrap; }
7171 # wrap { padding : 12px ; }
7272 .ui-listview > li .hiddenNode { display : none; }
7373 .ui-listview > li .hiddenNodeShow { display : block; }
142142 # rawActionIDspan div { border-radius : 0.3em 0 0 0.3em ; }
143143 # rawActionTextspan div { border-radius : 0 ; }
144144 # rawActionSend { border-radius : 0 0.3em 0.3em 0 ; }
145+
146+ .hiddenNodeShow a { background-color : # ffe5e5 !important ; }
147+
148+ # nodeList > li .ui-li-has-count > a { padding-right : 2.5em ; }
149+ @media (max-width : 599px ) {
150+ # nodeList > li .ui-li-has-count > a { padding-left : 5.25em ; }
151+ # nodeList > li .ui-li-has-count > a > span .ui-li-count {
152+ top : 70% ;
153+ }}
154+ @media (max-width : 767px ) {
155+ .sideButton { display : none !important ; }
156+ }
157+
158+ # nav-panel-popup {
159+ right : 0 !important ;
160+ left : auto !important ;
161+ }
162+ # nav-panel {
163+ width : 200px ;
164+ background : rgba (0 , 0 , 0 , .7 );
165+ border : 1px solid # 000 ;
166+ border-right : none;
167+ /*margin: -1px 0;*/
168+ /*padding-top:20px;*/
169+
170+ }
171+ # nav-panel > ul { margin-top : 0 !important ; }
172+ # nav-panel .ui-btn {
173+ /*margin: 2em 15px;*/
174+ }
175+
176+ .content-panel {
177+ margin : 1em 0 ;
178+ border : 1px solid # ddd ;
179+ display : block;
180+ box-shadow : 0 1px 3px rgba (0 , 0 , 0 , .15 );
181+ border-radius : .3125em ;
182+ text-shadow : 0 1px 0 # f3f3f3 ;
183+ padding-right : .5em ;
184+ }
185+
186+ @media (max-width : 539px ) {
187+ .content-panel { padding : 0 .5em ; }
188+ .nodeDetailImageWrapper { text-align : center; }
189+ # nodeDetailInputList { padding-left : 0 ; padding-top : 0 ; margin-top : 0 ;}
190+ }
191+
192+ @media (max-width : 448px ) {
193+ label .labelbold { margin : .4em .4em .1em .4em ; }
194+ }
195+
196+ @media (min-width : 540px ) {
197+ .nodeDetailImageWrapper {
198+ position : absolute;
199+ float : left;
200+ padding-top : 10px ;
201+ }
202+ # nodeDetailInputList { padding-left : 130px ; }
203+ }
204+
205+ @media (min-width : 1000px ) {
206+ .nodeDetailImageWrapper { padding-left : 10% ; }
207+ # nodeDetailInputList { padding-left : 25% ; padding-right : 10% ; }
208+ }
209+ @media (min-width : 1600px ) {
210+ .nodeDetailImageWrapper { padding-left : 20% ; }
211+ # nodeDetailInputList { padding-left : 30% ; padding-right : 25% ; }
212+ }
145213 </ style >
146214</ head >
147215< body >
148216 < div data-role ="page " id ="homepage ">
217+ < div id ="nav-panel " data-role ="popup " data-corners ="false " data-overlay-theme ="b " data-theme ="b " data-shadow ="true " data-tolerance ="0,0 ">
218+ < ul data-role ="listview " data-theme ="a " data-divider-theme ="a " style ="margin-top:-16px; " class ="nav-search ">
219+ < li data-icon ="delete ">
220+ < a id ="nav-panel-close " href ="# " data-rel ="close "> Close (Esc)</ a >
221+ </ li >
222+
223+ < li data-icon ="eye " data-iconpos ="left ">
224+ < a id ="btnHiddenNodesToggle " href ="# "> Show Hidden</ a >
225+ </ li >
226+ < li data-icon ="search ">
227+ < a id ="btnSearch " href ="# "> Search</ a >
228+ </ li >
229+ < li data-icon ="fa-terminal ">
230+ < a href ="#logpage "> Log / Terminal</ a >
231+ </ li >
232+ < li data-icon ="fa-gear ">
233+ < a href ="#settingspage "> Settings</ a >
234+ </ li >
235+ </ ul >
236+ </ div > <!-- /panel -->
237+
149238 < div data-role ="header ">
150239 < a href ="http://lowpowerlab.com/gateway " target ="new " data-role ="none "> < img src ="images/logo.png " alt ="LowPowerLab " title ="LowPowerLab.com " style ="float:left;display:inline;max-width:30px;padding:4px "/> </ a >
151240 < h1 > Moteino Gateway Dashboard</ h1 >
152241 < div class ="ui-btn-right " data-role ="controlgroup " data-type ="horizontal " data-mini ="true ">
153- < a id ="btnHiddenNodesToggle " href ="# " data-role ="button " data-icon ="eye " data-iconpos ="notext " title ="Show hidden nodes "> Show hidden</ a >
154- < a id ="btnSearch " href ="# " data-role ="button " data-icon ="search " data-iconpos ="notext " title ="search nodes "> Search</ a >
155- < a href ="#logpage " data-role ="button " data-icon ="fa-terminal " data-iconpos ="notext " title ="terminal/log "> Log</ a >
156- < a href ="#settingspage " data-role ="button " data-icon ="fa-gear " data-iconpos ="notext " title ="settings "> Settings</ a >
242+ < a class ="sideMenuButton " href ="#nav-panel " data-icon ="bars " data-role ="button " data-rel ="popup " data-transition ="slide " data-position-to ="window " > Menu</ a >
157243 </ div >
158244 </ div >
159245
@@ -194,13 +280,18 @@ <h3><span id="nodeDetailTitle">Node details</span> <span class="nodeUpdated">x</
194280 </ div >
195281 </ div >
196282 < div data-role ="main " class ="ui-content ">
197- < div class ="ui-field-contain ">
198- < label for ="nodeMoteType " class ="labelbold "> Type:</ label >
199- < select id ="nodeMoteType " data-mini ="true "> </ select >
200- < label for ="nodeLabel " class ="labelbold "> Label:</ label >
201- < input type ="text " name ="nodeLabel " id ="nodeLabel " placeholder ="node label... " />
202- < label for ="nodeDescr " class ="labelbold "> Description:</ label >
203- < input type ="text " name ="nodeDescr " id ="nodeDescr " placeholder ="description/location... " />
283+ < div class ="content-panel ">
284+ < div class ="nodeDetailImageWrapper ">
285+ < img id ="nodeDetailImage " class ="productimg ">
286+ </ div >
287+ < div id ="nodeDetailInputList " class ="ui-field-contain ">
288+ < label for ="nodeMoteType " class ="labelbold "> Type:</ label >
289+ < select id ="nodeMoteType " data-mini ="true "> </ select >
290+ < label for ="nodeLabel " class ="labelbold "> Label:</ label >
291+ < input type ="text " name ="nodeLabel " id ="nodeLabel " placeholder ="node label... " />
292+ < label for ="nodeDescr " class ="labelbold "> Description:</ label >
293+ < input type ="text " name ="nodeDescr " id ="nodeDescr " placeholder ="description/location... " />
294+ </ div >
204295 </ div >
205296 < ul id ="metricList " data-role ="listview " data-inset ="true " data-theme ="a " data-dividertheme ="b "> </ ul >
206297
@@ -671,11 +762,20 @@ <h1>Settings</h1>
671762
672763 function addGraphDataPoint ( point ) {
673764 if ( graphFrozen ) return ;
674- graphData . push ( point ) ;
675- if ( graphData . length > 10 )
676- graphData . shift ( ) ; //remove first point in graph
677- graphOptions . xaxis . min = graphView . start = graphData [ 0 ] [ 0 ] ;
678- graphOptions . xaxis . max = graphView . end = graphData [ graphData . length - 1 ] [ 0 ] ;
765+
766+ //shift visible timeline
767+ graphOptions . xaxis . min = graphView . start = point [ 0 ] - ( graphView . end - graphView . start ) ; //calculate new min
768+ graphOptions . xaxis . max = graphView . end = point [ 0 ] ;
769+
770+ graphData . push ( point ) ; //add new data point
771+
772+ //remove points that are invisible after shifting the visible timeline
773+ var i = 0 ;
774+ while ( graphData [ i ] ) {
775+ if ( graphData [ i ++ ] [ 0 ] < graphView . start )
776+ graphData . shift ( ) ;
777+ }
778+
679779 //plot = $.plot(metricGraph, [graphData], graphOptions);
680780 $ . plot ( metricGraph , [ { label :graphOptions . legendLbl , data :graphData } ] , graphOptions ) ;
681781 //redraw alternative:
@@ -684,14 +784,26 @@ <h1>Settings</h1>
684784 //plot.draw();
685785 $ ( graphStat ) . clone ( ) . appendTo ( '#metricGraph' ) ;
686786 }
787+
788+ /// resolves any metrics in the label or description of a node
789+ function nodeResolveString ( theString , metrics ) {
790+ for ( var key in metrics )
791+ {
792+ if ( / \{ .+ \} / ig. test ( theString ) == false ) return theString ; //if string has no more "{...}" just return it
793+ metric = metrics [ key ] ;
794+ theString = theString . replace ( '{' + metric . label + '}' , metricsValues ( { 0 :metric } , true ) ) ;
795+ theString = theString . replace ( '{' + metric . label + ':updated}' , ago ( metric . updated ) . tag ) ;
796+ }
797+ return theString ;
798+ }
687799
688- function metricsValues ( metrics ) {
800+ function metricsValues ( metrics , ignorePin = false ) {
689801 var label = '' ;
690802 var metric ;
691803 for ( var key in metrics )
692804 {
693805 metric = metrics [ key ] ;
694- if ( metric . pin == '1' || metrics . length == 1 )
806+ if ( metric . pin == '1' || metrics . length == 1 || ignorePin )
695807 {
696808 var agoText = ago ( metric . updated ) . text ;
697809 //metric.value + (metric.unit || '')
@@ -703,9 +815,9 @@ <h1>Settings</h1>
703815 return label ;
704816 }
705817
706- function getNodeIcon ( nodeType ) {
707- if ( motesDef != undefined && nodeType != undefined && motesDef [ nodeType ] != undefined )
708- return motesDef [ nodeType ] . icon || 'icon_default.png' ;
818+ function getNodeIcon ( node ) {
819+ if ( motesDef != undefined && node . type != undefined && motesDef [ node . type ] != undefined )
820+ return motesDef [ node . type ] . icon || 'icon_default.png' ;
709821 return 'icon_default.png' ;
710822 } ;
711823
@@ -730,7 +842,7 @@ <h1>Settings</h1>
730842 nodes [ node . _id ] = node ;
731843 var nodeValue = metricsValues ( node . metrics ) ;
732844 var lowBat = node . metrics . V != null && node . metrics . V . value < 3.55 ;
733- var newLI = $ ( '<li id="' + node . _id + '"><a node-id="' + node . _id + '" href="#nodedetails" class="nodedetails"><img class="productimg" src="images/' + getNodeIcon ( node . type ) + '"><h2>' + ( node . label || node . _id ) + ' ' + resolveRSSIImage ( node . rssi ) + ' ' + ( lowBat ? '<img src="images/lowbattery.png" style="max-width:32px"/> ' : '' ) + ago ( node . updated , 0 ) . tag + ( node . hidden ? ' <img class="listIcon20px" src="images/icon_hidden.png" />' : '' ) + '</h2><p>' + ( node . descr || ' ' ) + '</p>' + ( nodeValue ? '<span class="ui-li-count ui-li-count16">' + nodeValue + '</span>' : '' ) + '</a></li>' ) ;
845+ var newLI = $ ( '<li id="' + node . _id + '"><a node-id="' + node . _id + '" href="#nodedetails" class="nodedetails"><img class="productimg" src="images/' + getNodeIcon ( node ) + '"><h2>' + ( nodeResolveString ( node . label , node . metrics ) || node . _id ) + ' ' + resolveRSSIImage ( node . rssi ) + ' ' + ( lowBat ? '<img src="images/lowbattery.png" style="max-width:32px"/> ' : '' ) + ago ( node . updated , 0 ) . tag + ( node . hidden ? ' <img class="listIcon20px" src="images/icon_hidden.png" />' : '' ) + '</h2><p>' + ( nodeResolveString ( node . descr , node . metrics ) || ' ' ) + '</p>' + ( nodeValue ? '<span class="ui-li-count ui-li-count16">' + nodeValue + '</span>' : '' ) + '</a></li>' ) ;
734846 var existingNode = $ ( '#nodeList li#' + node . _id ) ;
735847 if ( node . hidden )
736848 if ( showHiddenNodes )
@@ -774,6 +886,7 @@ <h1>Settings</h1>
774886 $ ( '#nodeList' ) . listview ( 'refresh' ) ; //re-render the listview
775887 }
776888
889+ /// calculated a style for an "ago" label based on a given timestamp in the past
777890 function ago ( time , agoPrefix )
778891 {
779892 agoPrefix = ( typeof agoPrefix !== 'undefined' ) ? agoPrefix : true ;
@@ -799,6 +912,7 @@ <h1>Settings</h1>
799912 return { text :updated , color :theColor , tag :'<span data-time="' + time + '" class="nodeAgo" style="color:' + theColor + ';">' + updated + '</span>' } ;
800913 }
801914
915+ /// updates the "ago" labels text and color according the to elapsed time since the last update timestamp associated with those labels
802916 function updateAgos ( )
803917 {
804918 $ ( "span.nodeAgo" ) . each ( function ( ) {
@@ -823,13 +937,14 @@ <h1>Settings</h1>
823937 if ( $ ( "#searchBox" ) . is ( ":visible" ) )
824938 {
825939 $ ( "#searchBox" ) . slideUp ( 'fast' ) ;
826- $ ( "#btnSearch" ) . removeClass ( 'ui-btn-b ') ;
940+ $ ( "#btnSearch" ) . css ( 'background-color' , ' ') ;
827941 }
828942 else
829943 {
830944 $ ( "#searchBox" ) . slideDown ( 'fast' ) ;
831- $ ( "#btnSearch" ) . addClass ( 'ui-btn-b ') ;
945+ $ ( "#btnSearch" ) . css ( 'background-color' , '#9bffbe ') ;
832946 }
947+ $ ( "#nav-panel" ) . popup ( "close" ) ;
833948 } ) ;
834949
835950 $ ( "#btnHiddenNodesToggle" ) . click ( "tap" , function ( event ) {
@@ -842,9 +957,10 @@ <h1>Settings</h1>
842957 else
843958 {
844959 $ ( ".hiddenNode" ) . removeClass ( 'hiddenNode' ) . addClass ( 'hiddenNodeShow' ) ;
845- $ ( '#btnHiddenNodesToggle' ) . css ( 'background-color' , '#D00 ' ) ;
960+ $ ( '#btnHiddenNodesToggle' ) . css ( 'background-color' , '#ffcaca ' ) ;
846961 showHiddenNodes = true ;
847962 }
963+ $ ( "#nav-panel" ) . popup ( "close" ) ;
848964 } ) ;
849965
850966 $ ( "#btnHideNodeToggle" ) . click ( "tap" , function ( event ) {
@@ -872,10 +988,11 @@ <h1>Settings</h1>
872988
873989 function refreshNodeDetails ( node ) {
874990 $ ( '#nodeLabel' ) . val ( node . label || '' ) ;
875- $ ( '#nodeDetailTitle' ) . html ( node . label || 'Node details' ) ;
991+ $ ( '#nodeDetailTitle' ) . html ( nodeResolveString ( node . label , node . metrics ) || 'Node details' ) ;
876992 $ ( '#nodeMoteType' ) . val ( node . type || '' ) ;
877993 $ ( "#nodeMoteType" ) . selectmenu ( 'refresh' , true ) ;
878994 $ ( '#nodeDescr' ) . val ( node . descr || '' ) ;
995+ $ ( '#nodeDetailImage' ) . attr ( 'src' , 'images/' + getNodeIcon ( node ) ) ;
879996
880997 if ( node . hidden ) $ ( "#btnHideNodeToggle" ) . addClass ( 'hidden' ) . css ( 'background-color' , '#D00' ) ;
881998 else $ ( "#btnHideNodeToggle" ) . removeClass ( 'hidden' ) . css ( 'background-color' , '' ) ; ;
@@ -1023,7 +1140,7 @@ <h1>Settings</h1>
10231140 socket . emit ( 'EDITNODEEVENT' , selectedNodeId , eventKey , null , true ) ;
10241141 } ) ;
10251142
1026- $ ( '#nodeLabel' ) . keyup ( function ( ) { $ ( '#nodeDetailTitle' ) . html ( $ ( '#nodeLabel' ) . val ( ) || 'no label ' ) ; } ) ;
1143+ $ ( '#nodeLabel' ) . keyup ( function ( ) { $ ( '#nodeDetailTitle' ) . html ( nodeResolveString ( $ ( '#nodeLabel' ) . val ( ) , nodes [ selectedNodeId ] . metrics ) || 'Node details ' ) ; } ) ;
10271144 $ ( '#metricLabel' ) . keyup ( function ( ) { $ ( '#metricDetailTitle' ) . html ( $ ( '#metricLabel' ) . val ( ) || 'no label' ) ; } ) ;
10281145
10291146 $ ( "#btnPinMetric" ) . click ( "tap" , function ( event ) {
@@ -1174,6 +1291,17 @@ <h1>Settings</h1>
11741291 'font-weight' :'bold' ,
11751292 'text-shadow' :'0 1px 0 black' ,
11761293 } ) ;
1294+
1295+ $ ( "#nav-panel" ) . on ( {
1296+ popupbeforeposition : function ( ) {
1297+ var h = $ ( window ) . height ( ) ;
1298+ $ ( "#nav-panel" ) . css ( "height" , h ) ;
1299+ }
1300+ } ) ;
1301+
1302+ $ ( "#nav-panel-close" ) . click ( "tap" , function ( event ) {
1303+ $ ( "#nav-panel" ) . popup ( "close" ) ;
1304+ } ) ;
11771305 } ) ;
11781306 }
11791307 </ script >
0 commit comments