opening_hours
Version:
Library to parse and process opening_hours tag from OpenStreetMap data
471 lines (424 loc) • 21.3 kB
JavaScript
/* global $, , default_lat, default_lon, i18next, jQuery, mapCountryToLanguage, opening_hours, OpeningHoursTable, specification_url, YoHoursChecker */
/* Constants {{{ */
var nominatim_api_url = 'https://nominatim.openstreetmap.org/reverse';
// var nominatim_api_url = 'https://open.mapquestapi.com/nominatim/v1/reverse.php';
var evaluation_tool_colors = {
'ok': '#ADFF2F',
'warn': '#FFA500',
'error': '#DEB887',
};
/* }}} */
// load nominatim_data in JOSM {{{
// Using a different way to load stuff in JOSM than https://github.com/rurseekatze/OpenLinkMap/blob/master/js/small.js
// prevent josm remote plugin of showing message
// FIXME: Warning in console. Encoding stuff.
function josm(url_param) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:8111/' + url_param, true); // true makes this call asynchronous
xhr.onreadystatechange = function () { // need eventhandler since our call is async
if ( xhr.status !== 200 ) {
alert(i18next.t('texts.JOSM remote conn error'));
}
};
xhr.send(null);
}
// }}}
// add calculation for calendar week to date {{{
function dateAtWeek(date, week) {
var minutes_in_day = 60 * 24;
var msec_in_day = 1000 * 60 * minutes_in_day;
var msec_in_week = msec_in_day * 7;
var tmpdate = new Date(date.getFullYear(), 0, 1);
tmpdate.setDate(1 - (tmpdate.getDay() + 6) % 7 + week * 7); // start of week n where week starts on Monday
return Math.floor((date - tmpdate) / msec_in_week);
}
// }}}
/*
* The names of countries and states are localized in OSM and opening_hours.js
* (holidays) so we need to get the localized names from Nominatim as well.
*/
function reverseGeocodeLocation(query, guessed_language_for_location, on_success, on_error) {
if (typeof on_error === 'undefined') {
on_error = function() { };
}
if (query === '&lat=48.7769&lon=9.1844') {
/* Cached response to avoid two queries for each usage of the tool. */
return on_success({"place_id":"159221147","licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/www.openstreetmap.org\/copyright","osm_type":"relation","osm_id":"62611","lat":"48.6296972","lon":"9.1949534","display_name":"Baden-Württemberg, Deutschland","address":{"state":"Baden-Württemberg","country":"Deutschland","country_code":"de"},"boundingbox":["47.5324787","49.7912941","7.5117461","10.4955731"]});
}
var nominatim_api_url_template_query = nominatim_api_url
+ '?format=json'
+ query
+ '&zoom=5'
+ '&addressdetails=1'
+ '&email=ypid23@aol.de';
var nominatim_api_url_query = nominatim_api_url_template_query;
if (typeof accept_lanaguage === 'string') {
nominatim_api_url_query += '&accept-language=' + guessed_language_for_location;
}
$.getJSON(nominatim_api_url_query, function(nominatim_data) {
// console.log(JSON.stringify(nominatim_data, null, '\t'));
if (nominatim_data.address.country_code === guessed_language_for_location) {
on_success(nominatim_data);
} else {
nominatim_api_url_query += '&accept-language=' + mapCountryToLanguage(nominatim_data.address.country_code);
$.getJSON(nominatim_api_url_query, function(nominatim_data) {
on_success(nominatim_data);
}).error(on_error);
}
}).error(on_error);
}
function submitenter(myfield,e) {
Evaluate();
// var keycode;
// if (window.event) keycode = window.event.keyCode;
// else if (e) keycode = e.which;
// else return true;
// if (keycode === 13) {
// Evaluate();
// return false;
// } else
// return true;
}
/* JS for toggling examples on and off {{{ */
function toggle(control){
var elem = document.getElementById(control);
if (elem.style.display === "none") {
elem.style.display = "block";
} else {
elem.style.display = "none";
}
}
/* }}} */
function copyToClipboard(text) {
window.prompt('Copy to clipboard: Ctrl+C, Enter', text);
}
var lat, lon, string_lat, string_lon, nominatim;
var date;
function Evaluate (offset, reset) {
if (typeof offset === 'undefined') {
offset = 0;
}
if (document.forms.check.elements['lat'].value !== string_lat || document.forms.check.elements['lon'].value !== string_lon) {
string_lat = document.forms.check.elements['lat'].value;
string_lon = document.forms.check.elements['lon'].value;
lat = parseFloat(string_lat);
lon = parseFloat(string_lon);
if (typeof lat !== 'number' || typeof lon !== 'number') {
if (typeof lat !== 'number') {
document.forms.check.elements['lat'].value = default_lat;
}
if (typeof lon !== 'number') {
document.forms.check.elements['lon'].value = default_lon;
}
console.log('Please enter numbers for latitude and longitude.');
return;
}
reverseGeocodeLocation(
'&lat=' + lat + '&lon=' + lon,
mapCountryToLanguage(i18next.language),
function(nominatim_data) {
nominatim = nominatim_data;
document.forms.check.elements['cc'].value = nominatim.address.country_code;
document.forms.check.elements['state'].value = nominatim.address.state;
Evaluate();
},
function() {
/* Set fallback Nominatim answer to allow using the evaluation tool even without Nominatim. */
alert("Reverse geocoding of the coordinates using Nominatim was not successful. The evaluation of features of the opening_hours specification which depend this information will be unreliable. Otherwise, this tool will work as expected using a fallback answer. You might want to check your browser settings to fix this.");
nominatim = {"place_id":"44651229","licence":"Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https:\/\/www.openstreetmap.org\/copyright","osm_type":"way","osm_id":"36248375","lat":"49.5400039","lon":"9.7937133","display_name":"K 2847, Lauda-K\u00f6nigshofen, Main-Tauber-Kreis, Regierungsbezirk Stuttgart, Baden-W\u00fcrttemberg, Germany, European Union","address":{"road":"K 2847","city":"Lauda-K\u00f6nigshofen","county":"Main-Tauber-Kreis","state_district":"Regierungsbezirk Stuttgart","state":"Baden-W\u00fcrttemberg","country":"Germany","country_code":"de","continent":"European Union"}};
document.forms.check.elements['cc'].value = nominatim.address.country_code;
document.forms.check.elements['state'].value = nominatim.address.state;
Evaluate();
}
);
}
date = reset
? new Date()
: new Date(
document.forms.check.elements['yyyy'].value,
document.forms.check.elements['mm'].selectedIndex,
document.forms.check.elements['dd'].value,
document.forms.check.elements['HH'].value,
parseInt(document.forms.check.elements['MM'].value),
offset
);
function u2 (v) { return v>=0 && v<10 ? "0"+v : v; }
document.forms.check.elements['yyyy'].value = date.getFullYear();
document.forms.check.elements['mm'].selectedIndex = date.getMonth();
document.forms.check.elements['dd'].value = u2(date.getDate());
document.forms.check.elements['HH'].value = u2(date.getHours());
document.forms.check.elements['MM'].value = u2(date.getMinutes());
document.forms.check.elements['wday'].value = date.toLocaleString(i18next.language, {weekday: 'short'});
document.forms.check.elements['week'].value = 'W'+u2(dateAtWeek(date, 0) + 1);
var show_time_table = document.getElementById('show_time_table');
var show_warnings_or_errors = document.getElementById('show_warnings_or_errors');
var show_results = document.getElementById('show_results');
show_warnings_or_errors.innerHTML = '';
var crashed = false;
var value = document.forms.check.elements['expression'].value;
var diff_value = document.forms.check.elements['diff_value'].value;
var mode = parseInt(document.getElementById('mode').selectedIndex);
try {
var oh = new opening_hours(value, nominatim, {
'mode': mode,
'warnings_severity': 7,
'locale': i18next.language
});
var it = oh.getIterator(date);
} catch (err) {
crashed = err;
show_warnings_or_errors.innerHTML = `
<div class="error"> ${i18next.t('texts.filter.error')}
<div class="warning_error_message"> ${crashed} </div>
</div>`;
show_time_table.innerHTML = '';
show_results.innerHTML = '';
}
show_time_table.innerHTML = '<a href="javascript:josm(\'import?url=' + encodeURIComponent('https://overpass-api.de/api/xapi_meta?*[opening_hours='
+ document.forms.check.elements['expression'].value + ']') + '\')">' + i18next.t('texts.load all with JOSM') + '</a><br />';
if (!crashed) {
var prettified = oh.prettifyValue({});
var prettified_value_array = oh.prettifyValue({
// conf: { locale: i18next.language },
get_internals: true,
});
// var prettified_newline_sep = oh.prettifyValue({ conf: { locale: i18next.language, rule_sep_string: '\n', print_semicolon: false } });
show_results.innerHTML = '<p><span class="hd">' + i18next.t('words.status') + ':</span>'
+ '<input class="nostyle" size="10" name="status" readonly="readonly" />'
+ '<input class="nostyle" size="60" name="comment" readonly="readonly" />'
+ '</p>' + '<p><span class="hd">'
+ i18next.t('texts.MatchingRule') + ':</span>'
+ '<input class="nostyle w100" name="MatchingRule" readonly="readonly" />'
+ '</p>';
var used_selectors = { };
var value_explanation =
i18next.t('texts.prettified value for displaying') + ':<br />'
+ '<p class="value_explanation">';
// console.log(JSON.stringify(prettified_value_array, null, ' '));
// console.log(JSON.stringify(prettified_value_array, null, ' '));
for (var nrule = 0; nrule < prettified_value_array[0].length; nrule++) {
if (nrule !== 0) {
var rule_separator = (
prettified_value_array[1][nrule][1]
? ' ||'
: (
prettified_value_array[1][nrule][0][0][1] === 'rule separator'
? ','
: ';'
)
);
value_explanation +=
'<span title="'
+ i18next.t('texts.rule separator ' + rule_separator) + '"'
+ ' class="rule_separator"><a target="_blank" class="specification" href="'
+ specification_url + '#section:rule_separators'
+ '">' + rule_separator + '</a></span><br>';
}
value_explanation += '<span class="one_rule">';
for (var nselector = 0, sl = prettified_value_array[0][nrule].length; nselector < sl; nselector++) {
var selector_type = prettified_value_array[0][nrule][nselector][0][2];
var selector_value = prettified_value_array[0][nrule][nselector][1];
var fragment_identifier;
switch(selector_type) {
case '24/7':
fragment_identifier = 'selector_sequence';
break;
case 'state':
fragment_identifier = 'section:rule_modifier';
break;
case 'comment':
fragment_identifier = 'comment';
break;
default:
fragment_identifier = 'selector:' + selector_type;
}
value_explanation += '<span title="'
+ i18next.t('words.' + (selector_type.match(/(?:state|comment)/) ? 'modifier' : 'selector'), { name: selector_type }) + '"'
+ ' class="' + selector_type + '"><a target="_blank" class="specification" href="'
+ specification_url + '#' + fragment_identifier
+ '">' + selector_value + '</a></span>';
if (nselector + 1 < sl)
value_explanation += ' ';
used_selectors[selector_type] = true;
}
// console.log(value_explanation);
value_explanation += '</span>';
}
value_explanation += '</p></div>';
if (YoHoursChecker.canRead(value)) {
value_explanation = i18next.t('texts.refer to yohours', { href: 'https://projets.pavie.info/yohours/?oh=' + value })
+ '<br>'
+ value_explanation;
}
if (diff_value.length > 0) {
var is_equal_to;
try {
is_equal_to = oh.isEqualTo(new opening_hours(diff_value, nominatim, {
'mode': mode,
'warnings_severity': 7,
'locale': i18next.language
}));
} catch (err) {
$('input#diff_value').css({'background-color' : evaluation_tool_colors.error})
}
if (typeof is_equal_to === 'object') {
if (is_equal_to[0]) {
$('input#diff_value').css({'background-color' : evaluation_tool_colors.ok})
} else {
$('input#diff_value').css({'background-color' : evaluation_tool_colors.warn})
var human_readable_not_equal_output = jQuery.extend(true, {}, is_equal_to[1])
if (typeof human_readable_not_equal_output.deviation_for_time === 'object') {
human_readable_not_equal_output.deviation_for_time = {};
for (var time_code in is_equal_to[1].deviation_for_time) {
console.log(time_code);
var time_string = new Date(parseInt(time_code)).toLocaleString();
human_readable_not_equal_output.deviation_for_time[time_string] =
is_equal_to[1].deviation_for_time[time_code];
}
}
value_explanation = JSON.stringify(human_readable_not_equal_output, null, ' ')
+ '<br>'
+ value_explanation;
}
}
}
show_warnings_or_errors.innerHTML = value_explanation;
// if (prettified_newline_sep.split('\n').length > 1)
// show_results.innerHTML += '<p>' + i18next.t('texts.prettified value for displaying') + ':<br />'
// + '<textarea rows="' + prettified_newline_sep.split('\n').length
// + '" style="width: 100%" name="prettifiedValueNewlineSep" readonly="readonly">'
// + prettified_newline_sep + '</textarea></p>';
document.forms.check.elements['comment'].value = typeof it.getComment() !== 'undefined'
? it.getComment() : i18next.t('words.no') + ' ' + i18next.t('words.comment');
document.forms.check.elements['status'].value = (it.getState() ? i18next.t('words.open')
: (it.getUnknown() ? i18next.t('words.unknown') : i18next.t('words.closed')));
var rule_index = it.getMatchingRule();
document.forms.check.elements['MatchingRule'].value = typeof rule_index === 'undefined'
? i18next.t('words.none') : oh.prettifyValue({ 'rule_index': rule_index });
if (prettified !== value) {
show_warnings_or_errors.innerHTML = '<p>' + i18next.t('texts.prettified value',
{ copyFunc: 'javascript:newValue(\'' + prettified.replace(/"/g, '"') + '\')' }) + ':<br />'
+ '<input style="width: 100%" onclick="javascript:newValue(\'' + prettified.replace(/"/g, '"') + '\')" id="prettifiedValue" name="prettifiedValue" value="' + prettified.replace(/"/g, '"') + '" /></p>';
}
var warnings = oh.getWarnings();
if (warnings.length > 0) {
show_warnings_or_errors.innerHTML += `
<div class="warning"> ${i18next.t('texts.filter.error')}
<div class="warning_error_message"> ${warnings.join('\n')} </div>
</div>`;
}
if (prettified.length > 255) {
show_warnings_or_errors.innerHTML += `
<div class="warning"> ${i18next.t('texts.filter.error')}
<div class="warning_error_message"> ${i18next.t('texts.value to long for osm',
{ pretLength: prettified.length, valLength: value.length, maxLength: 255 })} </div>
</div>`;
}
show_time_table.innerHTML += OpeningHoursTable.drawTableAndComments(oh, it);
}
}
function EX (element) {
newValue(element.innerHTML);
return false;
}
function newValue(value) {
if (typeof document.forms.check.elements['prettifiedValue'] === 'object') {
document.forms.check.elements['prettifiedValue'].focus();
document.forms.check.elements['prettifiedValue'].focus();
}
document.forms.check.elements['expression'].value = value;
Evaluate();
}
function permalink () {
var exp = document.getElementById('expression').value;
var diff_value = document.getElementById('diff_value').value;
var lat = document.getElementById('lat').value;
var lon = document.getElementById('lon').value;
var mode = document.getElementById('mode').selectedIndex;
var permalink_url_query='?EXP='+encodeURIComponent(exp)+'&lat='+lat+'&lon='+lon+'&mode='+mode;
if (document.getElementById('permalink-include-timestamp').checked) {
permalink_url_query += '&DATE='+date.getTime();
}
if (diff_value !== '') {
permalink_url_query += '&diff_value='+encodeURIComponent(diff_value);
}
location = location.protocol+'//'+location.host+location.pathname+permalink_url_query;
}
function setCurrentPosition() {
if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(onPositionUpdate);
}
}
function onPositionUpdate(position) {
var lat = position.coords.latitude;
var lng = position.coords.longitude;
document.getElementById('lat').value = lat;
document.getElementById('lon').value = lng;
Evaluate();
console.log("Current position: " + lat + " " + lng);
}
window.onload = function () {
var prmarr = window.location.search.replace( "?", "" ).split("&");
var params = {};
var customCoords = false;
for ( var i = 0; i < prmarr.length; i++) {
var tmparr = prmarr[i].split("=");
params[tmparr[0]] = tmparr[1];
}
if (typeof params['EXP'] !== 'undefined') {
document.forms.check.elements['expression'].value = decodeURIComponent(params['EXP']);
}
if (typeof params['diff_value'] !== 'undefined') {
document.forms.check.elements['diff_value'].value = decodeURIComponent(params['diff_value']);
}
if (typeof params['lat'] !== 'undefined') {
document.forms.check.elements['lat'].value = decodeURIComponent(params['lat']);
customCoords = true;
}
if (typeof params['lon'] !== 'undefined') {
document.forms.check.elements['lon'].value = decodeURIComponent(params['lon']);
customCoords = true;
}
if (typeof params['mode'] !== 'undefined') {
document.forms.check.elements['mode'].value = decodeURIComponent(params['mode']);
}
if (typeof params['DATE'] !== 'undefined') {
var crashed = true;
try {
date = new Date(parseInt(params['DATE']));
crashed = false;
} catch (err) {
console.error(err);
}
if (!crashed) {
document.forms.check.elements['yyyy'].value = date.getFullYear();
document.forms.check.elements['mm'].selectedIndex = date.getMonth();
document.forms.check.elements['dd'].value = date.getDate();
document.forms.check.elements['HH'].value = date.getHours();
document.forms.check.elements['MM'].value = date.getMinutes();
}
Evaluate(0, false);
} else {
Evaluate(0, true);
}
if (navigator.geolocation && !customCoords) {
navigator.geolocation.getCurrentPosition(onPositionUpdate);
};
};
/* }}} */
$(document).ready(function () {
var permalink = document.getElementById('permalink');
if (permalink) {
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.name = 'name';
checkbox.value = 'value';
checkbox.id = 'permalink-include-timestamp';
checkbox.checked = true;
var label = document.createElement('label')
label.htmlFor = 'permalink-include-timestamp';
label.appendChild(document.createTextNode(i18next.t('texts.include timestamp?')));
permalink.appendChild(label);
permalink.appendChild(checkbox);
}
});