Improved www.ttss.krakow.pl
Jacek Kowalski
2017-04-12 68aeb4a997a99ac7dde426ad7a3c87205b4f89d2
commit | author | age
68aeb4 1 //var ttss_base = 'http://www.ttss.krakow.pl/internetservice';
c25e91 2 var ttss_base = '/proxy.php';
JK 3 var ttss_refresh = 20000; // 20 seconds
4
f50bae 5 var page_title = document.getElementsByTagName('title')[0];
JK 6
9e6757 7 var language = 'en';
JK 8 var lang_select = document.getElementById('lang-select');
9
68aeb4 10 var stop_id;
c25e91 11 var stop_name = document.getElementById('stop-name');
JK 12 var stop_name_form = stop_name.form;
13 var stop_name_autocomplete = document.getElementById('stop-name-autocomplete');
14 var stop_name_autocomplete_xhr;
e95a73 15 var stop_name_autocomplete_timer;
c25e91 16
JK 17 var times_xhr;
18 var times_timer;
19 var times_stop_name = document.getElementById('times-stop-name');
20 var times_alerts = document.getElementById('times-alerts');
21 var times_table = document.getElementById('times-table');
68aeb4 22 //var times_lines = document.getElementById('times-lines');
c25e91 23
68aeb4 24 var route_id;
c25e91 25 var route_xhr;
JK 26 var route_line = document.getElementById('route-line');
27 var route_table = document.getElementById('route-table');
28
29 var refresh_button = document.getElementById('refresh');
30 var refresh_text = document.getElementById('refresh-text');
31 var refresh_time;
32 var refresh_timer;
33
34 var alert = document.getElementById('alert');
35 var alert_text = document.getElementById('alert-text');
36 var alert_close = document.getElementById('alert-close');
37
0f9bcf 38 var nav = document.getElementsByTagName('nav')[0];
ac721d 39
c25e91 40 function parseStatus(status) {
JK 41     switch(status.status) {
42         case 'STOPPING':
9e6757 43             return lang.boarding_sign;
c25e91 44         case 'PREDICTED':
JK 45             if(status.actualRelativeTime <= 0)
9e6757 46                 return lang.boarding_sign;
c25e91 47             if(status.actualRelativeTime >= 60)
9e6757 48                 return lang.time_minutes_prefix + Math.floor(status.actualRelativeTime / 60) + lang.time_minutes_suffix;
JK 49             return lang.time_seconds_prefix + status.actualRelativeTime + lang.time_seconds_suffix;
c25e91 50         case 'DEPARTED':
9e6757 51             return lang.time_minutes_ago_prefix + Math.floor(-status.actualRelativeTime / 60) + lang.time_minutes_ago_suffix;
c25e91 52         default:
JK 53             return status.mixedTime;
54     }
55 }
56
57 function parseTime(date, time) {
58     var result = new Date(date.getFullYear(), date.getMonth(), date.getDay());
59     var time_split = time.split(':');
60     result.setHours(time_split[0]);
61     result.setMinutes(time_split[1]);
62     
63     if(result.getTime() - date.getTime() > 72000000) {
64         result.setTime(result.getTime() - 86400000);
65     }
66     
67     if(date.getTime() - result.getTime() > 72000000) {
68         result.setTime(result.getTime() + 86400000);
69     }
70     
71     return result;
72 }
73
74 function parseDelay(status) {
9e6757 75     if(!status.actualTime) return lang.unknown_sign;
JK 76     if(!status.plannedTime) return lang.unknown_sign;
c25e91 77     
JK 78     var now = new Date();
79     var actual = parseTime(now, status.actualTime);
80     var planned = parseTime(now, status.plannedTime);
81     
9e6757 82     return lang.time_minutes_prefix + ((actual.getTime() - planned.getTime()) / 1000 / 60) + lang.time_minutes_suffix;
c25e91 83 }
JK 84
8fb34c 85 function displayVehicle(vehicleInfo) {
JK 86     if(!vehicleInfo) return document.createTextNode('');
87     
88     var span = document.createElement('span');
89     span.className = 'vehicleInfo';
9e6757 90     
JK 91     var floor_type = '';
8fb34c 92     if(vehicleInfo.low == 0) {
9e6757 93         setText(span, lang.high_floor_sign);
JK 94         floor_type = lang.high_floor;
8fb34c 95     } else if(vehicleInfo.low == 1) {
9e6757 96         setText(span, lang.partially_low_floor_sign);
JK 97         floor_type = lang.partially_low_floor;
8fb34c 98     } else if(vehicleInfo.low == 2) {
9e6757 99         setText(span, lang.low_floor_sign);
JK 100         floor_type = lang.low_floor;
8fb34c 101     }
9e6757 102     
JK 103     span.title = lang.tram_type_pattern
104         .replace('$num', vehicleInfo.num)
105         .replace('$type', vehicleInfo.type)
106         .replace('$floor', floor_type);
107     
8fb34c 108     return span;
c25e91 109 }
JK 110
111 function fail(message, more) {
112     if(times_timer) clearTimeout(times_timer);
113     
114     setText(alert_text, message);
115     alert.style.display = 'block';
116     
117     console.log(message + ' More details follow.');
118     if(more) console.log(more);
119 }
120
121 function fail_ajax(data) {
122     // abort() is not a failure
123     if(data.readyState == 0 && data.statusText == 'abort') return;
124     
125     if(data.status == 0) {
9e6757 126         fail(lang.error_request_failed_connectivity, data);
c25e91 127     } else if (data.statusText) {
9e6757 128         fail(lang.error_request_failed_status.replace('$status', data.statusText), data);
c25e91 129     } else {
9e6757 130         fail(lang.error_request_failed, data);
c25e91 131     }
JK 132 }
133
e54544 134 function fail_hide() {
JK 135     alert.style.display = 'none';
136 }
137
ac721d 138 function loading_start() {
0f9bcf 139     nav.className += ' loading';
ac721d 140 }
JK 141
142 function loading_end() {
0f9bcf 143     nav.className = nav.className.replace(' loading', '');
ac721d 144 }
JK 145
68aeb4 146 function loadTimes(stopId) {
c25e91 147     if(!stopId) stopId = stop_id;
JK 148     if(!stopId) return;
149     
150     if(times_timer) clearTimeout(times_timer);
151     if(times_xhr) times_xhr.abort();
152     
68aeb4 153     console.log('loadTimes(' + stopId + ')');
9e6757 154     
JK 155     window.location.hash = '#!' + language + stop_id;
c25e91 156     refresh_button.removeAttribute('disabled');
JK 157     
ac721d 158     loading_start();
c25e91 159     times_xhr = $.get(
68aeb4 160         ttss_base + '/services/passageInfo/stopPassages/stop'
c25e91 161             + '?stop=' + encodeURIComponent(stopId)
JK 162             + '&mode=departure'
163     ).done(function(data) {
164         setText(times_stop_name, data.stopName);
9e6757 165         setText(page_title, lang.page_title_stop_name.replace('$stop', data.stopName));
c25e91 166         deleteChildren(times_alerts);
JK 167         deleteChildren(times_table);
68aeb4 168         //deleteChildren(times_lines);
c25e91 169         
JK 170         for(var i = 0, il = data.generalAlerts.length; i < il; i++) {
09944e 171             addParaWithText(times_alerts, data.generalAlerts[i].title);
c25e91 172         }
JK 173         
174         for(var i = 0, il = data.old.length; i < il; i++) {
175             var tr = document.createElement('tr');
176             addCellWithText(tr, data.old[i].patternText);
8fb34c 177             var dir_cell = addCellWithText(tr, data.old[i].direction);
JK 178             dir_cell.appendChild(displayVehicle(parseVehicle(data.old[i].vehicleId)));
c25e91 179             var status = parseStatus(data.old[i]);
JK 180             addCellWithText(tr, status);
181             addCellWithText(tr, '');
182             
183             tr.className = 'active';
68aeb4 184             tr.addEventListener('click', function(tripId){ return function(){ loadRoute(tripId); } }(data.old[i].tripId) );
c25e91 185             times_table.appendChild(tr);
JK 186         }
187         
188         for(var i = 0, il = data.actual.length; i < il; i++) {
189             var tr = document.createElement('tr');
190             addCellWithText(tr, data.actual[i].patternText);
8fb34c 191             var dir_cell = addCellWithText(tr, data.actual[i].direction);
JK 192             dir_cell.appendChild(displayVehicle(parseVehicle(data.actual[i].vehicleId)));
c25e91 193             var status = parseStatus(data.actual[i]);
e2b283 194             var status_cell = addCellWithText(tr, status);
c25e91 195             var delay = parseDelay(data.actual[i]);
e2b283 196             var delay_cell = addCellWithText(tr, delay);
c25e91 197             
9e6757 198             if(status == lang.boarding_sign) {
e2b283 199                 tr.className = 'success';
JK 200                 status_cell.className = 'status-boarding';
201             } else if(parseInt(delay) > 9) {
202                 tr.className = 'danger';
203                 delay_cell.className = 'status-delayed';
204             } else if(parseInt(delay) > 3) {
205                 tr.className = 'warning';
206             }
68aeb4 207             
JK 208             tr.addEventListener('click', function(tripId){ return function(){ loadRoute(tripId); } }(data.actual[i].tripId) );
c25e91 209             times_table.appendChild(tr);
JK 210         }
211         
68aeb4 212         /*
c25e91 213         for(var i = 0, il = data.routes.length; i < il; i++) {
JK 214             var tr = document.createElement('tr');
215             addCellWithText(tr, data.routes[i].name);
216             addCellWithText(tr, data.routes[i].directions.join(' - '));
217             addCellWithText(tr, data.routes[i].authority);
218             times_lines.appendChild(tr);
219         }
68aeb4 220         */
c25e91 221         
JK 222         startTimer(new Date());
e54544 223         fail_hide();
c25e91 224         
68aeb4 225         times_timer = setTimeout(function(){ loadTimes(); loadRoute(); }, ttss_refresh);
ac721d 226     }).fail(fail_ajax).always(loading_end);
68aeb4 227 }
JK 228
229 function loadRoute(tripId) {
230     if(!tripId) tripId = route_id;
231     if(!tripId) return;
232     
233     console.log('loadRoute(' + tripId + ')');
234     route_id = tripId;
235     
236     if(route_xhr) route_xhr.abort();
237     route_xhr = $.get(
238         ttss_base + '/services/tripInfo/tripPassages'
239             + '?tripId=' + encodeURIComponent(tripId)
240             + '&mode=departure'
241     ).done(function(data) {
242         if(!data.routeName || !data.directionText || data.old.length + data.actual.length == 0) {
243             route_id = null;
244             return;
245         }
246         
247         setText(route_line, data.routeName + ' ' + data.directionText);
248         deleteChildren(route_table);
249         
250         for(var i = 0, il = data.old.length; i < il; i++) {
251             var tr = document.createElement('tr');
252             addCellWithText(tr, data.old[i].actualTime || data.old[i].plannedTime);
253             addCellWithText(tr, data.old[i].stop_seq_num + '. ' + data.old[i].stop.name);
254             
255             tr.className = 'active';
256             route_table.appendChild(tr);
257         }
258         
259         for(var i = 0, il = data.actual.length; i < il; i++) {
260             var tr = document.createElement('tr');
261             addCellWithText(tr, data.actual[i].actualTime || data.actual[i].plannedTime);
262             addCellWithText(tr, data.actual[i].stop_seq_num + '. ' + data.actual[i].stop.name);
263             
264             if(data.actual[i].status == 'STOPPING') {
265                 tr.className = 'success';
266             }
267             route_table.appendChild(tr);
268         }
269     }).fail(fail_ajax);
c25e91 270 }
JK 271
272 function startTimer(date) {
273     if(date) {
9e6757 274         setText(refresh_text, lang.last_refreshed.replace('$time', lang.time_now));
c25e91 275         refresh_time = date;
JK 276     }
277     if(!refresh_time) return;
278     if(refresh_timer) clearInterval(refresh_timer);
279     
280     var now = new Date();
281     var ms = now.getTime() - refresh_time.getTime();
282     
283     var interval = 1000;
284     if(ms >= 120000) interval = 60000;
285     
286     refresh_timer = setInterval(function() {
287         var now = new Date();
288         var ms = now.getTime() - refresh_time.getTime();
289         
290         if(ms >= 120000) {
9e6757 291             setText(refresh_text, lang.last_refreshed.replace(
JK 292                 '$time',
293                 lang.time_minutes_ago_prefix + Math.floor(ms / 60000)
294                     + lang.time_minutes_ago_suffix
295             ));
c25e91 296             startTimer();
JK 297         } else {
9e6757 298             setText(refresh_text, lang.last_refreshed.replace(
JK 299                 '$time',
300                 lang.time_seconds_ago_prefix + Math.floor(ms / 1000)
301                     + lang.time_seconds_ago_suffix
302             ));
c25e91 303         }
JK 304     }, interval);
b50208 305 }
JK 306
9e6757 307 function translate() {
JK 308     var elements = document.querySelectorAll('*[data-translate]');
309     
310     var text_name;
311     for(var i = 0; i < elements.length; i++) {
312         text_name = elements[i].dataset.translate;
313         if(lang[text_name] == undefined) {
314             console.log('Missing translation: ' + text_name);
315             continue;
316         }
317         setText(elements[i], lang[text_name]);
318     }
319     
320     stop_name.setAttribute('placeholder', lang.stop_name_placeholder);
321     
322     if(stop_id) return;
323     
d59e36 324     setText(page_title, lang.page_title);
JK 325     
9e6757 326     if(stop_name_autocomplete.value) {
JK 327         setText(refresh_text, lang.select_stop_click_go);
328     } else {
329         setText(refresh_text, lang.enter_stop_name_to_begin);
330     }
331 }
332
333 function change_language(lang) {
334     if(!lang || lang.length != 2) return;
335     if(lang == language) return;
336     lang_select.value = lang;
337     if(!lang_select.value) {
338         lang_select.value = language;
339         return;
340     }
341     language = lang;
342     
343     var script = document.createElement('script');
344     script.type = 'text/javascript';
345     script.src = 'lang_' + lang + '.js';
346     script.id = 'lang_script';
347     script.onload = translate;
348     
349     document.body.removeChild(document.getElementById('lang_script'));
350     document.body.appendChild(script);
351     
352     window.location.hash = '#!' + language + stop_id;
353 }
354
355 function hash() {
356     if(window.location.hash.match(/^#![0-9]+$/)) {
357         loadTimes(parseInt(window.location.hash.substr(2)));
358     } else if(window.location.hash.match(/^#![a-z]{2}[0-9]*$/)) {
359         var stop = parseInt(window.location.hash.substr(4));
360         if(stop) stop_id = stop;
361         change_language(window.location.hash.substr(2, 2));
362         loadTimes(stop_id);
363     }
364 }
365
e95a73 366 function stop_autocomplete() {
JK 367     if(stop_name_autocomplete_xhr) stop_name_autocomplete_xhr.abort();
368     
369     stop_name_autocomplete_xhr = $.get(
ced309 370         'stops.php?query=' + encodeURIComponent(stop_name.value)
e95a73 371     ).done(function(data) {
JK 372         deleteChildren(stop_name_autocomplete);
ced309 373         for(var i = 0, il = data.length; i < il; i++) {
JK 374             if(data[i].type != 'stop') continue;
e95a73 375             if(data[i].id > 6000) continue;
JK 376             var opt = document.createElement('option');
377             opt.value = data[i].id;
378             setText(opt, decodeEntities(data[i].name));
379             stop_name_autocomplete.appendChild(opt);
380         }
381         
382         if(!stop_id) setText(refresh_text, lang.select_stop_click_go);
383     }).fail(fail_ajax);
384 }
385
c25e91 386 function init() {
JK 387     if(!window.jQuery) {
9e6757 388         fail(lang.jquery_not_loaded);
c25e91 389         return;
JK 390     }
391     
392     $.ajaxSetup({
393         dataType: 'json',
12310b 394         timeout: 10000,
9e6757 395     });
JK 396     
397     lang_select.addEventListener('input', function(e) {
398         change_language(lang_select.value);
c25e91 399     });
JK 400     
401     stop_name.addEventListener('input', function(e) {
402         if(!stop_name.value) return;
e95a73 403         if(stop_name_autocomplete_timer) clearTimeout(stop_name_autocomplete_timer);
c25e91 404         
e95a73 405         stop_name_autocomplete_timer = setTimeout(stop_autocomplete, 100);
c25e91 406     });
JK 407     
9e6757 408     setText(refresh_text, lang.enter_stop_name_to_begin);
c25e91 409     
JK 410     stop_name_form.addEventListener('submit', function(e) {
411         e.preventDefault();
412         if(!stop_name_autocomplete.value) return;
413         stop_id = stop_name_autocomplete.value;
68aeb4 414         loadTimes(stop_id);
c25e91 415     });
JK 416     
417     refresh_button.addEventListener('click', function(e) {
68aeb4 418         loadTimes();
JK 419         loadRoute();
c25e91 420     });
JK 421     
422     alert_close.addEventListener('click', function(e) {
423         alert.style.display = 'none';
424     });
492fc5 425     
9e6757 426     hash();
JK 427     
428     window.addEventListener('hashchange', hash);
c25e91 429 }
JK 430
431 init();