├── .gitignore ├── tsconfig.json ├── example └── example.html ├── .eslintrc.json ├── README.md ├── package.json ├── LICENSE ├── test └── parser.tests.js ├── src └── simple-opening-hours.ts └── dist ├── simple-opening-hours.js.map └── simple-opening-hours.js /.gitignore: -------------------------------------------------------------------------------- 1 | openingparser-tests._ts 2 | tasks.json 3 | node_modules 4 | .nyc_output -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "umd", 4 | "target": "es5", 5 | "noImplicitAny": false, 6 | "removeComments": false, 7 | "preserveConstEnums": true, 8 | "sourceMap": true, 9 | "watch": true, 10 | "outDir": "./dist" 11 | }, 12 | "exclude": [] 13 | } -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | Please open console 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "jsx": true 11 | }, 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | "no-const-assign": "warn", 16 | "no-this-before-super": "warn", 17 | "no-undef": "warn", 18 | "no-unreachable": "warn", 19 | "no-unused-vars": "warn", 20 | "constructor-super": "warn", 21 | "valid-typeof": "warn" 22 | } 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleOpeningHours() 2 | 3 | SimpleOpeningHours is a small JavaScript class to parse OpenStreetMap `opening_hours`. 4 | It only supports the human readable parts and not [this complete crazy overengineered specification](https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification). 5 | 6 | ## Supported opening_hours examples 7 | 8 | * `Mo-Sa 06:00-22:00` 9 | * `Mo-Fr 08:00-18:00; Sa 10:00-14:00` 10 | * `Mo-Fr 08:00-18:00; Sa,Su 10:00-20:00` 11 | * `Mo-Fr 08:00-12:00, We 14:00-18:00` 12 | * `Mo-Fr 08:00-12:00, 14:00-18:00` 13 | * `Mo-Fr 08:00-18:00; We off` 14 | * `24/7` 15 | 16 | ## Usage 17 | ```javascript 18 | var opening = new SimpleOpeningHours('Mo-Sa 06:00-22:00'); 19 | console.log('Is this open now?', opening.isOpenNow()); 20 | console.log('Is this open on 2016-10-01 18:00?', opening.isOpenOn(new Date('2016-10-01 18:00'))); 21 | console.table(opening.getTable()); 22 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-opening-hours", 3 | "version": "1.0.0", 4 | "description": "Less crazy opening hours parser", 5 | "main": "dist/simple-opening-hours.js", 6 | "directories": { 7 | "example": "example", 8 | "test": "test" 9 | }, 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "eslint": "^3.16.1", 13 | "tap": "^10.2.1", 14 | "nodemon": "1.11.0" 15 | }, 16 | "scripts": { 17 | "test": "tap test/*.js --cov", 18 | "compile": "tsc", 19 | "watch": "nodemon -x \"npm test\"", 20 | "develop": "compile & watch" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/pke/simple-opening-hours.git" 25 | }, 26 | "keywords": [ 27 | "opening hours" 28 | ], 29 | "author": "", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/pke/simple-opening-hours/issues" 33 | }, 34 | "homepage": "https://github.com/pke/simple-opening-hours#readme" 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Constantin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/parser.tests.js: -------------------------------------------------------------------------------- 1 | const t = require("tap") 2 | 3 | const { default: oh, map } = require("../dist/simple-opening-hours") 4 | 5 | t.ok((new oh("24/7")).isOpen()) 6 | t.ok((new oh(" 24/7")).isOpen()) 7 | t.ok((new oh(" 24/7 ")).isOpen()) 8 | t.ok((new oh("24/7 ")).isOpen()) 9 | t.ok((new oh("24 / 7")).isOpen()) 10 | t.ok((new oh("24/7")).getTable()) 11 | t.ok((new oh("24/7")).alwaysOpen) 12 | 13 | t.notOk((new oh("off")).isOpen()) 14 | t.notOk((new oh(" off")).isOpen()) 15 | t.notOk((new oh("off ")).isOpen()) 16 | t.notOk((new oh(" off ")).isOpen()) 17 | t.ok((new oh("off")).alwaysClosed) 18 | 19 | t.ok((new oh("Mo-Sa 06:00-22:00")).isOpen(new Date('2016-10-01 18:00'))) 20 | t.notOk((new oh("Mo 06:00-22:00")).isOpen(new Date('2016-10-01 18:00'))) 21 | t.ok((new oh("Mo-Sa 09:00+")).isOpen(new Date('2016-10-01 18:00'))) 22 | t.notOk((new oh("Mo-Sa off")).isOpen(new Date('2016-10-01 18:00'))) 23 | t.notOk((new oh("Mo-Sa 06:00-22:00")).isOpen(new Date('2016-10-01 05:00'))) 24 | t.ok((new oh("Mo-Sa 06:00-22:00")).getTable()) 25 | 26 | t.test("Simple Time tables", t => { 27 | const table = (new oh("Mo-Sa 06:00-22:00")).getTable() 28 | t.same(table, { 29 | su: [], 30 | mo: ["06:00-22:00"], 31 | tu: ["06:00-22:00"], 32 | we: ["06:00-22:00"], 33 | th: ["06:00-22:00"], 34 | fr: ["06:00-22:00"], 35 | sa: ["06:00-22:00"], 36 | ph: [], 37 | 38 | }) 39 | t.end() 40 | }) 41 | 42 | t.test("Complex Time tables", t => { 43 | const table = (new oh("Mo-Sa 06:00-14:00,15:00-22:00")).getTable() 44 | t.same(table, { 45 | su: [], 46 | mo: ["06:00-14:00", "15:00-22:00"], 47 | tu: ["06:00-14:00", "15:00-22:00"], 48 | we: ["06:00-14:00", "15:00-22:00"], 49 | th: ["06:00-14:00", "15:00-22:00"], 50 | fr: ["06:00-14:00", "15:00-22:00"], 51 | sa: ["06:00-14:00", "15:00-22:00"], 52 | ph: [], 53 | 54 | }) 55 | t.end() 56 | }) 57 | 58 | t.test("Time tables", t => { 59 | const openingHours = new oh("Mo-Sa 06:00-14:00") 60 | const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] 61 | const result = map(openingHours, (weekday, times) => { 62 | if (times && times.length) { 63 | return weekdays[weekday] + " " + times.map(time => time).join("\t") 64 | } else { 65 | return weekdays[weekday] + " Closed" 66 | } 67 | }).join("\n") 68 | t.equal(result, "Mon 06:00-14:00\nTue 06:00-14:00\nWed 06:00-14:00\nThu 06:00-14:00\nFri 06:00-14:00\nSat 06:00-14:00\nSun Closed") 69 | t.end() 70 | }) -------------------------------------------------------------------------------- /src/simple-opening-hours.ts: -------------------------------------------------------------------------------- 1 | export default class SimpleOpeningHours { 2 | /** 3 | * Creates the OpeningHours Object with OSM opening_hours string 4 | */ 5 | constructor(input: string) { 6 | this.parse(input); 7 | } 8 | 9 | /** 10 | * returns the OpeningHours Object 11 | */ 12 | public getTable() { 13 | return typeof this.openingHours === "object" ? this.openingHours : {}; 14 | } 15 | 16 | public isOpen(date?: Date): boolean { 17 | if (typeof this.openingHours === "boolean") { 18 | return this.openingHours 19 | } 20 | date = date || new Date() 21 | const testDay = date.getDay(); 22 | const testTime = date.getHours() + ":" + (date.getMinutes() < 10 ? ("0" + date.getMinutes()) : date.getMinutes()) 23 | let i = 0; 24 | let times: string[]; 25 | for (let key in this.openingHours) { 26 | if (i == testDay) { 27 | times = this.openingHours[key]; 28 | } 29 | i++; 30 | } 31 | let isOpen = false 32 | times.some(time => { 33 | const timeData = time.replace(/\+$/, "-24:00").split("-") 34 | if ((this.compareTime(testTime, timeData[0]) != -1) 35 | && (this.compareTime(timeData[1], testTime) != -1)) { 36 | isOpen = true; 37 | return true; 38 | } 39 | }); 40 | return isOpen; 41 | } 42 | 43 | /** 44 | * Parses the input and creates openingHours Object 45 | */ 46 | private parse(input:string) { 47 | if (/24\s*?\/\s*?7/.test(input)) { 48 | this.openingHours = this.alwaysOpen = true; 49 | return 50 | } else if (/\s*off\s*/.test(input)) { 51 | this.openingHours = false 52 | this.alwaysClosed = true 53 | return 54 | } 55 | this.init(); 56 | const parts = input.toLowerCase().replace(/\s*([-:,;])\s*/g, '$1').split(";") 57 | parts.forEach(part => { 58 | this.parseHardPart(part) 59 | }); 60 | } 61 | 62 | private parseHardPart(part: string) { 63 | if (part == "24/7") { 64 | part = "mo-su 00:00-24:00"; 65 | } 66 | let segments = part.split(/\ |\,/); 67 | 68 | let tempData = {} 69 | let days = [] 70 | let times = [] 71 | segments.forEach((segment) => { 72 | if (this.checkDay(segment)) { 73 | if (times.length == 0) { 74 | days = days.concat(this.parseDays(segment)); 75 | } 76 | else { 77 | //append 78 | days.forEach((day) => { 79 | if (tempData[day]) { 80 | tempData[day] = tempData[day].concat(times) 81 | } 82 | else { 83 | tempData[day] = times 84 | } 85 | }) 86 | days = this.parseDays(segment) 87 | times = []; 88 | } 89 | } 90 | if (this.isTimeRange(segment)) { 91 | if (segment == "off") { 92 | times = [] 93 | } 94 | else { 95 | times.push(segment) 96 | } 97 | } 98 | }) 99 | 100 | //commit last times to it days 101 | days.forEach((day) => { 102 | if (tempData[day]) { 103 | tempData[day] = tempData[day].concat(times) 104 | } 105 | else { 106 | tempData[day] = times 107 | } 108 | }) 109 | 110 | //apply data to main obj 111 | for (let key in tempData) { 112 | this.openingHours[key] = tempData[key]; 113 | } 114 | } 115 | 116 | private parseDays(part: string): string[] { 117 | let days = [] 118 | let softparts = part.split(','); 119 | softparts.forEach((part) => { 120 | let rangecount = (part.match(/\-/g) || []).length; 121 | if (rangecount == 0) { 122 | days.push(part) 123 | } 124 | else { 125 | days = days.concat(this.calcDayRange(part)) 126 | } 127 | }) 128 | 129 | return days 130 | } 131 | 132 | private init() { 133 | this.openingHours = { 134 | su: [], 135 | mo: [], 136 | tu: [], 137 | we: [], 138 | th: [], 139 | fr: [], 140 | sa: [], 141 | ph: [] 142 | } 143 | } 144 | 145 | /** 146 | * Calculates the days in range "mo-we" -> ["mo", "tu", "we"] 147 | */ 148 | private calcDayRange(range: string): string[] { 149 | let def = { 150 | su: 0, 151 | mo: 1, 152 | tu: 2, 153 | we: 3, 154 | th: 4, 155 | fr: 5, 156 | sa: 6 157 | } 158 | 159 | let rangeElements = range.split('-'); 160 | 161 | let dayStart = def[rangeElements[0]] 162 | let dayEnd = def[rangeElements[1]] 163 | 164 | let numberRange = this.calcRange(dayStart, dayEnd, 6); 165 | let outRange: string[] = []; 166 | numberRange.forEach(n => { 167 | for (let key in def) { 168 | if (def[key] == n) { 169 | outRange.push(key) 170 | } 171 | } 172 | }); 173 | return outRange; 174 | } 175 | 176 | /** 177 | * Creates a range between two number. 178 | * if the max value is 6 a range bewteen 6 and 2 is 6, 0, 1, 2 179 | */ 180 | private calcRange(min: number, max: number, maxval): number[] { 181 | if (min == max) { 182 | return [min] 183 | } 184 | let range = [min]; 185 | let rangepoint = min 186 | while (rangepoint < ((min < max) ? max : maxval)) { 187 | rangepoint++ 188 | range.push(rangepoint) 189 | } 190 | if (min > max) { 191 | //add from first in list to max value 192 | range = range.concat(this.calcRange(0, max, maxval)) 193 | } 194 | 195 | return range; 196 | } 197 | 198 | /** 199 | * Check if string is time range 200 | */ 201 | private isTimeRange(input: string): boolean { 202 | //e.g. 09:00+ 203 | if (input.match(/[0-9]{1,2}:[0-9]{2}\+/)) { 204 | return true 205 | } 206 | //e.g. 08:00-12:00 207 | if (input.match(/[0-9]{1,2}:[0-9]{2}\-[0-9]{1,2}:[0-9]{2}/)) { 208 | return true 209 | } 210 | //off 211 | if (input.match(/off/)) { 212 | return true 213 | } 214 | return false 215 | } 216 | 217 | /** 218 | * check if string is day or dayrange 219 | */ 220 | private checkDay(input: string): boolean { 221 | let days = ["mo", "tu", "we", "th", "fr", "sa", "su", "ph"] 222 | if (input.match(/\-/g)) { 223 | let rangeElements = input.split('-'); 224 | if (days.indexOf(rangeElements[0]) !== -1 225 | && days.indexOf(rangeElements[1]) !== -1) { 226 | return true 227 | } 228 | } 229 | else if (days.indexOf(input) !== -1) { 230 | return true 231 | } 232 | return false 233 | } 234 | 235 | /** 236 | * Compares to timestrings e.g. "18:00" 237 | * if time1 > time2 -> 1 238 | * if time1 < time2 -> -1 239 | * if time1 == time2 -> 0 240 | */ 241 | private compareTime(time1: string, time2: string) { 242 | const date1 = Number(time1.replace(":", "")) 243 | const date2 = Number(time2.replace(":", "")) 244 | if (date1 > date2) { 245 | return 1 246 | } else if (date1 < date2) { 247 | return -1 248 | } else { 249 | return 0 250 | } 251 | } 252 | 253 | private openingHours: Object | boolean 254 | private alwaysOpen?: boolean 255 | private alwaysClosed?: boolean 256 | } 257 | 258 | export function map(oh: SimpleOpeningHours, callback: (index:number, times: Array)=>T): T[] { 259 | const table = oh.getTable() 260 | return ["mo", "tu", "we", "th", "fr", "sa", "su"].map((weekday, index) => ( 261 | callback(((index + 1) % 7), table[weekday]) 262 | )) 263 | } -------------------------------------------------------------------------------- /dist/simple-opening-hours.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"simple-opening-hours.js","sourceRoot":"","sources":["../src/simple-opening-hours.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/simple-opening-hours.js: -------------------------------------------------------------------------------- 1 | (function (factory) { 2 | if (typeof module === "object" && typeof module.exports === "object") { 3 | var v = factory(require, exports); 4 | if (v !== undefined) module.exports = v; 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | define(["require", "exports"], factory); 8 | } 9 | })(function (require, exports) { 10 | "use strict"; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var SimpleOpeningHours = (function () { 13 | /** 14 | * Creates the OpeningHours Object with OSM opening_hours string 15 | */ 16 | function SimpleOpeningHours(input) { 17 | this.parse(input); 18 | } 19 | /** 20 | * returns the OpeningHours Object 21 | */ 22 | SimpleOpeningHours.prototype.getTable = function () { 23 | return typeof this.openingHours === "object" ? this.openingHours : {}; 24 | }; 25 | SimpleOpeningHours.prototype.isOpen = function (date) { 26 | var _this = this; 27 | if (typeof this.openingHours === "boolean") { 28 | return this.openingHours; 29 | } 30 | date = date || new Date(); 31 | var testDay = date.getDay(); 32 | var testTime = date.getHours() + ":" + (date.getMinutes() < 10 ? ("0" + date.getMinutes()) : date.getMinutes()); 33 | var i = 0; 34 | var times; 35 | for (var key in this.openingHours) { 36 | if (i == testDay) { 37 | times = this.openingHours[key]; 38 | } 39 | i++; 40 | } 41 | var isOpen = false; 42 | times.some(function (time) { 43 | var timeData = time.replace(/\+$/, "-24:00").split("-"); 44 | if ((_this.compareTime(testTime, timeData[0]) != -1) 45 | && (_this.compareTime(timeData[1], testTime) != -1)) { 46 | isOpen = true; 47 | return true; 48 | } 49 | }); 50 | return isOpen; 51 | }; 52 | /** 53 | * Parses the input and creates openingHours Object 54 | */ 55 | SimpleOpeningHours.prototype.parse = function (input) { 56 | var _this = this; 57 | if (/24\s*?\/\s*?7/.test(input)) { 58 | this.openingHours = this.alwaysOpen = true; 59 | return; 60 | } 61 | else if (/\s*off\s*/.test(input)) { 62 | this.openingHours = false; 63 | this.alwaysClosed = true; 64 | return; 65 | } 66 | this.init(); 67 | var parts = input.toLowerCase().replace(/\s*([-:,;])\s*/g, '$1').split(";"); 68 | parts.forEach(function (part) { 69 | _this.parseHardPart(part); 70 | }); 71 | }; 72 | SimpleOpeningHours.prototype.parseHardPart = function (part) { 73 | var _this = this; 74 | if (part == "24/7") { 75 | part = "mo-su 00:00-24:00"; 76 | } 77 | var segments = part.split(/\ |\,/); 78 | var tempData = {}; 79 | var days = []; 80 | var times = []; 81 | segments.forEach(function (segment) { 82 | if (_this.checkDay(segment)) { 83 | if (times.length == 0) { 84 | days = days.concat(_this.parseDays(segment)); 85 | } 86 | else { 87 | //append 88 | days.forEach(function (day) { 89 | if (tempData[day]) { 90 | tempData[day] = tempData[day].concat(times); 91 | } 92 | else { 93 | tempData[day] = times; 94 | } 95 | }); 96 | days = _this.parseDays(segment); 97 | times = []; 98 | } 99 | } 100 | if (_this.isTimeRange(segment)) { 101 | if (segment == "off") { 102 | times = []; 103 | } 104 | else { 105 | times.push(segment); 106 | } 107 | } 108 | }); 109 | //commit last times to it days 110 | days.forEach(function (day) { 111 | if (tempData[day]) { 112 | tempData[day] = tempData[day].concat(times); 113 | } 114 | else { 115 | tempData[day] = times; 116 | } 117 | }); 118 | //apply data to main obj 119 | for (var key in tempData) { 120 | this.openingHours[key] = tempData[key]; 121 | } 122 | }; 123 | SimpleOpeningHours.prototype.parseDays = function (part) { 124 | var _this = this; 125 | var days = []; 126 | var softparts = part.split(','); 127 | softparts.forEach(function (part) { 128 | var rangecount = (part.match(/\-/g) || []).length; 129 | if (rangecount == 0) { 130 | days.push(part); 131 | } 132 | else { 133 | days = days.concat(_this.calcDayRange(part)); 134 | } 135 | }); 136 | return days; 137 | }; 138 | SimpleOpeningHours.prototype.init = function () { 139 | this.openingHours = { 140 | su: [], 141 | mo: [], 142 | tu: [], 143 | we: [], 144 | th: [], 145 | fr: [], 146 | sa: [], 147 | ph: [] 148 | }; 149 | }; 150 | /** 151 | * Calculates the days in range "mo-we" -> ["mo", "tu", "we"] 152 | */ 153 | SimpleOpeningHours.prototype.calcDayRange = function (range) { 154 | var def = { 155 | su: 0, 156 | mo: 1, 157 | tu: 2, 158 | we: 3, 159 | th: 4, 160 | fr: 5, 161 | sa: 6 162 | }; 163 | var rangeElements = range.split('-'); 164 | var dayStart = def[rangeElements[0]]; 165 | var dayEnd = def[rangeElements[1]]; 166 | var numberRange = this.calcRange(dayStart, dayEnd, 6); 167 | var outRange = []; 168 | numberRange.forEach(function (n) { 169 | for (var key in def) { 170 | if (def[key] == n) { 171 | outRange.push(key); 172 | } 173 | } 174 | }); 175 | return outRange; 176 | }; 177 | /** 178 | * Creates a range between two number. 179 | * if the max value is 6 a range bewteen 6 and 2 is 6, 0, 1, 2 180 | */ 181 | SimpleOpeningHours.prototype.calcRange = function (min, max, maxval) { 182 | if (min == max) { 183 | return [min]; 184 | } 185 | var range = [min]; 186 | var rangepoint = min; 187 | while (rangepoint < ((min < max) ? max : maxval)) { 188 | rangepoint++; 189 | range.push(rangepoint); 190 | } 191 | if (min > max) { 192 | //add from first in list to max value 193 | range = range.concat(this.calcRange(0, max, maxval)); 194 | } 195 | return range; 196 | }; 197 | /** 198 | * Check if string is time range 199 | */ 200 | SimpleOpeningHours.prototype.isTimeRange = function (input) { 201 | //e.g. 09:00+ 202 | if (input.match(/[0-9]{1,2}:[0-9]{2}\+/)) { 203 | return true; 204 | } 205 | //e.g. 08:00-12:00 206 | if (input.match(/[0-9]{1,2}:[0-9]{2}\-[0-9]{1,2}:[0-9]{2}/)) { 207 | return true; 208 | } 209 | //off 210 | if (input.match(/off/)) { 211 | return true; 212 | } 213 | return false; 214 | }; 215 | /** 216 | * check if string is day or dayrange 217 | */ 218 | SimpleOpeningHours.prototype.checkDay = function (input) { 219 | var days = ["mo", "tu", "we", "th", "fr", "sa", "su", "ph"]; 220 | if (input.match(/\-/g)) { 221 | var rangeElements = input.split('-'); 222 | if (days.indexOf(rangeElements[0]) !== -1 223 | && days.indexOf(rangeElements[1]) !== -1) { 224 | return true; 225 | } 226 | } 227 | else if (days.indexOf(input) !== -1) { 228 | return true; 229 | } 230 | return false; 231 | }; 232 | /** 233 | * Compares to timestrings e.g. "18:00" 234 | * if time1 > time2 -> 1 235 | * if time1 < time2 -> -1 236 | * if time1 == time2 -> 0 237 | */ 238 | SimpleOpeningHours.prototype.compareTime = function (time1, time2) { 239 | var date1 = Number(time1.replace(":", "")); 240 | var date2 = Number(time2.replace(":", "")); 241 | if (date1 > date2) { 242 | return 1; 243 | } 244 | else if (date1 < date2) { 245 | return -1; 246 | } 247 | else { 248 | return 0; 249 | } 250 | }; 251 | return SimpleOpeningHours; 252 | }()); 253 | exports.default = SimpleOpeningHours; 254 | function map(oh, callback) { 255 | var table = oh.getTable(); 256 | return ["mo", "tu", "we", "th", "fr", "sa", "su"].map(function (weekday, index) { return (callback(((index + 1) % 7), table[weekday])); }); 257 | } 258 | exports.map = map; 259 | }); 260 | //# sourceMappingURL=simple-opening-hours.js.map --------------------------------------------------------------------------------