2017-04-23 | Jacek Kowalski | ![]() |
2017-04-23 | Jacek Kowalski | ![]() |
2017-04-23 | Jacek Kowalski | ![]() |
common.js | ●●●●● patch | view | raw | blame | history | |
index.js | ●●●●● patch | view | raw | blame | history | |
lang_en.js | ●●●●● patch | view | raw | blame | history | |
lang_pl.js | ●●●●● patch | view | raw | blame | history | |
map.css | ●●●●● patch | view | raw | blame | history | |
map.html | ●●●●● patch | view | raw | blame | history | |
map.js | ●●●●● patch | view | raw | blame | history |
common.js
@@ -31,6 +31,52 @@ setInterval(checkVersion, 3600000); } /* Parsing of received JSON parts */ function parseStatus(status) { switch(status.status) { case 'STOPPING': return lang.boarding_sign; case 'PREDICTED': if(status.actualRelativeTime <= 0) return lang.boarding_sign; if(status.actualRelativeTime >= 60) return lang.time_minutes_prefix + Math.floor(status.actualRelativeTime / 60) + lang.time_minutes_suffix; return lang.time_seconds_prefix + status.actualRelativeTime + lang.time_seconds_suffix; case 'DEPARTED': return lang.time_minutes_ago_prefix + Math.floor(-status.actualRelativeTime / 60) + lang.time_minutes_ago_suffix; default: return status.mixedTime; } } function parseTime(date, time) { var result = new Date(date.getFullYear(), date.getMonth(), date.getDay()); var time_split = time.split(':'); result.setHours(time_split[0]); result.setMinutes(time_split[1]); if(result.getTime() - date.getTime() > 72000000) { result.setTime(result.getTime() - 86400000); } if(date.getTime() - result.getTime() > 72000000) { result.setTime(result.getTime() + 86400000); } return result; } function parseDelay(status) { if(!status.actualTime) return lang.unknown_sign; if(!status.plannedTime) return lang.unknown_sign; var now = new Date(); var actual = parseTime(now, status.actualTime); var planned = parseTime(now, status.plannedTime); return lang.time_minutes_prefix + ((actual.getTime() - planned.getTime()) / 1000 / 60) + lang.time_minutes_suffix; } // Webservice-related functions function parseVehicle(vehicleId) { if(!vehicleId) return false; @@ -144,6 +190,32 @@ } } function displayVehicle(vehicleInfo) { if(!vehicleInfo) return document.createTextNode(''); var span = document.createElement('span'); span.className = 'vehicleInfo'; var floor_type = ''; if(vehicleInfo.low == 0) { setText(span, lang.high_floor_sign); floor_type = lang.high_floor; } else if(vehicleInfo.low == 1) { setText(span, lang.partially_low_floor_sign); floor_type = lang.partially_low_floor; } else if(vehicleInfo.low == 2) { setText(span, lang.low_floor_sign); floor_type = lang.low_floor; } span.title = lang.tram_type_pattern .replace('$num', vehicleInfo.num) .replace('$type', vehicleInfo.type) .replace('$floor', floor_type); return span; } // Element mangling function deleteChildren(element) { while(element.lastChild) element.removeChild(element.lastChild); index.js
@@ -42,77 +42,6 @@ var vehicle_data = document.getElementById('vehicle-data'); var vehicle_data_style = document.getElementById('vehicle-data-style'); function parseStatus(status) { switch(status.status) { case 'STOPPING': return lang.boarding_sign; case 'PREDICTED': if(status.actualRelativeTime <= 0) return lang.boarding_sign; if(status.actualRelativeTime >= 60) return lang.time_minutes_prefix + Math.floor(status.actualRelativeTime / 60) + lang.time_minutes_suffix; return lang.time_seconds_prefix + status.actualRelativeTime + lang.time_seconds_suffix; case 'DEPARTED': return lang.time_minutes_ago_prefix + Math.floor(-status.actualRelativeTime / 60) + lang.time_minutes_ago_suffix; default: return status.mixedTime; } } function parseTime(date, time) { var result = new Date(date.getFullYear(), date.getMonth(), date.getDay()); var time_split = time.split(':'); result.setHours(time_split[0]); result.setMinutes(time_split[1]); if(result.getTime() - date.getTime() > 72000000) { result.setTime(result.getTime() - 86400000); } if(date.getTime() - result.getTime() > 72000000) { result.setTime(result.getTime() + 86400000); } return result; } function parseDelay(status) { if(!status.actualTime) return lang.unknown_sign; if(!status.plannedTime) return lang.unknown_sign; var now = new Date(); var actual = parseTime(now, status.actualTime); var planned = parseTime(now, status.plannedTime); return lang.time_minutes_prefix + ((actual.getTime() - planned.getTime()) / 1000 / 60) + lang.time_minutes_suffix; } function displayVehicle(vehicleInfo) { if(!vehicleInfo) return document.createTextNode(''); var span = document.createElement('span'); span.className = 'vehicleInfo'; var floor_type = ''; if(vehicleInfo.low == 0) { setText(span, lang.high_floor_sign); floor_type = lang.high_floor; } else if(vehicleInfo.low == 1) { setText(span, lang.partially_low_floor_sign); floor_type = lang.partially_low_floor; } else if(vehicleInfo.low == 2) { setText(span, lang.low_floor_sign); floor_type = lang.low_floor; } span.title = lang.tram_type_pattern .replace('$num', vehicleInfo.num) .replace('$type', vehicleInfo.type) .replace('$floor', floor_type); return span; } function fail(message, more) { if(times_timer) clearTimeout(times_timer); lang_en.js
@@ -12,6 +12,7 @@ go_button: 'Go', refresh_button: '\u27f3 Refresh', last_refreshed: 'Last refreshed: $time', loading: 'Loading...', line_alert_pattern: 'Line $line: $alert', @@ -38,6 +39,12 @@ time_minutes_ago_prefix: '', time_minutes_ago_suffix: ' min ago', type_vehicle: 'Vehicle', type_stop: 'Stop', type_stoppoint: 'Stop point', departures_for_stop: 'Click for stop departures (and not only this stop point).', tram_type_pattern: '$num $type ($floor)', high_floor: 'high floor', high_floor_sign: '\u2010\u00A0', lang_pl.js
@@ -12,6 +12,7 @@ go_button: 'Dalej', refresh_button: '\u27f3 Odśwież', last_refreshed: 'Ostatnio odświeżone $time', loading: 'Ładowanie...', line_alert_pattern: 'Linia $line: $alert', @@ -38,6 +39,12 @@ time_minutes_ago_prefix: '', time_minutes_ago_suffix: ' min temu', type_vehicle: 'Pojazd', type_stop: 'Przystanek', type_stoppoint: 'Punkt przystankowy', departures_for_stop: 'Kliknij, by zobaczyć odjazdy dla całego przystanku (a nie tylko punktu przystankowego).', tram_type_pattern: '$num $type ($floor)', high_floor: 'wysokopodłogowy', high_floor_sign: '\u2010\u00A0', map.css
@@ -1,24 +1,108 @@ html, body, #map { width: 100%; height: 100%; margin: 0; font-family: sans-serif; overflow: hidden; } #map { position: absolute; top: 0; left: 0; right: 0; bottom: 0; } #map .ol-attribution { left: 0; right: auto; } #popup { color: black; background: white; padding: 5px; border: 1px solid black; border-radius: 10px; font-size: 12px; border-left: 1px solid black; font-size: 14px; position: absolute; width: 300px; right: -315px; top: 0; bottom: 0; transition: right .4s; transition: width .4s; opacity: .85; overflow-y: auto; } #popup.show { right: 0; } @media (max-width: 600px) { #popup { width: 90%; right: -100%; } #popup.show { right: 0%; } } #popup .close { float: right; cursor: pointer; font-size: 20px; } #popup .type { padding-bottom: 0; color: #444; font-size: 80%; } #popup p { margin: 0; padding: 5px; } #popup .bold { #popup .name { font-weight: bold; } #popup .vehicleInfo { font-size: 21px; margin: -4px 0 -5px; } #popup table { margin-top: 3px; border-top: 1px solid gray; width: 100%; border-collapse: collapse; } #popup table th { text-align: left; border-bottom: 1px solid #999; padding-top: 5px; } #popup table td { vertical-align: top; } #popup .active { background: #f5f5f5; color: gray; } #popup .success { background: #dff0d8; } #popup .warning { background: #fcf8e3; } #popup .danger { background: #f2dede; } #popup table .vehicleInfo { float: right; } #title { @@ -28,13 +112,16 @@ font-weight: bold; background-color: rgba(255,255,255,.6); } #fail { top: -10em; right: 0.5em; #fail, #popup .error { background: red; color: white; font-weight: bold; padding: 5px; } #fail { top: -10em; right: 0.5em; } .ol-zoom { top: 2.2em; @@ -43,7 +130,12 @@ a { color: #337ab7; text-decoration: none; cursor: pointer; } a:hover { text-decoration: underline; } .small { font-size: 80%; } map.html
@@ -11,8 +11,8 @@ <div id="map"> <div id="title" class="ol-unselectable ol-control"><a href="/">TTSS Kraków</a></div> <div id="fail" class="ol-unselectable ol-control"></div> <div id="popup"></div> </div> <div id="popup"></div> <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7" crossorigin="anonymous"></script> <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList"></script> <script src="https://openlayers.org/en/v4.1.0/build/ol.js" integrity="sha384-FkrvSU9IkctjoF6eTvw82jZvg4Rn04zw5X2nCQnYF+vT8IbAY+DJ23IIoaVDtiMe" crossorigin="anonymous"></script> map.js
@@ -1,5 +1,5 @@ //var ttss_base = 'http://www.ttss.krakow.pl/internetservice'; var ttss_base = '/proxy.php'; var ttss_base = 'proxy.php'; var ttss_refresh = 10000; // 10 seconds var vehicles_xhr = null; @@ -14,10 +14,13 @@ var stop_points_source = null; var stop_points_layer = null; var feature_id = null; var feature_xhr = null; var feature_timer = null; var map = null; var popup_feature_id = null; var map_sphere = null; var popup_element = document.getElementById('popup'); var popup = null; var fail_element = document.getElementById('fail'); var ignore_hashchange = false; @@ -29,33 +32,33 @@ fail_element.style.top = '0.5em'; } function fail_ajax(data) { function fail_popup(msg) { addElementWithText(popup_element, 'p', msg).className = 'error'; } function fail_ajax_generic(data, fnc) { // abort() is not a failure if(data.readyState == 0 && data.statusText == 'abort') return; if(data.status == 0) { fail(lang.error_request_failed_connectivity, data); fnc(lang.error_request_failed_connectivity, data); } else if (data.statusText) { fail(lang.error_request_failed_status.replace('$status', data.statusText), data); fnc(lang.error_request_failed_status.replace('$status', data.statusText), data); } else { fail(lang.error_request_failed, data); fnc(lang.error_request_failed, data); } } function popupHide() { popup.setPosition(undefined); popup_feature_id = null; function fail_ajax(data) { fail_ajax_generic(data, fail); } function popupShow(coordinates, id) { popup.setPosition(coordinates); if(id) { popup_feature_id = id; } function fail_ajax_popup(data) { fail_ajax_generic(data, fail_popup); } function getGeometry(object) { return new ol.geom.Point(ol.proj.fromLonLat([object.longitude / 3600000.0, object.latitude / 3600000.0])) return new ol.geom.Point(ol.proj.fromLonLat([object.longitude / 3600000.0, object.latitude / 3600000.0])); } function updateVehicles() { @@ -77,8 +80,8 @@ if(vehicle.isDeleted) { if(vehicle_feature) { vehicles_source.removeFeature(vehicle_feature); if(popup_feature_id == vehicle_feature.getId()) { popupHide(); if(feature_id == vehicle_feature.getId()) { featureClicked(); } } continue; @@ -128,10 +131,6 @@ } else { vehicle_feature.setProperties(vehicle); vehicle_feature.getStyle().getImage().setRotation(Math.PI * parseFloat(vehicle.heading) / 180.0); if(popup_feature_id == vehicle_feature.getId()) { popupShow(vehicle_feature.getGeometry().getCoordinates()); } } } @@ -191,9 +190,103 @@ }).fail(fail_ajax); } function vehicleTable(tripId, table) { if(feature_xhr) feature_xhr.abort(); if(feature_timer) clearTimeout(feature_timer); feature_xhr = $.get( ttss_base + '/services/tripInfo/tripPassages' + '?tripId=' + encodeURIComponent(tripId) + '&mode=departure' ).done(function(data) { if(!data.routeName || !data.directionText || data.old.length + data.actual.length == 0) { return; } deleteChildren(table); for(var i = 0, il = data.old.length; i < il; i++) { var tr = document.createElement('tr'); addCellWithText(tr, data.old[i].actualTime || data.old[i].plannedTime); addCellWithText(tr, data.old[i].stop_seq_num + '. ' + data.old[i].stop.name); tr.className = 'active'; table.appendChild(tr); } for(var i = 0, il = data.actual.length; i < il; i++) { var tr = document.createElement('tr'); addCellWithText(tr, data.actual[i].actualTime || data.actual[i].plannedTime); addCellWithText(tr, data.actual[i].stop_seq_num + '. ' + data.actual[i].stop.name); if(data.actual[i].status == 'STOPPING') { tr.className = 'success'; } table.appendChild(tr); } feature_timer = setTimeout(function() { vehicleTable(tripId, table); }, ttss_refresh); }).fail(fail_ajax_popup); } function stopTable(stopType, stopId, table) { if(feature_xhr) feature_xhr.abort(); if(feature_timer) clearTimeout(feature_timer); feature_xhr = $.get( ttss_base + '/services/passageInfo/stopPassages/' + stopType + '?' + stopType + '=' + encodeURIComponent(stopId) + '&mode=departure' ).done(function(data) { deleteChildren(table); for(var i = 0, il = data.old.length; i < il; i++) { var tr = document.createElement('tr'); addCellWithText(tr, data.old[i].patternText); var dir_cell = addCellWithText(tr, data.old[i].direction); var vehicle = parseVehicle(data.old[i].vehicleId); dir_cell.appendChild(displayVehicle(vehicle)); var status = parseStatus(data.old[i]); addCellWithText(tr, status); addCellWithText(tr, ''); tr.className = 'active'; table.appendChild(tr); } for(var i = 0, il = data.actual.length; i < il; i++) { var tr = document.createElement('tr'); addCellWithText(tr, data.actual[i].patternText); var dir_cell = addCellWithText(tr, data.actual[i].direction); var vehicle = parseVehicle(data.actual[i].vehicleId); dir_cell.appendChild(displayVehicle(vehicle)); var status = parseStatus(data.actual[i]); var status_cell = addCellWithText(tr, status); var delay = parseDelay(data.actual[i]); var delay_cell = addCellWithText(tr, delay); if(status == lang.boarding_sign) { tr.className = 'success'; status_cell.className = 'status-boarding'; } else if(parseInt(delay) > 9) { tr.className = 'danger'; delay_cell.className = 'status-delayed'; } else if(parseInt(delay) > 3) { tr.className = 'warning'; } table.appendChild(tr); } feature_timer = setTimeout(function() { stopTable(stopType, stopId, table); }, ttss_refresh); }).fail(fail_ajax_popup); } function featureClicked(feature) { if(!feature) { popupHide(); feature_id = null; $(popup_element).removeClass('show'); ignore_hashchange = true; window.location.hash = ''; @@ -205,20 +298,96 @@ deleteChildren(popup_element); addParaWithText(popup_element, feature.get('name')).className = 'bold'; var close = addParaWithText(popup_element, '×'); close.className = 'close'; close.addEventListener('click', function() { featureClicked(); }); var type; var name = feature.get('name'); var additional; var table = document.createElement('table'); var thead = document.createElement('thead'); var tbody = document.createElement('tbody'); table.appendChild(thead); table.appendChild(tbody); switch(feature.getId().substr(0, 1)) { case 'v': var vehicle_type = parseVehicle(feature.get('id')); if(vehicle_type) { addParaWithText(popup_element, vehicle_type.num + ' ' + vehicle_type.type); type = lang.type_vehicle; if(!feature.get('vehicle_type')) { break; } var span = displayVehicle(feature.get('vehicle_type')); additional = document.createElement('p'); setText(additional, span.title); additional.insertBefore(span, additional.firstChild); addElementWithText(thead, 'th', lang.header_time); addElementWithText(thead, 'th', lang.header_stop); vehicleTable(feature.get('tripId'), tbody); break; case 's': type = lang.type_stop; addElementWithText(thead, 'th', lang.header_line); addElementWithText(thead, 'th', lang.header_direction); addElementWithText(thead, 'th', lang.header_time); addElementWithText(thead, 'th', lang.header_delay); stopTable('stop', feature.get('shortName'), tbody); break; case 'p': type = lang.type_stoppoint; additional = document.createElement('p'); additional.className = 'small'; addElementWithText(additional, 'a', lang.departures_for_stop).addEventListener( 'click', function() { featureClicked(stops_source.forEachFeature(function(stop_feature) { if(stop_feature.get('shortName') == feature.get('shortName')) { return stop_feature; } })); } ); addElementWithText(thead, 'th', lang.header_line); addElementWithText(thead, 'th', lang.header_direction); addElementWithText(thead, 'th', lang.header_time); addElementWithText(thead, 'th', lang.header_delay); stopTable('stopPoint', feature.get('stopPoint'), tbody); break; } var loader = addElementWithText(tbody, 'td', lang.loading); loader.className = 'active'; loader.colspan = thead.childNodes.length; addParaWithText(popup_element, type).className = 'type'; addParaWithText(popup_element, name).className = 'name'; if(additional) { popup_element.appendChild(additional); } popup_element.appendChild(table); ignore_hashchange = true; window.location.hash = '#!' + feature.getId(); popupShow(coordinates, feature.getId()); map.getView().animate({ center: feature.getGeometry().getCoordinates(), }); $(popup_element).addClass('show'); feature_id = feature.getId(); } function hash() { @@ -260,9 +429,26 @@ } featureClicked(feature); if(feature) { map.getView().setCenter(feature.getGeometry().getCoordinates()); } function getDistance(c1, c2) { if(c1.getGeometry) { c1 = c1.getGeometry().getCoordinates(); } if(c2.getGeometry) { c2 = c2.getGeometry().getCoordinates(); } var c1 = ol.proj.transform(c1, 'EPSG:3857', 'EPSG:4326'); var c2 = ol.proj.transform(c2, 'EPSG:3857', 'EPSG:4326'); return map_sphere.haversineDistance(c1, c2); } function returnClosest(point, f1, f2) { if(!f1) return f2; if(!f2) return f1; return (getDistance(point, f1) < getDistance(point, f2)) ? f1 : f2; } function init() { @@ -298,13 +484,6 @@ source: vehicles_source, }); popup = new ol.Overlay({ element: popup_element, positioning: 'bottom-center', stopEvent: false, offset: [0, -12] }); map = new ol.Map({ target: 'map', layers: [ @@ -315,7 +494,6 @@ stop_points_layer, vehicles_layer, ], overlays: [popup], view: new ol.View({ center: ol.proj.fromLonLat([19.94, 50.06]), zoom: 13 @@ -333,10 +511,29 @@ }) ]), }); map_sphere = new ol.Sphere(6378137); // Display popup on click map.on('singleclick', function(e) { var point = e.coordinate; var feature = map.forEachFeatureAtPixel(e.pixel, function(feature) { return feature; }); if(!feature) { if(stops_layer.getVisible()) { feature = returnClosest(point, feature, stops_source.getClosestFeatureToCoordinate(point)); } if(stop_points_layer.getVisible()) { feature = returnClosest(point, feature, stop_points_source.getClosestFeatureToCoordinate(point)); } if(vehicles_layer.getVisible()) { feature = returnClosest(point, feature, vehicles_source.getClosestFeatureToCoordinate(point)); } if(getDistance(point, feature) > 200) { feature = null; } } featureClicked(feature); });