- Timestamp:
- 2014-09-08T23:45:36+02:00 (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/data/validator/opening_hours.js
r7174 r7514 1 /* 2 * For information see https://github.com/ypid/opening_hours.js 3 * and the doc directory which contains internal documentation and design. 4 */ 5 /* jshint laxbreak: true */ 6 /* jshint boss: true */ 7 /* jshint loopfunc: true */ 8 1 9 (function (root, factory) { 2 10 // constants (holidays, error correction) {{{ 3 11 // holidays {{{ 4 12 var holidays = { 5 'fr': { 13 'fr': { // {{{ 6 14 'PH': { // http://fr.wikipedia.org/wiki/F%C3%AAtes_et_jours_f%C3%A9ri%C3%A9s_en_France 7 "Jour de l'an" 8 "Vendredi saint" 9 "Lundi de Pâques" 10 "Saint-Pierre-Chanel" 11 "Fête du Travail" 12 "Fête de la Victoire" 13 "Abolition de l'esclavage" 14 "Abolition de l'esclavage" 15 "Jeudi de l'Ascension" 16 "Lundi de Pentecôte" 17 "Abolition de l'esclavage" 18 "Fête de l'autonomie" 19 "Fête nationale" 20 "Fête Victor Schoelcher" 21 "Fête du Territoire" 22 "Assomption" 23 "Fête de la citoyenneté" 24 "Toussaint" 25 "Armistice" 26 "Abolition de l'esclavage" 27 "Noël" 28 "Saint-Étienne " 15 "Jour de l'an" : [ 1, 1 ], 16 "Vendredi saint" : [ 'easter', -2, [ 'Moselle', 'Bas-Rhin', 'Haut-Rhin', 'Guadeloupe', 'Martinique', 'Polynésie française' ] ], 17 "Lundi de Pâques" : [ 'easter', 1 ], 18 "Saint-Pierre-Chanel" : [ 4, 28, [ 'Wallis-et-Futuna' ] ], 19 "Fête du Travail" : [ 5, 1 ], 20 "Fête de la Victoire" : [ 5, 8 ], 21 "Abolition de l'esclavage" : [ 5, 22, [ 'Martinique' ] ], 22 "Abolition de l'esclavage" : [ 5, 27, [ 'Guadeloupe' ] ], 23 "Jeudi de l'Ascension" : [ 'easter', 39 ], 24 "Lundi de Pentecôte" : [ 'easter', 50 ], 25 "Abolition de l'esclavage" : [ 6, 10, [ 'Guyane' ] ], 26 "Fête de l'autonomie" : [ 6, 29, [ 'Polynésie française' ] ], 27 "Fête nationale" : [ 7, 14 ], 28 "Fête Victor Schoelcher" : [ 7, 21, [ 'Guadeloupe', 'Martinique' ] ], 29 "Fête du Territoire" : [ 7, 29, [ 'Wallis-et-Futuna' ] ], 30 "Assomption" : [ 8, 15 ], 31 "Fête de la citoyenneté" : [ 9, 24, [ 'Nouvelle-Calédonie' ] ], 32 "Toussaint" : [ 11, 1 ], 33 "Armistice" : [ 11, 11 ], 34 "Abolition de l'esclavage" : [ 12, 20, [ 'Réunion' ] ], 35 "Noël" : [ 12, 25 ], 36 "Saint-Étienne " : [ 12, 26, [ 'Moselle', 'Bas-Rhin', 'Haut-Rhin' ] ] 29 37 } 30 }, 31 'de': { 38 }, // }}} 39 'de': { // {{{ 32 40 'PH': { // http://de.wikipedia.org/wiki/Feiertage_in_Deutschland 33 41 'Neujahrstag' : [ 1, 1 ], // month 1, day 1, whole Germany … … 47 55 '1. Weihnachtstag' : [ 12, 25 ], 48 56 '2. Weihnachtstag' : [ 12, 26 ], 49 // 'Silvester' 57 // 'Silvester' : [ 12, 31 ], // for testing 50 58 }, 51 59 'Baden-Württemberg': { // does only apply in Baden-Württemberg … … 53 61 // You may use this instead of the country wide with some 54 62 // additional holidays for some states, if one state 55 // totally disagrees about how to do publicholidays …63 // totally disagrees about how to do holidays … 56 64 // 'PH': { 57 // 65 // '2. Weihnachtstag' : [ 12, 26 ], 58 66 // }, 59 67 … … 1076 1084 ], 1077 1085 }, 1078 }, 1079 'at': { 1086 }, // }}} 1087 'at': { // {{{ 1080 1088 'PH': { // http://de.wikipedia.org/wiki/Feiertage_in_%C3%96sterreich 1081 'Neujahrstag' : [ 1, 1 ],1082 'Heilige Drei Könige' : [ 1, 6 ],1083 // 'Josef' 1084 // 'Karfreitag' 1085 'Ostermontag' : [ 'easter', 1 ],1086 'Staatsfeiertag' : [ 5, 1 ],1087 // 'Florian' 1088 'Christi Himmelfahrt' : [ 'easter', 39 ],1089 'Pfingstmontag' : [ 'easter', 50 ],1090 'Fronleichnam' : [ 'easter', 60 ],1091 'Mariä Himmelfahrt' : [ 8, 15 ],1092 // 'Rupert' 1093 // 'Tag der Volksabstimmung' 1094 'Nationalfeiertag' : [ 10, 26 ],1095 'Allerheiligen' : [ 11, 1 ],1096 // 'Martin' 1097 // 'Leopold' 1098 'Mariä Empfängnis' : [ 12, 8 ],1099 // 'Heiliger Abend' 1100 'Christtag' : [ 12, 25 ],1101 'Stefanitag' : [ 12, 26 ],1102 // 'Silvester' 1089 'Neujahrstag' : [ 1, 1 ], 1090 'Heilige Drei Könige' : [ 1, 6 ], 1091 // 'Josef' : [ 3, 19, [ 'Kärnten', 'Steiermark', 'Tirol', 'Vorarlberg' ] ], 1092 // 'Karfreitag' : [ 'easter', -2 ], 1093 'Ostermontag' : [ 'easter', 1 ], 1094 'Staatsfeiertag' : [ 5, 1 ], 1095 // 'Florian' : [ 5, 4, [ 'Oberösterreich' ] ], 1096 'Christi Himmelfahrt' : [ 'easter', 39 ], 1097 'Pfingstmontag' : [ 'easter', 50 ], 1098 'Fronleichnam' : [ 'easter', 60 ], 1099 'Mariä Himmelfahrt' : [ 8, 15 ], 1100 // 'Rupert' : [ 9, 24, [ 'Salzburg' ] ], 1101 // 'Tag der Volksabstimmung' : [ 10, 10, [ 'Kärnten' ] ], 1102 'Nationalfeiertag' : [ 10, 26 ], 1103 'Allerheiligen' : [ 11, 1 ], 1104 // 'Martin' : [ 11, 11, [ 'Burgenland' ] ], 1105 // 'Leopold' : [ 11, 15, [ 'Niederösterreich', 'Wien' ] ], 1106 'Mariä Empfängnis' : [ 12, 8 ], 1107 // 'Heiliger Abend' : [ 12, 24 ], 1108 'Christtag' : [ 12, 25 ], 1109 'Stefanitag' : [ 12, 26 ], 1110 // 'Silvester' : [ 12, 31 ], 1103 1111 }, 1104 }, 1105 'ca': { 1112 }, // }}} 1113 'ca': { // {{{ 1106 1114 'PH': { // https://en.wikipedia.org/wiki/Public_holidays_in_Canada 1107 "New Year's Day" 1108 "Good Friday" 1109 "Canada Day" 1110 "Labour Day" 1111 "Christmas Day" 1115 "New Year's Day" : [ 1, 1 ], 1116 "Good Friday" : [ 'easter', -2 ], 1117 "Canada Day" : [ 'canadaDay', 0 ], 1118 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1119 "Christmas Day" : [ 12, 25 ] 1112 1120 }, 1113 1121 'Alberta': { 1114 1122 'PH': { 1115 "New Year's Day" 1116 "Alberta Family Day" 1117 "Good Friday" 1118 "Easter Monday" 1119 "Victoria Day" 1120 "Canada Day" 1121 "Heritage Day" 1122 "Labour Day" 1123 "Thanksgiving" 1124 "Remembrance Day" 1125 "Christmas Day" 1126 "Boxing Day" 1123 "New Year's Day" : [ 1, 1 ], 1124 "Alberta Family Day" : [ 'firstFebruaryMonday', 14 ], 1125 "Good Friday" : [ 'easter', -2 ], 1126 "Easter Monday" : [ 'easter', 1 ], 1127 "Victoria Day" : [ 'victoriaDay', 0 ], 1128 "Canada Day" : [ 'canadaDay', 0 ], 1129 "Heritage Day" : [ 'firstAugustMonday', 0 ], 1130 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1131 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1132 "Remembrance Day" : [ 11, 11 ], 1133 "Christmas Day" : [ 12, 25 ], 1134 "Boxing Day" : [ 12, 26 ] 1127 1135 }, 1128 1136 }, 1129 1137 'British Columbia': { 1130 1138 'PH': { 1131 "New Year's Day" 1132 "Family Day" 1133 "Good Friday" 1134 "Victoria Day" 1135 "Canada Day" 1136 "British Columbia Day" 1137 "Labour Day" 1138 "Thanksgiving" 1139 "Remembrance Day" 1140 "Christmas Day" 1139 "New Year's Day" : [ 1, 1 ], 1140 "Family Day" : [ 'firstFebruaryMonday', 7 ], 1141 "Good Friday" : [ 'easter', -2 ], 1142 "Victoria Day" : [ 'victoriaDay', 0 ], 1143 "Canada Day" : [ 'canadaDay', 0 ], 1144 "British Columbia Day" : [ 'firstAugustMonday', 0 ], 1145 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1146 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1147 "Remembrance Day" : [ 11, 11 ], 1148 "Christmas Day" : [ 12, 25 ] 1141 1149 }, 1142 1150 }, 1143 1151 'Manitoba': { 1144 1152 'PH': { 1145 "New Year's Day" 1146 "Louis Riel Day" 1147 "Good Friday" 1148 "Victoria Day" 1149 "Canada Day" 1150 "Civic Holiday" 1151 "Labour Day" 1152 "Thanksgiving" 1153 "Remembrance Day" 1154 "Christmas Day" 1153 "New Year's Day" : [ 1, 1 ], 1154 "Louis Riel Day" : [ 'firstFebruaryMonday', 14 ], 1155 "Good Friday" : [ 'easter', -2 ], 1156 "Victoria Day" : [ 'victoriaDay', 0 ], 1157 "Canada Day" : [ 'canadaDay', 0 ], 1158 "Civic Holiday" : [ 'firstAugustMonday', 0 ], 1159 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1160 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1161 "Remembrance Day" : [ 11, 11 ], 1162 "Christmas Day" : [ 12, 25 ] 1155 1163 }, 1156 1164 }, 1157 1165 'New Brunswick': { 1158 1166 'PH': { 1159 "New Year's Day" 1160 "Good Friday" 1161 "Victoria Day" 1162 "Canada Day" 1163 "New Brunswick Day" 1164 "Labour Day" 1165 "Thanksgiving" 1166 "Remembrance Day" 1167 "Christmas Day" 1168 "Boxing Day" 1167 "New Year's Day" : [ 1, 1 ], 1168 "Good Friday" : [ 'easter', -2 ], 1169 "Victoria Day" : [ 'victoriaDay', 0 ], 1170 "Canada Day" : [ 'canadaDay', 0 ], 1171 "New Brunswick Day" : [ 'firstAugustMonday', 0 ], 1172 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1173 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1174 "Remembrance Day" : [ 11, 11 ], 1175 "Christmas Day" : [ 12, 25 ], 1176 "Boxing Day" : [ 12, 26 ] 1169 1177 }, 1170 1178 }, 1171 1179 'Newfoundland and Labrador': { 1172 1180 'PH': { 1173 "New Year's Day" 1174 "Saint Patrick's Day" 1175 "Good Friday" 1176 "Saint George's Day" 1177 "Discovery Day" 1178 "Memorial Day" 1179 "Orangemen's Day" 1180 "Labour Day" 1181 "Armistice Day" 1182 "Christmas Day" 1181 "New Year's Day" : [ 1, 1 ], 1182 "Saint Patrick's Day" : [ 3, 17 ], 1183 "Good Friday" : [ 'easter', -2 ], 1184 "Saint George's Day" : [ 4, 23 ], 1185 "Discovery Day" : [ 6, 24 ], 1186 "Memorial Day" : [ 7, 1 ], 1187 "Orangemen's Day" : [ 7, 12 ], 1188 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1189 "Armistice Day" : [ 11, 11 ], 1190 "Christmas Day" : [ 12, 25 ] 1183 1191 }, 1184 1192 }, 1185 1193 'Northwest Territories': { 1186 1194 'PH': { 1187 "New Year's Day" 1188 "Good Friday" 1189 "Victoria Day" 1190 "National Aboriginal Day" 1191 "Canada Day" 1192 "Civic Holiday" 1193 "Labour Day" 1194 "Thanksgiving" 1195 "Remembrance Day" 1196 "Christmas Day" 1195 "New Year's Day" : [ 1, 1 ], 1196 "Good Friday" : [ 'easter', -2 ], 1197 "Victoria Day" : [ 'victoriaDay', 0 ], 1198 "National Aboriginal Day" : [ 6, 21 ], 1199 "Canada Day" : [ 'canadaDay', 0 ], 1200 "Civic Holiday" : [ 'firstAugustMonday', 0 ], 1201 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1202 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1203 "Remembrance Day" : [ 11, 11 ], 1204 "Christmas Day" : [ 12, 25 ] 1197 1205 }, 1198 1206 }, 1199 1207 'Nova Scotia': { 1200 1208 'PH': { 1201 "New Year's Day" 1202 "Good Friday" 1203 "Victoria Day" 1204 "Canada Day" 1205 "Natal Day" 1206 "Labour Day" 1207 "Thanksgiving" 1208 "Remembrance Day" 1209 "Christmas Day" 1210 "Boxing Day" 1209 "New Year's Day" : [ 1, 1 ], 1210 "Good Friday" : [ 'easter', -2 ], 1211 "Victoria Day" : [ 'victoriaDay', 0 ], 1212 "Canada Day" : [ 'canadaDay', 0 ], 1213 "Natal Day" : [ 'firstAugustMonday', 0 ], 1214 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1215 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1216 "Remembrance Day" : [ 11, 11 ], 1217 "Christmas Day" : [ 12, 25 ], 1218 "Boxing Day" : [ 12, 26 ] 1211 1219 }, 1212 1220 }, 1213 1221 'Nunavut': { 1214 1222 'PH': { 1215 "New Year's Day" 1216 "Good Friday" 1217 "Victoria Day" 1218 "Canada Day" 1219 "Nunavut Day" 1220 "Civic Holiday" 1221 "Labour Day" 1222 "Thanksgiving" 1223 "Remembrance Day" 1224 "Christmas Day" 1223 "New Year's Day" : [ 1, 1 ], 1224 "Good Friday" : [ 'easter', -2 ], 1225 "Victoria Day" : [ 'victoriaDay', 0 ], 1226 "Canada Day" : [ 'canadaDay', 0 ], 1227 "Nunavut Day" : [ 7, 9 ], 1228 "Civic Holiday" : [ 'firstAugustMonday', 0 ], 1229 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1230 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1231 "Remembrance Day" : [ 11, 11 ], 1232 "Christmas Day" : [ 12, 25 ] 1225 1233 }, 1226 1234 }, … … 1242 1250 'Prince Edward Island': { 1243 1251 'PH': { 1244 "New Year's Day" 1245 "Islander Day" 1246 "Good Friday" 1247 "Easter Monday" 1248 "Victoria Day" 1249 "Canada Day" 1250 "Civic Holiday" 1251 "Gold Cup Parade Day" 1252 "Labour Day" 1253 "Thanksgiving" 1254 "Remembrance Day" 1255 "Christmas Day" 1256 "Boxing Day" 1252 "New Year's Day" : [ 1, 1 ], 1253 "Islander Day" : [ 'firstFebruaryMonday', 14 ], 1254 "Good Friday" : [ 'easter', -2 ], 1255 "Easter Monday" : [ 'easter', 1 ], 1256 "Victoria Day" : [ 'victoriaDay', 0 ], 1257 "Canada Day" : [ 'canadaDay', 0 ], 1258 "Civic Holiday" : [ 'firstAugustMonday', 0 ], 1259 "Gold Cup Parade Day" : [ 'firstAugustMonday', 18 ], 1260 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1261 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1262 "Remembrance Day" : [ 11, 11 ], 1263 "Christmas Day" : [ 12, 25 ], 1264 "Boxing Day" : [ 12, 26 ] 1257 1265 }, 1258 1266 }, … … 1272 1280 'Saskatchewan': { 1273 1281 'PH': { 1274 "New Year's Day" 1275 "Family Day" 1276 "Good Friday" 1277 "Victoria Day" 1278 "Canada Day" 1279 "Saskatchewan Day" 1280 "Labour Day" 1281 "Thanksgiving" 1282 "Remembrance Day" 1283 "Christmas Day" 1282 "New Year's Day" : [ 1, 1 ], 1283 "Family Day" : [ 'firstFebruaryMonday', 14 ], 1284 "Good Friday" : [ 'easter', -2 ], 1285 "Victoria Day" : [ 'victoriaDay', 0 ], 1286 "Canada Day" : [ 'canadaDay', 0 ], 1287 "Saskatchewan Day" : [ 'firstAugustMonday', 0 ], 1288 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1289 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1290 "Remembrance Day" : [ 11, 11 ], 1291 "Christmas Day" : [ 12, 25 ] 1284 1292 }, 1285 1293 }, 1286 1294 'Yukon': { 1287 1295 'PH': { 1288 "New Year's Day" 1289 "Heritage Day" 1290 "Good Friday" 1291 "Easter Monday" 1292 "Victoria Day" 1293 "Canada Day" 1294 "Discovery Day" 1295 "Labour Day" 1296 "Thanksgiving" 1297 "Remembrance Day" 1298 "Christmas Day" 1299 "Boxing Day" 1296 "New Year's Day" : [ 1, 1 ], 1297 "Heritage Day" : [ 'lastFebruarySunday', -2 ], 1298 "Good Friday" : [ 'easter', -2 ], 1299 "Easter Monday" : [ 'easter', 1 ], 1300 "Victoria Day" : [ 'victoriaDay', 0 ], 1301 "Canada Day" : [ 'canadaDay', 0 ], 1302 "Discovery Day" : [ 'firstAugustMonday', 14 ], 1303 "Labour Day" : [ 'firstSeptemberMonday', 0 ], 1304 "Thanksgiving" : [ 'firstOctoberMonday', 7 ], 1305 "Remembrance Day" : [ 11, 11 ], 1306 "Christmas Day" : [ 12, 25 ], 1307 "Boxing Day" : [ 12, 26 ] 1300 1308 }, 1301 1309 }, 1302 }, 1303 'ua': { 1310 }, // }}} 1311 'ua': { // {{{ 1304 1312 'PH': { // http://uk.wikipedia.org/wiki/%D0%A1%D0%B2%D1%8F%D1%82%D0%B0_%D1%82%D0%B0_%D0%BF%D0%B0%D0%BC%27%D1%8F%D1%82%D0%BD%D1%96_%D0%B4%D0%BD%D1%96_%D0%B2_%D0%A3%D0%BA%D1%80%D0%B0%D1%97%D0%BD%D1%96 1305 "Новий рік" 1306 "Різдво" 1307 "Міжнародний жіночий день" 1308 "Великдень" 1309 "День Праці 1" 1310 "День Праці 2" 1311 "День Перемоги" 1312 "День Конституції України" 1313 "День Незалежності України" 1313 "Новий рік" : [ 1, 1 ], 1314 "Різдво" : [ 1, 7 ], 1315 "Міжнародний жіночий день" : [ 3, 8 ], 1316 "Великдень" : [ 'orthodox easter', 1 ], 1317 "День Праці 1" : [ 5, 1 ], 1318 "День Праці 2" : [ 5, 2 ], 1319 "День Перемоги" : [ 5, 9 ], 1320 "День Конституції України" : [ 6, 28 ], 1321 "День Незалежності України" : [ 8, 24 ], 1314 1322 } 1315 }, 1316 'si': { 1323 }, // }}} 1324 'si': { // {{{ 1317 1325 'PH': { // http://www.vlada.si/o_sloveniji/politicni_sistem/prazniki/ 1318 'novo leto' 1319 'Prešernov dan, slovenski kulturni praznik' 1320 'velikonočna nedelja' 1321 'velikonočni ponedeljek' 1322 'dan upora proti okupatorju' 1323 'praznik dela 1' 1324 'praznik dela 2' 1325 'binkoštna nedelja - binkošti' 1326 'dan državnosti' 1327 'Marijino vnebovzetje' 1328 'dan reformacije' 1329 'dan spomina na mrtve' 1330 'božič' 1331 'dan samostojnosti in enotnosti' 1326 'novo leto' : [ 1, 1 ], 1327 'Prešernov dan, slovenski kulturni praznik' : [ 2, 8 ], 1328 'velikonočna nedelja' : [ 'easter', 0 ], 1329 'velikonočni ponedeljek' : [ 'easter', 1 ], 1330 'dan upora proti okupatorju' : [ 4, 27 ], 1331 'praznik dela 1' : [ 5, 1 ], 1332 'praznik dela 2' : [ 5, 2 ], 1333 'binkoštna nedelja - binkošti' : [ 'easter', 49 ], 1334 'dan državnosti' : [ 6, 25 ], 1335 'Marijino vnebovzetje' : [ 8, 15 ], 1336 'dan reformacije' : [ 10, 31 ], 1337 'dan spomina na mrtve' : [ 11, 1 ], 1338 'božič' : [ 12, 25 ], 1339 'dan samostojnosti in enotnosti' : [ 12, 26 ], 1332 1340 }, 1333 }, 1341 }, // }}} 1334 1342 }; 1335 1343 // }}} … … 1341 1349 // Key to word_error_correction is the token name except wrong_words 1342 1350 var word_error_correction = { 1343 wrong_words: { 1344 'Assuming "<ok>" for "<ko>"': { 1345 spring: 'Mar-May', 1346 summer: 'Jun-Aug', 1347 autumn: 'Sep-Nov', 1348 winter: 'Dec-Feb', 1351 wrong_words: { /* {{{ */ 1352 'Assuming "<ok>" for "<ko>".': { 1353 'Frühling': 'Mar-May', 1354 'Frühjahr': 'Mar-May', 1355 'Sommer': 'Jun-Aug', 1356 'Herbst': 'Sep-Nov', 1357 'winter': 'Dec-Feb', 1358 }, '"<ko>" wird als "<ok>" interpertiert.': { 1359 'spring': 'Mar-May', 1360 'summer': 'Jun-Aug', 1361 'autumn': 'Sep-Nov', 1362 // 'winter': 'Dec-Feb', // Same as in English. 1349 1363 // morning: '08:00-12:00', 1350 1364 // evening: '13:00-18:00', … … 1352 1366 'daytime': 'sunrise-sunset', 1353 1367 }, 'Bitte benutze die englische Schreibweise "<ok>" für "<ko>".': { 1354 sommer:'summer',1368 'sommer': 'summer', 1355 1369 'werktag': 'Mo-Fr', 1356 1370 'werktags': 'Mo-Fr', 1357 }, 'Bitte benutze "<ok>" für "<ko>". Beispiel: "Mo-Fr 08:00-12:00; Tu off"': { 1358 ruhetag: 'off', 1359 ruhetage: 'off', 1360 geschlossen: 'off', 1361 ausser: 'off', 1362 außer: 'off', 1371 }, 'Bitte benutze "<ok>" für "<ko>". Beispiel: "Mo-Fr 08:00-12:00; Tu off".': { 1372 'ruhetag': 'off', 1373 'ruhetage': 'off', 1374 'geschlossen': 'off', 1375 'geschl': 'off', 1376 // 'ausser': 'off', 1377 // 'außer': 'off', 1363 1378 }, 'Neem de engelse afkorting "<ok>" voor "<ko>" alstublieft.': { 1364 'gesloten': 'off', 1365 'feestdag': 'PH', 1379 'gesloten': 'off', 1380 'feestdag': 'PH', 1381 'feestdagen': 'PH', 1366 1382 }, 'Assuming "<ok>" for "<ko>". Please avoid using "workday": http://wiki.openstreetmap.org/wiki/Talk:Key:opening_hours#need_syntax_for_holidays_and_workingdays': { 1367 // // Used around 260 times but the problem is, that work day might be different in other countries. 1368 'wd': 'Mo-Fr', 1369 'weekday': 'Mo-Fr', 1370 'weekdays': 'Mo-Fr', 1371 'vardagar': 'Mo-Fr', 1372 }, 'Please use notation something like "Mo off" instead "<ko>".': { 1373 except: 'off', 1374 }, 'Please ommit "<ko>" or use a colon instead: "12:00-14:00".': { 1375 h: '', 1376 }, 'Please ommit "<ko>".': { 1377 season: '', 1378 hs: '', 1379 hrs: '', 1380 hours: '', 1381 }, 'Please ommit "<ko>". The key must not be in the value.': { 1383 // Used around 260 times but the problem is, that work day might be different in other countries. 1384 'wd': 'Mo-Fr', 1385 'on work day': 'Mo-Fr', 1386 'on work days': 'Mo-Fr', 1387 'weekday': 'Mo-Fr', 1388 'weekdays': 'Mo-Fr', 1389 'vardagar': 'Mo-Fr', 1390 }, 'Please use something like "Mo off" instead "<ko>".': { 1391 'except': 'off', 1392 }, 'Please omit "<ko>" or use a colon instead: "12:00-14:00".': { 1393 'h': '', 1394 }, 'Please omit "<ko>".': { 1395 'season': '', 1396 'hs': '', 1397 'hrs': '', 1398 'hours': '', 1399 '·': '', 1400 }, 'Please omit "<ko>". The key must not be in the value.': { 1382 1401 'opening_hours=': '', 1383 }, 'Please ommit "<ko>". You might want to express open end which can be specified as "12:00+" for example.': { 1384 from: '', 1385 }, 'You can use notation "<ok>" for "<ko>". You might want to express open end which can be specified as "12:00+" for example.': { 1402 }, 'Please omit "<ko>". You might want to express open end which can be specified as "12:00+" for example.': { 1403 'from': '', 1404 }, 'You can use notation "<ok>" for "<ko>" in the case that you want to express open end times. Example: "12:00+".': { 1405 'til late': '+', 1406 'till late': '+', 1386 1407 '-late': '+', 1408 '-open end': '+', 1409 '-openend': '+', 1387 1410 }, 'Please use notation "<ok>" for "<ko>". If the times are unsure or vary consider a comment e.g. 12:00-14:00 "only on sunshine".': { 1388 1411 '~': '-', … … 1392 1415 }, 'You can use notation "<ok>" for "<ko>" temporally if the syntax will still be valid.': { 1393 1416 '?': 'unknown "please add this if known"', 1417 }, 'Please use notation "<ok>" for "<ko>". Although using "–" is typographical correct, the opening_hours syntax is defined with the normal hyphen. Correct typography should be done on application level …': { 1418 '–': '-', 1394 1419 }, 'Please use notation "<ok>" for "<ko>".': { 1395 '→': '-', 1396 '–': '-', 1397 '−': '-', 1398 '=': '-', 1399 'ー': '-', 1400 to: '-', 1401 'до': '-', 1402 a: '-', // language unknown 1403 as: '-', // language unknown 1404 'á': '-', // language unknown 1405 'ás': '-', // language unknown 1406 'à': '-', // language unknown 1407 'às': '-', // language unknown 1408 'ate': '-', // language unknown 1409 'till': '-', 1410 'til': '-', 1411 'until': '-', 1412 'through': '-', 1413 and: ',', 1414 '&': ',', 1415 ':': ':', 1416 '°°': ':00', 1417 'daily': 'Mo-Su', 1418 'everyday': 'Mo-Su', 1419 'every day': 'Mo-Su', 1420 always: '24/7', 1421 nonstop: '24/7', 1422 '24x7': '24/7', 1423 'anytime': '24/7', 1424 'all day': '24/7', 1425 'all days': 'Mo-Su', 1426 'every day': 'Mo-Su', 1427 '7days': 'Mo-Su', 1428 '7j/7': 'Mo-Su', // I guess that it means that 1429 '7/7': 'Mo-Su', // I guess that it means that 1430 '7 days': 'Mo-Su', 1431 '7 days a week': 'Mo-Su', 1432 'midnight': '00:00', 1433 holiday: 'PH', 1434 holidays: 'PH', 1420 '→': '-', 1421 '−': '-', 1422 '=': '-', 1423 'ー': '-', 1424 'to': '-', 1425 'до': '-', 1426 'a': '-', // language unknown 1427 'as': '-', // language unknown 1428 'á': '-', // language unknown 1429 'ás': '-', // language unknown 1430 'às': '-', // language unknown 1431 'ate': '-', // language unknown 1432 'till': '-', 1433 'til': '-', 1434 'until': '-', 1435 'through': '-', 1436 'and': ',', 1437 '&': ',', 1438 // '/': ',', // Can not be corrected as / is a valid token 1439 ':': ':', 1440 '°°': ':00', 1441 'always': '24/7', 1442 'nonstop': '24/7', 1443 '24x7': '24/7', 1444 'anytime': '24/7', 1445 'all day': '24/7', 1446 'daily': 'Mo-Su', 1447 'everyday': 'Mo-Su', 1448 'every day': 'Mo-Su', 1449 'all days': 'Mo-Su', 1450 '7j/7': 'Mo-Su', // I guess that it means that 1451 '7/7': 'Mo-Su', // I guess that it means that 1452 /* {{{ 1453 * Fixing this causes to ignore the following warning: "There should be no 1454 * reason to differ more than 6 days from a constrained 1455 * weekdays. If so tell us …". 1456 * The following mistake is expected to occur more often. 1457 */ 1458 '7days': 'Mo-Su', 1459 '7 days': 'Mo-Su', 1460 // }}} 1461 '7 days a week': 'Mo-Su', 1462 '7 days/week': 'Mo-Su', 1463 '24 hours 7 days a week': '24/7', 1464 '24 hours': '00:00-24:00', 1465 'midday': '12:00', 1466 'midnight': '00:00', 1467 'holiday': 'PH', 1468 'holidays': 'PH', 1435 1469 'public holidays': 'PH', 1436 'public holiday': 'PH', 1470 'public holiday': 'PH', 1471 'day after public holiday': 'PH +1 day', 1472 'one day after public holiday': 'PH +1 day', 1473 'day before public holiday': 'PH -1 day', 1474 'one day before public holiday': 'PH -1 day', 1475 'school holiday': 'SH', 1476 'school holidays': 'SH', 1437 1477 // summerholiday: 'SH', 1438 1478 // summerholidays: 'SH', 1439 weekend: 'Sa,Su', 1440 weekends: 'Sa,Su', 1441 'daylight': 'sunrise-sunset', 1442 'оff': 'off', // Russian o 1479 /* Not implemented {{{ */ 1480 // 'day after school holiday': 'SH +1 day', 1481 // 'one day after school holiday': 'SH +1 day', 1482 // 'day before school holiday': 'SH -1 day', 1483 // 'one day before school holiday': 'SH -1 day', 1484 /* }}} */ 1485 'weekend': 'Sa,Su', 1486 'weekends': 'Sa,Su', 1487 'daylight': 'sunrise-sunset', 1488 }, 'Please use notation "<ok>" for "<ko>". Those characters look very similar but are not the same!': { 1489 'оff': 'off', // Russian o 1443 1490 }, 'Please use time format in 24 hours notation ("<ko>"). If PM is used you might have to convert the hours to the 24 hours notation.': { 1444 1491 'pm': '', … … 1449 1496 'uhr': '', 1450 1497 'geöffnet': '', 1451 }, 'Bitte verzichte auf "<ko>". Sie möchten eventuell eine Öffnungszeit ohne vorgegebenes Ende angeben. Beispiel: "12:00+"': { 1452 ab: '', 1453 von: '', 1498 'zwischen': '', 1499 }, 'Bitte verzichte auf "<ko>". Sie möchten eventuell eine Öffnungszeit ohne vorgegebenes Ende (Open End) angeben. Beispiel: "12:00+"': { 1500 'ab': '', 1501 'von': '', 1502 }, 'Es sieht so aus also möchten Sie zusätzliche Einschränkungen für eine Öffnungszeit geben. Falls sich dies nicht mit der Syntax ausdrücken lässt können Kommentare verwendet werden. Zusätzlich sollte eventuell das Schlüsselwort `open` benutzt werden. Bitte probiere "<ok>" für "<ko>".': { 1503 'damen': 'open "Damen"', 1504 'herren': 'open "Herren"', 1454 1505 }, 'Bitte benutze die Schreibweise "<ok>" für "<ko>".': { 1455 bis: '-', 1456 'täglich': 'Mo-Su', 1506 'bis': '-', 1507 'täglich': 'Mo-Su', 1508 'schulferien': 'SH', 1509 'sonn-/feiertag': 'PH,Su', 1510 'sonn-/feiertags': 'PH,Su', 1511 'an sonn- und feiertagen': 'PH,Su', 1512 'nur sonn-/feiertags': 'PH,Su', 1513 }, 'Bitte benutze die Schreibweise "<ok>" für "<ko>". Es ist war typografisch korrekt aber laut der Spezifikation für opening_hours nicht erlaubt. Siehe auch: http://wiki.openstreetmap.org/wiki/DE:Key:opening_hours:specification.': { 1514 '„': '"', 1515 '“': '"', 1516 '”': '"', 1517 }, 'Please use notation "<ok>" for "<ko>". The used quote signs might be typographically correct but are not defined in the specification. See http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification.': { 1518 '«': '"', 1519 '»': '"', 1520 '‚': '"', 1521 '‘': '"', 1522 '’': '"', 1523 '「': '"', 1524 '」': '"', 1525 '『': '"', 1526 '』': '"', 1527 }, 'Please use notation "<ok>" for "<ko>". The used quote signs are not defined in the specification. See http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification.': { 1528 "'": '"', 1529 }, 'You might want to use comments instead of brackets (which are not valid in this context). If you do, replace "<ok>" with "<ko>".': { 1530 // '(': '"', 1531 // ')': '"', 1457 1532 }, 'Bitte benutze die Schreibweise "<ok>" als Ersatz für "und" bzw. "u.".': { 1458 und: ',',1459 u: ',',1533 'und': ',', 1534 'u': ',', 1460 1535 }, 'Bitte benutze die englische Abkürzung "<ok>" für "<ko>".': { 1461 feiertag: 'PH',1462 feiertags: 'PH',1463 feiertage: 'PH',1464 feiertagen: 'PH'1536 'feiertag': 'PH', 1537 'feiertags': 'PH', 1538 'feiertage': 'PH', 1539 'feiertagen': 'PH' 1465 1540 }, 'S\'il vous plaît utiliser "<ok>" pour "<ko>".': { 1466 'fermé': 'off',1467 'et': ',',1468 'à': '-',1541 'fermé': 'off', 1542 'et': ',', 1543 'à': '-', 1469 1544 'jours fériés': 'PH', 1470 }, 'Neem de engelse afkorting "<ok>" voor "<ko>" alstublieft.': {1471 feestdag: 'PH',1472 feestdagen: 'PH',1473 1545 } 1474 }, 1475 1476 month: { 1546 }, /* }}} */ 1547 1548 month: { /* {{{ */ 1477 1549 'default': { 1478 jan: 0,1479 feb: 1,1480 mar: 2,1481 apr: 3,1482 may: 4,1483 jun: 5,1484 jul: 6,1485 aug: 7,1486 sep: 8,1487 oct: 9,1488 nov: 10,1489 dec: 11,1550 'jan': 0, 1551 'feb': 1, 1552 'mar': 2, 1553 'apr': 3, 1554 'may': 4, 1555 'jun': 5, 1556 'jul': 6, 1557 'aug': 7, 1558 'sep': 8, 1559 'oct': 9, 1560 'nov': 10, 1561 'dec': 11, 1490 1562 }, 'Please use the English abbreviation "<ok>" for "<ko>".': { 1491 1563 'jänner': 0, // Austria 1492 january: 0,1493 february: 1,1494 march: 2,1495 april: 3,1496 // may: 4,1497 june: 5,1498 july: 6,1499 august: 7,1500 september: 8,1501 sept: 8,1502 october: 9,1503 november: 10,1504 december: 11,1564 'january': 0, 1565 'february': 1, 1566 'march': 2, 1567 'april': 3, 1568 // 'may': 4, 1569 'june': 5, 1570 'july': 6, 1571 'august': 7, 1572 'september': 8, 1573 'sept': 8, 1574 'october': 9, 1575 'november': 10, 1576 'december': 11, 1505 1577 }, 'Bitte benutze die englische Abkürzung "<ok>" für "<ko>".': { 1506 januar: 0,1507 februar: 1,1578 'januar': 0, 1579 'februar': 1, 1508 1580 'märz': 2, 1509 maerz: 2,1510 mai: 4,1511 juni: 5,1512 juli: 6,1513 okt: 9,1514 oktober: 9,1515 dez: 11,1516 dezember: 11,1581 'maerz': 2, 1582 'mai': 4, 1583 'juni': 5, 1584 'juli': 6, 1585 'okt': 9, 1586 'oktober': 9, 1587 'dez': 11, 1588 'dezember': 11, 1517 1589 }, 'S\'il vous plaît utiliser l\'abréviation "<ok>" pour "<ko>".': { 1518 janvier: 0,1519 février: 1,1520 fév: 1,1521 mars: 2,1522 avril: 3,1523 avr: 3,1524 mai: 4,1525 juin: 5,1526 juillet: 6,1527 août: 7,1528 aoû: 7,1529 septembre: 8,1530 octobre: 9,1531 novembre: 10,1532 décembre: 11,1590 'janvier': 0, 1591 'février': 1, 1592 'fév': 1, 1593 'mars': 2, 1594 'avril': 3, 1595 'avr': 3, 1596 'mai': 4, 1597 'juin': 5, 1598 'juillet': 6, 1599 'août': 7, 1600 'aoû': 7, 1601 'septembre': 8, 1602 'octobre': 9, 1603 'novembre': 10, 1604 'décembre': 11, 1533 1605 }, 'Neem de engelse afkorting "<ok>" voor "<ko>" alstublieft.': { 1534 januari: 0,1535 februari: 1,1536 maart: 2,1537 mei: 4,1538 augustus: 7,1606 'januari': 0, 1607 'februari': 1, 1608 'maart': 2, 1609 'mei': 4, 1610 'augustus': 7, 1539 1611 } 1612 }, /* }}} */ 1613 1614 calcday: { 1615 'default': { 1616 'day': 'day', 1617 'days': 'days', 1618 }, 1540 1619 }, 1541 1620 1542 weekday: { // good source: http://www.omniglot.com/language/time/days.htm1621 weekday: { // {{{ Good source: http://www.omniglot.com/language/time/days.htm */ 1543 1622 'default': { 1544 su: 0,1545 mo: 1,1546 tu: 2,1547 we: 3,1548 th: 4,1549 fr: 5,1550 sa: 6,1623 'su': 0, 1624 'mo': 1, 1625 'tu': 2, 1626 'we': 3, 1627 'th': 4, 1628 'fr': 5, 1629 'sa': 6, 1551 1630 }, 'Assuming "<ok>" for "<ko>"': { 1552 m: 1,1553 w: 3,1554 f: 5,1631 'm': 1, 1632 'w': 3, 1633 'f': 5, 1555 1634 }, 'Please use the abbreviation "<ok>" for "<ko>".': { 1556 sun: 0, 1557 sunday: 0, 1558 sundays: 0, 1559 mon: 1, 1560 monday: 1, 1561 mondays: 1, 1562 tue: 2, 1563 tuesday: 2, 1564 tuesdays: 2, 1565 wed: 3, 1566 wednesday: 3, 1567 wednesdays: 3, 1568 thu: 4, 1569 thur: 4, 1570 thursday: 4, 1571 thursdays: 4, 1572 fri: 5, 1573 friday: 5, 1574 fridays: 5, 1575 sat: 6, 1576 saturday: 6, 1577 saturdays: 6, 1635 'sun': 0, 1636 'sunday': 0, 1637 'sundays': 0, 1638 'mon': 1, 1639 'monday': 1, 1640 'mondays': 1, 1641 'tue': 2, 1642 'tues': 2, // Used here: http://www.westerhambeauty.co.uk/contact.php 1643 'tuesday': 2, 1644 'tuesdays': 2, 1645 'wed': 3, 1646 'weds': 3, 1647 'wednesday': 3, 1648 'wednesdays': 3, 1649 'thu': 4, 1650 'thur': 4, 1651 'thurs': 4, 1652 'thursday': 4, 1653 'thursdays': 4, 1654 'fri': 5, 1655 'friday': 5, 1656 'fridays': 5, 1657 'sat': 6, 1658 'saturday': 6, 1659 'saturdays': 6, 1578 1660 }, 'Bitte benutze die englische Abkürzung "<ok>" für "<ko>". Could also mean Saturday in Polish …': { 1579 so: 0,1661 'so': 0, 1580 1662 }, 'Bitte benutze die englische Abkürzung "<ok>" für "<ko>".': { 1581 son: 0,1582 sonntag: 0,1663 'son': 0, 1664 'sonntag': 0, 1583 1665 'sonn-': 0, 1584 sonntags: 0,1585 montag: 1,1586 montags: 1,1587 di: 2,1588 die: 2,1589 dienstag: 2,1590 dienstags: 2,1591 mi: 3,1592 mit: 3,1593 mittwoch: 3,1594 mittwochs: 3,1666 'sonntags': 0, 1667 'montag': 1, 1668 'montags': 1, 1669 'di': 2, 1670 'die': 2, 1671 'dienstag': 2, 1672 'dienstags': 2, 1673 'mi': 3, 1674 'mit': 3, 1675 'mittwoch': 3, 1676 'mittwochs': 3, 1595 1677 'do': 4, 1596 don: 4,1597 donnerstag: 4,1598 donnerstags: 4,1599 fre: 5,1600 freitag: 5,1601 freitags: 5,1602 sam: 6,1603 samstag: 6,1604 samstags: 6,1678 'don': 4, 1679 'donnerstag': 4, 1680 'donnerstags': 4, 1681 'fre': 5, 1682 'freitag': 5, 1683 'freitags': 5, 1684 'sam': 6, 1685 'samstag': 6, 1686 'samstags': 6, 1605 1687 }, 'S\'il vous plaît utiliser l\'abréviation "<ok>" pour "<ko>".': { 1606 dim: 0,1607 dimanche: 0,1608 lu: 1,1609 lun: 1,1610 lundi: 1,1611 mardi: 2,1612 mer: 3,1613 mercredi: 3,1614 je: 4,1615 jeu: 4,1616 jeudi: 4,1617 ve: 5,1618 ven: 5,1619 vendredi: 5,1620 samedi: 6,1688 'dim': 0, 1689 'dimanche': 0, 1690 'lu': 1, 1691 'lun': 1, 1692 'lundi': 1, 1693 'mardi': 2, 1694 'mer': 3, 1695 'mercredi': 3, 1696 'je': 4, 1697 'jeu': 4, 1698 'jeudi': 4, 1699 've': 5, 1700 'ven': 5, 1701 'vendredi': 5, 1702 'samedi': 6, 1621 1703 }, 'Neem de engelse afkorting "<ok>" voor "<ko>" alstublieft.': { 1622 zo: 0,1623 zon: 0,1624 zontag: 0, // correct?1625 zondag: 0,1626 maandag: 1,1627 din: 2,1628 dinsdag: 2,1629 wo: 3,1630 woe: 3,1631 woensdag: 3,1632 donderdag: 4,1633 vr: 5,1634 vri: 5,1635 vrijdag: 5,1636 za: 6,1637 zat: 6,1638 zaterdag: 6,1704 'zo': 0, 1705 'zon': 0, 1706 'zontag': 0, // correct? 1707 'zondag': 0, 1708 'maandag': 1, 1709 'din': 2, 1710 'dinsdag': 2, 1711 'wo': 3, 1712 'woe': 3, 1713 'woensdag': 3, 1714 'donderdag': 4, 1715 'vr': 5, 1716 'vri': 5, 1717 'vrijdag': 5, 1718 'za': 6, 1719 'zat': 6, 1720 'zaterdag': 6, 1639 1721 }, 'Please use the English abbreviation "<ok>" for "<ko>".': { // FIXME: Translate to Czech. 1640 1722 'neděle': 0, … … 1651 1733 'pá': 5, 1652 1734 'sobota': 6, 1653 }, 'Please use the English abbreviation "<ok>" for "<ko>".': { 1654 // Spanish. 1735 }, 'Please use the English abbreviation "<ok>" (Spanish) for "<ko>".': { 1655 1736 'martes': 0, 1656 1737 'miércoles': 1, … … 1660 1741 'domingo': 5, 1661 1742 'lunes': 6, 1662 // Indonesian.1743 }, 'Please use the English abbreviation "<ok>" (Indonesian) for "<ko>".': { 1663 1744 'selasa': 0, 1664 1745 'rabu': 1, … … 1668 1749 'minggu': 5, 1669 1750 'senin': 6, 1670 // Swedish1751 }, 'Please use the English abbreviation "<ok>" (Swedish) for "<ko>".': { 1671 1752 'söndag': 0, 1672 1753 'söndagar': 0, … … 1674 1755 'ma': 1, 1675 1756 'tisdag': 2, 1676 'onsdag': 3, 1757 'onsdag': 3, // Same in Danish 1677 1758 'torsdag': 4, 1678 1759 'fredag': 5, 1679 1760 'lördag': 6, 1680 1761 'lördagar': 6, 1681 // Polish1762 }, 'Please use the English abbreviation "<ok>" (Polish) for "<ko>".': { 1682 1763 'niedziela': 0, 'niedz': 0, 'n': 0, 'ndz': 0, 1683 1764 'poniedziałek': 1, 'poniedzialek': 1, 'pon': 1, 'pn': 1, … … 1687 1768 'piątek': 5, 'piatek': 5, 'pt': 5, 1688 1769 'sobota': 6, 'sob': 6, // 'so': 6 // abbreviation also used in German 1689 // Russian1770 }, 'Please use the English abbreviation "<ok>" (Russian) for "<ko>".': { 1690 1771 'воскресенье' : 0, 1691 1772 'Вс' : 0, … … 1704 1785 'суббота' : 6, 1705 1786 'subbota' : 6, 1706 // Danish1787 }, 'Please use the English abbreviation "<ok>" (Danish) for "<ko>".': { 1707 1788 'søndag' : 0, 1708 1789 'mandag' : 1, 1709 1790 'tirsdag': 2, 1710 'onsdag' : 3, 1791 'onsdag' : 3, // Same in Swedish 1711 1792 'torsdag': 4, 1712 1793 'fredag' : 5, 1713 1794 'lørdag' : 6, 1714 1795 }, 1715 }, 1716 1717 timevar: { / / Special time variables which actual value depends on the date and the position of the facility.1796 }, /* }}} */ 1797 1798 timevar: { /* {{{ Special time variables which actual value depends on the date and the position of the facility. */ 1718 1799 'default': { 1719 1800 'sunrise': 'sunrise', … … 1729 1810 'sonnenuntergang': 'sunset', 1730 1811 }, 1731 }, 1812 }, /* }}} */ 1732 1813 1733 1814 'event': { // variable events … … 1755 1836 return function(value, nominatiomJSON, oh_mode) { 1756 1837 // short constants {{{ 1757 var word_value_replacement = { // if the correct values can not be calculated1838 var word_value_replacement = { // If the correct values can not be calculated. 1758 1839 dawn : 60 * 5 + 30, 1759 1840 sunrise : 60 * 6, … … 1761 1842 dusk : 60 * 18 + 30, 1762 1843 }; 1763 var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];1844 var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; 1764 1845 var weekdays = ['Su','Mo','Tu','We','Th','Fr','Sa']; 1765 1846 var default_prettify_conf = { 1766 'leading_zero_hour': true, // enforce leading zero 1847 // Update README.md if changed. 1848 'zero_pad_hour': true, // enforce ("%02d", hour) 1767 1849 'one_zero_if_hour_zero': false, // only one zero "0" if hour is zero "0" 1768 'leave_off_closed': true, // leave keywords of and closedas is1850 'leave_off_closed': true, // leave keywords "off" and "closed" as is 1769 1851 'keyword_for_off_closed': 'off', // use given keyword instead of "off" or "closed" 1770 ' block_sep_string': ' ', // separate blocks by string1771 'print_semicolon': true, // print token which separates normal blocks1852 'rule_sep_string': ' ', // separate rules by string 1853 'print_semicolon': true, // print token which separates normal rules 1772 1854 'leave_weekday_sep_one_day_betw': true, // use the separator (either "," or "-" which is used to separate days which follow to each other like Sa,Su or Su-Mo 1773 'sep_one_day_between': ',' // separator which should be used 1855 'sep_one_day_between': ',', // separator which should be used 1856 'zero_pad_month_and_week_numbers': false, // Format week (e.g. `week 01`) and month day numbers (e.g. `Jan 01`) with "%02d". 1774 1857 }; 1775 1858 … … 1777 1860 var msec_in_day = 1000 * 60 * minutes_in_day; 1778 1861 var msec_in_week = msec_in_day * 7; 1779 // }}} 1780 1781 // The big picture -- How does this library work? {{{ 1782 //====================================================================== 1783 // Constructor - entry to parsing code 1784 //====================================================================== 1785 // Terminology: 1786 // 1787 // Mo-Fr 10:00-11:00; Th 10:00-12:00 1788 // \_____block_____/ \____block___/ 1789 // 1790 // The README refers to blocks as rules, which is more intuitive but less clear. 1791 // Because of that only the README uses the term rule in that context. 1792 // In all internal parts of this project, the term block is used. 1793 // 1794 // Mo-Fr Jan 10:00-11:00 1795 // \__/ \_/ \_________/ 1796 // selectors (left to right: weekday, month, time) 1797 // 1798 // Logic: 1799 // - Tokenize 1800 // Foreach block: 1801 // - Run top-level (block) parser 1802 // - Which calls sub parser for specific selector types 1803 // - Which produce selector functions 1862 1863 var library_name = 'opening_hours.js'; 1864 var repository_url = 'https://github.com/ypid/' + library_name; 1865 var issues_url = repository_url + '/issues?state=open'; 1804 1866 // }}} 1805 1867 … … 1827 1889 if (typeof oh_mode == 'undefined') { 1828 1890 oh_mode = 0; 1829 } else if (!(typeof oh_mode == 'number' && (oh_mode == 0 || oh_mode == 1 || oh_mode == 2))) {1830 throw 'The third constructor parameter is oh_mode and must be a number (0, 1 or 2)' 1891 } else if (!(typeof oh_mode == 'number' && (oh_mode === 0 || oh_mode == 1 || oh_mode == 2))) { 1892 throw 'The third constructor parameter is oh_mode and must be a number (0, 1 or 2)'; 1831 1893 } 1832 1894 // }}} 1833 1895 1834 // put tokenized blocks into list{{{1835 if (value.match(/^( \s*;?\s*)+$/))1896 // Tokenize value and generate selector functions. {{{ 1897 if (value.match(/^(?:\s*;?\s*)+$/)) 1836 1898 throw 'Value contains nothing meaningful which can be parsed'; 1837 1899 1838 var parsing_warnings = []; 1900 var parsing_warnings = []; // Elements are fed into function formatWarnErrorMessage(nrule, at, message) 1839 1901 var done_with_warnings = false; // The functions which throw warnings can be called multiple times. 1840 var has_token = {}; 1902 var done_with_selector_reordering = false; 1903 var done_with_selector_reordering_warnings = false; 1841 1904 var tokens = tokenize(value); 1842 // console.log(JSON.stringify(tokens, null, ' \t'));1905 // console.log(JSON.stringify(tokens, null, ' ')); 1843 1906 var prettified_value = ''; 1844 var used_subparsers = {}; // Used sub parsers for one block, will be reset for each block. Declared in global namespace, because it is manipulation inside various sub parsers.1845 1907 var week_stable = true; 1846 1908 1847 var blocks = []; 1848 1849 for (var nblock = 0; nblock < tokens.length; nblock++) { 1850 if (tokens[nblock][0].length == 0) continue; 1851 // Block does contain nothing useful e.g. second block of '10:00-12:00;' (empty) which needs to be handled. 1909 var rules = []; 1910 var new_tokens = []; 1911 1912 for (var nrule = 0; nrule < tokens.length; nrule++) { 1913 if (tokens[nrule][0].length === 0) { 1914 // Rule does contain nothing useful e.g. second rule of '10:00-12:00;' (empty) which needs to be handled. 1915 parsing_warnings.push([nrule, -1, 1916 'This rule does not contain anything useful. Please remove this empty rule.' 1917 + (nrule == tokens.length - 1 && nrule > 0 && !tokens[nrule][1] ? 1918 ' Might it be possible that you are a programmer and adding a semicolon after each statement is hardwired in your muscle memory ;) ?' 1919 + ' The thing is that the semicolon in the opening_hours syntax is defined as rule separator.' 1920 + ' So for compatibility reasons you should omit this last semicolon.': '') 1921 ]); 1922 continue; 1923 } 1852 1924 1853 1925 var continue_at = 0; 1926 var next_rule_is_additional = false; 1854 1927 do { 1855 if (continue_at == tokens[n block][0].length) break;1856 // Additional block does contain nothing useful e.g. second blockof '10:00-12:00,' (empty) which needs to be handled.1928 if (continue_at == tokens[nrule][0].length) break; 1929 // Additional rule does contain nothing useful e.g. second rule of '10:00-12:00,' (empty) which needs to be handled. 1857 1930 1858 1931 var selectors = { … … 1874 1947 date: [], 1875 1948 1876 fallback: tokens[n block][1],1949 fallback: tokens[nrule][1], 1877 1950 additional: continue_at ? true : false, 1878 1951 meaning: true, 1879 1952 unknown: false, 1880 1953 comment: undefined, 1881 build_from_token_ block: undefined,1954 build_from_token_rule: undefined, 1882 1955 }; 1883 1956 1884 selectors.build_from_token_ block = [ nblock, continue_at];1885 continue_at = parseGroup(tokens[n block][0], continue_at, selectors, nblock);1886 if (typeof continue_at == 'object') 1957 selectors.build_from_token_rule = [ nrule, continue_at, new_tokens.length ]; 1958 continue_at = parseGroup(tokens[nrule][0], continue_at, selectors, nrule); 1959 if (typeof continue_at == 'object') { 1887 1960 continue_at = continue_at[0]; 1888 else1961 } else { 1889 1962 continue_at = 0; 1963 } 1964 1965 // console.log('Current tokens: ' + JSON.stringify(tokens[nrule], null, ' ')); 1966 1967 new_tokens.push( 1968 [ 1969 tokens[nrule][0].slice( 1970 selectors.build_from_token_rule[1], 1971 continue_at === 0 1972 ? tokens[nrule][0].length 1973 : continue_at 1974 ), 1975 tokens[nrule][1], 1976 tokens[nrule][2], 1977 ] 1978 ); 1979 1980 if (next_rule_is_additional && new_tokens.length > 1) { 1981 // Move 'rule separator' from last token of last rule to first token of this rule. 1982 new_tokens[new_tokens.length - 1][0].unshift(new_tokens[new_tokens.length - 2][0].pop()); 1983 } 1984 1985 next_rule_is_additional = continue_at === 0 ? false : true; 1890 1986 1891 1987 if (selectors.year.length > 0) … … 1903 1999 1904 2000 // console.log('weekday: ' + JSON.stringify(selectors.weekday, null, '\t')); 1905 blocks.push(selectors);2001 rules.push(selectors); 1906 2002 1907 2003 // This handles selectors with time ranges wrapping over midnight (e.g. 10:00-02:00) 1908 // it generates wrappers for all selectors and creates a new block.2004 // it generates wrappers for all selectors and creates a new rule. 1909 2005 if (selectors.wraptime.length > 0) { 1910 2006 var wrapselectors = { … … 1917 2013 1918 2014 wrapped: true, 2015 // build_from_token_rule: selectors.build_from_token_rule, 2016 // Not (yet) needed. 1919 2017 }; 1920 2018 … … 1928 2026 } 1929 2027 1930 blocks.push(wrapselectors);2028 rules.push(wrapselectors); 1931 2029 } 1932 } while (continue_at) 2030 } while (continue_at); 2031 } 2032 // console.log(JSON.stringify(tokens, null, ' ')); 2033 // console.log(JSON.stringify(new_tokens, null, ' ')); 2034 // }}} 2035 2036 /* Format warning or error message for the user. {{{ 2037 * 2038 * :param nrule: Rule number starting with zero. 2039 * :param at: Token position at which the issue occurred. 2040 * :param message: Human readable string with the message. 2041 * :returns: String with position of the warning or error marked for the user. 2042 */ 2043 function formatWarnErrorMessage(nrule, at, message) { 2044 // FIXME: Change to new_tokens. 2045 if (typeof nrule == 'number') { 2046 var pos = 0; 2047 if (nrule == -1) { // Usage of rule index not required because we do have access to value.length. 2048 pos = value.length - at; 2049 } else { // Issue accrued at a later time, position in string needs to be reconstructed. 2050 if (typeof tokens[nrule][0][at] == 'undefined') { 2051 if (typeof tokens[nrule][0] && at == -1) { 2052 pos = value.length; 2053 if (typeof tokens[nrule+1] == 'object' && typeof tokens[nrule+1][2] == 'number') { 2054 pos -= tokens[nrule+1][2]; 2055 } else if (typeof tokens[nrule][2] == 'number') { 2056 pos -= tokens[nrule][2]; 2057 } 2058 } else { 2059 // Given position is invalid. 2060 // 2061 formatLibraryBugMessage('Bug in warning generation code which could not determine the exact position of the warning or error in value.'); 2062 pos = value.length; 2063 if (typeof tokens[nrule][2] != 'undefined') { 2064 // Fallback: Point to last token in the rule which caused the problem. 2065 // Run real_test regularly to fix the problem before a user is confronted with it. 2066 pos -= tokens[nrule][2]; 2067 console.warn('Last token for rule: ' + tokens[nrule]); 2068 console.log(value.substring(0, pos) + ' <--- (' + message + ')'); 2069 console.log('\n'); 2070 } { 2071 console.warn('tokens[nrule][2] is undefined. This is ok if nrule is the last rule.'); 2072 } 2073 } 2074 } else { 2075 pos = value.length; 2076 if (typeof tokens[nrule][0][at+1] != 'undefined') { 2077 pos -= tokens[nrule][0][at+1][2]; 2078 } else if (typeof tokens[nrule][2] != 'undefined') { 2079 pos -= tokens[nrule][2]; 2080 } 2081 } 2082 } 2083 return value.substring(0, pos) + ' <--- (' + message + ')'; 2084 } else if (typeof nrule == 'string') { 2085 return nrule.substring(0, at) + ' <--- (' + message + ')'; 2086 } 1933 2087 } 1934 2088 // }}} 1935 2089 1936 /* Tokenization function: Splits string into parts. {{{ 2090 /* Format internal library error message. {{{ 2091 * 2092 * :param message: Human readable string with the error message. 2093 * :returns: Error message for the user. 2094 */ 2095 function formatLibraryBugMessage(message) { 2096 if (typeof message == 'undefined') 2097 message = ''; 2098 else 2099 message = ' ' + message; 2100 2101 message = 'An error occurred during evaluation of the value "' + value + '".' 2102 + ' Please file a bug report here: ' + issues_url + '.' 2103 + message; 2104 console.log(message); 2105 return message; 2106 } 2107 // }}} 2108 2109 /* Tokenize input stream. {{{ 1937 2110 * 1938 2111 * :param value: Raw opening_hours value. 1939 * :returns: Tokenized list object. Complex structure. You can print the 1940 * thing as JSON if you would like to know in details. 1941 * The most inner list has the following items: [ internal_value, token_name, value_length ]. 2112 * :returns: Tokenized list object. Complex structure. Check the 2113 * internal documentation in the doc directory for details. 1942 2114 */ 1943 2115 function tokenize(value) { 1944 var all_tokens = new Array(); 1945 var curr_block_tokens = new Array(); 1946 1947 var last_block_fallback_terminated = false; 1948 1949 while (value != '') { 2116 var all_tokens = []; 2117 var curr_rule_tokens = []; 2118 2119 var last_rule_fallback_terminated = false; 2120 2121 while (value !== '') { 2122 // console.log("Parsing value: " + value); 1950 2123 var tmp; 1951 if (tmp = value.match(/^ (?:week\b|open\b|unknown\b)/i)) {2124 if (tmp = value.match(/^week\b/i)) { 1952 2125 // Reserved keywords. 1953 curr_block_tokens.push([tmp[0].toLowerCase(), tmp[0].toLowerCase(), value.length ]); 2126 curr_rule_tokens.push([tmp[0].toLowerCase(), tmp[0].toLowerCase(), value.length ]); 2127 value = value.substr(tmp[0].length); 2128 } else if (tmp = value.match(/^(?:off\b|closed\b|open\b|unknown\b)/i)) { 2129 // Reserved keywords. 2130 curr_rule_tokens.push([tmp[0].toLowerCase(), 'state', value.length ]); 1954 2131 value = value.substr(tmp[0].length); 1955 2132 } else if (tmp = value.match(/^24\/7/i)) { 1956 2133 // Reserved keyword. 1957 has_token[tmp[0]] = true; 1958 curr_block_tokens.push([tmp[0], tmp[0], value.length ]); 1959 value = value.substr(tmp[0].length); 1960 } else if (tmp = value.match(/^(?:off|closed)/i)) { 1961 // Reserved keywords. 1962 curr_block_tokens.push([tmp[0].toLowerCase(), 'closed', value.length ]); 2134 curr_rule_tokens.push([tmp[0], tmp[0], value.length ]); 1963 2135 value = value.substr(tmp[0].length); 1964 2136 } else if (tmp = value.match(/^(?:PH|SH)/i)) { 1965 2137 // special day name (holidays) 1966 curr_ block_tokens.push([tmp[0].toUpperCase(), 'holiday', value.length ]);2138 curr_rule_tokens.push([tmp[0].toUpperCase(), 'holiday', value.length ]); 1967 2139 value = value.substr(2); 1968 } else if (tmp = value.match(/^days?/i)) { 1969 curr_block_tokens.push([tmp[0].toLowerCase(), 'calcday', value.length ]); 1970 value = value.substr(tmp[0].length); 1971 } else if (tmp = value.match(/^(&|_|→|–|−|=|opening_hours=|ー|\?|~|~|:|°°|25x7|7[ ]?days( a week|)|all days?|every day|-late|public holidays?|7j?\/7|every day|до|рм|ам|jours fériés|sonn-|[a-zäößàáéøčěíúýřПнВсо]+\b)\.?/i)) { 1972 // Handle all remaining words with error tolerance. 2140 } else if (tmp = value.match(/^(&|_|→|–|−|=|·|opening_hours=|ー|\?|~|~|:|°°|24x7|24 hours 7 days a week|24 hours|7 ?days(?:(?: a |\/)week)?|7j?\/7|all days?|every day|-?(?:(?:till? )?late|open[ ]?end)|(?:(?:one )?day (?:before|after) )?(?:school|public) holidays?|days?\b|до|рм|ам|jours fériés|on work days?|(?:nur |an )?sonn-(?:(?: und |\/)feiertag(?:s|en))?|[a-zäößàáéøčěíúýřПнВсо]+\b|à|á|mo|tu|we|th|fr|sa|su|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.?/i)) { 2141 /* Handle all remaining words and specific other characters with error tolerance. 2142 * 2143 * à|á: Word boundary does not work with unicode chars: 'test à test'.match(/\bà\b/i) 2144 * https://stackoverflow.com/questions/10590098/javascript-regexp-word-boundaries-unicode-characters 2145 * Order in the regular expression capturing group is important in some cases. 2146 * 2147 * mo|tu|we|th|fr|sa|su|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec: Prefer defended keywords 2148 * if used in cases like 'mo12:00-14:00' (when keyword is followed by number). 2149 */ 1973 2150 var correct_val = returnCorrectWordOrToken(tmp[1].toLowerCase(), value.length); 2151 // console.log('Error tolerance for string "' + tmp[1] + '" returned "' + correct_val + '".'); 1974 2152 if (typeof correct_val == 'object') { 1975 curr_ block_tokens.push([ correct_val[0], correct_val[1], value.length ]);2153 curr_rule_tokens.push([ correct_val[0], correct_val[1], value.length ]); 1976 2154 value = value.substr(tmp[0].length); 1977 2155 } else if (typeof correct_val == 'string') { 1978 2156 if (tmp[1].toLowerCase() == 'pm') { 1979 var hours_token_at = curr_block_tokens.length - 3; 1980 if (hours_token_at > 0) { 1981 if (matchTokens(curr_block_tokens, hours_token_at, 1982 'number', 'timesep', 'number') 1983 ) { 1984 var hours_token = curr_block_tokens[hours_token_at]; 1985 } else if (matchTokens(curr_block_tokens, hours_token_at + 2, 1986 'number') 1987 ) { 1988 hours_token_at += 2; 1989 var hours_token = curr_block_tokens[hours_token_at]; 2157 var hours_token_at = curr_rule_tokens.length - 1; 2158 var hours_token; 2159 if (hours_token_at >= 0) { 2160 if (hours_token_at -2 >= 0 && 2161 matchTokens( 2162 curr_rule_tokens, hours_token_at - 2, 2163 'number', 'timesep', 'number' 2164 ) 2165 ) { 2166 hours_token_at -= 2; 2167 hours_token = curr_rule_tokens[hours_token_at]; 2168 } else if (matchTokens(curr_rule_tokens, hours_token_at, 'number')) { 2169 hours_token = curr_rule_tokens[hours_token_at]; 1990 2170 } 1991 if (hours_token[0] <= 12) { 2171 2172 if (typeof hours_token == 'object' && hours_token[0] <= 12) { 1992 2173 hours_token[0] += 12; 1993 curr_ block_tokens[hours_token_at] = hours_token;2174 curr_rule_tokens[hours_token_at] = hours_token; 1994 2175 } 1995 2176 } 1996 2177 } 1997 value = correct_val + value.substr(tmp[0].length); 2178 var correct_tokens = tokenize(correct_val)[0]; 2179 if (correct_tokens[1] === true) { // last_rule_fallback_terminated 2180 throw formatLibraryBugMessage(); 2181 } 2182 for (var i = 0; i < correct_tokens[0].length; i++) { 2183 curr_rule_tokens.push([correct_tokens[0][i][0], correct_tokens[0][i][1], value.length]); 2184 // value.length - tmp[0].length does not have the desired effect for all test cases. 2185 } 2186 2187 value = value.substr(tmp[0].length); 2188 // value = correct_val + value.substr(tmp[0].length); 2189 // Does not work because it would generate the wrong length for formatWarnErrorMessage. 1998 2190 } else { 1999 2191 // other single-character tokens 2000 curr_ block_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length - 1 ]);2192 curr_rule_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length - 1 ]); 2001 2193 value = value.substr(1); 2002 2194 } 2003 2195 } else if (tmp = value.match(/^\d+/)) { 2004 2196 // number 2005 if (tmp[0] > 1900) // Assumed to be a year number. 2006 curr_block_tokens.push([tmp[0], 'year', value.length ]); 2007 else 2008 curr_block_tokens.push([+tmp[0], 'number', value.length ]); 2197 if (Number(tmp[0]) > 1900) { // Assumed to be a year number. 2198 curr_rule_tokens.push([tmp[0], 'year', value.length ]); 2199 if (Number(tmp[0]) >= 2100) // Probably an error 2200 parsing_warnings.push([ -1, value.length - 1, 2201 'The number ' + Number(tmp[0]) + ' will be interpreted as year.' 2202 + ' This is probably not intended. Times can be specified as "12:00".' 2203 ]); 2204 } else { 2205 curr_rule_tokens.push([Number(tmp[0]), 'number', value.length ]); 2206 } 2207 2009 2208 value = value.substr(tmp[0].length); 2010 } else if (tmp = value.match(/^"([^"]*)"/)) { 2011 // comment 2012 curr_block_tokens.push([tmp[1], 'comment', value.length ]); 2209 } else if (tmp = value.match(/^"([^"]+)"/)) { 2210 // Comment following the specification. 2211 // Any character is allowed inside the comment except " itself. 2212 curr_rule_tokens.push([tmp[1], 'comment', value.length ]); 2213 value = value.substr(tmp[0].length); 2214 } else if (tmp = value.match(/^(["'„“‚‘’«「『])([^"'“”‘’»」』;|]*)(["'”“‘’»」』])/)) { 2215 // Comments with error tolerance. 2216 // The comments still have to be somewhat correct meaning 2217 // the start and end quote signs used have to be 2218 // appropriate. So “testing„ will not match as it is not a 2219 // quote but rather something unknown which the user should 2220 // fix first. 2221 // console.log('Matched: ' + JSON.stringify(tmp)); 2222 for (var pos = 1; pos <= 3; pos += 2) { 2223 // console.log('Pos: ' + pos + ', substring: ' + tmp[pos]); 2224 var correct_val = returnCorrectWordOrToken(tmp[pos], 2225 value.length - (pos == 3 ? tmp[1].length + tmp[2].length : 0) 2226 ); 2227 if (typeof correct_val != 'string' && tmp[pos] != '"') { 2228 throw formatLibraryBugMessage( 2229 'A character for error tolerance was allowed in the regular expression' 2230 + ' but is not covered by word_error_correction' 2231 + ' which is needed to format a proper message for the user.' 2232 ); 2233 } 2234 } 2235 curr_rule_tokens.push([tmp[2], 'comment', value.length ]); 2013 2236 value = value.substr(tmp[0].length); 2014 2237 } else if (value.match(/^;/)) { 2015 // semicolon terminates block2016 // next tokens belong to a new block2017 all_tokens.push([ curr_ block_tokens, last_block_fallback_terminated, value.length ]);2238 // semicolon terminates rule 2239 // next tokens belong to a new rule 2240 all_tokens.push([ curr_rule_tokens, last_rule_fallback_terminated, value.length ]); 2018 2241 value = value.substr(1); 2019 2242 2020 curr_ block_tokens = [];2021 last_ block_fallback_terminated = false;2243 curr_rule_tokens = []; 2244 last_rule_fallback_terminated = false; 2022 2245 } else if (value.match(/^\|\|/)) { 2023 // || terminates block2024 // next tokens belong to a fallback block2025 if (curr_ block_tokens.length== 0)2246 // || terminates rule 2247 // Next tokens belong to a fallback rule. 2248 if (curr_rule_tokens.length === 0) 2026 2249 throw formatWarnErrorMessage(-1, value.length - 2, 'Rule before fallback rule does not contain anything useful'); 2027 2250 2028 all_tokens.push([ curr_block_tokens, last_block_fallback_terminated, value.length ]); 2251 all_tokens.push([ curr_rule_tokens, last_rule_fallback_terminated, value.length ]); 2252 curr_rule_tokens = []; 2253 // curr_rule_tokens = [ [ '||', 'rule separator', value.length ] ]; 2254 // FIXME: Use this. Unknown bug needs to be solved in the process. 2029 2255 value = value.substr(2); 2030 2256 2031 curr_block_tokens = []; 2032 last_block_fallback_terminated = true; 2257 last_rule_fallback_terminated = true; 2033 2258 } else if (value.match(/^(?:␣|\s)/)) { 2034 // Using "␣" as space is not expected to be a normal mistake. Just ignore it to make using taginfo easier. 2259 // Using "␣" as space is not expected to be a normal 2260 // mistake. Just ignore it to make using taginfo easier. 2035 2261 value = value.substr(1); 2036 2262 } else if (tmp = value.match(/^\s+/)) { … … 2041 2267 if (value[0] == '.' && !done_with_warnings) 2042 2268 parsing_warnings.push([ -1, value.length - 1, 'Please use ":" as hour/minute-separator' ]); 2043 curr_ block_tokens.push([ ':', 'timesep', value.length ]);2269 curr_rule_tokens.push([ ':', 'timesep', value.length ]); 2044 2270 value = value.substr(1); 2045 2271 } else { 2046 2272 // other single-character tokens 2047 curr_ block_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length ]);2273 curr_rule_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length ]); 2048 2274 value = value.substr(1); 2049 2275 } 2050 2276 } 2051 2277 2052 all_tokens.push([ curr_ block_tokens, last_block_fallback_terminated ]);2278 all_tokens.push([ curr_rule_tokens, last_rule_fallback_terminated ]); 2053 2279 2054 2280 return all_tokens; … … 2064 2290 * * (valid) opening_hours sub string. 2065 2291 * * object with [ internal_value, token_name ] if value is correct. 2066 * * undefined if word could not be found (and thus not be corrected).2292 * * undefined if word could not be found (and thus is not be corrected). 2067 2293 */ 2068 2294 function returnCorrectWordOrToken(word, value_length) { … … 2089 2315 } 2090 2316 if (typeof correct_abbr == 'undefined') { 2091 throw 'Please file a bug for opening_hours.js.' 2092 + ' Including the stacktrace.' 2317 throw formatLibraryBugMessage('Including the stacktrace.'); 2093 2318 } 2094 2319 if (token_name != 'timevar') { 2095 2320 // Everything else than timevar: 2096 // E.g. 'Mo' arestart with a upper case letter.2321 // E.g. 'Mo' start with a upper case letter. 2097 2322 // It just looks better. 2098 2323 correct_abbr = correct_abbr.charAt(0).toUpperCase() … … 2118 2343 */ 2119 2344 function getWarnings(it) { 2120 if (typeof it == 'object') { // getWarnings was called in a state without critical errors. We can do extended tests. 2121 2122 // Check if 24/7 is used and it does not mean 24/7 because there are other blocks. 2345 if (!done_with_warnings && typeof it == 'object') { 2346 /* getWarnings was called in a state without critical errors. 2347 * We can do extended tests. 2348 */ 2349 2350 /* Place all tests in this function if an additional (high 2351 * level) test is added and this does not require to rewrite 2352 * big parts of (sub) selector parsers only to get the 2353 * position. If that is the case, then rather place the test 2354 * code in the (sub) selector parser function directly. 2355 */ 2356 2357 var wide_range_selectors = [ 'year', 'month', 'week', 'holiday' ]; 2358 var small_range_selectors = [ 'weekday', 'time', '24/7', 'state', 'comment']; 2359 2360 // How many times was a selector_type used per rule? {{{ 2361 var used_selectors = []; 2362 var used_selectors_types_array = []; 2363 var has_token = {}; 2364 2365 for (var nrule = 0; nrule < new_tokens.length; nrule++) { 2366 if (new_tokens[nrule][0].length === 0) continue; 2367 // Rule does contain nothing useful e.g. second rule of '10:00-12:00;' (empty) which needs to be handled. 2368 2369 var selector_start_end_type = [ 0, 0, undefined ], 2370 prettified_group_value = []; 2371 // console.log(new_tokens[nrule][0]); 2372 2373 used_selectors[nrule] = {}; 2374 used_selectors_types_array[nrule] = []; 2375 2376 do { 2377 selector_start_end_type = getSelectorRange(new_tokens[nrule][0], selector_start_end_type[1]); 2378 // console.log(selector_start_end_type, new_tokens[nrule][0].length); 2379 2380 if (selector_start_end_type[0] == selector_start_end_type[1] && 2381 new_tokens[nrule][0][selector_start_end_type[0]][0] == '24/7' 2382 ) { 2383 has_token['24/7'] = true; 2384 } 2385 2386 if (typeof used_selectors[nrule][selector_start_end_type[2]] != 'object') { 2387 used_selectors[nrule][selector_start_end_type[2]] = [ selector_start_end_type[1] ]; 2388 } else { 2389 used_selectors[nrule][selector_start_end_type[2]].push(selector_start_end_type[1]); 2390 } 2391 used_selectors_types_array[nrule].push(selector_start_end_type[2]); 2392 2393 selector_start_end_type[1]++; 2394 } while (selector_start_end_type[1] < new_tokens[nrule][0].length); 2395 } 2396 // console.log('used_selectors: ' + JSON.stringify(used_selectors, null, ' ')); 2397 // }}} 2398 2399 for (var nrule = 0; nrule < used_selectors.length; nrule++) { 2400 2401 /* Check if more than one not connected selector of the same type is used in one rule {{{ */ 2402 for (var selector_type in used_selectors[nrule]) { 2403 // console.log(selector_type + ' use at: ' + used_selectors[nrule][selector_type].length); 2404 if (used_selectors[nrule][selector_type].length > 1) { 2405 parsing_warnings.push([nrule, used_selectors[nrule][selector_type][used_selectors[nrule][selector_type].length - 1], 2406 'You have used ' + used_selectors[nrule][selector_type].length 2407 + (selector_type.match(/^(?:comment|state)/) ? 2408 ' ' + selector_type 2409 + (selector_type == 'state' ? ' keywords' : 's') 2410 + ' in one rule.' 2411 + ' You may only use one in one rule.' 2412 : 2413 ' not connected ' + selector_type 2414 + (selector_type.match(/^(?:month|weekday)$/) ? 's' : ' ranges') 2415 + ' in one rule.' 2416 + ' This is probably an error.' 2417 + ' Equal selector types can (and should) always be written in conjunction separated by comma or something.' 2418 + ' Example for time ranges "12:00-13:00,15:00-18:00".' 2419 + ' Example for weekdays "Mo-We,Fr".' 2420 ) 2421 + ' Rules can be separated by ";".' ] 2422 ); 2423 done_with_selector_reordering = true; // Correcting the selector order makes no sense if this kind of issue exists. 2424 } 2425 } 2426 /* }}} */ 2427 2428 /* Check if change default state rule is not the first rule {{{ */ 2429 if ( typeof used_selectors[nrule].state === 'object' 2430 && Object.keys(used_selectors[nrule]).length === 1 2431 ) { 2432 2433 if (nrule !== 0) { 2434 parsing_warnings.push([nrule, new_tokens[nrule][0].length - 1, 2435 "This rule which changes the default state (which is closed) for all following rules is not the first rule." 2436 + " The rule will overwrite all previous rules." 2437 + " It can be legitimate to change the default state to open for example" 2438 + " and then only specify for which times the facility is closed." 2439 ]); 2440 } 2441 /* }}} */ 2442 /* Check if a rule (with state open) has no time selector {{{ */ 2443 } else if (typeof used_selectors[nrule].time === 'undefined') { 2444 if ( ( typeof used_selectors[nrule].state === 'object' 2445 && new_tokens[nrule][0][used_selectors[nrule].state[0]][0] === 'open' 2446 && typeof used_selectors[nrule].comment === 'undefined' 2447 ) || ( typeof used_selectors[nrule].comment === 'undefined' 2448 && typeof used_selectors[nrule].state === 'undefined' 2449 ) && 2450 typeof used_selectors[nrule]['24/7'] === 'undefined' 2451 ) { 2452 2453 parsing_warnings.push([nrule, new_tokens[nrule][0].length - 1, 2454 "This rule is not very explicit because there is no time selector being used." 2455 + " Please add a time selector to this rule or use a comment to make it more explicit." 2456 ]); 2457 } 2458 } 2459 /* }}} */ 2460 2461 /* Check if empty comment was given {{{ */ 2462 if (typeof used_selectors[nrule].comment === 'object' 2463 && new_tokens[nrule][0][used_selectors[nrule].comment[0]][0].length === 0 2464 ) { 2465 2466 parsing_warnings.push([nrule, used_selectors[nrule].comment[0], 2467 "You have used an empty comment." 2468 + " Please either write something in the comment or use the keyword unknown instead." 2469 ]); 2470 } 2471 /* }}} */ 2472 2473 /* Check if rule with closed|off modifier is additional {{{ */ 2474 /* FIXME: Enable this test. */ 2475 if (typeof new_tokens[nrule][0][0] === 'object' 2476 && new_tokens[nrule][0][0][0] === ',' 2477 && new_tokens[nrule][0][0][1] === 'rule separator' 2478 && typeof used_selectors[nrule].state === 'object' 2479 && ( 2480 new_tokens[nrule][0][used_selectors[nrule].state[0]][0] === 'closed' 2481 || new_tokens[nrule][0][used_selectors[nrule].state[0]][0] === 'off' 2482 ) 2483 ) { 2484 2485 // parsing_warnings.push([nrule, new_tokens[nrule][0].length - 1, 2486 // "This rule will be evaluated as closed but it was specified as additional rule." 2487 // + " It is enough to specify this rule as normal rule using the \";\" character." 2488 // + " See https://wiki.openstreetmap.org/wiki/Key:opening_hours:specification#explain:rule_modifier:closed." 2489 // ]); 2490 } 2491 /* }}} */ 2492 2493 /* Check for valid use of <separator_for_readability> {{{ */ 2494 for (var i = 0; i < used_selectors_types_array[nrule].length - 1; i++) { 2495 var selector_type = used_selectors_types_array[nrule][i]; 2496 var next_selector_type = used_selectors_types_array[nrule][i+1]; 2497 if ( ( wide_range_selectors.indexOf(selector_type) != -1 2498 && wide_range_selectors.indexOf(next_selector_type) != -1 2499 ) || ( small_range_selectors.indexOf(selector_type) != -1 2500 && small_range_selectors.indexOf(next_selector_type) != -1) 2501 ) { 2502 2503 if (new_tokens[nrule][0][used_selectors[nrule][selector_type][0]][0] == ':') { 2504 parsing_warnings.push([nrule, used_selectors[nrule][selector_type][0], 2505 "You have used the optional symbol <separator_for_readability> in the wrong place." 2506 + " Please check the syntax specification to see where it could be used or remove it." 2507 ]); 2508 } 2509 } 2510 } 2511 /* }}} */ 2512 2513 } 2514 2515 /* Check if 24/7 is used and it does not mean 24/7 because there are other rules {{{ */ 2123 2516 var has_advanced = it.advance(); 2124 2517 … … 2130 2523 + ' e.g. "open; Mo 12:00-14:00 off".']); 2131 2524 } 2525 /* }}} */ 2526 2527 prettifyValue(); 2132 2528 } 2529 done_with_warnings = true; 2133 2530 2134 2531 var warnings = []; 2532 // FIXME: Sort based on parsing_warnings[1], tricky … 2135 2533 for (var i = 0; i < parsing_warnings.length; i++) { 2136 2534 warnings.push( formatWarnErrorMessage(parsing_warnings[i][0], parsing_warnings[i][1], parsing_warnings[i][2]) ); … … 2138 2536 return warnings; 2139 2537 } 2538 2539 /* Helpers for getWarnings {{{ */ 2540 2541 /* Check if token is the begin of a selector and why. {{{ 2542 * 2543 * :param tokens: List of token objects. 2544 * :param at: Position where to start. 2545 * :returns: 2546 * * false the current token is not the begin of a selector. 2547 * * Position in token array from where the decision was made that 2548 * the token is the start of a selector. 2549 */ 2550 function tokenIsTheBeginOfSelector(tokens, at) { 2551 if (typeof tokens[at][3] == 'string') { 2552 return 3; 2553 } else if (tokens[at][1] == 'comment' 2554 || tokens[at][1] == 'state' 2555 || tokens[at][1] == '24/7' 2556 || tokens[at][1] == 'rule separator' 2557 ){ 2558 2559 return 1; 2560 } else { 2561 return false; 2562 } 2563 } 2564 /* }}} */ 2565 2566 /* Get start and end position of a selector. {{{ 2567 * For example this value 'Mo-We,Fr' will return the position of the 2568 * token lexeme 'Mo' and 'Fr' e.g. there indexes [ 0, 4 ] in the 2569 * selector array of tokens. 2570 * 2571 * :param tokens: List of token objects. 2572 * :param at: Position where to start. 2573 * :returns: Array: 2574 * 0. Index of first token in selector array of tokens. 2575 * 1. Index of last token in selector array of tokens. 2576 * 2. Selector type. 2577 */ 2578 function getSelectorRange(tokens, at) { 2579 var selector_start = at, 2580 selector_end, 2581 pos_in_token_array; 2582 2583 for (; selector_start >= 0; selector_start--) { 2584 pos_in_token_array = tokenIsTheBeginOfSelector(tokens, selector_start); 2585 if (pos_in_token_array) 2586 break; 2587 } 2588 selector_end = selector_start; 2589 2590 if (pos_in_token_array === 1) { 2591 // Selector consists of a single token. 2592 2593 // Include tailing colon. 2594 if (selector_end + 1 < tokens.length && tokens[selector_end + 1][0] == ':') 2595 selector_end++; 2596 2597 return [ selector_start, selector_end, tokens[selector_start][pos_in_token_array] ]; 2598 } 2599 2600 for (selector_end++; selector_end < tokens.length ; selector_end++) { 2601 if (tokenIsTheBeginOfSelector(tokens, selector_end)) 2602 return [ selector_start, selector_end - 1, tokens[selector_start][pos_in_token_array] ]; 2603 } 2604 2605 return [ selector_start, selector_end - 1, tokens[selector_start][pos_in_token_array] ]; 2606 } 2607 /* }}} */ 2608 /* }}} */ 2609 /* }}} */ 2610 2611 /* Prettify raw value from user. {{{ 2612 * The value is generated by putting the tokens back together to a string. 2613 * 2614 * :param argument_hash: Hash which can contain: 2615 * 'conf': Configuration hash. 2616 * 'get_internals: If true export internal data structures. 2617 * 'rule_index: Only prettify the rule with this index. 2618 * :returns: Prettified value string or object if get_internals is true. 2619 */ 2620 function prettifyValue(argument_hash) { 2621 var user_conf = {}, 2622 get_internals = false, 2623 rule_index; 2624 if (typeof argument_hash != 'undefined') { 2625 2626 if (typeof argument_hash.conf === 'object') 2627 user_conf = argument_hash.conf; 2628 2629 if (typeof argument_hash.rule_index === 'number') 2630 rule_index = argument_hash.rule_index; 2631 2632 if (argument_hash.get_internals === true) 2633 get_internals = true; 2634 } 2635 2636 for (var key in default_prettify_conf) { 2637 if (typeof user_conf[key] == 'undefined') 2638 user_conf[key] = default_prettify_conf[key]; 2639 } 2640 2641 prettified_value = ''; 2642 var prettified_value_array = []; 2643 2644 for (var nrule = 0; nrule < new_tokens.length; nrule++) { 2645 if (new_tokens[nrule][0].length === 0) continue; 2646 // Rule does contain nothing useful e.g. second rule of '10:00-12:00;' (empty) which needs to be handled. 2647 2648 if (typeof rule_index == 'number') { 2649 if (rule_index != nrule) continue; 2650 } else { 2651 if (nrule !== 0) 2652 prettified_value += ( 2653 new_tokens[nrule][1] 2654 ? user_conf.rule_sep_string + '|| ' 2655 : ( 2656 new_tokens[nrule][0][0][1] == 'rule separator' 2657 ? ',' 2658 : ( 2659 user_conf.print_semicolon 2660 ? ';' 2661 : '' 2662 ) 2663 ) + 2664 user_conf.rule_sep_string); 2665 } 2666 2667 var selector_start_end_type = [ 0, 0, undefined ], 2668 prettified_group_value = []; 2669 // console.log(new_tokens[nrule][0]); 2670 var count = 0; 2671 2672 2673 do { 2674 selector_start_end_type = getSelectorRange(new_tokens[nrule][0], selector_start_end_type[1]); 2675 // console.log(selector_start_end_type, new_tokens[nrule][0].length, count); 2676 2677 if (count > 50) { 2678 throw formatLibraryBugMessage('infinite loop'); 2679 } 2680 2681 if (selector_start_end_type[2] != 'rule separator') { 2682 prettified_group_value.push( 2683 [ 2684 selector_start_end_type, 2685 prettifySelector( 2686 new_tokens[nrule][0], 2687 selector_start_end_type[0], 2688 selector_start_end_type[1], 2689 selector_start_end_type[2], 2690 user_conf 2691 ), 2692 ] 2693 ); 2694 } 2695 2696 selector_start_end_type[1]++; 2697 count++; 2698 // console.log(selector_start_end_type, new_tokens[nrule][0].length, count); 2699 } while (selector_start_end_type[1] < new_tokens[nrule][0].length); 2700 // console.log('Prettified value: ' + JSON.stringify(prettified_group_value, null, ' ')); 2701 var not_sorted_prettified_group_value = prettified_group_value.slice(); 2702 2703 if (!done_with_selector_reordering) { 2704 prettified_group_value.sort( 2705 function (a, b) { 2706 var selector_order = [ 'year', 'month', 'week', 'holiday', 'weekday', 'time', '24/7', 'state', 'comment']; 2707 return selector_order.indexOf(a[0][2]) - selector_order.indexOf(b[0][2]); 2708 } 2709 ); 2710 } 2711 var old_prettified_value_length = prettified_value.length; 2712 2713 prettified_value += prettified_group_value.map( 2714 function (array) { 2715 return array[1]; 2716 } 2717 ).join(' '); 2718 2719 prettified_value_array.push( prettified_group_value ); 2720 2721 if (!done_with_selector_reordering_warnings) { 2722 for (var i = 0, l = not_sorted_prettified_group_value.length; i < l; i++) { 2723 if (not_sorted_prettified_group_value[i] != prettified_group_value[i]) { 2724 // console.log(i + ': ' + prettified_group_value[i][0][2]); 2725 var length = i + old_prettified_value_length; // i: Number of spaces in string. 2726 for (var x = 0; x <= i; x++) { 2727 length += prettified_group_value[x][1].length; 2728 // console.log('Length: ' + length + ' ' + prettified_group_value[x][1]); 2729 } 2730 // console.log(length); 2731 parsing_warnings.push([ prettified_value, length, 2732 'The selector "' + prettified_group_value[i][0][2] + '" was switched with' 2733 + ' the selector "' + not_sorted_prettified_group_value[i][0][2] + '"' 2734 + ' for readablitity and compatibiltity reasons.' 2735 ]); 2736 } 2737 } 2738 } 2739 } 2740 2741 done_with_selector_reordering_warnings = true; 2742 // console.log(JSON.stringify(prettified_value_array, null, ' ')); 2743 2744 if (get_internals) { 2745 return [ prettified_value_array, new_tokens ]; 2746 } else { 2747 return prettified_value; 2748 } 2749 } 2140 2750 // }}} 2141 2751 2142 /* Function to check token array for specific pattern{{{2752 /* Check selector array of tokens for specific token name pattern. {{{ 2143 2753 * 2144 2754 * :param tokens: List of token objects. … … 2173 2783 return res; 2174 2784 return [ res[0], new Date(res[1].getTime() - shift) ]; 2175 } 2785 }; 2176 2786 } 2177 2787 // }}} … … 2180 2790 * 2181 2791 * :param tokens: List of token objects. 2182 * :param at: Position at which the matching should begin.2792 * :param at: Position where to start. 2183 2793 * :param selectors: Reference to selector object. 2184 * :param nblock: Block number starting with 0. 2185 * :param conf: Configuration for prettifyValue. 2794 * :param nrule: Rule number starting with 0. 2186 2795 * :returns: See selector code. 2187 2796 */ 2188 function parseGroup(tokens, at, selectors, nblock, conf) { 2189 var prettified_group_value = ''; 2190 used_subparsers = { 'time ranges': [ ] }; 2797 function parseGroup(tokens, at, selectors, nrule) { 2798 var rule_modifier_specified = false; 2191 2799 2192 2800 // console.log(tokens); // useful for debugging of tokenize 2193 2801 while (at < tokens.length) { 2194 var old_at = at;2195 2802 // console.log('Parsing at position', at +':', tokens[at]); 2196 2803 if (matchTokens(tokens, at, 'weekday')) { … … 2212 2819 || matchTokens(tokens, at, 'year', 'event') 2213 2820 || matchTokens(tokens, at, 'event')) { 2214 at = parseMonthdayRange(tokens, at, nblock); 2821 2822 at = parseMonthdayRange(tokens, at, nrule); 2215 2823 week_stable = false; 2216 2824 } else if (matchTokens(tokens, at, 'year')) { … … 2219 2827 } else if (matchTokens(tokens, at, 'month')) { 2220 2828 at = parseMonthRange(tokens, at); 2221 // week_stable = false; // decided based on actual values2829 // week_stable = false; // Decided based on the actual value/tokens. 2222 2830 } else if (matchTokens(tokens, at, 'week')) { 2223 at = parseWeekRange(tokens, at + 1); 2224 week_stable = false; 2225 2226 // if (prettified_group_value[-1] != ' ') 2227 // prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 1); 2228 } else if (at != 0 && at != tokens.length - 1 && tokens[at][0] == ':') { 2229 // Ignore colon if they appear somewhere else than as time separator. 2230 // Except the start or end of the value. 2231 // This provides compatibility with the syntax proposed by Netzwolf: 2232 // http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification 2233 if (!done_with_warnings && (matchTokens(tokens, at-1, 'weekday') || matchTokens(tokens, at-1, 'holiday'))) 2234 parsing_warnings.push([nblock, at, 'Please don’t use ":" after ' + tokens[at-1][1] + '.']); 2235 2236 if (prettified_group_value[-1] != ' ') 2237 prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 1); 2831 tokens[at][3] = 'week'; 2832 at = parseWeekRange(tokens, at); 2833 2834 } else if (at !== 0 && at != tokens.length - 1 && tokens[at][0] == ':') { 2835 /* Ignore colon if they appear somewhere else than as time separator. 2836 * Except the start or end of the value. 2837 * This provides compatibility with the syntax proposed by Netzwolf: 2838 * http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification#separator_for_readability 2839 * Check for valid use of <separator_for_readability> is implemented in function getWarnings(). 2840 */ 2841 2842 if (!done_with_warnings && matchTokens(tokens, at-1, 'holiday')) 2843 parsing_warnings.push([nrule, at, 'Please don’t use ":" after ' + tokens[at-1][1] + '.']); 2844 2238 2845 at++; 2239 2846 } else if (matchTokens(tokens, at, 'number', 'timesep') … … 2241 2848 || matchTokens(tokens, at, '(', 'timevar') 2242 2849 || matchTokens(tokens, at, 'number', '-')) { 2850 2243 2851 at = parseTimeRange(tokens, at, selectors, false); 2244 2852 2245 used_subparsers['time ranges'].push(at); 2246 } else if (matchTokens(tokens, at, 'closed')) { 2247 selectors.meaning = false; 2853 } else if (matchTokens(tokens, at, 'state')) { 2854 2855 if (tokens[at][0] == 'open') { 2856 selectors.meaning = true; 2857 } else if (tokens[at][0] == 'closed' || tokens[at][0] == 'off') { 2858 selectors.meaning = false; 2859 } else { 2860 selectors.meaning = false; 2861 selectors.unknown = true; 2862 } 2863 2864 rule_modifier_specified = true; 2248 2865 at++; 2249 if ( matchTokens(tokens, at, ',')) // additional block2866 if (typeof tokens[at] == 'object' && tokens[at][0] == ',') // additional rule 2250 2867 at = [ at + 1 ]; 2251 2868 2252 if (typeof used_subparsers['state keywords'] != 'object')2253 used_subparsers['state keywords'] = [ at ];2254 else2255 used_subparsers['state keywords'].push(at);2256 } else if (matchTokens(tokens, at, 'open')) {2257 selectors.meaning = true;2258 at++;2259 if (matchTokens(tokens, at, ',')) // additional block2260 at = [ at + 1 ];2261 2262 if (typeof used_subparsers['state keywords'] != 'object')2263 used_subparsers['state keywords'] = [ at ];2264 else2265 used_subparsers['state keywords'].push(at);2266 } else if (matchTokens(tokens, at, 'unknown')) {2267 selectors.meaning = false;2268 selectors.unknown = true;2269 at++;2270 if (matchTokens(tokens, at, ',')) // additional block2271 at = [ at + 1 ];2272 2273 if (typeof used_subparsers['state keywords'] != 'object')2274 used_subparsers['state keywords'] = [ at ];2275 else2276 used_subparsers['state keywords'].push(at);2277 2869 } else if (matchTokens(tokens, at, 'comment')) { 2278 2870 selectors.comment = tokens[at][0]; 2279 if (at > 0) { 2280 if (!matchTokens(tokens, at - 1, 'open') 2281 && !matchTokens(tokens, at - 1, 'closed')) { 2282 // Then it is unknown. Either with unknown explicitly 2283 // specified or just a comment behind. 2284 selectors.meaning = false; 2285 selectors.unknown = true; 2286 } 2287 } else { // block starts with comment 2288 selectors.time.push(function(date) { return [true]; }); 2289 // Not needed. If there is no selector it automatically matches everything. 2290 // WRONG: This only works if there is no other selector in this selector group ... 2871 if (!rule_modifier_specified) { 2872 // Then it is unknown. Either with unknown explicitly 2873 // specified or just a comment. 2291 2874 selectors.meaning = false; 2292 2875 selectors.unknown = true; 2293 2876 } 2877 2878 rule_modifier_specified = true; 2294 2879 at++; 2295 if ( matchTokens(tokens, at, ',')) // additional block2880 if (typeof tokens[at] == 'object' && tokens[at][0] == ',') // additional rule 2296 2881 at = [ at + 1 ]; 2297 2298 if (typeof used_subparsers['comments'] != 'object') 2299 used_subparsers['comments'] = [ at ]; 2300 else 2301 used_subparsers['comments'].push(at); 2882 } else if ((at === 0 || at == tokens.length - 1) && matchTokens(tokens, at, 'rule separator')) { 2883 at++; 2884 console.log("value: " + nrule); 2885 // throw formatLibraryBugMessage('Not implemented yet.'); 2302 2886 } else { 2303 2887 var warnings = getWarnings(); 2304 throw formatWarnErrorMessage(n block, at, 'Unexpected token: "' + tokens[at][1]2888 throw formatWarnErrorMessage(nrule, at, 'Unexpected token: "' + tokens[at][1] 2305 2889 + '" This means that the syntax is not valid at that point or it is currently not supported.') 2306 2890 + (warnings ? ' ' + warnings.join('; ') : ''); 2307 2891 } 2308 2892 2309 if (typeof conf != 'undefined') { 2310 // 'Mo: 12:00-13:00' -> 'Mo 12:00-13:00' 2311 if (used_subparsers['time ranges'] && old_at > 1 && tokens[old_at-1][0] == ':' 2312 && matchTokens(tokens, old_at - 2, 'weekday')) 2313 prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 2) + ' '; 2314 2315 // 'week 1, week 3' -> 'week 1,week 3' 2316 if (prettified_group_value.substr(prettified_group_value.length -2, 2) == ', ' 2317 && matchTokens(tokens, old_at, 'week')) 2318 prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 1); 2319 2320 prettified_group_value += prettifySelector(tokens, old_at, at, conf, used_subparsers['time ranges'].length); 2321 } 2322 2323 if (typeof at == 'object') // additional block 2893 if (typeof at == 'object') { // additional rule 2894 tokens[at[0] - 1][1] = 'rule separator'; 2324 2895 break; 2325 }2326 2327 prettified_value += prettified_group_value.replace(/\s+$/, '');2328 2329 if (!done_with_warnings) {2330 for (var subparser_name in used_subparsers) {2331 if (used_subparsers[subparser_name].length > 1) {2332 parsing_warnings.push([nblock, used_subparsers[subparser_name][used_subparsers[subparser_name].length - 1] - 1,2333 'You have used ' + used_subparsers[subparser_name].length2334 + (subparser_name.match(/^(?:comments|state keywords)/) ?2335 ' ' + subparser_name + ' in one rule.'2336 + ' You may only use one in one rule.'2337 :2338 ' not connected ' + subparser_name + ' in one rule.'2339 + ' This is probably an error.'2340 + ' Equal selector types can (and should) always be written in conjunction separated by comma or something.'2341 + ' Example for time ranges "12:00-13:00,15:00-18:00".'2342 + ' Example for weekdays "Mo-We,Fr".'2343 )2344 + ' Rules can be separated by ";".' ]2345 );2346 }2347 2896 } 2348 2897 } 2349 2898 2350 2899 return at; 2900 } 2901 2902 function get_last_token_pos_in_token_group(tokens, at, last_at) { 2903 for (at++; at < last_at; at++) { 2904 if (typeof tokens[at] != 'undefined') { 2905 if (typeof tokens[at][3] == 'string' 2906 || tokens[at][1] == 'comment' 2907 || tokens[at][1] == 'state'){ 2908 2909 return at - 1; 2910 } 2911 } 2912 } 2913 return last_at; 2351 2914 } 2352 2915 // }}} … … 2370 2933 * 2371 2934 * :param date: Date object. 2372 * :param day: Integer number for day of week. Starting with zero (Sunday).2935 * :param weekday: Integer number for day of week. Starting with zero (Sunday). 2373 2936 * :returns: Moved date object. 2374 2937 */ 2375 function dateAtNextWeekday(date, day) {2376 var delta = day - date.getDay();2938 function dateAtNextWeekday(date, weekday) { 2939 var delta = weekday - date.getDay(); 2377 2940 return new Date(date.getFullYear(), date.getMonth(), date.getDate() + delta + (delta < 0 ? 7 : 0)); 2378 2941 } … … 2421 2984 // Negative number 2422 2985 func(-tokens[at+1][0], -tokens[at+1][0], at); 2423 at += 2 2986 at += 2; 2424 2987 } else if (matchTokens(tokens, at, 'number')) { 2425 2988 // Single number … … 2427 2990 at++; 2428 2991 } else { 2429 throw formatWarnErrorMessage(n block, at + matchTokens(tokens, at, '-'),2992 throw formatWarnErrorMessage(nrule, at + matchTokens(tokens, at, '-'), 2430 2993 'Unexpected token in number range: ' + tokens[at][1]); 2431 2994 } … … 2453 3016 2454 3017 // bad number 2455 if (from == 0 || from < -5 || from > 5)2456 throw formatWarnErrorMessage(n block, at,3018 if (from === 0 || from < -5 || from > 5) 3019 throw formatWarnErrorMessage(nrule, at, 2457 3020 'Number between -5 and 5 (except 0) expected'); 2458 3021 2459 3022 if (from == to) { 2460 if (number != 0)2461 throw formatWarnErrorMessage(n block, at,3023 if (number !== 0) 3024 throw formatWarnErrorMessage(nrule, at, 2462 3025 'You can not use more than one constrained weekday in a month range'); 2463 3026 number = from; 2464 3027 } else { 2465 throw formatWarnErrorMessage(n block, at+2,3028 throw formatWarnErrorMessage(nrule, at+2, 2466 3029 'You can not use a range of constrained weekdays in a month range'); 2467 3030 } … … 2469 3032 2470 3033 if (!matchTokens(tokens, endat, ']')) 2471 throw formatWarnErrorMessage(n block, endat, '"]" expected.');3034 throw formatWarnErrorMessage(nrule, endat, '"]" expected.'); 2472 3035 2473 3036 return [ number, endat + 1 ]; … … 2476 3039 2477 3040 // Check if period is ok. Period 0 or 1 don’t make much sense. 2478 /* List parser for constrained weekdays in month range {{{2479 * e.g. Su[-1] which selects the last Sunday of the month.2480 *2481 * :param tokens: List of token objects.2482 * :param at: Position where to start.2483 * :returns: Array:2484 * 0. Constrained weekday number.2485 * 1. Position at which the token does not belong to the list any more (after ']' token).2486 */2487 3041 function checkPeriod(at, period, period_type, parm_string) { 2488 3042 if (done_with_warnings) … … 2490 3044 2491 3045 if (period === 0) { 2492 throw formatWarnErrorMessage(n block, at,3046 throw formatWarnErrorMessage(nrule, at, 2493 3047 'You can not use '+ period_type +' ranges with period equals zero.'); 2494 3048 } else if (period === 1) { 2495 3049 if (typeof parm_string == 'string' && parm_string == 'no_end_year') 2496 parsing_warnings.push([n block, at,3050 parsing_warnings.push([nrule, at, 2497 3051 'Please don’t use '+ period_type +' ranges with period equals one.' 2498 3052 + ' If you want to express that a facility is open starting from a year without limit use "<year>+".']); 2499 3053 else 2500 parsing_warnings.push([n block, at,3054 parsing_warnings.push([nrule, at, 2501 3055 'Please don’t use '+ period_type +' ranges with period equals one.']); 2502 3056 } 2503 3057 } 2504 3058 3059 /* Get date moved to constrained weekday (and moved for add_days. {{{ 3060 * E.g. used for 'Aug Su[-1] -1 day'. 3061 * 3062 * :param year: Year as integer. 3063 * :param month: Month as integer starting with zero. 3064 * :param weekday: Integer number for day of week. Starting with zero (Sunday). 3065 * :param constrained_weekday: Position where to start. 3066 * :returns: Date object. 3067 */ 2505 3068 function getDateForConstrainedWeekday(year, month, weekday, constrained_weekday, add_days) { 2506 3069 var tmp_date = dateAtNextWeekday( … … 2514 3077 return tmp_date; 2515 3078 } 2516 2517 function formatWarnErrorMessage(nblock, at, message) { 2518 var pos = 0; 2519 if (nblock == -1) { // Usage of block index not required because we do have access to value.length. 2520 pos = value.length - at; 2521 } else { // Issue accrued at a later time, position in string needs to be reconstructed. 2522 if (typeof tokens[nblock][0][at] == 'undefined') { 2523 pos = value.length; 2524 if (typeof tokens[nblock][0][tokens[nblock][0].length-1] != 'undefined') { 2525 // pos -= tokens[nblock][0][tokens[nblock][0].length-1][2]; 2526 console.warn("FIXME"); 2527 } 2528 } else { 2529 pos = value.length; 2530 if (typeof tokens[nblock][0][at+1] != 'undefined') { 2531 pos -= tokens[nblock][0][at+1][2]; 2532 } else if (typeof tokens[nblock][2] != 'undefined') { 2533 pos -= tokens[nblock][2]; 2534 } else { 2535 } 2536 } 2537 } 2538 return value.substring(0, pos) + ' <--- (' + message + ')'; 2539 } 2540 2541 // check if date is valid 2542 function isValidDate(month, day, nblock, at) { 2543 // month == 0 is Jan 2544 2545 // May use this instead. Does not say, what is wrong as good was implementation below. 3079 // }}} 3080 3081 /* Check if date is valid. {{{ 3082 * 3083 * :param month: Month as integer starting with zero. 3084 * :param date: Day of month as integer. 3085 * :returns: undefined. There is no real return value. This function just throws an exception if something is wrong. 3086 */ 3087 function checkIfDateIsValid(month, day, nrule, at) { 3088 // May use this instead. The problem is that this does not give feedback as precise as the code which is used in this function. 2546 3089 // var testDate = new Date(year, month, day); 2547 3090 // if (testDate.getDate() != day || testDate.getMonth() != month || testDate.getFullYear() != year) { … … 2551 3094 // https://en.wikipedia.org/wiki/Month#Julian_and_Gregorian_calendars 2552 3095 if (day < 1 || day > 31) 2553 throw formatWarnErrorMessage(n block, at, 'Day must be between 1 and 31.');3096 throw formatWarnErrorMessage(nrule, at, 'Day must be between 1 and 31.'); 2554 3097 if ((month==3 || month==5 || month==8 || month==10) && day==31) 2555 throw formatWarnErrorMessage(n block, at, 'Month ' + months[month] + " doesn't have 31 days.!");3098 throw formatWarnErrorMessage(nrule, at, 'Month ' + months[month] + " doesn't have 31 days.!"); 2556 3099 if (month == 1 && day == 30) 2557 throw formatWarnErrorMessage(n block, at, 'Month ' + months[1]+ " either has 28 or 29 days (leap years).");3100 throw formatWarnErrorMessage(nrule, at, 'Month ' + months[1]+ " either has 28 or 29 days (leap years)."); 2558 3101 } 2559 3102 // }}} 2560 2561 // Time range parser (10:00-12:00,14:00-16:00) {{{ 2562 // 2563 // extended_open_end: <time> - <time> + 2564 // at is here A (if extended_open_end is true) 3103 // }}} 3104 3105 /* Time range parser (10:00-12:00,14:00-16:00) {{{ 3106 * 3107 * :param tokens: List of token objects. 3108 * :param at: Position where to start. 3109 * :param selectors: Reference to selector object. 3110 * :param extended_open_end: Used for combined time range with open end. 3111 * extended_open_end: <time> - <time> + 3112 * param at is here A (if extended_open_end is true) 3113 * :returns: Position at which the token does not belong to the selector anymore. 3114 */ 2565 3115 function parseTimeRange(tokens, at, selectors, extended_open_end) { 3116 if (!extended_open_end) 3117 tokens[at][3] = 'time'; 3118 2566 3119 for (; at < tokens.length; at++) { 2567 3120 var has_time_var_calc = [], has_normal_time = []; // element 0: start time, 1: end time 2568 has_normal_time[0] = matchTokens(tokens, at, 'number', 'timesep', 'number'); 2569 has_time_var_calc[0] = matchTokens(tokens, at, '(', 'timevar'); 3121 has_normal_time[0] = matchTokens(tokens, at, 'number', 'timesep', 'number'); 3122 has_time_var_calc[0] = matchTokens(tokens, at, '(', 'timevar'); 3123 var minutes_from, 3124 minutes_to; 2570 3125 if (has_normal_time[0] || matchTokens(tokens, at, 'timevar') || has_time_var_calc[0]) { 2571 3126 // relying on the fact that always *one* of them is true … … 2575 3130 var timevar_add = [ 0, 0 ]; 2576 3131 var timevar_string = []; // capture timevar string like 'sunrise' to calculate it for the current date. 3132 var point_in_time_period; 2577 3133 2578 3134 // minutes_from 2579 3135 if (has_normal_time[0]) { 2580 var minutes_from = getMinutesByHoursMinutes(tokens, nblock, at+has_time_var_calc[0]);3136 minutes_from = getMinutesByHoursMinutes(tokens, nrule, at+has_time_var_calc[0]); 2581 3137 } else { 2582 3138 timevar_string[0] = tokens[at+has_time_var_calc[0]][0]; 2583 varminutes_from = word_value_replacement[timevar_string[0]];3139 minutes_from = word_value_replacement[timevar_string[0]]; 2584 3140 2585 3141 if (has_time_var_calc[0]) { … … 2594 3150 has_open_end = true; 2595 3151 } else { 2596 if (oh_mode == 0) { 2597 throw formatWarnErrorMessage(nblock, at+( 3152 if (oh_mode === 0) { 3153 throw formatWarnErrorMessage(nrule, 3154 at+( 2598 3155 has_normal_time[0] ? ( 2599 typeof tokens[at+3] == 'object' ? 3 : 2 2600 ) : ( 2601 has_time_var_calc[0] ? 2 : 1 2602 ) 2603 ), 3156 typeof tokens[at+3] == 'object' ? 3 : 2 3157 ) : ( 3158 has_time_var_calc[0] ? 2 : ( 3159 typeof tokens[at+1] != 'undefined' ? 1 : 0 3160 ) 3161 ) 3162 ), 2604 3163 'hyphen (-) or open end (+) in time range ' 2605 3164 + (has_time_var_calc[0] ? 'calculation ' : '') + 'expected.' 2606 + ' For working with points in time, the mode for opening_hours.jshas to be altered.'3165 + ' For working with points in time, the mode for ' + library_name + ' has to be altered.' 2607 3166 + ' Maybe wrong tag?'); 2608 3167 } else { 2609 varminutes_to = minutes_from + 1;3168 minutes_to = minutes_from + 1; 2610 3169 is_point_in_time = true; 2611 3170 } … … 2615 3174 // minutes_to 2616 3175 if (has_open_end) { 3176 if (extended_open_end === 1) 3177 minutes_from += minutes_in_day; 2617 3178 if (minutes_from >= 22 * 60) 2618 varminutes_to = minutes_from + 8 * 60;3179 minutes_to = minutes_from + 8 * 60; 2619 3180 else if (minutes_from >= 17 * 60) 2620 varminutes_to = minutes_from + 10 * 60;3181 minutes_to = minutes_from + 10 * 60; 2621 3182 else 2622 varminutes_to = minutes_in_day;3183 minutes_to = minutes_in_day; 2623 3184 } else if (!is_point_in_time) { 2624 3185 has_normal_time[1] = matchTokens(tokens, at_end_time, 'number', 'timesep', 'number'); 2625 3186 has_time_var_calc[1] = matchTokens(tokens, at_end_time, '(', 'timevar'); 2626 3187 if (!has_normal_time[1] && !matchTokens(tokens, at_end_time, 'timevar') && !has_time_var_calc[1]) { 2627 throw formatWarnErrorMessage(nblock, at_end_time, 'time range does not continue as expected'); 3188 throw formatWarnErrorMessage(nrule, at_end_time - (typeof tokens[at_end_time] != 'undefined' ? 0 : 1), 3189 'Time range does not continue as expected'); 2628 3190 } else { 2629 3191 if (has_normal_time[1]) { 2630 var minutes_to = getMinutesByHoursMinutes(tokens, nblock, at_end_time);3192 minutes_to = getMinutesByHoursMinutes(tokens, nrule, at_end_time); 2631 3193 } else { 2632 timevar_string[1] = tokens[at_end_time+has_time_var_calc[1]][0] 2633 varminutes_to = word_value_replacement[timevar_string[1]];3194 timevar_string[1] = tokens[at_end_time+has_time_var_calc[1]][0]; 3195 minutes_to = word_value_replacement[timevar_string[1]]; 2634 3196 } 2635 3197 … … 2647 3209 if (matchTokens(tokens, at, '/', 'number')) { 2648 3210 if (matchTokens(tokens, at + 2, 'timesep', 'number')) { // /hours:minutes 2649 var point_in_time_period = getMinutesByHoursMinutes(tokens, nblock, at + 1);3211 point_in_time_period = getMinutesByHoursMinutes(tokens, nrule, at + 1); 2650 3212 at += 4; 2651 3213 } else { // /minutes 2652 varpoint_in_time_period = tokens[at + 1][0];3214 point_in_time_period = tokens[at + 1][0]; 2653 3215 at += 2; 2654 3216 if (matchTokens(tokens, at, 'timesep')) 2655 throw formatWarnErrorMessage(n block, at,3217 throw formatWarnErrorMessage(nrule, at, 2656 3218 'Time period does not continue as expected. Exampe "/01:30".'); 2657 3219 } 2658 3220 2659 if (oh_mode == 0) 2660 throw formatWarnErrorMessage(nblock, at - 1, 3221 // Check at this later state in the if condition to get the correct position. 3222 if (oh_mode === 0) 3223 throw formatWarnErrorMessage(nrule, at - 1, 2661 3224 'opening_hours is running in "time range mode". Found point in time.'); 2662 3225 2663 3226 is_point_in_time = true; 2664 3227 } else if (matchTokens(tokens, at, '+')) { 2665 parseTimeRange(tokens, at_end_time, selectors, true);3228 parseTimeRange(tokens, at_end_time, selectors, minutes_to < minutes_from ? 1 : true); 2666 3229 at++; 2667 3230 } else if (oh_mode == 1 && !is_point_in_time) { 2668 throw formatWarnErrorMessage(n block, at_end_time,3231 throw formatWarnErrorMessage(nrule, at_end_time, 2669 3232 'opening_hours is running in "points in time mode". Found time range.'); 2670 3233 } … … 2677 3240 } 2678 3241 2679 // normalize minutes into range3242 // Normalize minutes into range. 2680 3243 if (!extended_open_end && minutes_from >= minutes_in_day) 2681 throw formatWarnErrorMessage(n block, at_end_time - 1,3244 throw formatWarnErrorMessage(nrule, at_end_time - 2, 2682 3245 'Time range starts outside of the current day'); 2683 3246 if (minutes_to < minutes_from || ((has_normal_time[0] && has_normal_time[1]) && minutes_from == minutes_to)) 2684 3247 minutes_to += minutes_in_day; 2685 3248 if (minutes_to > minutes_in_day * 2) 2686 throw formatWarnErrorMessage(n block, at_end_time + (has_normal_time[1] ? 4 : (has_time_var_calc[1] ? 7 : 1)) - 2,3249 throw formatWarnErrorMessage(nrule, at_end_time + (has_normal_time[1] ? 4 : (has_time_var_calc[1] ? 7 : 1)) - 2, 2687 3250 'Time spanning more than two midnights not supported'); 2688 3251 2689 // this shortcut makes always-open range check faster 2690 if (!(minutes_from == 0 && minutes_to == minutes_in_day)) { 3252 // This shortcut makes always-open range check faster. 3253 if (minutes_from === 0 && minutes_to == minutes_in_day) { 3254 selectors.time.push(function(date) { return [true]; }); 3255 } else { 2691 3256 if (minutes_to > minutes_in_day) { // has_normal_time[1] must be true 2692 selectors.time.push(function(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period ) { return function(date) {3257 selectors.time.push(function(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period, extended_open_end) { return function(date) { 2693 3258 var ourminutes = date.getHours() * 60 + date.getMinutes(); 2694 3259 2695 3260 if (timevar_string[0]) { 2696 var date_from = eval('SunCalc.getTimes(date, lat, lon).' + timevar_string[0]);3261 var date_from = SunCalc.getTimes(date, lat, lon)[timevar_string[0]]; 2697 3262 minutes_from = date_from.getHours() * 60 + date_from.getMinutes() + timevar_add[0]; 2698 3263 } 2699 3264 if (timevar_string[1]) { 2700 var date_to = eval('SunCalc.getTimes(date, lat, lon).' + timevar_string[1]);3265 var date_to = SunCalc.getTimes(date, lat, lon)[timevar_string[1]]; 2701 3266 minutes_to = date_to.getHours() * 60 + date_to.getMinutes() + timevar_add[1]; 2702 3267 minutes_to += minutes_in_day; … … 2725 3290 return [false, dateAtDayMinutes(date, minutes_from)]; 2726 3291 else 2727 return [true, dateAtDayMinutes(date, minutes_to), has_open_end ];3292 return [true, dateAtDayMinutes(date, minutes_to), has_open_end, extended_open_end]; 2728 3293 } 2729 }}(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period ));2730 2731 selectors.wraptime.push(function(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period ) { return function(date) {3294 }}(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period, extended_open_end)); 3295 3296 selectors.wraptime.push(function(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period, extended_open_end) { return function(date) { 2732 3297 var ourminutes = date.getHours() * 60 + date.getMinutes(); 2733 3298 2734 3299 if (timevar_string[0]) { 2735 var date_from = eval('SunCalc.getTimes(date, lat, lon).' + timevar_string[0]);3300 var date_from = SunCalc.getTimes(date, lat, lon)[timevar_string[0]]; 2736 3301 minutes_from = date_from.getHours() * 60 + date_from.getMinutes() + timevar_add[0]; 2737 3302 } 2738 3303 if (timevar_string[1]) { 2739 var date_to = eval('SunCalc.getTimes(date, lat, lon).' + timevar_string[1]);3304 var date_to = SunCalc.getTimes(date, lat, lon)[timevar_string[1]]; 2740 3305 minutes_to = date_to.getHours() * 60 + date_to.getMinutes() + timevar_add[1]; 2741 3306 // minutes_in_day does not need to be added. … … 2758 3323 } else { 2759 3324 if (ourminutes < minutes_to) 2760 return [true, dateAtDayMinutes(date, minutes_to), has_open_end ];3325 return [true, dateAtDayMinutes(date, minutes_to), has_open_end, extended_open_end]; 2761 3326 } 2762 3327 return [false, undefined]; 2763 }}(minutes_from, minutes_to - minutes_in_day, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period ));3328 }}(minutes_from, minutes_to - minutes_in_day, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period, extended_open_end)); 2764 3329 } else { 2765 selectors.time.push(function(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period ) { return function(date) {3330 selectors.time.push(function(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period, extended_open_end) { return function(date) { 2766 3331 var ourminutes = date.getHours() * 60 + date.getMinutes(); 2767 3332 2768 3333 if (timevar_string[0]) { 2769 var date_from = eval('SunCalc.getTimes(date, lat, lon).' + timevar_string[0]);3334 var date_from = SunCalc.getTimes(date, lat, lon)[timevar_string[0]]; 2770 3335 minutes_from = date_from.getHours() * 60 + date_from.getMinutes() + timevar_add[0]; 2771 3336 } 2772 3337 if (timevar_string[1]) { 2773 var date_to = eval('SunCalc.getTimes(date, lat, lon).' + timevar_string[1]);3338 var date_to = SunCalc.getTimes(date, lat, lon)[timevar_string[1]]; 2774 3339 minutes_to = date_to.getHours() * 60 + date_to.getMinutes() + timevar_add[1]; 2775 3340 } else if (is_point_in_time && typeof point_in_time_period != 'number') { … … 2798 3363 return [false, dateAtDayMinutes(date, minutes_from + minutes_in_day)]; 2799 3364 } 2800 }}(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period ));3365 }}(minutes_from, minutes_to, timevar_string, timevar_add, has_open_end, is_point_in_time, point_in_time_period, extended_open_end)); 2801 3366 } 2802 } else {2803 selectors.time.push(function(date) { return [true]; });2804 3367 } 2805 3368 2806 3369 } else if (matchTokens(tokens, at, 'number', '-', 'number')) { // "Mo 09-18" (Please don’t use this) -> "Mo 09:00-18:00". 2807 varminutes_from = tokens[at][0] * 60;2808 varminutes_to = tokens[at+2][0] * 60;3370 minutes_from = tokens[at][0] * 60; 3371 minutes_to = tokens[at+2][0] * 60; 2809 3372 if (!done_with_warnings) 2810 parsing_warnings.push([nblock, at + 2, 2811 'Time range without minutes specified. Not very explicit! Please use this syntax instead e.g. "12:00-14:00".']); 3373 parsing_warnings.push([nrule, at + 2, 3374 'Time range without minutes specified. Not very explicit!' 3375 + ' Please use this syntax instead "' 3376 + (tokens[at][0] < 10 ? '0' : '') + tokens[at][0] + ':00-' 3377 + (tokens[at+2][0] < 10 ? '0' : '') + tokens[at+2][0] + ':00".']); 2812 3378 2813 3379 if (minutes_from >= minutes_in_day) 2814 throw formatWarnErrorMessage(n block, at,3380 throw formatWarnErrorMessage(nrule, at, 2815 3381 'Time range starts outside of the current day'); 2816 3382 if (minutes_to < minutes_from) 2817 3383 minutes_to += minutes_in_day; 2818 3384 if (minutes_to > minutes_in_day * 2) 2819 throw formatWarnErrorMessage(n block, at + 2,3385 throw formatWarnErrorMessage(nrule, at + 2, 2820 3386 'Time spanning more than two midnights not supported'); 2821 3387 … … 2852 3418 2853 3419 at += 3; 2854 } else { // additional block3420 } else { // additional rule 2855 3421 if (matchTokens(tokens, at, '(')) 2856 throw formatWarnErrorMessage(n block, at, 'Missing variable time (e.g. sunrise) after: "' + tokens[at][1] + '"');3422 throw formatWarnErrorMessage(nrule, at, 'Missing variable time (e.g. sunrise) after: "' + tokens[at][1] + '"'); 2857 3423 if (matchTokens(tokens, at, 'number', 'timesep')) 2858 throw formatWarnErrorMessage(n block, at+2, 'Missing minutes in time range after: "' + tokens[at+1][1] + '"');3424 throw formatWarnErrorMessage(nrule, at+1, 'Missing minutes in time range after: "' + tokens[at+1][1] + '"'); 2859 3425 if (matchTokens(tokens, at, 'number')) 2860 throw formatWarnErrorMessage(nblock, at+2, 'Missing time seperator in time range after: "' + tokens[at][1] + '"'); 3426 throw formatWarnErrorMessage(nrule, at + (typeof tokens[at+1] != 'undefined' ? 1 : 0), 3427 'Missing time separator in time range after: "' + tokens[at][1] + '"'); 2861 3428 return [ at ]; 2862 3429 } … … 2868 3435 return at; 2869 3436 } 2870 2871 // get time in minutes from <hour>:<minute> {{{ 2872 // Only used if throwing an error is wanted. 2873 function getMinutesByHoursMinutes(tokens, nblock, at) { 3437 // }}} 3438 3439 /* Helpers for time range parser {{{ */ 3440 3441 /* Get time in minutes from <hour>:<minute> (tokens). {{{ 3442 * Only used if throwing an error is wanted. 3443 * 3444 * :param tokens: List of token objects. 3445 * :param nrule: Rule number starting with 0. 3446 * :param at: Position at which the time begins. 3447 * :returns: Time in minutes. 3448 */ 3449 function getMinutesByHoursMinutes(tokens, nrule, at) { 2874 3450 if (tokens[at+2][0] > 59) 2875 throw formatWarnErrorMessage(n block, at+2,3451 throw formatWarnErrorMessage(nrule, at+2, 2876 3452 'Minutes are greater than 59.'); 2877 3453 return tokens[at][0] * 60 + tokens[at+2][0]; … … 2879 3455 // }}} 2880 3456 2881 // get time in minutes from "(sunrise-01:30)" {{{ 2882 // Extract the added or subtracted time from "(sunrise-01:30)" 2883 // returns time in minutes e.g. -90 3457 /* Get time in minutes from "(sunrise-01:30)" {{{ 3458 * Extract the added or subtracted time from "(sunrise-01:30)" 3459 * returns time in minutes e.g. -90. 3460 * 3461 * :param tokens: List of token objects. 3462 * :param at: Position where the specification for the point in time could be. 3463 * :returns: Time in minutes on suggest, throws an exception otherwise. 3464 */ 2884 3465 function parseTimevarCalc(tokens, at) { 3466 var error; 2885 3467 if (matchTokens(tokens, at+2, '+') || matchTokens(tokens, at+2, '-')) { 2886 3468 if (matchTokens(tokens, at+3, 'number', 'timesep', 'number')) { 2887 3469 if (matchTokens(tokens, at+6, ')')) { 2888 3470 var add_or_subtract = tokens[at+2][0] == '+' ? '1' : '-1'; 2889 var minutes = getMinutesByHoursMinutes(tokens, n block, at+3) * add_or_subtract;2890 if (minutes == 0)2891 parsing_warnings.push([ n block, at+5, 'Adding zero in a variable time calculation does not change the variable time.'2892 + ' Please omit the calculation (example: " 12:00-sunset").' ]3471 var minutes = getMinutesByHoursMinutes(tokens, nrule, at+3) * add_or_subtract; 3472 if (minutes === 0) 3473 parsing_warnings.push([ nrule, at+5, 'Adding zero in a variable time calculation does not change the variable time.' 3474 + ' Please omit the calculation (example: "sunrise-(sunset-00:00)").' ] 2893 3475 ); 2894 3476 return minutes; … … 2904 3486 2905 3487 if (error) 2906 throw formatWarnErrorMessage(n block, error[0],3488 throw formatWarnErrorMessage(nrule, error[0], 2907 3489 'Calculcation with variable time is not in the right syntax' + error[1]); 2908 3490 } 2909 // }}} 2910 // }}} 2911 2912 // Weekday range parser (Mo,We-Fr,Sa[1-2,-1],PH) {{{ 2913 function parseWeekdayRange(tokens, at, selectors) { 3491 /* }}} */ 3492 /* }}} */ 3493 3494 /* Weekday range parser (Mo,We-Fr,Sa[1-2,-1],PH). {{{ 3495 * 3496 * :param tokens: List of token objects. 3497 * :param at: Position where the weekday tokens could be. 3498 * :param selectors: Reference to selector object. 3499 * :returns: Position at which the token does not belong to the selector anymore. 3500 */ 3501 function parseWeekdayRange(tokens, at, selectors, in_holiday_selector) { 3502 if (!in_holiday_selector) { 3503 in_holiday_selector = true; 3504 tokens[at][3] = 'weekday'; 3505 } 3506 2914 3507 for (; at < tokens.length; at++) { 2915 3508 if (matchTokens(tokens, at, 'weekday', '[')) { … … 2921 3514 2922 3515 // bad number 2923 if (from == 0 || from < -5 || from > 5)2924 throw formatWarnErrorMessage(n block, at,3516 if (from === 0 || from < -5 || from > 5) 3517 throw formatWarnErrorMessage(nrule, at, 2925 3518 'Number between -5 and 5 (except 0) expected'); 2926 3519 … … 2930 3523 for (var i = from; i <= to; i++) { 2931 3524 // bad number 2932 if (i == 0 || i < -5 || i > 5)2933 throw formatWarnErrorMessage(n block, at+2,3525 if (i === 0 || i < -5 || i > 5) 3526 throw formatWarnErrorMessage(nrule, at+2, 2934 3527 'Number between -5 and 5 (except 0) expected.'); 2935 3528 … … 2937 3530 } 2938 3531 } else { 2939 throw formatWarnErrorMessage(n block, at+2,3532 throw formatWarnErrorMessage(nrule, at+2, 2940 3533 'Bad range: ' + from + '-' + to); 2941 3534 } … … 2943 3536 2944 3537 if (!matchTokens(tokens, endat, ']')) 2945 throw formatWarnErrorMessage(n block, endat, '"]" or more numbers expected.');3538 throw formatWarnErrorMessage(nrule, endat, '"]" or more numbers expected.'); 2946 3539 2947 3540 var add_days = getMoveDays(tokens, endat+1, 6, 'constrained weekdays'); 2948 3541 week_stable = false; 2949 3542 2950 // Create selector for each list element 3543 // Create selector for each list element. 2951 3544 for (var nnumber = 0; nnumber < numbers.length; nnumber++) { 2952 3545 … … 2987 3580 } 2988 3581 3582 var target_day_with_added_moved_days_this_month; 2989 3583 if (add_days > 0) { 2990 vartarget_day_with_added_moved_days_this_month = dateAtNextWeekday(3584 target_day_with_added_moved_days_this_month = dateAtNextWeekday( 2991 3585 new Date(date.getFullYear(), date.getMonth() + (number > 0 ? 0 : 1) -1, 1), weekday); 2992 3586 target_day_with_added_moved_days_this_month.setDate(target_day_with_added_moved_days_this_month.getDate() … … 2996 3590 return [true, dateAtDayMinutes(date, minutes_in_day)]; 2997 3591 } else if (add_days < 0) { 2998 vartarget_day_with_added_moved_days_this_month = dateAtNextWeekday(3592 target_day_with_added_moved_days_this_month = dateAtNextWeekday( 2999 3593 new Date(date.getFullYear(), date.getMonth() + (number > 0 ? 0 : 1) + 1, 1), weekday); 3000 3594 target_day_with_added_moved_days_this_month.setDate(target_day_with_added_moved_days_this_month.getDate() … … 3065 3659 } else if (matchTokens(tokens, at, 'holiday')) { 3066 3660 week_stable = false; 3067 return parseHoliday(tokens, at, selectors, true );3661 return parseHoliday(tokens, at, selectors, true, in_holiday_selector); 3068 3662 } else { 3069 throw formatWarnErrorMessage(n block, at, 'Unexpected token in weekday range: ' + tokens[at][1]);3663 throw formatWarnErrorMessage(nrule, at, 'Unexpected token in weekday range: ' + tokens[at][1]); 3070 3664 } 3071 3665 … … 3074 3668 } 3075 3669 3076 if (typeof used_subparsers['weekdays'] != 'object')3077 used_subparsers['weekdays'] = [ at ];3078 else3079 used_subparsers['weekdays'].push(at);3080 3081 3670 return at; 3082 3671 } 3083 3672 // }}} 3673 3674 /* Get the number of days a date should be moved (if any). {{{ 3675 * 3676 * :param tokens: List of token objects. 3677 * :param at: Position where the date moving tokens could be. 3678 * :param max_differ: Maximal number of days to move (could also be zero if there are no day move tokens). 3679 * :returns: Array: 3680 * 0. Days to add. 3681 * 1. How many tokens. 3682 */ 3084 3683 function getMoveDays(tokens, at, max_differ, name) { 3085 var add_days = [ 0, 0 ]; // [ ' add days', 'how many tokens' ]3684 var add_days = [ 0, 0 ]; // [ 'days to add', 'how many tokens' ] 3086 3685 add_days[0] = matchTokens(tokens, at, '+') || (matchTokens(tokens, at, '-') ? -1 : 0); 3087 if (add_days[0] != 0 && matchTokens(tokens, at+1, 'number', 'calcday')) {3686 if (add_days[0] !== 0 && matchTokens(tokens, at+1, 'number', 'calcday')) { 3088 3687 // continues with '+ 5 days' or something like that 3089 3688 if (tokens[at+1][0] > max_differ) 3090 throw formatWarnErrorMessage(n block, at+2,3689 throw formatWarnErrorMessage(nrule, at+2, 3091 3690 'There should be no reason to differ more than ' + max_differ + ' days from a ' + name + '. If so tell us …'); 3092 3691 add_days[0] *= tokens[at+1][0]; 3093 if (add_days[0] == 0 && !done_with_warnings)3094 parsing_warnings.push([ n block, at+2, 'Adding 0 does not change the date. Please omit this.' ]);3692 if (add_days[0] === 0 && !done_with_warnings) 3693 parsing_warnings.push([ nrule, at+2, 'Adding 0 does not change the date. Please omit this.' ]); 3095 3694 add_days[1] = 3; 3096 3695 } else { … … 3101 3700 // }}} 3102 3701 3103 // Holiday parser for public and school holidays (PH,SH) {{{ 3104 // push_to_weekday will push the selector into the weekday selector array which has the desired side effect of working in conjunction with the weekday selectors (either the holiday match or the weekday), which is the normal and expected behavior. 3105 function parseHoliday(tokens, at, selectors, push_to_weekday) { 3702 /* Holiday parser for public and school holidays (PH,SH) {{{ 3703 * 3704 * :param tokens: List of token objects. 3705 * :param at: Position where to start. 3706 * :param selectors: Reference to selector object. 3707 * :param push_to_weekday: Will push the selector into the weekday selector array which has the desired side effect of working in conjunction with the weekday selectors (either the holiday match or the weekday), which is the normal and expected behavior. 3708 * :returns: Position at which the token does not belong to the selector anymore. 3709 */ 3710 function parseHoliday(tokens, at, selectors, push_to_weekday, in_holiday_selector) { 3711 if (!in_holiday_selector) { 3712 3713 if (push_to_weekday) 3714 tokens[at][3] = 'weekday'; 3715 else 3716 tokens[at][3] = 'holiday'; // Could also be holiday but this is not important here. 3717 } 3718 3106 3719 for (; at < tokens.length; at++) { 3107 3720 if (matchTokens(tokens, at, 'holiday')) { … … 3240 3853 } 3241 3854 } else if (matchTokens(tokens, at, 'weekday')) { 3242 return parseWeekdayRange(tokens, at, selectors );3855 return parseWeekdayRange(tokens, at, selectors, true); 3243 3856 } else { 3244 throw formatWarnErrorMessage(n block, at, 'Unexpected token (school holiday parser): ' + tokens[at][1]);3857 throw formatWarnErrorMessage(nrule, at, 'Unexpected token (school holiday parser): ' + tokens[at][1]); 3245 3858 } 3246 3859 … … 3253 3866 3254 3867 // Helpers for holiday parsers {{{ 3255 // Returns a number for a date which can then be used to compare just the dates (without the time). 3256 // This is necessary because a selector could be called for the middle of the day and we need to tell if it matches that day. 3257 // Example: Returns 20150015 for Jan 01 2015 3868 3869 /* Returns a number for a date which can then be used to compare just the dates (without the time). {{{ 3870 * This is necessary because a selector could be called for the middle of the day and we need to tell if it matches that day. 3871 * Example: Returns 20150015 for Jan 01 2015 3872 * 3873 * :param date: Date object. 3874 * :param include_year: Boolean. If true include the year. 3875 * :returns: Number for the date. 3876 */ 3258 3877 function getValueForDate(date, include_year) { 3259 // Implicit because undefined evaluates to false 3878 // Implicit because undefined evaluates to false. 3260 3879 // include_year = typeof include_year != 'undefined' ? include_year : false; 3261 3880 3262 3881 return (include_year ? date.getFullYear() * 10000 : 0) + date.getMonth() * 100 + date.getDate(); 3263 3882 } 3883 // }}} 3264 3884 3265 3885 // return the school holiday definition e.g. [ 5, 25, /* to */ 6, 5 ], … … 3274 3894 if (typeof holiday == 'undefined') { 3275 3895 if (fatal) { 3276 throw 'School holiday ' + SH_hash.name + ' has no definition for the year ' + year + '.'; 3896 throw formatLibraryBugMessage('School holiday ' + SH_hash.name + ' has no definition for the year ' + year + '.' 3897 + ' You can also add them: ' + repository_url); 3277 3898 } else { 3278 3899 return undefined; … … 3297 3918 } else if (holidays[location_cc][type_of_holidays]) { 3298 3919 // holidays are only defined country wide 3299 matching_holiday = {}; // holidays in the country wide scope can be limited to certain states3920 var matching_holiday = {}; // holidays in the country wide scope can be limited to certain states 3300 3921 for (var holiday_name in holidays[location_cc][type_of_holidays]) { 3301 3922 if (typeof holidays[location_cc][type_of_holidays][holiday_name][2] === 'object') { 3302 if (-1 != indexOf.call(holidays[location_cc][type_of_holidays][holiday_name][2],location_state))3923 if (-1 != holidays[location_cc][type_of_holidays][holiday_name][2].indexOf(location_state)) 3303 3924 matching_holiday[holiday_name] = holidays[location_cc][type_of_holidays][holiday_name]; 3304 3925 } else { … … 3306 3927 } 3307 3928 } 3308 if (Object.keys(matching_holiday).length == 0)3309 throw'There are no holidays ' + type_of_holidays + ' defined for country ' + location_cc + '.'3310 + ' Please add them: https://github.com/ypid/opening_hours.js ';3929 if (Object.keys(matching_holiday).length === 0) 3930 throw formatLibraryBugMessage('There are no holidays ' + type_of_holidays + ' defined for country ' + location_cc + '.' 3931 + ' You can also add them: ' + repository_url); 3311 3932 return matching_holiday; 3312 3933 } else { 3313 throw 'Holidays ' + type_of_holidays + ' are not defined for country ' + location_cc3314 + ' and state ' + location_state + '.'3315 + ' Please add them.';3934 throw formatLibraryBugMessage('Holidays ' + type_of_holidays + ' are not defined for country ' + location_cc 3935 + ' and state ' + location_state + '.' 3936 + ' You can also add them: ' + repository_url); 3316 3937 } 3317 3938 } 3318 3939 } else { 3319 throw 'No holidays are defined for country ' + location_cc + '. Please add them: https://github.com/ypid/opening_hours.js '; 3940 throw formatLibraryBugMessage('No holidays are defined for country ' + location_cc + '.' 3941 + ' You can also add them: ' + repository_url); 3320 3942 } 3321 3943 } else { // we have no idea which holidays do apply because the country code was not provided 3322 throw 'Country code missing which is needed to select the correct holidays (see README how to provide it)' 3944 throw 'Country code missing which is needed to select the correct holidays (see README how to provide it)'; 3323 3945 } 3324 3946 } … … 3344 3966 var oD = (19*oC + 15) % 30; 3345 3967 var oE = (2*oA+4*oB - oD + 34) % 7; 3346 var oF = oD+oE 3347 3348 if (oF < 9) {oDate = new Date(Y, 4-1, oF+4);} 3349 else {if ((oF+4)<31) {oDate = new Date(Y, 4-1, oF+4);} 3350 else {oDate = new Date(Y, 5-1, oF-26);}} 3968 var oF = oD+oE; 3969 3970 var oDate; 3971 if (oF < 9) { 3972 oDate = new Date(Y, 4-1, oF+4); 3973 } else { 3974 if ((oF+4)<31) { 3975 oDate = new Date(Y, 4-1, oF+4); 3976 } else { 3977 oDate = new Date(Y, 5-1, oF-26); 3978 } 3979 } 3351 3980 3352 3981 // calculate last Sunday in February … … 3368 3997 var firstMonday = 1 + ((8 - first.getDay()) % 7); 3369 3998 firstMondays[i] = firstMonday; 3370 } ;3999 } 3371 4000 3372 4001 return { … … 3387 4016 3388 4017 var sorted_holidays = []; 4018 var next_holiday; 3389 4019 3390 4020 for (var holiday_name in applying_holidays) { … … 3394 4024 throw 'Movable day ' + applying_holidays[holiday_name][0] + ' can not not be calculated.' 3395 4025 + ' Please add the formula how to calculate it.'; 3396 varnext_holiday = new Date(selected_movableDay.getFullYear(),4026 next_holiday = new Date(selected_movableDay.getFullYear(), 3397 4027 selected_movableDay.getMonth(), 3398 4028 selected_movableDay.getDate() … … 3404 4034 + ' days is not in the year of the movable day anymore. Currently not supported.'; 3405 4035 } else { 3406 varnext_holiday = new Date(year,4036 next_holiday = new Date(year, 3407 4037 applying_holidays[holiday_name][0] - 1, 3408 4038 applying_holidays[holiday_name][1] … … 3426 4056 // }}} 3427 4057 3428 // Year range parser (2013,2016-2018,2020/2) {{{ 4058 /* Year range parser (2013,2016-2018,2020/2). {{{ 4059 * 4060 * :param tokens: List of token objects. 4061 * :param at: Position where to start. 4062 * :returns: Position at which the token does not belong to the selector anymore. 4063 */ 3429 4064 function parseYearRange(tokens, at) { 4065 tokens[at][3] = 'year'; 3430 4066 for (; at < tokens.length; at++) { 3431 4067 if (matchTokens(tokens, at, 'year')) { 3432 var is_range = false, has_period = false; 4068 var is_range = false, 4069 has_period, 4070 period; 3433 4071 if (matchTokens(tokens, at+1, '-', 'year', '/', 'number')) { 3434 varis_range = true;3435 varhas_period = true;3436 varperiod = parseInt(tokens[at+4][0]);4072 is_range = true; 4073 has_period = true; 4074 period = parseInt(tokens[at+4][0]); 3437 4075 checkPeriod(at+4, period, 'year'); 3438 4076 } else { 3439 varis_range = matchTokens(tokens, at+1, '-', 'year');3440 varhas_period = matchTokens(tokens, at+1, '/', 'number');4077 is_range = matchTokens(tokens, at+1, '-', 'year'); 4078 has_period = matchTokens(tokens, at+1, '/', 'number'); 3441 4079 if (has_period) { 3442 varperiod = parseInt(tokens[at+2][0]);4080 period = parseInt(tokens[at+2][0]); 3443 4081 checkPeriod(at+2, period, 'year', 'no_end_year'); 3444 4082 } else if (matchTokens(tokens, at+1, '+')) { 3445 varperiod = 1;4083 period = 1; 3446 4084 has_period = 2; 3447 4085 } … … 3453 4091 // handle reversed range 3454 4092 if (tokens[at+2][0] == year_from) 3455 throw formatWarnErrorMessage(n block, at,4093 throw formatWarnErrorMessage(nrule, at, 3456 4094 'A year range in which the start year is equal to the end year does not make sense.' 3457 4095 + ' Please remove the end year. E.g. "' + year_from + ' May 23"'); 3458 4096 else 3459 throw formatWarnErrorMessage(n block, at,4097 throw formatWarnErrorMessage(nrule, at, 3460 4098 'A year range in which the start year is greater than the end year does not make sense.' 3461 4099 + ' Please turn it over.'); … … 3471 4109 } else if (has_period) { 3472 4110 if (year_from <= ouryear) { 3473 if (is_range && year_to < ouryear)4111 if (is_range && ouryear > year_to) 3474 4112 return [false]; 3475 4113 if (period > 0) { 3476 if ((ouryear - year_from) % period == 0) {4114 if ((ouryear - year_from) % period === 0) { 3477 4115 return [true, new Date(ouryear + 1, 0, 1)]; 3478 4116 } else { … … 3494 4132 at += 1 + (is_range ? 2 : 0) + (has_period ? (has_period == 2 ? 1 : 2) : 0); 3495 4133 } else { 3496 throw formatWarnErrorMessage(n block, at, 'Unexpected token in year range: ' + tokens[at][1]);4134 throw formatWarnErrorMessage(nrule, at, 'Unexpected token in year range: ' + tokens[at][1]); 3497 4135 } 3498 4136 … … 3501 4139 } 3502 4140 3503 if (typeof used_subparsers['year ranges'] != 'object')3504 used_subparsers['year ranges'] = [ at ];3505 else3506 used_subparsers['year ranges'].push(at);3507 3508 4141 return at; 3509 4142 } 3510 4143 // }}} 3511 4144 3512 // Week range parser (week 11-20, week 1-53/2) {{{ 4145 /* Week range parser (week 11-20, week 1-53/2). {{{ 4146 * 4147 * :param tokens: List of token objects. 4148 * :param at: Position where to start. 4149 * :returns: Position at which the token does not belong to the selector anymore. 4150 */ 3513 4151 function parseWeekRange(tokens, at) { 3514 4152 for (; at < tokens.length; at++) { 4153 if (matchTokens(tokens, at, 'week')) { 4154 at++; 4155 } 3515 4156 if (matchTokens(tokens, at, 'number')) { 3516 var is_range = matchTokens(tokens, at+1, '-', 'number'), has_period = false; 4157 var is_range = matchTokens(tokens, at+1, '-', 'number'), period = 0; 4158 var week_from = tokens[at][0]; 4159 var week_to = is_range ? tokens[at+2][0] : week_from; 4160 if (week_from > week_to) { 4161 throw formatWarnErrorMessage(nrule, at+2, 4162 'You have specified a week range in reverse order or leaping over a year. This is (currently) not supported.'); 4163 } 4164 if (week_from < 1) { 4165 throw formatWarnErrorMessage(nrule, at, 4166 'You have specified a week date less then one. A valid week date range is 1-53.'); 4167 } 4168 if (week_to > 53) { 4169 throw formatWarnErrorMessage(nrule, is_range ? at+2 : at, 4170 'You have specified a week date greater then 53. A valid week date range is 1-53.'); 4171 } 3517 4172 if (is_range) { 3518 has_period = matchTokens(tokens, at+3, '/', 'number'); 3519 // if (week_stable) { 3520 // if (tokens[at][0] == 1 && tokens[at+2][0] >) // Maximum? 3521 // week_stable = true; 3522 // else 3523 // week_stable = false; 3524 // } else { 3525 // week_stable = false; 3526 // } 3527 } 3528 3529 selectors.week.push(function(tokens, at, is_range, has_period) { return function(date) { 3530 var ourweek = Math.floor((date - dateAtWeek(date, 0)) / msec_in_week); 3531 3532 var week_from = tokens[at][0] - 1; 3533 var week_to = is_range ? tokens[at+2][0] - 1 : week_from; 3534 3535 var start_of_next_year = new Date(date.getFullYear() + 1, 0, 1); 3536 3537 // before range 3538 if (ourweek < week_from) 3539 return [false, getMinDate(dateAtWeek(date, week_from), start_of_next_year)]; 3540 3541 // we're after range, set check date to next year 3542 if (ourweek > week_to) 3543 return [false, start_of_next_year]; 3544 3545 // we're in range 3546 var period; 3547 if (has_period) { 3548 var period = tokens[at+4][0]; 3549 if (period > 1) { 3550 var in_period = (ourweek - week_from) % period == 0; 3551 if (in_period) 3552 return [true, getMinDate(dateAtWeek(date, ourweek + 1), start_of_next_year)]; 3553 else 3554 return [false, getMinDate(dateAtWeek(date, ourweek + period - 1), start_of_next_year)]; 4173 period = matchTokens(tokens, at+3, '/', 'number'); 4174 if (period) { 4175 period = tokens[at+4][0]; 4176 if (period < 2) { 4177 throw formatWarnErrorMessage(nrule, at+4, 4178 'You have specified a week period which is less than two.' 4179 + ' If you want to select the whole range from week ' + week_from + ' to week ' + week_to + ' then just omit the "/' + period + '".'); 4180 } else if (period > 26) { 4181 throw formatWarnErrorMessage(nrule, at+4, 4182 'You have specified a week period which is greater than 26.' 4183 + ' 26.5 is the half of the maximum 53 week dates per year so a week date period greater than 26 would only apply once per year.' 4184 + ' Please specify the week selector as "week ' + week_from + '" if that is what you want to express.'); 3555 4185 } 3556 4186 } 3557 3558 return [true, getMinDate(dateAtWeek(date, week_to + 1), start_of_next_year)]; 3559 }}(tokens, at, is_range, has_period)); 3560 3561 at += 1 + (is_range ? 2 : 0) + (has_period ? 2 : 0); 4187 } 4188 4189 if (week_stable && (!(week_from <= 1 && week_to >= 53) || period)) { 4190 week_stable = false; 4191 } 4192 4193 if (!period && week_from == 1 && week_to == 53) { 4194 /* Shortcut and work around bug. */ 4195 selectors.week.push(function(date) { return [true]; }); 4196 } else { 4197 4198 selectors.week.push(function(week_from, week_to, is_range, period) { return function(date) { 4199 var ourweek = date.getWeekNumber(); 4200 4201 // console.log("week_from: %s, week_to: %s", week_from, week_to); 4202 // console.log("ourweek: %s, date: %s", ourweek, date); 4203 4204 // before range 4205 if (ourweek < week_from) { 4206 // console.log("Before: " + getNextDateOfISOWeek(week_from, date)); 4207 return [false, getNextDateOfISOWeek(week_from, date)]; 4208 } 4209 4210 // we're after range, set check date to next year 4211 if (ourweek > week_to) { 4212 // console.log("After"); 4213 return [false, getNextDateOfISOWeek(week_from, date)]; 4214 } 4215 4216 // we're in range 4217 if (period) { 4218 var in_period = (ourweek - week_from) % period === 0; 4219 if (in_period) { 4220 return [true, getNextDateOfISOWeek(ourweek + 1, date)]; 4221 } else { 4222 return [false, getNextDateOfISOWeek(ourweek + period - 1, date)]; 4223 } 4224 } 4225 4226 // console.log("Match"); 4227 return [true, getNextDateOfISOWeek(week_to == 53 ? 1 : week_to + 1, date)]; 4228 }}(week_from, week_to, is_range, period)); 4229 } 4230 4231 at += 1 + (is_range ? 2 : 0) + (period ? 2 : 0); 3562 4232 } else { 3563 throw formatWarnErrorMessage(n block, at, 'Unexpected token in week range: ' + tokens[at][1]);4233 throw formatWarnErrorMessage(nrule, at, 'Unexpected token in week range: ' + tokens[at][1]); 3564 4234 } 3565 4235 3566 4236 if (!matchTokens(tokens, at, ',')) 3567 4237 break; 3568 3569 if (!matchTokens(tokens, at+1, 'number')) { 3570 at++; // we don‘t need the comma in parseGroup 3571 break; 4238 } 4239 4240 return at; 4241 } 4242 4243 // http://stackoverflow.com/a/6117889 4244 Date.prototype.getWeekNumber = function(){ 4245 var d = new Date(+this); 4246 d.setHours(0,0,0); 4247 d.setDate(d.getDate()+4-(d.getDay()||7)); 4248 return Math.ceil((((d-new Date(d.getFullYear(),0,1))/8.64e7)+1)/7); 4249 }; 4250 // http://stackoverflow.com/a/16591175 4251 function getDateOfISOWeek(w, y) { 4252 var simple = new Date(y, 0, 1 + (w - 1) * 7); 4253 var dow = simple.getDay(); 4254 var ISOweekStart = simple; 4255 if (dow <= 4) 4256 ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1); 4257 else 4258 ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay()); 4259 return ISOweekStart; 4260 } 4261 function getNextDateOfISOWeek(week, date) { 4262 var next_date; 4263 for (var i = -1; i <= 1; i++) { 4264 next_date = getDateOfISOWeek(week, date.getFullYear() + i); 4265 if (next_date.getTime() > date.getTime()) { 4266 return next_date; 3572 4267 } 3573 4268 } 3574 3575 if (typeof used_subparsers['week ranges'] != 'object') 3576 used_subparsers['week ranges'] = [ at ]; 3577 else 3578 used_subparsers['week ranges'].push; 3579 3580 return at; 3581 } 3582 3583 function dateAtWeek(date, week) { 3584 var tmpdate = new Date(date.getFullYear(), 0, 1); 3585 tmpdate.setDate(1 - (tmpdate.getDay() + 6) % 7 + week * 7); // start of week n where week starts on Monday 3586 return tmpdate; 3587 } 3588 3589 function getMinDate(date /*, ...*/) { 3590 for (var i = 1; i < arguments.length; i++) 3591 if (arguments[i].getTime() < date.getTime()) 3592 date = arguments[i]; 3593 return date; 4269 throw formatLibraryBugMessage(); 3594 4270 } 3595 4271 // }}} 3596 4272 3597 // Month range parser (Jan,Feb-Mar) {{{ 3598 // push_to_monthday will push the selector into the monthday selector array which has the desired side effect of working in conjunction with the monthday selectors (either the month match or the monthday). 3599 function parseMonthRange(tokens, at, push_to_monthday) { 4273 /* Month range parser (Jan,Feb-Mar). {{{ 4274 * 4275 * :param tokens: List of token objects. 4276 * :param at: Position where to start. 4277 * :param push_to_monthday: Will push the selector into the monthday selector array which has the desired side effect of working in conjunction with the monthday selectors (either the month match or the monthday). 4278 * :returns: Position at which the token does not belong to the selector anymore. 4279 */ 4280 function parseMonthRange(tokens, at, push_to_monthday, in_selector) { 4281 if (!in_selector) 4282 tokens[at][3] = 'month'; 4283 3600 4284 for (; at < tokens.length; at++) { 3601 4285 // Use parseMonthdayRange if '<month> <daynum>' and not '<month> <hour>:<minute>' 3602 4286 if (matchTokens(tokens, at, 'month', 'number') && !matchTokens(tokens, at+2, 'timesep', 'number')) { 3603 return parseMonthdayRange(tokens, at, n block, true);4287 return parseMonthdayRange(tokens, at, nrule, true); 3604 4288 } else if (matchTokens(tokens, at, 'month')) { 3605 4289 // Single month (Jan) or month range (Feb-Mar) 3606 4290 var is_range = matchTokens(tokens, at+1, '-', 'month'); 3607 4291 4292 var month_from = tokens[at][0]; 4293 var month_to = is_range ? tokens[at+2][0] : month_from; 4294 3608 4295 if (is_range && week_stable) { 3609 var month_from = tokens[at][0]; 3610 var month_to = tokens[at+2][0]; 3611 if (month_from == (month_to + 1) % 12) 3612 week_stable = true; 3613 else 4296 if (month_from !== (month_to + 1) % 12) 3614 4297 week_stable = false; 3615 4298 } else { … … 3617 4300 } 3618 4301 3619 var selector = function(tokens, at, is_range) { return function(date) { 4302 var inside = true; 4303 4304 // handle reversed range 4305 if (month_to < month_from) { 4306 var tmp = month_to; 4307 month_to = month_from - 1; 4308 month_from = tmp + 1; 4309 inside = false; 4310 } 4311 4312 var selector = function(tokens, at, month_from, month_to, is_range, inside) { return function(date) { 3620 4313 var ourmonth = date.getMonth(); 3621 var month_from = tokens[at][0];3622 var month_to = is_range ? tokens[at+2][0] : month_from;3623 3624 var inside = true;3625 3626 // handle reversed range3627 if (month_to < month_from) {3628 var tmp = month_to;3629 month_to = month_from - 1;3630 month_from = tmp + 1;3631 inside = false;3632 }3633 4314 3634 4315 // handle full range … … 3641 4322 return [inside, dateAtNextMonth(date, month_to + 1)]; 3642 4323 } 3643 }}(tokens, at, is_range);4324 }}(tokens, at, month_from, month_to, is_range, inside); 3644 4325 3645 4326 if (push_to_monthday === true) … … 3650 4331 at += is_range ? 3 : 1; 3651 4332 } else { 3652 throw formatWarnErrorMessage(n block, at, 'Unexpected token in month range: ' + tokens[at][1]);4333 throw formatWarnErrorMessage(nrule, at, 'Unexpected token in month range: ' + tokens[at][1]); 3653 4334 } 3654 4335 … … 3656 4337 break; 3657 4338 } 3658 3659 if (typeof used_subparsers['months'] != 'object')3660 used_subparsers['months'] = [ at ];3661 else3662 used_subparsers['months'].push(at);3663 4339 3664 4340 return at; … … 3670 4346 // }}} 3671 4347 3672 // Month day range parser (Jan 26-31; Jan 26-Feb 26) {{{ 3673 // push_to_month will push the selector into the month selector array which has the desired side effect of working in conjunction with the month selectors (either the month match or the monthday). 3674 function parseMonthdayRange(tokens, at, nblock, push_to_month) { 4348 /* Month day range parser (Jan 26-31; Jan 26-Feb 26). {{{ 4349 * 4350 * :param tokens: List of token objects. 4351 * :param at: Position where to start. 4352 * :param nrule: Rule number starting with 0. 4353 * :param push_to_month: Will push the selector into the month selector array which has the desired side effect of working in conjunction with the month selectors (either the month match or the monthday). 4354 * :returns: Position at which the token does not belong to the selector anymore. 4355 */ 4356 function parseMonthdayRange(tokens, at, nrule, push_to_month) { 4357 if (!push_to_month) 4358 tokens[at][3] = 'month'; 4359 3675 4360 for (; at < tokens.length; at++) { 3676 var has_year = [], has_month = [], has_event = [], has_calc = [], has_constrained_weekday = [] , has_calc = [];4361 var has_year = [], has_month = [], has_event = [], has_calc = [], has_constrained_weekday = []; 3677 4362 has_year[0] = matchTokens(tokens, at, 'year'); 3678 4363 has_month[0] = matchTokens(tokens, at+has_year[0], 'month', 'number'); 3679 4364 has_event[0] = matchTokens(tokens, at+has_year[0], 'event'); 4365 3680 4366 if (has_event[0]) 3681 4367 has_calc[0] = getMoveDays(tokens, at+has_year[0]+1, 200, 'event like easter'); 3682 4368 4369 var at_range_sep; 3683 4370 if (matchTokens(tokens, at+has_year[0], 'month', 'weekday', '[')) { 3684 4371 has_constrained_weekday[0] = getConstrainedWeekday(tokens, at+has_year[0]+3); 3685 4372 has_calc[0] = getMoveDays(tokens, has_constrained_weekday[0][1], 6, 'constrained weekdays'); 3686 varat_range_sep = has_constrained_weekday[0][1] + (typeof has_calc[0] != 'undefined' && has_calc[0][1] ? 3 : 0);4373 at_range_sep = has_constrained_weekday[0][1] + (typeof has_calc[0] != 'undefined' && has_calc[0][1] ? 3 : 0); 3687 4374 } else { 3688 varat_range_sep = at+has_year[0]4375 at_range_sep = at+has_year[0] 3689 4376 + (has_event[0] 3690 4377 ? (typeof has_calc[0] != 'undefined' && has_calc[0][1] ? 4 : 1) … … 3692 4379 } 3693 4380 4381 var at_sec_event_or_month; 3694 4382 if ((has_month[0] || has_event[0] || has_constrained_weekday[0]) && matchTokens(tokens, at_range_sep, '-')) { 3695 has_year[1] 3696 varat_sec_event_or_month = at_range_sep+1+has_year[1];4383 has_year[1] = matchTokens(tokens, at_range_sep+1, 'year'); 4384 at_sec_event_or_month = at_range_sep+1+has_year[1]; 3697 4385 has_month[1] = matchTokens(tokens, at_sec_event_or_month, 'month', 'number'); 3698 4386 if (!has_month[1]) { … … 3711 4399 3712 4400 if (has_month[0]) 3713 isValidDate(tokens[at+has_year[0]][0], tokens[at+has_year[0]+1][0], nblock, at+has_year[0]+1);4401 checkIfDateIsValid(tokens[at+has_year[0]][0], tokens[at+has_year[0]+1][0], nrule, at+has_year[0]+1); 3714 4402 if (has_month[1]) 3715 isValidDate(tokens[at_sec_event_or_month][0], tokens[at_sec_event_or_month+1][0], nblock, at+has_year[0]+1);3716 3717 var selector = function(tokens, at, n block, has_year, has_event, has_calc, at_sec_event_or_month, has_constrained_weekday) { return function(date) {4403 checkIfDateIsValid(tokens[at_sec_event_or_month][0], tokens[at_sec_event_or_month+1][0], nrule, at_sec_event_or_month+1); 4404 4405 var selector = function(tokens, at, nrule, has_year, has_event, has_calc, at_sec_event_or_month, has_constrained_weekday) { return function(date) { 3718 4406 var start_of_next_year = new Date(date.getFullYear() + 1, 0, 1); 3719 4407 4408 var movableDays, 4409 from_date; 3720 4410 if (has_event[0]) { 3721 varmovableDays = getMovableEventsForYear(has_year[0] ? parseInt(tokens[at][0]) : date.getFullYear());3722 var from_date= movableDays[tokens[at+has_year[0]][0]];4411 movableDays = getMovableEventsForYear(has_year[0] ? parseInt(tokens[at][0]) : date.getFullYear()); 4412 from_date = movableDays[tokens[at+has_year[0]][0]]; 3723 4413 3724 4414 if (typeof has_calc[0] != 'undefined' && has_calc[0][1]) { … … 3726 4416 from_date.setDate(from_date.getDate() + has_calc[0][0]); 3727 4417 if (from_year_before_calc != from_date.getFullYear()) 3728 throw formatWarnErrorMessage(n block, at+has_year[0]+has_calc[0][1]*3,4418 throw formatWarnErrorMessage(nrule, at+has_year[0]+has_calc[0][1]*3, 3729 4419 'The movable day ' + tokens[at+has_year[0]][0] + ' plus ' + has_calc[0][0] 3730 4420 + ' days is not in the year of the movable day anymore. Currently not supported.'); 3731 4421 } 3732 4422 } else if (has_constrained_weekday[0]) { 3733 varfrom_date = getDateForConstrainedWeekday((has_year[0] ? tokens[at][0] : date.getFullYear()), // year4423 from_date = getDateForConstrainedWeekday((has_year[0] ? tokens[at][0] : date.getFullYear()), // year 3734 4424 tokens[at+has_year[0]][0], // month 3735 4425 tokens[at+has_year[0]+1][0], // weekday 3736 4426 has_constrained_weekday[0], 3737 4427 has_calc[0]); 3738 // var from_date_without_calc = getDateForConstrainedWeekday((has_year[0] ? tokens[at][0] : date.getFullYear()), // year3739 // tokens[at+has_year[0]][0], // month3740 // tokens[at+has_year[0]+1][0], // weekday3741 // has_constrained_weekday[0],3742 // [ 0, 0 ]);3743 // if (from_date_without_calc.getFullYear() != from_date.getFullYear())3744 // throw formatWarnErrorMessage(nblock, at+has_year[0]+has_calc[0][1],3745 // 'The constrained ' + weekdays[tokens[at+has_year[0]+1][0]] + ' plus ' + has_calc[0][0]3746 // + ' days is not in the year of the movable day anymore. Currently not supported.');3747 4428 } else { 3748 varfrom_date = new Date((has_year[0] ? tokens[at][0] : date.getFullYear()),4429 from_date = new Date((has_year[0] ? tokens[at][0] : date.getFullYear()), 3749 4430 tokens[at+has_year[0]][0], tokens[at+has_year[0]+1][0]); 3750 4431 } 3751 4432 4433 var to_date; 3752 4434 if (has_event[1]) { 3753 varmovableDays = getMovableEventsForYear(has_year[1]4435 movableDays = getMovableEventsForYear(has_year[1] 3754 4436 ? parseInt(tokens[at_sec_event_or_month-1][0]) 3755 4437 : date.getFullYear()); 3756 var to_date= movableDays[tokens[at_sec_event_or_month][0]];4438 to_date = movableDays[tokens[at_sec_event_or_month][0]]; 3757 4439 3758 4440 if (typeof has_calc[1] != 'undefined' && has_calc[1][1]) { … … 3760 4442 to_date.setDate(to_date.getDate() + has_calc[1][0]); 3761 4443 if (to_year_before_calc != to_date.getFullYear()) 3762 throw formatWarnErrorMessage(n block, at_sec_event_or_month+has_calc[1][1],4444 throw formatWarnErrorMessage(nrule, at_sec_event_or_month+has_calc[1][1], 3763 4445 'The movable day ' + tokens[at_sec_event_or_month][0] + ' plus ' + has_calc[1][0] 3764 4446 + ' days is not in the year of the movable day anymore. Currently not supported.'); 3765 4447 } 3766 4448 } else if (has_constrained_weekday[1]) { 3767 varto_date = getDateForConstrainedWeekday((has_year[1] ? tokens[at_sec_event_or_month-1][0] : date.getFullYear()), // year4449 to_date = getDateForConstrainedWeekday((has_year[1] ? tokens[at_sec_event_or_month-1][0] : date.getFullYear()), // year 3768 4450 tokens[at_sec_event_or_month][0], // month 3769 4451 tokens[at_sec_event_or_month+1][0], // weekday … … 3771 4453 has_calc[1]); 3772 4454 } else { 3773 varto_date = new Date((has_year[1] ? tokens[at_sec_event_or_month-1][0] : date.getFullYear()),4455 to_date = new Date((has_year[1] ? tokens[at_sec_event_or_month-1][0] : date.getFullYear()), 3774 4456 tokens[at_sec_event_or_month][0], tokens[at_sec_event_or_month+1][0] + 1); 3775 4457 } … … 3792 4474 return [!inside]; 3793 4475 } else { 3794 // // back matching, if from_date is moved to last year3795 // var from_date_next_year = getDateForConstrainedWeekday(date.getFullYear() + 1, // year3796 // tokens[at+has_year[0]][0], // month3797 // tokens[at+has_year[0]+1][0], // weekday3798 // has_constrained_weekday[0],3799 // has_calc[0]);3800 // if (date.getFullYear() == from_date_next_year.getFullYear()) {3801 // if (date.getTime() < from_date_next_year.getTime()) {3802 // return [!inside, from_date_next_year];3803 // }3804 // }3805 3806 4476 return [!inside, start_of_next_year]; 3807 4477 } 3808 4478 } 3809 }}(tokens, at, n block, has_year, has_event, has_calc, at_sec_event_or_month, has_constrained_weekday);4479 }}(tokens, at, nrule, has_year, has_event, has_calc, at_sec_event_or_month, has_constrained_weekday); 3810 4480 3811 4481 if (push_to_month === true) … … 3848 4518 && (matchTokens(tokens, at_timesep_if_monthRange+2, '+') 3849 4519 || matchTokens(tokens, at_timesep_if_monthRange+2, '-') 3850 || oh_mode != 0)) 3851 return parseMonthRange(tokens, at); 4520 || oh_mode !== 0)) { 4521 return parseMonthRange(tokens, at, true, true); 4522 } 3852 4523 } 3853 4524 3854 4525 // error checking {{{ 3855 4526 if (range_to < range_from) 3856 throw formatWarnErrorMessage(n block, at+has_year+3,4527 throw formatWarnErrorMessage(nrule, at+has_year+3, 3857 4528 'Range in wrong order. From day is greater than to day.'); 3858 isValidDate(month, range_from, nblock, at+1 + has_year); 3859 isValidDate(month, range_to - 1 /* added previously */, 3860 nblock, at+has_year+(is_range ? 3 : 1)); 4529 4530 checkIfDateIsValid(month, range_from, nrule, at+1 + has_year); 4531 checkIfDateIsValid(month, range_to - 1 /* added previously */, 4532 nrule, at+has_year+(is_range ? 3 : 1)); 3861 4533 // }}} 3862 4534 … … 3885 4557 var in_period = nday % period; 3886 4558 3887 if (in_period == 0)4559 if (in_period === 0) 3888 4560 return [true, new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1)]; 3889 4561 else … … 3908 4580 } else if (has_event[0]) { 3909 4581 3910 var selector = function(tokens, at, n block, has_year, add_days) { return function(date) {4582 var selector = function(tokens, at, nrule, has_year, add_days) { return function(date) { 3911 4583 3912 4584 // console.log('enter selector with date: ' + date); … … 3920 4592 event_date.setDate(event_date.getDate() + add_days[0]); 3921 4593 if (date.getFullYear() != event_date.getFullYear()) 3922 throw formatWarnErrorMessage(n block, at+has_year+add_days[1], 'The movable day ' + tokens[at+has_year][0] + ' plus '4594 throw formatWarnErrorMessage(nrule, at+has_year+add_days[1], 'The movable day ' + tokens[at+has_year][0] + ' plus ' 3923 4595 + add_days[0] 3924 4596 + ' days is not in the year of the movable day anymore. Currently not supported.'); … … 3933 4605 return [false, new Date(date.getFullYear() + 1, 0, 1)]; 3934 4606 3935 }}(tokens, at, n block, has_year[0], has_calc[0]);4607 }}(tokens, at, nrule, has_year[0], has_calc[0]); 3936 4608 3937 4609 if (push_to_month === true) … … 3945 4617 at = parseMonthRange(tokens, at); 3946 4618 } else if (matchTokens(tokens, at, 'month')) { 3947 return parseMonthRange(tokens, at, true );4619 return parseMonthRange(tokens, at, true, true); 3948 4620 } else { 3949 4621 // throw 'Unexpected token in monthday range: "' + tokens[at] + '"'; … … 3955 4627 } 3956 4628 3957 if (typeof used_subparsers['monthday ranges'] != 'object')3958 used_subparsers['monhday ranges'] = [ at ];3959 else3960 used_subparsers['monhday ranges'].push(at);3961 3962 4629 return at; 3963 4630 } 3964 4631 // }}} 3965 4632 3966 // Main selector traversal function (return state array for date) {{{ 4633 /* Main selector traversal function (return state array for date). {{{ 4634 * Checks for given date which rule and those which state and comment applies. 4635 * 4636 * :param date: Date object. 4637 * :returns: Array: 4638 * 0. resultstate: State: true for 'open', false for 'closed'. 4639 * 1. changedate: Next change as date object. 4640 * 2. unknown: true if state open is not sure. 4641 * 3. comment: Comment which applies for this time range (from date to changedate). 4642 * 4. match_rule: Rule number starting with 0 (nrule). 4643 */ 3967 4644 this.getStatePair = function(date) { 3968 4645 var resultstate = false; … … 3970 4647 var unknown = false; 3971 4648 var comment; 3972 var match_ block;3973 3974 var date_matching_ blocks = [];3975 3976 for (var n block = 0; nblock < blocks.length; nblock++) {3977 var matching_date_ block= true;3978 // console.log(n block, 'length', blocks[nblock].date.length);4649 var match_rule; 4650 4651 var date_matching_rules = []; 4652 4653 for (var nrule = 0; nrule < rules.length; nrule++) { 4654 var matching_date_rule = true; 4655 // console.log(nrule, 'length', rules[nrule].date.length); 3979 4656 3980 4657 // Try each date selector type 3981 for (var ndateselector = 0; ndateselector < blocks[nblock].date.length; ndateselector++) {3982 var dateselectors = blocks[nblock].date[ndateselector];3983 // console.log(n block, ndateselector);4658 for (var ndateselector = 0; ndateselector < rules[nrule].date.length; ndateselector++) { 4659 var dateselectors = rules[nrule].date[ndateselector]; 4660 // console.log(nrule, ndateselector); 3984 4661 3985 4662 var has_matching_selector = false; … … 3999 4676 4000 4677 if (!has_matching_selector) { 4001 matching_date_ block= false;4678 matching_date_rule = false; 4002 4679 // We can ignore other date selectors, as the state won't change 4003 4680 // anyway until THIS selector matches (due to conjunction of date … … 4010 4687 } 4011 4688 4012 if (matching_date_ block) {4689 if (matching_date_rule) { 4013 4690 // The following lines implement date overwriting logic (e.g. for 4014 // "Mo-Fr 10:00-20:00; We 10:00-16:00", We block overrides Mo-Fr block.4691 // "Mo-Fr 10:00-20:00; We 10:00-16:00", We rule overrides Mo-Fr rule partly (We). 4015 4692 // 4016 4693 // This is the only way to be consistent. I thought about ("22:00-02:00; Tu 12:00-14:00") letting Th override 22:00-02:00 partly: 4017 4694 // Like: Th 00:00-02:00,12:00-14:00 but this would result in including 22:00-00:00 for Th which is probably not what you want. 4018 if (blocks[nblock].date.length > 0 && (blocks[nblock].meaning || blocks[nblock].unknown) 4019 && !blocks[nblock].wrapped && !blocks[nblock].additional && !blocks[nblock].fallback) { 4020 // var old_date_matching_blocks = date_matching_blocks; 4021 date_matching_blocks = []; 4022 // for (var nblock = 0; nblock < old_date_matching_blocks.length; nblock++) { 4023 // if (!blocks[old_date_matching_blocks[nblock]].wrapped) 4024 // date_matching_blocks.push(nblock); 4695 if ((rules[nrule].date.length > 0 || nrule > 0 && rules[nrule].meaning && rules[nrule-1].date.length === 0) 4696 && (rules[nrule].meaning || rules[nrule].unknown) 4697 && !rules[nrule].wrapped && !rules[nrule].additional && !rules[nrule].fallback 4698 ) { 4699 4700 // var old_date_matching_rules = date_matching_rules; 4701 date_matching_rules = []; 4702 // for (var nrule = 0; nrule < old_date_matching_rules.length; nrule++) { 4703 // if (!rules[old_date_matching_rules[nrule]].wrapped) 4704 // date_matching_rules.push(nrule); 4025 4705 // } 4026 4706 } 4027 date_matching_ blocks.push(nblock);4707 date_matching_rules.push(nrule); 4028 4708 } 4029 4709 } 4030 4710 4031 block:4032 for (var n block = 0; nblock < date_matching_blocks.length; nblock++) {4033 var block = date_matching_blocks[nblock];4034 4035 // console.log('Processing block ' + block + ':\t' + blocks[block].comment + ' with date', date,4036 // 'and', blocks[block].time.length, 'time selectors');4711 rule: 4712 for (var nrule = 0; nrule < date_matching_rules.length; nrule++) { 4713 var rule = date_matching_rules[nrule]; 4714 4715 // console.log('Processing rule ' + rule + ': with date ' + date 4716 // + ' and ' + rules[rule].time.length + ' time selectors (comment: "' + rules[rule].comment + '").'); 4037 4717 4038 4718 // there is no time specified, state applies to the whole day 4039 if ( blocks[block].time.length== 0) {4719 if (rules[rule].time.length === 0) { 4040 4720 // console.log('there is no time', date); 4041 if (! blocks[block].fallback || (blocks[block].fallback && !(resultstate || unknown))) {4042 resultstate = blocks[block].meaning;4043 unknown = blocks[block].unknown;4044 match_ block = block;4045 4046 if (typeof blocks[block].comment != 'undefined')4047 comment = blocks[block].comment;4721 if (!rules[rule].fallback || (rules[rule].fallback && !(resultstate || unknown))) { 4722 resultstate = rules[rule].meaning; 4723 unknown = rules[rule].unknown; 4724 match_rule = rule; 4725 4726 if (typeof rules[rule].comment != 'undefined') 4727 comment = rules[rule].comment; 4048 4728 else if (typeof comment == 'object') // holiday name 4049 4729 comment = comment[0]; 4050 4730 4051 if (blocks[block].fallback) 4052 break block; // fallback block matched, no need for checking the rest 4731 // if (rules[rule].fallback) 4732 // break rule; // fallback rule matched, no need for checking the rest 4733 // WRONG: What if closing rules follow? 4053 4734 } 4054 4735 } 4055 4736 4056 for (var timesel = 0; timesel < blocks[block].time.length; timesel++) {4057 var res = blocks[block].time[timesel](date);4737 for (var timesel = 0; timesel < rules[rule].time.length; timesel++) { 4738 var res = rules[rule].time[timesel](date); 4058 4739 4059 4740 // console.log('res:', res); 4060 4741 if (res[0]) { 4061 if (! blocks[block].fallback || (blocks[block].fallback && !(resultstate || unknown))) {4062 resultstate = blocks[block].meaning;4063 unknown = blocks[block].unknown;4064 match_ block = block;4065 4066 if (typeof blocks[block].comment == 'string') // only use comment if one is specified4067 comment = blocks[block].comment;4742 if (!rules[rule].fallback || (rules[rule].fallback && !(resultstate || unknown))) { 4743 resultstate = rules[rule].meaning; 4744 unknown = rules[rule].unknown; 4745 match_rule = rule; 4746 4747 if (typeof rules[rule].comment == 'string') // only use comment if one is specified 4748 comment = rules[rule].comment; 4068 4749 else if (typeof comment == 'object') // holiday name 4069 4750 comment = comment[0]; 4070 4751 else if (comment === 'Specified as open end. Closing time was guessed.') 4071 comment = blocks[block].comment;4752 comment = rules[rule].comment; 4072 4753 4073 4754 // open end 4074 if ( typeof res[2] == 'boolean' && res[2]&& (resultstate || unknown)) {4755 if (res[2] === true && (resultstate || unknown)) { 4075 4756 if (typeof comment == 'undefined') 4076 4757 comment = 'Specified as open end. Closing time was guessed.'; 4758 4077 4759 resultstate = false; 4078 4760 unknown = true; 4761 4762 /* Hack to make second rule in '07:00+,12:00-16:00; 16:00-24:00 closed "needed because of open end"' obsolete {{{ */ 4763 if (typeof rules[rule].time[timesel+1] == 'function') { 4764 4765 var next_res = rules[rule].time[timesel+1](date); 4766 if ( !next_res[0] 4767 // && next_res[2] 4768 && typeof next_res[1] == 'object' 4769 // && getValueForDate(next_res[1], true) != getValueForDate(date, true) // Just to be sure. 4770 && rules[rule].time[timesel](new Date(date.getTime() - 1))[0] 4771 /* To keep the following two apart: 4772 * 'sunrise-14:00,14:00+', 4773 * '07:00+,12:00-16:00', 4774 */ 4775 ) { 4776 4777 // console.log("07:00+,12:00-16:00 matched."); 4778 4779 resultstate = false; 4780 unknown = false; 4781 } 4782 } 4783 4784 /* Hack to handle '17:00+,13:00-02:00' {{{ */ 4785 /* Not enabled. To complicated, just don‘t use them … 4786 * It gets even crazier … 4787 * Time wrapping over midnight is 4788 * stored in the next internal rule: 4789 * '17:00-00:00 unknown "Specified as open end. Closing time was guessed.", 13:00-00:00 open' // First internal rule. 4790 * + ', ' overwritten part: 00:00-03:00 open + '00:00-02:00 open', // Second internal rule. 4791 */ 4792 if ( false 4793 && typeof rules[rule-1] == 'object' 4794 && rules[rule].build_from_token_rule.toString() == rules[rule-1].build_from_token_rule.toString() 4795 && typeof rules[rule] == 'object' 4796 && rules[rule].build_from_token_rule.toString() == rules[rule].build_from_token_rule.toString() 4797 ) { 4798 4799 var last_wrapping_time_selector = rules[rule].time[rules[rule].time.length - 1]; 4800 var last_w_res = last_wrapping_time_selector(new Date(date.getTime() - 1)); 4801 // console.log(last_w_res); 4802 4803 if ( last_w_res[0] 4804 && typeof last_w_res[2] == 'undefined' 4805 && (typeof last_w_res[2] == 'undefined' || last_w_res[2] === false) // Not match for 'Tu 23:59-40:00+' 4806 && typeof last_w_res[1] == 'object' 4807 && date.getTime() == last_w_res[1].getTime() 4808 ) { 4809 4810 // '05:00-06:00,17:00+,13:00-02:00', 4811 4812 // console.log("17:00+,13:00-02:00 matched."); 4813 // console.log(JSON.stringify(rules, null, ' ')); 4814 4815 resultstate = false; 4816 unknown = false; 4817 } 4818 /* }}} */ 4819 } 4820 /* }}} */ 4079 4821 } 4080 4822 4081 if ( blocks[block].fallback) {4823 if (rules[rule].fallback) { 4082 4824 if (typeof changedate === 'undefined' || (typeof res[1] !== 'undefined' && res[1] < changedate)) 4083 4825 changedate = res[1]; 4084 4826 4085 // break block; // Fallback blockmatched, no need for checking the rest.4086 // WRONG: What if 'off' is used after fallback block.4827 // break rule; // Fallback rule matched, no need for checking the rest. 4828 // WRONG: What if 'off' is used after fallback rule. 4087 4829 } 4088 4830 } … … 4093 4835 } 4094 4836 4095 // console.log('changedate', changedate, resultstate, comment, match_ block);4096 return [ resultstate, changedate, unknown, comment, match_ block];4097 } 4837 // console.log('changedate', changedate, resultstate, comment, match_rule); 4838 return [ resultstate, changedate, unknown, comment, match_rule ]; 4839 }; 4098 4840 // }}} 4099 4841 4100 // Generate prettified value based on tokens {{{ 4101 function prettifySelector(tokens, at, last_at, conf, used_parseTimeRange) { 4842 /* Generate prettified value for selector based on tokens. {{{ 4843 * 4844 * :param tokens: List of token objects. 4845 * :param at: Position where to start. 4846 * :param last_at: Position where to stop. 4847 * :param conf: Configuration options. 4848 * :returns: Prettified value. 4849 */ 4850 function prettifySelector(tokens, selector_start, selector_end, selector_type, conf) { 4851 4102 4852 var value = ''; 4103 var start_at = at; 4104 while (at < last_at) { 4105 if (matchTokens(tokens, at, 'weekday')) { // FIXME 4853 var at = selector_start; 4854 while (at <= selector_end) { 4855 // console.log('At: ' + at + ', token: ' + tokens[at]); 4856 if (matchTokens(tokens, at, 'weekday')) { 4106 4857 if (!conf.leave_weekday_sep_one_day_betw 4107 && at - s tart_at > 1 && (matchTokens(tokens, at-1, ',') || matchTokens(tokens, at-1, '-'))4858 && at - selector_start > 1 && (matchTokens(tokens, at-1, ',') || matchTokens(tokens, at-1, '-')) 4108 4859 && matchTokens(tokens, at-2, 'weekday') 4109 && tokens[at][0] == (tokens[at-2][0] + 1) % 7) 4860 && tokens[at][0] == (tokens[at-2][0] + 1) % 7) { 4110 4861 value = value.substring(0, value.length - 1) + conf.sep_one_day_between; 4111 4862 } 4112 4863 value += weekdays[tokens[at][0]]; 4113 } else if (at - s tart_at > 0 // e.g. '09:0' -> '09:00'4114 && used_parseTimeRange > 04864 } else if (at - selector_start > 0 // e.g. '09:0' -> '09:00' 4865 && selector_type == 'time' 4115 4866 && matchTokens(tokens, at-1, 'timesep') 4116 4867 && matchTokens(tokens, at, 'number')) { 4117 4868 value += (tokens[at][0] < 10 ? '0' : '') + tokens[at][0].toString(); 4118 } else if ( used_parseTimeRange > 0// e.g. '9:00' -> ' 09:00'4119 && conf. leading_zero_hour4869 } else if (selector_type == 'time' // e.g. '9:00' -> ' 09:00' 4870 && conf.zero_pad_hour 4120 4871 && at != tokens.length 4121 4872 && matchTokens(tokens, at, 'number') … … 4123 4874 value += ( 4124 4875 tokens[at][0] < 10 ? 4125 (tokens[at][0] == 0 && conf.one_zero_if_hour_zero ?4876 (tokens[at][0] === 0 && conf.one_zero_if_hour_zero ? 4126 4877 '' : '0') : 4127 4878 '') + tokens[at][0].toString(); 4128 } else if ( used_parseTimeRange > 0// e.g. '9-18' -> '09:00-18:00'4129 && at + 2 < last_at4879 } else if (selector_type == 'time' // e.g. '9-18' -> '09:00-18:00' 4880 && at + 2 <= selector_end 4130 4881 && matchTokens(tokens, at, 'number') 4131 4882 && matchTokens(tokens, at+1, '-') 4132 4883 && matchTokens(tokens, at+2, 'number')) { 4133 4884 value += (tokens[at][0] < 10 ? 4134 (tokens[at][0] == 0 && conf.one_zero_if_hour_zero ? '' : '0')4885 (tokens[at][0] === 0 && conf.one_zero_if_hour_zero ? '' : '0') 4135 4886 : '') + tokens[at][0].toString(); 4136 4887 value += ':00-' … … 4142 4893 } else if (matchTokens(tokens, at, 'closed')) { 4143 4894 value += (conf.leave_off_closed ? tokens[at][0] : conf.keyword_for_off_closed); 4144 } else if (at - s tart_at > 0 && matchTokens(tokens, at, 'number')4145 && (matchTokens(tokens, at-1, 'month') 4146 || matchTokens(tokens, at-1, 'week') 4895 } else if (at - selector_start > 0 && matchTokens(tokens, at, 'number') 4896 && (matchTokens(tokens, at-1, 'month') && selector_type == 'month' 4897 || matchTokens(tokens, at-1, 'week') && selector_type == 'week' 4147 4898 )) { 4148 value += ' ' + tokens[at][0]; 4149 } else if (at - start_at > 0 && matchTokens(tokens, at, 'month') 4899 value += ' ' 4900 + (conf.zero_pad_month_and_week_numbers && tokens[at][0] < 10 ? '0' : '') 4901 + tokens[at][0]; 4902 } else if (at - selector_start > 0 && matchTokens(tokens, at, 'month') 4150 4903 && matchTokens(tokens, at-1, 'year')) { 4151 4904 value += ' ' + months[[tokens[at][0]]]; 4152 } else if (at - s tart_at > 0 && matchTokens(tokens, at, 'event')4905 } else if (at - selector_start > 0 && matchTokens(tokens, at, 'event') 4153 4906 && matchTokens(tokens, at-1, 'year')) { 4154 4907 value += ' ' + tokens[at][0]; 4155 4908 } else if (matchTokens(tokens, at, 'month')) { 4156 4909 value += months[[tokens[at][0]]]; 4157 if (at + 1 < last_at&& matchTokens(tokens, at+1, 'weekday'))4910 if (at + 1 <= selector_end && matchTokens(tokens, at+1, 'weekday')) 4158 4911 value += ' '; 4159 } else if (at + 2 < last_at4912 } else if (at + 2 <= selector_end 4160 4913 && (matchTokens(tokens, at, '-') || matchTokens(tokens, at, '+')) 4161 4914 && matchTokens(tokens, at+1, 'number', 'calcday')) { 4162 4915 value += ' ' + tokens[at][0] + tokens[at+1][0] + ' day' + (Math.abs(tokens[at+1][0]) == 1 ? '' : 's'); 4163 4916 at += 2; 4917 } else if (at == selector_end 4918 && selector_type == 'weekday' 4919 && tokens[at][0] == ':') { 4920 // Do nothing. 4164 4921 } else { 4165 // if (matchTokens(tokens, at, 'open') || matchTokens(tokens, at, 'unknown'))4166 // value += ' ';4167 4168 4922 value += tokens[at][0].toString(); 4169 4923 } 4170 4924 at++; 4171 4925 } 4172 return value + ' ';4926 return value; 4173 4927 } 4174 4928 // }}} … … 4179 4933 //====================================================================== 4180 4934 4181 //======================================================================4182 4935 // Iterator interface {{{ 4183 //======================================================================4184 4936 this.getIterator = function(date) { 4185 4937 return new function(oh) { … … 4190 4942 var state = oh.getStatePair(date); 4191 4943 4944 /* getDate {{{ */ 4945 this.getDate = function() { 4946 return prevstate[1]; 4947 }; 4948 /* }}} */ 4949 4950 /* setDate {{{ */ 4192 4951 this.setDate = function(date) { 4193 4952 if (typeof date != 'object') 4194 throw 'Date asparameter needed.';4953 throw 'Date parameter needed.'; 4195 4954 4196 4955 prevstate = [ undefined, date, undefined, undefined, undefined ]; 4197 4956 state = oh.getStatePair(date); 4198 } 4199 4200 this.getDate = function() { 4201 return prevstate[1]; 4202 } 4203 4957 }; 4958 /* }}} */ 4959 4960 /* getState: Check whether facility is `open' {{{ */ 4204 4961 this.getState = function() { 4205 4962 return state[0]; 4206 } 4207 4963 }; 4964 /* }}} */ 4965 4966 /* getUnknown: Checks whether the opening state is conditional or unknown {{{ */ 4208 4967 this.getUnknown = function() { 4209 4968 return state[2]; 4210 } 4211 4969 }; 4970 /* }}} */ 4971 4972 /* getStateString: Get state string. Either 'open', 'unknown' or 'closed' {{{ */ 4212 4973 this.getStateString = function(past) { 4213 4974 return (state[0] ? 'open' : (state[2] ? 'unknown' : (past ? 'closed' : 'close'))); 4214 } 4215 4975 }; 4976 /* }}} */ 4977 4978 /* getComment: Get the comment, undefined in none {{{ */ 4216 4979 this.getComment = function() { 4217 4980 return state[3]; 4218 } 4219 4220 this.getMatchingRule = function(user_conf) { 4981 }; 4982 /* }}} */ 4983 4984 /* getMatchingRule: Get the rule which matched thus deterrents the current state {{{ */ 4985 this.getMatchingRule = function() { 4221 4986 if (typeof state[4] == 'undefined') 4222 4987 return undefined; 4223 4988 4224 if (typeof user_conf != 'object') 4225 var user_conf = {}; 4226 for (key in default_prettify_conf) { 4227 if (typeof user_conf[key] == 'undefined') 4228 user_conf[key] = default_prettify_conf[key]; 4229 } 4230 4231 var really_done_with_warnings = done_with_warnings; // getWarnings can be called later. 4232 done_with_warnings = true; 4233 prettified_value = ''; 4234 var selectors = { // Not really needed. This whole thing is only necessary because of the token used for additional blocks. 4235 time: [], weekday: [], holiday: [], week: [], month: [], monthday: [], year: [], wraptime: [], 4236 4237 fallback: false, // does not matter 4238 additional: false, 4239 meaning: true, 4240 unknown: false, 4241 comment: undefined, 4242 }; 4243 4244 // token block index used to build the selectors for this block. 4245 var token_block = blocks[state[4]].build_from_token_block; 4246 parseGroup(tokens[token_block[0]][0], token_block[1], selectors, state[4], user_conf); 4247 4248 if (prettified_value[prettified_value.length - 1] == ',') 4249 prettified_value = prettified_value.substr(0, prettified_value.length - 1); 4250 4251 done_with_warnings = really_done_with_warnings; 4252 4253 return prettified_value; 4254 } 4255 4989 return rules[state[4]].build_from_token_rule[2]; 4990 }; 4991 /* }}} */ 4992 4993 /* advance: Advances to the next position {{{ */ 4256 4994 this.advance = function(datelimit) { 4257 4995 if (typeof datelimit === 'undefined') … … 4266 5004 4267 5005 // console.log('\n' + 'previous check time:', prevstate[1] 4268 //+ ', current check time:',4269 //// (state[1].getHours() < 10 ? '0' : '') + state[1].getHours() +4270 //// ':'+(state[1].getMinutes() < 10 ? '0' : '')+ state[1].getMinutes(), state[1].getDate(),4271 //state[1],4272 //(state[0] ? 'open' : (state[2] ? 'unknown' : 'closed')) + ', comment:', state[3]);5006 // + ', current check time:', 5007 // // (state[1].getHours() < 10 ? '0' : '') + state[1].getHours() + 5008 // // ':'+(state[1].getMinutes() < 10 ? '0' : '')+ state[1].getMinutes(), state[1].getDate(), 5009 // state[1], 5010 // (state[0] ? 'open' : (state[2] ? 'unknown' : 'closed')) + ', comment:', state[3]); 4273 5011 4274 5012 // We're going backwards or staying at place. … … 4286 5024 } while (state[0] === prevstate[0] && state[2] === prevstate[2] && state[3] === prevstate[3]); 4287 5025 return true; 4288 } 5026 }; 5027 /* }}} */ 4289 5028 }(this); 4290 } 5029 }; 4291 5030 // }}} 4292 5031 4293 5032 // Simple API {{{ 4294 5033 4295 // Get parse warnings. 4296 // Returns an empty string if there are no warnings. 5034 this.getState = function(date) { 5035 var it = this.getIterator(date); 5036 return it.getState(); 5037 }; 5038 5039 this.getUnknown = function(date) { 5040 var it = this.getIterator(date); 5041 return it.getUnknown(); 5042 }; 5043 5044 this.getStateString = function(date, past) { 5045 var it = this.getIterator(date); 5046 return it.getStateString(past); 5047 }; 5048 5049 this.getComment = function(date) { 5050 var it = this.getIterator(date); 5051 return it.getComment(); 5052 }; 5053 5054 this.getMatchingRule = function(date) { 5055 var it = this.getIterator(date); 5056 return it.getMatchingRule(); 5057 }; 5058 5059 /* Not available for iterator API {{{ */ 5060 /* getWarnings: Get warnings, empty list if none {{{ */ 4297 5061 this.getWarnings = function() { 4298 5062 var it = this.getIterator(); 4299 5063 return getWarnings(it); 4300 } 4301 4302 // Get a nicely formated value. 4303 this.prettifyValue = function(user_conf) { 4304 if (typeof user_conf != 'object') 4305 var user_conf = {}; 4306 4307 for (key in default_prettify_conf) { 4308 if (typeof user_conf[key] == 'undefined') 4309 user_conf[key] = default_prettify_conf[key]; 4310 } 4311 4312 var really_done_with_warnings = done_with_warnings; // getWarnings can be called later. 4313 done_with_warnings = true; 4314 4315 prettified_value = ''; 4316 for (var nblock = 0; nblock < tokens.length; nblock++) { 4317 if (tokens[nblock][0].length == 0) continue; 4318 // Block does contain nothing useful e.g. second block of '10:00-12:00;' (empty) which needs to be handled. 4319 4320 if (nblock != 0) 4321 prettified_value += (tokens[nblock][1] 4322 ? user_conf.block_sep_string + '|| ' 4323 : (user_conf.print_semicolon ? ';' : '') + user_conf.block_sep_string); 4324 4325 var continue_at = 0; 4326 do { 4327 if (continue_at == tokens[nblock][0].length) break; 4328 // Block does contain nothing useful e.g. second block of '10:00-12:00,' (empty) which needs to be handled. 4329 4330 var selectors = { // Not really needed. This whole thing is only necessary because of the token used for additional blocks. 4331 time: [], weekday: [], holiday: [], week: [], month: [], monthday: [], year: [], wraptime: [], 4332 4333 fallback: tokens[nblock][1], 4334 additional: continue_at ? true : false, 4335 meaning: true, 4336 unknown: false, 4337 comment: undefined, 4338 }; 4339 4340 continue_at = parseGroup(tokens[nblock][0], continue_at, selectors, nblock, user_conf); 4341 4342 if (typeof continue_at == 'object') { 4343 continue_at = continue_at[0]; 4344 prettified_value += user_conf.block_sep_string; 4345 } else { 4346 continue_at = 0; 4347 } 4348 4349 } while (continue_at) 4350 } 4351 4352 done_with_warnings = really_done_with_warnings; 4353 4354 return prettified_value; 4355 } 4356 4357 // Check whether facility is `open' on the given date (or now). 4358 this.getState = function(date) { 4359 var it = this.getIterator(date); 4360 return it.getState(); 4361 } 4362 4363 // If the state of a amenity is conditional. Conditions can be expressed in comments. 4364 // True will only be returned if the state is false as the getState only 4365 // returns true if the amenity is really open. So you may want to check 4366 // the result of getUnknown if getState returned false. 4367 this.getUnknown = function(date) { 4368 var it = this.getIterator(date); 4369 return it.getUnknown(); 4370 } 4371 4372 // Return state string. Either 'open', 'unknown' or 'closed'. 4373 this.getStateString = function(date, past) { 4374 var it = this.getIterator(date); 4375 return it.getStateString(past); 4376 } 4377 4378 // Returns the comment. 4379 // If no comment is specified this function will return undefined. 4380 this.getComment = function(date) { 4381 var it = this.getIterator(date); 4382 return it.getComment(); 4383 } 4384 4385 // Return the block which matched thus deterrents the current state. 4386 this.getMatchingRule = function(date) { 4387 var it = this.getIterator(date); 4388 return it.getMatchingRule(); 4389 } 4390 4391 // Returns time of next status change. 5064 }; 5065 /* }}} */ 5066 5067 /* prettifyValue: Get a nicely formated value {{{ */ 5068 this.prettifyValue = function(argument_hash) { 5069 this.getWarnings(); 5070 /* getWarnings has to be run before prettifyValue because some 5071 * decisions if a certain aspect makes sense to prettify or not 5072 * are based on the warnings. 5073 * Basically, both functions depend on each other in some way :( 5074 * See done_with_selector_reordering. 5075 */ 5076 return prettifyValue(argument_hash); 5077 }; 5078 /* }}} */ 5079 5080 /* getNextChange: Get time of next status change {{{ */ 4392 5081 this.getNextChange = function(date, maxdate) { 4393 5082 var it = this.getIterator(date); … … 4395 5084 return undefined; 4396 5085 return it.getDate(); 4397 } 4398 4399 // Checks whether open intervals are same for every week. 5086 }; 5087 /* }}} */ 5088 5089 /* isWeekStable: Checks whether open intervals are same for every week. {{{ */ 4400 5090 this.isWeekStable = function() { 4401 5091 return week_stable; 4402 } 4403 // }}} 5092 }; 5093 /* }}} */ 5094 /* }}} */ 5095 /* }}} */ 4404 5096 4405 5097 // High-level API {{{ 4406 5098 4407 / / return array of open intervals between two dates5099 /* getOpenIntervals: Get array of open intervals between two dates {{{ */ 4408 5100 this.getOpenIntervals = function(from, to) { 4409 5101 var res = []; … … 4416 5108 while (it.advance(to)) { 4417 5109 if (it.getState() || it.getUnknown()) { 4418 if (res.length != 0 && typeof res[res.length - 1][1] == 'undefined') {5110 if (res.length !== 0 && typeof res[res.length - 1][1] == 'undefined') { 4419 5111 // last state was also open or unknown 4420 5112 res[res.length - 1][1] = it.getDate(); … … 4422 5114 res.push([it.getDate(), undefined, it.getUnknown(), it.getComment()]); 4423 5115 } else { 4424 if (res.length != 0 && typeof res[res.length - 1][1] == 'undefined') {5116 if (res.length !== 0 && typeof res[res.length - 1][1] == 'undefined') { 4425 5117 // only use the first time as closing/change time and ignore closing times which might follow 4426 5118 res[res.length - 1][1] = it.getDate(); … … 4433 5125 4434 5126 return res; 4435 } 4436 4437 // return total number of milliseconds a facility is open within a given date range 5127 }; 5128 /* }}} */ 5129 5130 /* getOpenDuration: Get total number of milliseconds a facility is open,unknown within a given date range {{{ */ 4438 5131 this.getOpenDuration = function(from, to) { 4439 5132 // console.log('-----------'); … … 4482 5175 4483 5176 return [ open, unknown ]; 4484 } 4485 // }}} 4486 // }}} 4487 } 5177 }; 5178 /* }}} */ 5179 /* }}} */ 5180 /* }}} */ 5181 }; 4488 5182 })); 4489 5183 // vim: set ts=4 sw=4 tw=0 noet foldmarker={{{,}}} foldlevel=0 foldmethod=marker :
Note:
See TracChangeset
for help on using the changeset viewer.