1+ if ( ! javaxt ) var javaxt = { } ;
2+ if ( ! javaxt . express ) javaxt . express = { } ;
3+
4+ //******************************************************************************
5+ //** Activity Meter
6+ //******************************************************************************
7+ /**
8+ * Panel used to render user activity using real-time charts. Requires D3.
9+ *
10+ ******************************************************************************/
11+
12+ javaxt . express . ActivityMeter = function ( parent , config ) {
13+
14+ var me = this ;
15+ var defaultConfig = {
16+ maxIdleTime : 5 * 60 * 1000 , //5 minutes
17+ lineGraph : {
18+ refeshRate : 500
19+ } ,
20+ userChart : {
21+
22+ }
23+ } ;
24+
25+ var lineChart , pieChart ; //d3 svg
26+ var requestsPerMinute = { } ;
27+ var activeUsers = { } ;
28+
29+
30+ //**************************************************************************
31+ //** Constructor
32+ //**************************************************************************
33+ var init = function ( ) {
34+
35+ //Parse config
36+ config = merge ( config , defaultConfig ) ;
37+ if ( ! config . style ) config . style = javaxt . dhtml . style . default ;
38+
39+ var div = createElement ( "div" , parent , "user-stats" ) ;
40+
41+
42+ //Create main table
43+ var table = createTable ( div ) ;
44+ var tr = table . addRow ( ) ;
45+
46+
47+ //Create line chart
48+ createLineChart ( tr . addColumn ( {
49+ width : "100%" ,
50+ height : "100%" ,
51+ padding : "20px"
52+ } ) ) ;
53+
54+
55+ //Create donut chart
56+ createDonutChart ( tr . addColumn ( {
57+ padding : "20px" ,
58+ height : "100%"
59+ } ) ) ;
60+
61+
62+ me . el = div ;
63+ addShowHide ( me ) ;
64+ } ;
65+
66+
67+ //**************************************************************************
68+ //** notify
69+ //**************************************************************************
70+ this . notify = function ( op , model , id , userID ) {
71+ var currTime = getCurrentTime ( ) ;
72+ var numRequests = requestsPerMinute [ currTime ] ;
73+ requestsPerMinute [ currTime ] = ! numRequests ? 1 : numRequests + 1 ;
74+ } ;
75+
76+
77+ //**************************************************************************
78+ //** clear
79+ //**************************************************************************
80+ this . clear = function ( ) {
81+ if ( lineChart ) {
82+ lineChart . stop ( ) ;
83+ lineChart . clear ( ) ;
84+ }
85+ if ( pieChart ) {
86+ pieChart . stop ( ) ;
87+ pieChart . clear ( ) ;
88+ }
89+ } ;
90+
91+
92+ //**************************************************************************
93+ //** update
94+ //**************************************************************************
95+ this . update = function ( _activeUsers ) {
96+ activeUsers = _activeUsers ;
97+ if ( lineChart ) lineChart . start ( ) ;
98+ if ( pieChart ) pieChart . start ( ) ;
99+ } ;
100+
101+
102+ //**************************************************************************
103+ //** createLineChart
104+ //**************************************************************************
105+ var createLineChart = function ( parent ) {
106+
107+ var div = createElement ( "div" , parent , {
108+ width : "100%" ,
109+ height : "100%"
110+ } ) ;
111+
112+
113+
114+
115+ onRender ( div , function ( ) {
116+
117+ var _chart = d3 . select ( div ) ;
118+ const bb = _chart . node ( ) . getBoundingClientRect ( ) ;
119+
120+
121+ const xScale = d3 . scaleLinear ( )
122+ . domain ( [ 0 , 100 ] )
123+ . range ( [ 0 , bb . width ] ) ;
124+
125+ const yScale = d3 . scaleLinear ( )
126+ . domain ( [ 0 , 10 ] )
127+ . range ( [ bb . height , 0 ] ) ;
128+
129+
130+ var data = new Array ( 100 ) . fill ( 0 ) ;
131+
132+ lineChart = _chart
133+ . append ( 'svg' )
134+ . attr ( 'width' , '100%' )
135+ . attr ( 'height' , '100%' ) ;
136+
137+
138+ const line = d3 . line ( )
139+ . curve ( d3 . curveBasis )
140+ . x ( ( _ , i ) => xScale ( i ) )
141+ . y ( ( d ) => yScale ( d ) ) ;
142+
143+
144+ lineChart . append ( 'path' )
145+ . attr ( 'd' , line ( data ) )
146+ . style ( "stroke" , "#777" )
147+ . style ( "stroke-width" , 1 )
148+ . style ( "fill" , "none" ) ;
149+
150+
151+
152+ lineChart . start = function ( ) {
153+ lineChart . stop ( ) ;
154+ lineChart . interval = setInterval ( function ( ) {
155+
156+ var currTime = getCurrentTime ( ) ;
157+ var numRequests = requestsPerMinute [ currTime ] ;
158+ if ( ! numRequests ) numRequests = 0 ;
159+ delete requestsPerMinute [ currTime ] ;
160+
161+ data . pop ( ) ;
162+ data . unshift ( numRequests ) ;
163+
164+ var lineColor = "#777" ;
165+ for ( var i in data ) {
166+ if ( data [ i ] > 0 ) {
167+ lineColor = "#6699CC" ;
168+ break ;
169+ }
170+ }
171+
172+ lineChart . selectAll ( 'path' )
173+ . data ( [ data ] )
174+ . attr ( 'd' , line )
175+ . style ( "stroke" , lineColor ) ;
176+
177+ } , config . lineGraph . refeshRate ) ;
178+
179+ } ;
180+
181+ lineChart . stop = function ( ) {
182+ clearInterval ( lineChart . interval ) ;
183+ } ;
184+
185+ lineChart . clear = function ( ) {
186+ data = new Array ( 100 ) . fill ( 0 ) ;
187+ lineChart . selectAll ( 'path' )
188+ . data ( [ data ] )
189+ . attr ( 'd' , line ) ;
190+ } ;
191+
192+ lineChart . start ( ) ;
193+ } ) ;
194+ } ;
195+
196+
197+ //**************************************************************************
198+ //** createDonutChart
199+ //**************************************************************************
200+ var createDonutChart = function ( parent ) {
201+
202+ var div = createElement ( "div" , parent , {
203+ width : "350px" ,
204+ height : "100%"
205+ } ) ;
206+
207+ //title: "Active Users",
208+
209+
210+ onRender ( div , function ( ) {
211+
212+
213+ var width = div . offsetWidth ;
214+ var height = div . offsetHeight ;
215+ var radius = Math . min ( width , height ) / 2 ;
216+ var cutout = 0.65 ; //percent
217+ var innerRadius = radius * cutout ;
218+ var arc = d3 . arc ( )
219+ . innerRadius ( innerRadius )
220+ . outerRadius ( radius ) ;
221+
222+
223+ //Create pie chart
224+ pieChart = d3 . select ( div ) . append ( "svg" )
225+ . attr ( "width" , "100%" )
226+ . attr ( "height" , "100%" )
227+ . append ( "g" )
228+ . attr ( "transform" , "translate(" + width / 2 + "," + height / 2 + ")" ) ;
229+
230+
231+ //Creat pie function to parse data
232+ var pie = d3 . pie ( ) . value ( function ( d ) { return d . value ; } ) ;
233+
234+
235+ //Create label in the center of the pie chart
236+ var label = pieChart . append ( "text" )
237+ . attr ( "text-anchor" , "middle" )
238+ . attr ( 'font-size' , '4em' )
239+ . attr ( 'y' , 20 ) ;
240+
241+ //Create function to animate changes in the pie chart
242+ var arcTween = function ( a ) {
243+ var i = d3 . interpolate ( this . _current , a ) ;
244+ this . _current = i ( 0 ) ;
245+ return function ( t ) {
246+ return arc ( i ( t ) ) ;
247+ } ;
248+ } ;
249+
250+
251+ //Create function to generate data for the pie chart
252+ var getData = function ( ) {
253+ var data = [ ] ;
254+ for ( var key in activeUsers ) {
255+ var lastUpdate = new Date ( activeUsers [ key ] ) . getTime ( ) ;
256+ var currTime = new Date ( ) . getTime ( ) ;
257+ var elapsedTime = currTime - lastUpdate ;
258+ if ( true ) { //if (elapsedTime<config.maxIdleTime){
259+ data . push ( {
260+ key : key ,
261+ value : config . maxIdleTime - elapsedTime
262+ } ) ;
263+ }
264+ }
265+ return data ;
266+ } ;
267+
268+
269+ //Create function to colorize categorical data
270+ var getColor = d3 . scaleOrdinal ( d3 . schemeCategory10 ) ;
271+
272+
273+ //Create render function used to update the chart
274+ var render = function ( data ) {
275+
276+ var numActiveUsers = Object . keys ( data ) . length ;
277+ label . text ( numActiveUsers + "" ) ;
278+
279+ if ( numActiveUsers === 0 ) {
280+ data . push ( {
281+ key : "-1" ,
282+ value : 1
283+ } ) ;
284+ }
285+
286+
287+
288+
289+ var pieData = pie ( data ) ;
290+
291+
292+ // add transition to new path
293+ pieChart . selectAll ( "path" ) . data ( pieData ) . transition ( ) . duration ( 100 ) . attrTween ( "d" , arcTween ) ;
294+
295+
296+
297+ // add any new paths
298+ pieChart . selectAll ( "path" )
299+ . data ( pieData )
300+ . enter ( ) . append ( "path" )
301+ . attr ( "d" , arc )
302+ . attr ( "fill" , function ( d , i ) {
303+ if ( d . data . key === "-1" ) return "#dcdcdc" ;
304+ return getColor ( d . data . key ) ;
305+ } )
306+ . style ( "opacity" , 0.8 )
307+ . each ( function ( d ) { this . _current = d ; } ) ;
308+
309+ // remove data not being used
310+ pieChart . selectAll ( "path" )
311+ . data ( pieData ) . exit ( ) . remove ( ) ;
312+
313+ } ;
314+
315+
316+
317+
318+
319+ pieChart . start = function ( ) {
320+ pieChart . stop ( ) ;
321+ pieChart . interval = setInterval ( function ( ) {
322+
323+ var inactiveUsers = [ ] ;
324+ for ( var key in activeUsers ) {
325+ var lastUpdate = activeUsers [ key ] ;
326+ var currTime = new Date ( ) . getTime ( ) ;
327+ var elapsedTime = currTime - lastUpdate ;
328+ if ( elapsedTime > config . maxIdleTime ) {
329+ inactiveUsers . push ( key ) ;
330+ }
331+ }
332+
333+
334+ //Prune inactive users
335+ for ( var i in inactiveUsers ) {
336+ var userID = inactiveUsers [ i ] ;
337+ delete activeUsers [ userID ] ;
338+ }
339+
340+ render ( getData ( ) ) ;
341+
342+ } , 1000 ) ;
343+ } ;
344+
345+ pieChart . stop = function ( ) {
346+ clearInterval ( pieChart . interval ) ;
347+ } ;
348+
349+ pieChart . clear = function ( ) {
350+ render ( [ ] ) ;
351+ //pieChart.selectAll("*").remove();
352+ } ;
353+
354+ pieChart . start ( ) ;
355+
356+ } ) ;
357+ } ;
358+
359+
360+ //**************************************************************************
361+ //** getCurrentTime
362+ //**************************************************************************
363+ var getCurrentTime = function ( ) {
364+ var d = new Date ( ) ;
365+ return d . getUTCFullYear ( ) +
366+ pad ( d . getUTCMonth ( ) + 1 ) +
367+ pad ( d . getUTCDate ( ) ) +
368+ pad ( d . getUTCHours ( ) ) +
369+ pad ( d . getUTCMinutes ( ) ) ;
370+ } ;
371+
372+
373+ //**************************************************************************
374+ //** pad
375+ //**************************************************************************
376+ var pad = function ( i ) {
377+ return ( i < 9 ? "0" + i : i + "" ) ;
378+ } ;
379+
380+
381+ //**************************************************************************
382+ //** Utils
383+ //**************************************************************************
384+ var createElement = javaxt . dhtml . utils . createElement ;
385+ var createTable = javaxt . dhtml . utils . createTable ;
386+ var addShowHide = javaxt . dhtml . utils . addShowHide ;
387+ var onRender = javaxt . dhtml . utils . onRender ;
388+ var merge = javaxt . dhtml . utils . merge ;
389+
390+
391+ init ( ) ;
392+ } ;
0 commit comments