Improved www.ttss.krakow.pl
Jacek Kowalski
2017-04-12 969115b78ffc6c228d5eb0abdacfdd758470f299
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 + ')');
969115 154     stop_id = stopId;
9e6757 155     
969115 156     window.location.hash = '#!' + language + stopId;
c25e91 157     refresh_button.removeAttribute('disabled');
JK 158     
ac721d 159     loading_start();
c25e91 160     times_xhr = $.get(
68aeb4 161         ttss_base + '/services/passageInfo/stopPassages/stop'
c25e91 162             + '?stop=' + encodeURIComponent(stopId)
JK 163             + '&mode=departure'
164     ).done(function(data) {
165         setText(times_stop_name, data.stopName);
9e6757 166         setText(page_title, lang.page_title_stop_name.replace('$stop', data.stopName));
c25e91 167         deleteChildren(times_alerts);
JK 168         deleteChildren(times_table);
68aeb4 169         //deleteChildren(times_lines);
c25e91 170         
JK 171         for(var i = 0, il = data.generalAlerts.length; i < il; i++) {
09944e 172             addParaWithText(times_alerts, data.generalAlerts[i].title);
c25e91 173         }
JK 174         
175         for(var i = 0, il = data.old.length; i < il; i++) {
176             var tr = document.createElement('tr');
177             addCellWithText(tr, data.old[i].patternText);
8fb34c 178             var dir_cell = addCellWithText(tr, data.old[i].direction);
JK 179             dir_cell.appendChild(displayVehicle(parseVehicle(data.old[i].vehicleId)));
c25e91 180             var status = parseStatus(data.old[i]);
JK 181             addCellWithText(tr, status);
182             addCellWithText(tr, '');
183             
184             tr.className = 'active';
68aeb4 185             tr.addEventListener('click', function(tripId){ return function(){ loadRoute(tripId); } }(data.old[i].tripId) );
c25e91 186             times_table.appendChild(tr);
JK 187         }
188         
189         for(var i = 0, il = data.actual.length; i < il; i++) {
190             var tr = document.createElement('tr');
191             addCellWithText(tr, data.actual[i].patternText);
8fb34c 192             var dir_cell = addCellWithText(tr, data.actual[i].direction);
JK 193             dir_cell.appendChild(displayVehicle(parseVehicle(data.actual[i].vehicleId)));
c25e91 194             var status = parseStatus(data.actual[i]);
e2b283 195             var status_cell = addCellWithText(tr, status);
c25e91 196             var delay = parseDelay(data.actual[i]);
e2b283 197             var delay_cell = addCellWithText(tr, delay);
c25e91 198             
9e6757 199             if(status == lang.boarding_sign) {
e2b283 200                 tr.className = 'success';
JK 201                 status_cell.className = 'status-boarding';
202             } else if(parseInt(delay) > 9) {
203                 tr.className = 'danger';
204                 delay_cell.className = 'status-delayed';
205             } else if(parseInt(delay) > 3) {
206                 tr.className = 'warning';
207             }
68aeb4 208             
JK 209             tr.addEventListener('click', function(tripId){ return function(){ loadRoute(tripId); } }(data.actual[i].tripId) );
c25e91 210             times_table.appendChild(tr);
JK 211         }
212         
68aeb4 213         /*
c25e91 214         for(var i = 0, il = data.routes.length; i < il; i++) {
JK 215             var tr = document.createElement('tr');
216             addCellWithText(tr, data.routes[i].name);
217             addCellWithText(tr, data.routes[i].directions.join(' - '));
218             addCellWithText(tr, data.routes[i].authority);
219             times_lines.appendChild(tr);
220         }
68aeb4 221         */
c25e91 222         
JK 223         startTimer(new Date());
e54544 224         fail_hide();
c25e91 225         
68aeb4 226         times_timer = setTimeout(function(){ loadTimes(); loadRoute(); }, ttss_refresh);
ac721d 227     }).fail(fail_ajax).always(loading_end);
68aeb4 228 }
JK 229
230 function loadRoute(tripId) {
231     if(!tripId) tripId = route_id;
232     if(!tripId) return;
233     
234     console.log('loadRoute(' + tripId + ')');
235     route_id = tripId;
236     
237     if(route_xhr) route_xhr.abort();
238     route_xhr = $.get(
239         ttss_base + '/services/tripInfo/tripPassages'
240             + '?tripId=' + encodeURIComponent(tripId)
241             + '&mode=departure'
242     ).done(function(data) {
243         if(!data.routeName || !data.directionText || data.old.length + data.actual.length == 0) {
244             route_id = null;
245             return;
246         }
247         
248         setText(route_line, data.routeName + ' ' + data.directionText);
249         deleteChildren(route_table);
250         
251         for(var i = 0, il = data.old.length; i < il; i++) {
252             var tr = document.createElement('tr');
253             addCellWithText(tr, data.old[i].actualTime || data.old[i].plannedTime);
254             addCellWithText(tr, data.old[i].stop_seq_num + '. ' + data.old[i].stop.name);
255             
256             tr.className = 'active';
257             route_table.appendChild(tr);
258         }
259         
260         for(var i = 0, il = data.actual.length; i < il; i++) {
261             var tr = document.createElement('tr');
262             addCellWithText(tr, data.actual[i].actualTime || data.actual[i].plannedTime);
263             addCellWithText(tr, data.actual[i].stop_seq_num + '. ' + data.actual[i].stop.name);
264             
265             if(data.actual[i].status == 'STOPPING') {
266                 tr.className = 'success';
267             }
268             route_table.appendChild(tr);
269         }
270     }).fail(fail_ajax);
c25e91 271 }
JK 272
273 function startTimer(date) {
274     if(date) {
9e6757 275         setText(refresh_text, lang.last_refreshed.replace('$time', lang.time_now));
c25e91 276         refresh_time = date;
JK 277     }
278     if(!refresh_time) return;
279     if(refresh_timer) clearInterval(refresh_timer);
280     
281     var now = new Date();
282     var ms = now.getTime() - refresh_time.getTime();
283     
284     var interval = 1000;
285     if(ms >= 120000) interval = 60000;
286     
287     refresh_timer = setInterval(function() {
288         var now = new Date();
289         var ms = now.getTime() - refresh_time.getTime();
290         
291         if(ms >= 120000) {
9e6757 292             setText(refresh_text, lang.last_refreshed.replace(
JK 293                 '$time',
294                 lang.time_minutes_ago_prefix + Math.floor(ms / 60000)
295                     + lang.time_minutes_ago_suffix
296             ));
c25e91 297             startTimer();
JK 298         } else {
9e6757 299             setText(refresh_text, lang.last_refreshed.replace(
JK 300                 '$time',
301                 lang.time_seconds_ago_prefix + Math.floor(ms / 1000)
302                     + lang.time_seconds_ago_suffix
303             ));
c25e91 304         }
JK 305     }, interval);
b50208 306 }
JK 307
9e6757 308 function translate() {
JK 309     var elements = document.querySelectorAll('*[data-translate]');
310     
311     var text_name;
312     for(var i = 0; i < elements.length; i++) {
313         text_name = elements[i].dataset.translate;
314         if(lang[text_name] == undefined) {
315             console.log('Missing translation: ' + text_name);
316             continue;
317         }
318         setText(elements[i], lang[text_name]);
319     }
320     
321     stop_name.setAttribute('placeholder', lang.stop_name_placeholder);
322     
323     if(stop_id) return;
324     
d59e36 325     setText(page_title, lang.page_title);
JK 326     
9e6757 327     if(stop_name_autocomplete.value) {
JK 328         setText(refresh_text, lang.select_stop_click_go);
329     } else {
330         setText(refresh_text, lang.enter_stop_name_to_begin);
331     }
332 }
333
334 function change_language(lang) {
335     if(!lang || lang.length != 2) return;
336     if(lang == language) return;
337     lang_select.value = lang;
338     if(!lang_select.value) {
339         lang_select.value = language;
340         return;
341     }
342     language = lang;
343     
344     var script = document.createElement('script');
345     script.type = 'text/javascript';
346     script.src = 'lang_' + lang + '.js';
347     script.id = 'lang_script';
348     script.onload = translate;
349     
350     document.body.removeChild(document.getElementById('lang_script'));
351     document.body.appendChild(script);
352     
353     window.location.hash = '#!' + language + stop_id;
354 }
355
356 function hash() {
357     if(window.location.hash.match(/^#![0-9]+$/)) {
358         loadTimes(parseInt(window.location.hash.substr(2)));
359     } else if(window.location.hash.match(/^#![a-z]{2}[0-9]*$/)) {
360         var stop = parseInt(window.location.hash.substr(4));
361         if(stop) stop_id = stop;
362         change_language(window.location.hash.substr(2, 2));
363         loadTimes(stop_id);
364     }
365 }
366
e95a73 367 function stop_autocomplete() {
JK 368     if(stop_name_autocomplete_xhr) stop_name_autocomplete_xhr.abort();
369     
370     stop_name_autocomplete_xhr = $.get(
ced309 371         'stops.php?query=' + encodeURIComponent(stop_name.value)
e95a73 372     ).done(function(data) {
JK 373         deleteChildren(stop_name_autocomplete);
ced309 374         for(var i = 0, il = data.length; i < il; i++) {
JK 375             if(data[i].type != 'stop') continue;
e95a73 376             if(data[i].id > 6000) continue;
JK 377             var opt = document.createElement('option');
378             opt.value = data[i].id;
379             setText(opt, decodeEntities(data[i].name));
380             stop_name_autocomplete.appendChild(opt);
381         }
382         
383         if(!stop_id) setText(refresh_text, lang.select_stop_click_go);
384     }).fail(fail_ajax);
385 }
386
c25e91 387 function init() {
JK 388     if(!window.jQuery) {
9e6757 389         fail(lang.jquery_not_loaded);
c25e91 390         return;
JK 391     }
392     
393     $.ajaxSetup({
394         dataType: 'json',
12310b 395         timeout: 10000,
9e6757 396     });
JK 397     
398     lang_select.addEventListener('input', function(e) {
399         change_language(lang_select.value);
c25e91 400     });
JK 401     
402     stop_name.addEventListener('input', function(e) {
403         if(!stop_name.value) return;
e95a73 404         if(stop_name_autocomplete_timer) clearTimeout(stop_name_autocomplete_timer);
c25e91 405         
e95a73 406         stop_name_autocomplete_timer = setTimeout(stop_autocomplete, 100);
c25e91 407     });
JK 408     
9e6757 409     setText(refresh_text, lang.enter_stop_name_to_begin);
c25e91 410     
JK 411     stop_name_form.addEventListener('submit', function(e) {
412         e.preventDefault();
413         if(!stop_name_autocomplete.value) return;
969115 414         loadTimes(stop_name_autocomplete.value);
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();