Improved www.ttss.krakow.pl
Jacek Kowalski
2016-12-06 0782c8657856968dba7cfc3d9e06351dc79f758e
commit | author | age
896879 1 <!--
JK 2 URLs:
3
4 MODE=arrival,departure
5
6 http://www.ttss.krakow.pl
7
8 /internetservice/services/passageInfo/stopPassages/stop?stop=84&mode=MODE
9 /internetservice/services/passageInfo/stopPassages/stopPoint?stopPoint=stopPoint&mode=MODE
10
11 /internetservice/services/routeInfo/routeStops?routeId=
12
13 /internetservice/services/lookup/autocomplete/nearStops/json?lat=&lon=
14 /internetservice/services/lookup/autocomplete/json?query=
15 -->
16 <!DOCTYPE html>
17 <html lang="en">
18     <head>
19         <title>TTSS Cracow</title>
20         <meta charset="utf-8">
21         <meta http-equiv="X-UA-Compatible" content="IE=edge">
22         <meta name="viewport" content="width=device-width, initial-scale=1">
23         <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
24         <style type="text/css">
25             tr.active {
26                 color: gray;
27             }
28             .form-round-left {
29                 border-top-left-radius: 4px !important;
30                 border-bottom-left-radius: 4px !important;
31                 border-right: 0;
32             }
33             .navbar-text {
34                 margin-right: 15px;
35                 margin-left: 15px;
36             }
37             .input-group-btn:first-child {
38                 width: 50%;
39             }
40         </style>
41     </head>
42     <body>
43         <nav class="navbar navbar-default">
44             <div class="container-fluid">
45                 <div class="navbar-header">
46                     <a class="navbar-brand" href="#">TTSS KRK</a>
47                 </div>
48                 <form class="navbar-form navbar-left">
49                     <div class="input-group">
50                         <span class="input-group-btn">
51                             <input type="text" class="form-control form-round-left" id="stop-name" placeholder="Stop name" />
52                         </span>
53                         <select class="form-control" id="stop-name-autocomplete">
54                         </select>
55                         <span class="input-group-btn">
56                             <button type="submit" class="btn btn-default">Go</button>
57                         </span>
58                     </div>
59                 </form>
60                 
61                 <button type="button" class="btn btn-default navbar-btn pull-right" id="refresh" disabled="disabled">
62                     <span class="glyphicon glyphicon-refresh" aria-hidden="true"></span>
63                     Refresh
64                 </button>
65                 
66                 <p class="navbar-text pull-right" id="refresh-text">Initializing...</p>
67             </div>
68         </nav>
69         <div class="container-fluid">
70             <div id="alert" class="alert alert-danger alert-dismissible" style="display:none">
71                 <a href="#" class="close" id="alert-close" aria-label="close">&times;</a>
72                 <strong>Error occured!</strong> <span id="alert-text"></span>
73             </div>
74             
75             <div class="row">
76                 <div class="col-md-6">
77                     <h2 id="times-stop-name"></h2>
78                     
79                     <div id="times-alerts"></div>
80                     
81                     <table class="table table-striped table-condensed">
82                         <thead>
83                             <tr>
84                                 <th>Line</th>
85                                 <th>Direction</th>
86                                 <th>Time</th>
87                                 <th>Delay</th>
88                             </tr>
89                         </thead>
90                         <tbody id="times-table">
91                         </tbody>
92                     </table>
93                 </div>
94                 <div class="col-md-6">
95                     <h3>Lines</h3>
96                     
97                     <table class="table table-condensed">
98                         <thead>
99                             <tr>
100                                 <th>Line</th>
101                                 <th>Route</th>
102                                 <th>Carrier</th>
103                             </tr>
104                         </thead>
105                         <tbody id="times-lines">
106                             
107                         </tbody>
108                     </table>
109                     
110                     <!--
111                     <h3 id="route-line"></h3>
112                     
113                     <table class="table table-condensed">
114                         <thead>
115                             <tr>
116                                 <th>Stop</th>
117                             </tr>
118                         </thead>
119                         <tbody id="route-table">
120                             
121                         </tbody>
122                     </table>
123                     -->
124                 </div>
125             </div>
126         </div>
127         <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7"  crossorigin="anonymous"></script>
128         <script type="text/javascript">
129             //var ttss_base = 'http://www.ttss.krakow.pl/internetservice/services';
130             var ttss_base = '/proxy.php';
131             var ttss_refresh = 20000; // 20 seconds
132             
133             var stop_id;
134             var stop_name = document.getElementById('stop-name');
135             var stop_name_form = stop_name.form;
136             var stop_name_autocomplete = document.getElementById('stop-name-autocomplete');
137             var stop_name_autocomplete_xhr;
138             
139             var times_xhr;
140             var times_timer;
141             var times_stop_name = document.getElementById('times-stop-name');
142             var times_alerts = document.getElementById('times-alerts');
143             var times_table = document.getElementById('times-table');
144             var times_lines = document.getElementById('times-lines');
145             
146             /*
147             var route_xhr;
148             var route_line = document.getElementById('route-line');
149             var route_table = document.getElementById('route-table');
150             */
151             
152             var refresh_button = document.getElementById('refresh');
153             var refresh_text = document.getElementById('refresh-text');
154             var refresh_time;
155             var refresh_timer;
156             
157             var alert = document.getElementById('alert');
158             var alert_text = document.getElementById('alert-text');
159             var alert_close = document.getElementById('alert-close');
160             
161             function parseStatus(status) {
162                 switch(status.status) {
163                     case 'STOPPING':
164                         return '<<<';
165                     case 'PREDICTED':
166                         if(status.actualRelativeTime <= 0)
167                             return '<<<';
168                         if(status.actualRelativeTime >= 60)
169                             return Math.floor(status.actualRelativeTime / 60) + ' min';
170                         return status.actualRelativeTime + ' s';
171                     case 'DEPARTED':
172                         return Math.floor(-status.actualRelativeTime / 60) + ' min ago';
173                     default:
174                         return status.mixedTime;
175                 }
176             }
177             
178             function parseTime(date, time) {
179                 var result = new Date(date.getFullYear(), date.getMonth(), date.getDay());
180                 var time_split = time.split(':');
181                 result.setHours(time_split[0]);
182                 result.setMinutes(time_split[1]);
183                 
184                 if(result.getTime() - date.getTime() > 72000000) {
185                     result.setTime(result.getTime() - 86400000);
186                 }
187                 
188                 if(date.getTime() - result.getTime() > 72000000) {
189                     result.setTime(result.getTime() + 86400000);
190                 }
191                 
192                 return result;
193             }
194             
195             function parseDelay(status) {
196                 if(!status.actualTime) return '?';
197                 if(!status.plannedTime) return '?';
198                 
199                 var now = new Date();
200                 var actual = parseTime(now, status.actualTime);
201                 var planned = parseTime(now, status.plannedTime);
202                 
203                 return ((actual.getTime() - planned.getTime()) / 1000 / 60) + ' min';
204             }
205             
206             function deleteChildren(element) {
207                 while(element.lastChild) element.removeChild(element.lastChild);
208             }
209             
210             function addElementWithText(parent, element, text) {
211                 var elem = document.createElement(element);
212                 elem.appendChild(document.createTextNode(text));
213                 parent.appendChild(elem);
214                 return elem;
215             }
216             
217             function addCellWithText(parent, text) {
218                 addElementWithText(parent, 'td', text);
219             }
220             
221             function addParaWithText(parent, text) {
222                 addElementWithText(parent, 'p', text);
223             }
224             
225             function setText(element, text) {
226                 deleteChildren(element);
227                 element.appendChild(document.createTextNode(text));
228             }
229             
230             function fail(message, more) {
231                 if(refresh_timer) clearInterval(refresh_timer);
232                 if(times_timer) clearTimeout(times_timer);
233                 
234                 setText(alert_text, message);
235                 alert.style.display = 'block';
236                 
237                 console.log(message + ' More details follow.');
238                 if(more) console.log(more);
239             }
240             
241             function fail_ajax(data) {
242                 // abort() is not a failure
243                 if(data.readyState == 0 && data.statusText == 'abort') return;
244                 
245                 if(data.status == 0) {
246                     fail('Request failed - please check your network connectivity.', data);
247                 } else if (data.statusText) {
248                     fail('Internet request failed with error: ' + data.statusText + '.', data);
249                 } else {
250                     fail('Internet request failed.', data);
251                 }
252             }
253             
254             function loadTimes(stopId = null, clearRoute = false) {
255                 if(!stopId) stopId = stop_id;
256                 if(!stopId) return;
257                 
258                 if(times_timer) clearTimeout(times_timer);
259                 if(times_xhr) times_xhr.abort();
260                 
261                 refresh_button.removeAttribute('disabled');
262                 
263                 times_xhr = $.get(
264                     ttss_base + '/passageInfo/stopPassages/stop' 
265                         + '?stop=' + encodeURIComponent(stopId)
266                         + '&mode=departure'
267                 ).done(function(data) {
268                     setText(times_stop_name, data.stopName);
269                     deleteChildren(times_alerts);
270                     deleteChildren(times_table);
271                     deleteChildren(times_lines);
272                     /*
273                     if(clearRoute) {
274                         deleteChildren(route_line);
275                         deleteChildren(route_table);
276                     }
277                     */
278                     
279                     for(var i = 0, il = data.generalAlerts.length; i < il; i++) {
280                         addParaWithText(times_alerts, data.generalAlerts[i]);
281                     }
282                     
283                     for(var i = 0, il = data.old.length; i < il; i++) {
284                         var tr = document.createElement('tr');
285                         addCellWithText(tr, data.old[i].patternText);
286                         addCellWithText(tr, data.old[i].direction);
287                         var status = parseStatus(data.old[i]);
288                         addCellWithText(tr, status);
289                         addCellWithText(tr, '');
290                         
291                         tr.className = 'active';
292                         times_table.appendChild(tr);
293                     }
294                     
295                     for(var i = 0, il = data.actual.length; i < il; i++) {
296                         var tr = document.createElement('tr');
297                         addCellWithText(tr, data.actual[i].patternText);
298                         addCellWithText(tr, data.actual[i].direction);
299                         var status = parseStatus(data.actual[i]);
300                         addCellWithText(tr, status);
301                         var delay = parseDelay(data.actual[i]);
302                         addCellWithText(tr, delay);
303                         
304                         if(status == '<<<') tr.className = 'success';
305                         else if(parseInt(delay) > 9) tr.className = 'danger';
306                         else if(parseInt(delay) > 3) tr.className = 'warning';
307                         times_table.appendChild(tr);
308                     }
309                     
310                     for(var i = 0, il = data.routes.length; i < il; i++) {
311                         var tr = document.createElement('tr');
312                         addCellWithText(tr, data.routes[i].name);
313                         addCellWithText(tr, data.routes[i].directions.join(' - '));
314                         addCellWithText(tr, data.routes[i].authority);
315                         
316                         /*
317                         tr.addEventListener('click', function(routeId, routeTr){ return function(e) {
318                             var trs = tr.parentNode;
319                             for(var i = 0; i < trs.childNodes.length; i++) {
320                                 trs.childNodes[i].removeAttribute('class');
321                             }
322                             routeTr.className = 'warning';
323                             
324                             if(route_xhr) route_xhr.abort();
325                             route_xhr = $.get(
326                                 ttss_base + '/routeInfo/routeStops'
327                                     + '?routeId=' + encodeURIComponent(routeId)
328                             ).done(function(data) {
329                                 setText(route_line, data.route.name + ': '
330                                     + data.route.directions.join(' - '));
331                                 deleteChildren(route_table);
332                                 
333                                 routeTr.className = 'success';
334                                 
335                                 for(var i = 0, il = data.stops.length; i < il; i++) {
336                                     var tr = document.createElement('tr');
337                                     addCellWithText(tr, data.stops[i].name);
338                                     route_table.appendChild(tr);
339                                 }
340                             }).fail(fail_ajax);
341                         }}(data.routes[i].id, tr));
342                         */
343                         
344                         times_lines.appendChild(tr);
345                         
346                         for(var j = 0, jl = data.routes[i].alerts.length; j < jl; j++) {
347                             addParaWithText(
348                                 times_alerts,
349                                 'Line ' +  data.routes[i].name + ': '
350                                     + data.routes[i].alerts[j]
351                             );
352                         }
353                     }
354                     
355                     startTimer(new Date());
356                     
357                     times_timer = setTimeout(function(){ loadTimes(); }, ttss_refresh);
358                 }).fail(fail_ajax);
359             }
360             
361             function declinate(num, singular, plural) {
362                 if(num == 1) return num + ' ' + singular;
363                 return num + ' ' + plural;
364             }
365             
366             function startTimer(date) {
367                 if(date) {
368                     setText(refresh_text, 'Last refreshed: just now')
369                     refresh_time = date;
370                 }
371                 if(!refresh_time) return;
372                 if(refresh_timer) clearInterval(refresh_timer);
373                 
374                 var now = new Date();
375                 var ms = now.getTime() - refresh_time.getTime();
376                 
377                 var interval = 1000;
378                 if(ms >= 120000) interval = 60000;
379                 
380                 refresh_timer = setInterval(function() {
381                     var now = new Date();
382                     var ms = now.getTime() - refresh_time.getTime();
383                     
384                     if(ms >= 120000) {
385                         setText(refresh_text, 'Last refreshed: '
386                             + declinate(Math.floor(ms / 60000), 'minute ago', 'minutes ago'));
387                         startTimer();
388                     } else {
389                         setText(refresh_text, 'Last refreshed: '
390                             + declinate(Math.floor(ms / 1000), 'second ago', 'seconds ago'));
391                     }
392                 }, interval);
393             }
394             
395             function init() {
396                 if(!window.jQuery) {
397                     fail('Required JavaScript jQuery library failed to load. You may try refreshing the page.');
398                     return;
399                 }
400                 
401                 $.ajaxSetup({
402                     dataType: 'json',
403                     timeout: 3000,
404                 });
405                 
406                 stop_name.addEventListener('input', function(e) {
407                     if(!stop_name.value) return;
408                     if(stop_name_autocomplete_xhr) stop_name_autocomplete_xhr.abort();
409                     
410                     stop_name_autocomplete_xhr = $.get(
411                         ttss_base + '/lookup/autocomplete/json'
412                             + '?query=' + encodeURIComponent(stop_name.value)
413                     ).done(function(data) {
414                         deleteChildren(stop_name_autocomplete);
415                         for(var i = 1, il = data.length; i < il; i++) {
416                             var opt = document.createElement('option');
417                             opt.value = data[i].id;
418                             opt.appendChild(document.createTextNode(data[i].name));
419                             stop_name_autocomplete.appendChild(opt);
420                         }
421                         
422                         if(!stop_id) setText(refresh_text, 'Select the stop and click "Go"');
423                     }).fail(fail_ajax);
424                 });
425                 
426                 setText(refresh_text, 'Enter the stop name to begin');
427                 
428                 stop_name_form.addEventListener('submit', function(e) {
429                     e.preventDefault();
430                     if(!stop_name_autocomplete.value) return;
431                     stop_id = stop_name_autocomplete.value;
432                     loadTimes(stop_id, true);
433                 });
434                 
435                 refresh_button.addEventListener('click', function(e) {
436                     loadTimes(stop_id);
437                 });
438                 
439                 alert_close.addEventListener('click', function(e) {
440                     alert.style.display = 'none';
441                 });
442             }
443             
444             init();
445         </script>
446     </body>
447 </html>