diff --git a/.classpath b/.classpath index 1c172a91d61..43e9d845202 100644 --- a/.classpath +++ b/.classpath @@ -1,10 +1,10 @@ - + - + @@ -13,7 +13,7 @@ - + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 7ce4ded9556..412d513a5e0 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -6,6 +6,7 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.7 diff --git a/.settings/org.eclipse.jdt.groovy.core.prefs b/.settings/org.eclipse.jdt.groovy.core.prefs index a68b5bf3970..f8dc8b14eb0 100644 --- a/.settings/org.eclipse.jdt.groovy.core.prefs +++ b/.settings/org.eclipse.jdt.groovy.core.prefs @@ -1,2 +1,2 @@ eclipse.preferences.version=1 -groovy.compiler.level=-1 +groovy.compiler.level=23 diff --git a/CONTRIBUTION b/CONTRIBUTION index 2e4d453ff37..df5de61beb9 100644 --- a/CONTRIBUTION +++ b/CONTRIBUTION @@ -1,78 +1,83 @@ -JOSM was originally designed and coded by Immanuel Scholz, -and is now maintained by the OpenStreetMap community. - -The current JOSM maintainer is Dirk Stöcker. -A lot of administration work is done by Paul Hartmann and Vincent Privat. - -Major code contributions from (in alphabetical order): - -Stefan Breunig -David Earl -Gabriel Ebner -Dave Hansen -Paul Hartmann -Karl Guggisberg -Matthias Julius -Alexei Kasatkin -Jiri Klement -Ulf Lamping -Simon Legner -Raphael Mack -Upliner Mikhalych -Vincent Privat -Frederik Ramm -Dirk Stöcker - -Many minor contributions and patches by others; see SVN history -at http://josm.openstreetmap.de/svn/ for details. Use -"josm-dev AT openstreetmap.org" to contact still active authors. - -Copyright rests with the contributors. - --------------------------------- EXTERNAL CODE -------------------------------- - -The jpeg metadata extraction code is from Drew Noakes -(http://code.google.com/p/metadata-extractor/) and licensed -with Apache license version 2.0. - -The Bzip2 code is from Keiron Liddle (Apache project) and licensed -with Apache license version 2.0. - -The signpost code (http://code.google.com/p/oauth-signpost/) -is from Matthias Käppler and licensed with the Apache License 2.0. - -The NTv2 transformation code (http://jgridshift.sourceforge.net/) -is from Peter Yuill and licensed with LGPL. - -The MultiSplitPane is from Hans Muller and licensed with LGPL -(https://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html). - -The Alphanum Algorithm is from David Koelle and license with LGPL -(http://www.davekoelle.com/alphanum.html) - -The Diff code (http://www.bmsi.com/java/#diff) -is from Stuart D. Gathman and licensed with GPL. - -The Ellipsoid code (http://www.i3s.unice.fr/~johan/gps/) -is from Johan Montagnat and licensed with GPL. - -The JSON code (https://java.net/projects/jsonp/) -is from Oracle (RI for JSR 353: Java API for JSON Processing) -and licensed with GPL / classpath exception. - -The opening hour validation uses code from opening_hour.js -(https://github.com/ypid/opening_hours.js) which is licensed -with the New (2-clause) BSD license. - ------------------------------------- ICONS ------------------------------------ - -Most icons have been nicked from GNOME and the GIMP, both under GPL. -Some are originally LGPL but redistributed here under GPL. - -The keyboard icon comes fom: -- source: http://www.iconfinder.net/index.php?q=key&page=icondetails&iconid=8553&size=128&q=key&s12=on&s16=on&s22=on&s32=on&s48=on&s64=on&s128=on -- licence: GPL -- designer: Paolino, http://www.paolinoland.it/ -- original filename: keyboard.png -- original size: 128x128 -- modifications: icon was cropped, then resized +JOSM was originally designed and coded by Immanuel Scholz, +and is now maintained by the OpenStreetMap community. + +The current JOSM maintainer is Dirk Stöcker. +A lot of administration work is done by Paul Hartmann and Vincent Privat. + +Major code contributions from (in alphabetical order): + +Stefan Breunig +David Earl +Gabriel Ebner +Dave Hansen +Paul Hartmann +Karl Guggisberg +Matthias Julius +Alexei Kasatkin +Jiri Klement +Ulf Lamping +Simon Legner +Raphael Mack +Upliner Mikhalych +Vincent Privat +Frederik Ramm +Dirk Stöcker + +Many minor contributions and patches by others; see SVN history +at http://josm.openstreetmap.de/svn/ for details. Use +"josm-dev AT openstreetmap.org" to contact still active authors. + +The logo has been designed by Ilya Palikov. + +Copyright rests with the contributors. + +-------------------------------- EXTERNAL CODE -------------------------------- + +The jpeg metadata extraction code is from Drew Noakes +(http://code.google.com/p/metadata-extractor/) and licensed +with Apache license version 2.0. + +The Bzip2 code is from Apache Commons Compress and licensed +with Apache license version 2.0. + +The signpost code (http://code.google.com/p/oauth-signpost/) +is from Matthias Käppler and licensed with the Apache License 2.0. + +The mail/url validator routines use code from Apache Commons +Validator which is licensed with Apache license version 2.0. + +The NTv2 transformation code (http://jgridshift.sourceforge.net/) +is from Peter Yuill and licensed with LGPL. + +The MultiSplitPane is from Hans Muller and licensed with LGPL +(https://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html). + +The Alphanum Algorithm is from David Koelle and license with LGPL +(http://www.davekoelle.com/alphanum.html) + +The Diff code (http://www.bmsi.com/java/#diff) +is from Stuart D. Gathman and licensed with GPL. + +The Ellipsoid code (http://www.i3s.unice.fr/~johan/gps/) +is from Johan Montagnat and licensed with GPL. + +The JSON code (https://java.net/projects/jsonp/) +is from Oracle (RI for JSR 353: Java API for JSON Processing) +and licensed with GPL / classpath exception. + +The opening hour validation uses code from opening_hour.js +(https://github.com/ypid/opening_hours.js) which is licensed +with the New (2-clause) BSD license. + +------------------------------------ ICONS ------------------------------------ + +Most icons have been nicked from GNOME and the GIMP, both under GPL. +Some are originally LGPL but redistributed here under GPL. + +The keyboard icon comes fom: +- source: http://www.iconfinder.net/index.php?q=key&page=icondetails&iconid=8553&size=128&q=key&s12=on&s16=on&s22=on&s32=on&s48=on&s64=on&s128=on +- licence: GPL +- designer: Paolino, http://www.paolinoland.it/ +- original filename: keyboard.png +- original size: 128x128 +- modifications: icon was cropped, then resized diff --git a/README b/README index 0c921e32e31..368f6f2df3e 100644 --- a/README +++ b/README @@ -16,7 +16,7 @@ How to get Java Runtime Environment ----------------------------------- You need JRE Version 7, or later. -Microsoft Windows and Apple Mac OS X users should visit http://www.java.com +Microsoft Windows and Apple Mac OS X users should visit https://www.java.com and download the latest Java executable for their system. Linux users should visit http://www.oracle.com/technetwork/java/index.html @@ -58,14 +58,17 @@ This is an overview of the files and directories in the JOSM code repository: - build.xml ant build file (standard way to create a JOSM binary) - CONTRIBUTION list of major code contributors - data/ data files that will be included in the JOSM jar file - - *.gsb NTv2 grid files for projection support + - fonts/ font files used for map rendering + - projection/ projection files + - *.gsb NTv2 grid files for projection support + - epsg list of projection definitions - *.lang translation data - *.xsd xml schema files for validation of configuration files - - epsg list of projection definitions - help-browser.css CSS file for the help sites (HTML content is downloaded from the website on demand, but displayed inside the programm in a Java web browser component.) - - ignoretags.cfg, tagchecker.cfg - data files used by the JOSM validator feature + - validator/ data files used by the JOSM validator feature + - *.cfg files designed for the old tagchecker, still used + - *.mapcss default validation rules for the new mapcss-based tagchecker - data_nodist/ data files that are useful for development, but not distributed - exif-direction-example.jpg sample image, that contains direction information in the EXIF header @@ -87,14 +90,18 @@ This is an overview of the files and directories in the JOSM code repository: - styles/standard images for the main map style (external repository) - images_nodist/ images, which are not for distribution, but may be useful later (e.g. high resolution and vector versions) -- josm.jnlp Java Web Start launcher file (used on the website) -- lib/ libraries (directory is empty at the moment) +- josm.jnlp Java Web Start launcher file (used on the website for the tested version) +- josm-latest.jnlp Java Web Start launcher file (used on the website for the latest version) - LICENSE the JOSM license terms -- macosx/ files needed to create the MacOS package +- linux/ files useful for Linux distributions, including Appdata files, .desktop + files, Debian/Ubuntu scripts, man pages, icons, etc. +- macosx/ files needed to create the MacOS X package - netbeans/ preconfigured Netbeans project - optimize-images short script to decrease size of PNG images - patches/ patches for external libraries used in JOSM (see below) - README this file +- resources/ resource files that will be included in the JOSM jar file +- scripts/ various scripts used by JOSM developers - src/ the source code of the program - start.html HTML page to run the applet version of JOSM - styles/ map styles included in JOSM @@ -107,15 +114,18 @@ This is an overview of the files and directories in the JOSM code repository: - performance/ performance tests (source code) - unit/ unit tests (source code) - tools/ libraries and tools that help in the development process - - animal-sniffer-ant-tasks-1.8.jar - TODO: what is this? + - animal-sniffer-ant-tasks-1.13.jar + used to build and check code signatures to ensure plugins binary compatibility - appbundler-1.0ea.jar used to build Mac OS X package for Oracle Java 7 - findbugs/ libs and config files for findbugs (automatically detects common bugs and potential problems in source code); can be launched as an ant target in build.xml + - groovy-all-2.3.9.jar used for some unit tests and various scripts - jacocoant.jar used to include coverage data into JUnit test reports - javacc.jar used in the build process to generate some .java files from a javacc source file (src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj) - proguard.jar optimize final binary jar - see build.xml (not used in production so far) + - xmltask.jar used to edit XML files from Ant for the OSX package +- windows/ files needed to create the Windows installer The 'patches' directory ----------------------- @@ -132,34 +142,35 @@ Of course, it is also possible to apply the patch files manually one by one. Third party libraries --------------------- -There are a couple of third party libraries which are directly included in the source code tree, in particular: +There are some third party libraries which are directly included in the source code tree, in particular: * jmapviewer: Java component to browse a TMS map src/org/openstreetmap/gui (svn external) -> http://svn.openstreetmap.org/applications/viewer/jmapviewer/ -* Apache Ant's bzip2: Support for bzip2 compression when opening files - src/org/apache/tools/bzip2 (svn external) - -> http://svn.apache.org/repos/asf/ant/core/trunk/src/main/org/apache/tools/bzip2 * Apache commons codec: Better Base64 support src/org/apache/commons/codec (svn external) -> http://svn.apache.org/repos/asf/commons/proper/codec/trunk/src/main/java/org/apache/commons/codec +* Apache commons compress: Support for bzip2 compression when opening files + src/org/apache/commons/compress/compressors (svn external) + -> http://svn.apache.org/repos/asf/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors +* Apache commons validator: Improved validator routines + src/org/openstreetmap/josm/data/validation/routines + -> http://commons.apache.org/proper/commons-validator * SVG Salamander: Support for SVG image format src/com/kitfox/svg - -> http://svgsalamander.java.net/ + -> https://svgsalamander.java.net/ * Metadata Extractor: Read EXIF Metadata of photos src/com/drew - -> http://www.drewnoakes.com/code/exif/ + -> https://www.drewnoakes.com/code/exif/ * Signpost: OAuth library src/oauth, src/com/google - -> http://code.google.com/p/oauth-signpost/ + -> https://code.google.com/p/oauth-signpost/ * GNU getopt Java port: Command line argument processing library src/gnu/getopt - -> http://www.urbanophile.com/~arenn/hacking/download.html + -> http://www.urbanophile.com/arenn/hacking/download.html * MultiSplitPane: Small lib for GUI layout management src/org/openstreetmap/josm/gui/MultiSplitLayout.java, MultiSplitPane.java -> http://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html * swinghelper: Class CheckThreadViolationRepaintManager to find classpath violations src/org/jdesktop/swinghelper/debug/CheckThreadViolationRepaintManager.java - -> http://java.net/projects/swinghelper - - + -> https://java.net/projects/swinghelper diff --git a/build.xml b/build.xml index d82d3b88eff..43de593459b 100644 --- a/build.xml +++ b/build.xml @@ -25,12 +25,6 @@ - - - - - - - - @@ -186,6 +177,13 @@ Build-Date: ${build.tstamp} + + + + + + + @@ -217,13 +215,14 @@ Build-Date: ${build.tstamp} - - @@ -278,11 +277,8 @@ Build-Date: ${build.tstamp} - - - - + @@ -329,7 +325,7 @@ Build-Date: ${build.tstamp} - + @@ -452,7 +448,7 @@ Build-Date: ${build.tstamp} - + @@ -524,6 +520,8 @@ Build-Date: ${build.tstamp} + + @@ -534,6 +532,7 @@ Build-Date: ${build.tstamp} + @@ -541,8 +540,15 @@ Build-Date: ${build.tstamp} + + + + + + + @@ -574,5 +580,4 @@ Build-Date: ${build.tstamp} - diff --git a/data/ast.lang b/data/ast.lang new file mode 100644 index 00000000000..71bde654bbf Binary files /dev/null and b/data/ast.lang differ diff --git a/data/bg.lang b/data/bg.lang index 4f491f22325..0cc1de9f9d3 100644 Binary files a/data/bg.lang and b/data/bg.lang differ diff --git a/data/ca.lang b/data/ca.lang index 17f0825f528..178d11109cb 100644 Binary files a/data/ca.lang and b/data/ca.lang differ diff --git a/data/cs.lang b/data/cs.lang index d70e0620423..ec2c12911d3 100644 Binary files a/data/cs.lang and b/data/cs.lang differ diff --git a/data/da.lang b/data/da.lang index cb4c2d6c137..d8600050946 100644 Binary files a/data/da.lang and b/data/da.lang differ diff --git a/data/de.lang b/data/de.lang index 0d88fcd2e4f..7bbcd8decbd 100644 Binary files a/data/de.lang and b/data/de.lang differ diff --git a/data/defaultpresets.xml b/data/defaultpresets.xml index 0c2f03859e2..6b9b7bc324d 100644 --- a/data/defaultpresets.xml +++ b/data/defaultpresets.xml @@ -7,169 +7,36 @@ http://www.kde.org http://openclipart.org/media/view/media/clip_art http://www.sjjb.co.uk/mapicons/ (meanwhile in osm svn) ---> - + + + + + + + + + + + + + + + + + + - + @@ -190,6 +57,10 @@ Note that for a match, at least one positive and no negative is required. + + + + @@ -227,12 +98,6 @@ Note that for a match, at least one positive and no negative is required. - - - - - - @@ -256,30 +121,39 @@ Note that for a match, at least one positive and no negative is required. - - + + + + + + + + + - + - + - + - + - + - + - + + + + - @@ -301,36 +175,38 @@ Note that for a match, at least one positive and no negative is required. - - - - - - + + + + + + + - - - + + - + - + - + - - + - - + + + + + - - + + @@ -360,7 +236,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -395,13 +271,13 @@ Note that for a match, at least one positive and no negative is required. + de.href="http://wiki.openstreetmap.org/wiki/DE:Tag:power=generator" + fr.href="http://wiki.openstreetmap.org/wiki/FR:Tag:power=generator" + it.href="http://wiki.openstreetmap.org/wiki/IT:Tag:power=generator" + ja.href="http://wiki.openstreetmap.org/wiki/JA:Tag:power=generator" + pl.href="http://wiki.openstreetmap.org/wiki/Pl:Tag:power=generator" + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:power=generator" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:power=generator" /> @@ -423,9 +299,26 @@ Note that for a match, at least one positive and no negative is required. + + + + + + + + + + + - - + + + + + + + + @@ -437,6 +330,7 @@ Note that for a match, at least one positive and no negative is required. fr.href="http://wiki.openstreetmap.org/wiki/FR:Tag:highway=motorway" it.href="http://wiki.openstreetmap.org/wiki/IT:Tag:highway=motorway" ja.href="http://wiki.openstreetmap.org/wiki/JA:Tag:highway=motorway" + ko.href="http://wiki.openstreetmap.org/wiki/Ko:Tag:highway=motorway" nl.href="http://wiki.openstreetmap.org/wiki/NL:Tag:highway=motorway" pl.href="http://wiki.openstreetmap.org/wiki/Pl:Tag:highway=motorway" pt.href="http://wiki.openstreetmap.org/wiki/Pt:Tag:highway=motorway" @@ -449,6 +343,7 @@ Note that for a match, at least one positive and no negative is required. + @@ -459,7 +354,8 @@ Note that for a match, at least one positive and no negative is required. - + + + @@ -483,13 +380,15 @@ Note that for a match, at least one positive and no negative is required. - + + + @@ -511,20 +411,16 @@ Note that for a match, at least one positive and no negative is required. - + + - + + @@ -536,13 +432,15 @@ Note that for a match, at least one positive and no negative is required. - + + - + - + + - + - + + @@ -590,9 +491,10 @@ Note that for a match, at least one positive and no negative is required. - + - + + - + - + + - + - + + - + - + + - + - - + + - - + - + + - + + - + + - - - + + - + + - + - + + - + - - + + - + - + + + + + + + - + - + + - + - - - + + - + + - + + + + - + - - - - - - - - - - - - - - - + + - + @@ -933,6 +840,7 @@ Note that for a match, at least one positive and no negative is required. de.href="http://wiki.openstreetmap.org/wiki/DE:Tag:highway=track" fi.href="http://wiki.openstreetmap.org/wiki/Fi:Tag:highway=track" fr.href="http://wiki.openstreetmap.org/wiki/FR:Tag:highway=track" + id.href="http://wiki.openstreetmap.org/wiki/Id:Tag:highway=track" it.href="http://wiki.openstreetmap.org/wiki/IT:Tag:highway=track" ja.href="http://wiki.openstreetmap.org/wiki/JA:Tag:highway=track" pl.href="http://wiki.openstreetmap.org/wiki/Pl:Tag:highway=track" @@ -945,16 +853,15 @@ Note that for a match, at least one positive and no negative is required. - - + + - - + + sv.href="http://wiki.openstreetmap.org/wiki/Sv:Tag:highway=path" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:highway=path" /> - - + + @@ -982,15 +890,14 @@ Note that for a match, at least one positive and no negative is required. - + - + - - + - - - + + - + - + - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:highway=cycleway" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:highway=cycleway" /> - - - - + + + - + - - - + + - + - - + + - - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:highway=footway" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:highway=footway" /> - + - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:highway=steps" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:highway=steps" /> - - + + + + + + + + + + + + + + + + + + + + + - + - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:highway=motorway_junction" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:highway=motorway_junction" /> - + - + - + - + - - - - - + + + + + + + - + + + + + - - + + + - - + + - - + + - + - + - - + + - + - + - + - + - + @@ -1273,7 +1225,19 @@ Note that for a match, at least one positive and no negative is required. - + + + + + + + + + - + - + - + - + - + - + - - + + - + - - + + + - + - + - - + + - + - - + + - + - + - + - - + + - - + + + pl.href="http://wiki.openstreetmap.org/wiki/Pl:Tag:barrier=kerb" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:barrier=kerb" /> - - - + + + + - - + + - + - + @@ -1511,33 +1490,55 @@ Note that for a match, at least one positive and no negative is required. - + + - + + - + - - + + - - - + + + @@ -1545,23 +1546,22 @@ Note that for a match, at least one positive and no negative is required. - + - + - + - + - - - - - + + @@ -1580,11 +1580,12 @@ Note that for a match, at least one positive and no negative is required. + - + + - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Key:covered" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Key:covered" /> @@ -1619,12 +1622,14 @@ Note that for a match, at least one positive and no negative is required. - + @@ -1634,7 +1639,7 @@ Note that for a match, at least one positive and no negative is required. - + - - - + + + @@ -1670,7 +1675,7 @@ Note that for a match, at least one positive and no negative is required. - + - + - + - + - + - + + + + + - + - + - - + + - - + + - + - + + - + - + + - + - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:natural=water" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:natural=water" /> + - - - - - - + + + + + + - + - + + - - + + + + - + + + + + + + + + + + + + + + + - + + + - + - + - + - + + - - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:man_made=pier" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:man_made=pier" /> - + - + - + - + - + - + - + - + @@ -2033,15 +2088,17 @@ Note that for a match, at least one positive and no negative is required. - + - + - + - + - + - + @@ -2078,7 +2139,7 @@ Note that for a match, at least one positive and no negative is required. - + - + - + - + + de.href="http://wiki.openstreetmap.org/wiki/DE:Tag:railway=disused" + ja.href="http://wiki.openstreetmap.org/wiki/JA:Tag:railway=disused" /> - + - + - + - - + + - + + - - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - @@ -2223,12 +2267,23 @@ Note that for a match, at least one positive and no negative is required. - + + + + + + + + + + + + + + - + @@ -2238,8 +2293,8 @@ Note that for a match, at least one positive and no negative is required. - - + + @@ -2251,52 +2306,19 @@ Note that for a match, at least one positive and no negative is required. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - - - + + + @@ -2304,34 +2326,37 @@ Note that for a match, at least one positive and no negative is required. - + + - + - + - - - + + + - + - + + - - - + + + - + + - - + + - - + + - + - + - + - + + - + + - + + - - + + + - - - + + + + - - - + + + - - - - + + + + + - - - + + + - + - - + + - + - + + - + - - - + + + + - + @@ -2543,8 +2599,8 @@ Note that for a match, at least one positive and no negative is required. - - + + @@ -2556,6 +2612,7 @@ Note that for a match, at least one positive and no negative is required. ja.href="http://wiki.openstreetmap.org/wiki/JA:Tag:amenity=bicycle_parking" pl.href="http://wiki.openstreetmap.org/wiki/Pl:Tag:amenity=bicycle_parking" pt.href="http://wiki.openstreetmap.org/wiki/Pt:Tag:amenity=bicycle_parking" + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:amenity=bicycle_parking" ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:amenity=bicycle_parking" uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:amenity=bicycle_parking" /> @@ -2564,8 +2621,9 @@ Note that for a match, at least one positive and no negative is required. - - + + + - + @@ -2586,8 +2644,8 @@ Note that for a match, at least one positive and no negative is required. - - + + - - + + + + + + + + + + + + + + - + - + @@ -2624,7 +2695,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -2632,8 +2703,8 @@ Note that for a match, at least one positive and no negative is required. - - + + @@ -2647,7 +2718,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -2666,7 +2737,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -2694,7 +2765,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -2703,7 +2774,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -2724,10 +2795,12 @@ Note that for a match, at least one positive and no negative is required. - + @@ -2737,21 +2810,23 @@ Note that for a match, at least one positive and no negative is required. - - + + - + - - + + - - - - - + + + + + - + @@ -2782,15 +2857,15 @@ Note that for a match, at least one positive and no negative is required. - - - + + + - + - + - + - + - + - + - + + - + - + @@ -2899,10 +2981,11 @@ Note that for a match, at least one positive and no negative is required. - + - + - - + + @@ -2921,7 +3005,7 @@ Note that for a match, at least one positive and no negative is required. - + - + - + + + + + + - - + + + - + - + - - - - + + + - - - + + + - + + - + - - + + + - + - - - + + + + - + - - + + + - + - - - + + + + - + - - - + + + - + - - + + - + + - + - + - - - + + + + - + - - - + + + - + - + - - + + - + - + - - - + + + - + - + - - + + - + - + - - + + - + - + - - - + + + - + - + - - - + + + - + - + - - - + + + - + - - + - - - + + + - + - + - - + + + - - - + + + - - + + + + + + + - + + + - + + + + - - + + + + - - + + + + + + - + + - + - + - - - + + + + + + + - + - - + + - + - - + + - + - - - + + + - + - - + + - - - - + + - - - + + + - - + + - - + + - + - - + - - + + - - + + + - - + + + + + + + + - - + + + + + + + + - - + + + - + - + - + - - + + - - - + + + + - - - + + + - - + + @@ -3624,7 +3784,7 @@ Note that for a match, at least one positive and no negative is required. - + + - - + + @@ -3651,6 +3812,7 @@ Note that for a match, at least one positive and no negative is required. pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:amenity=place_of_worship" ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:amenity=place_of_worship" sv.href="http://wiki.openstreetmap.org/wiki/Sv:Tag:amenity=place_of_worship" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:amenity=place_of_worship" zh_TW.href="http://wiki.openstreetmap.org/wiki/Zh-hant:Tag:amenity=place_of_worship" /> @@ -3659,8 +3821,9 @@ Note that for a match, at least one positive and no negative is required. - - + + + - - + + + - - + + + - - + + + - - + + + - - - + + + + - + - - + + + @@ -3802,9 +3971,10 @@ Note that for a match, at least one positive and no negative is required. ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:amenity=public_building" /> - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:amenity=post_office" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:amenity=post_office" /> - - + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - - + + @@ -3980,17 +4171,17 @@ Note that for a match, at least one positive and no negative is required. - - + + - - - - + + + + - - - + + + + - - - + + + + - - + + @@ -4025,9 +4226,8 @@ Note that for a match, at least one positive and no negative is required. - - - + + - - - - - - - - - - - - + + + + - + - - - + + + + - - - + + + - + - + - + + + + + + + + + + + + + - + - + - + + + - - + + + - + - - - + + - + - + - + - - + + - - + + - + - + - - + + + - - + + + - - - - + + + + - + - + - + + + + + + + + + + + + + + + + + + - + - + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + - - - + + + + - - - + + + @@ -4378,22 +4652,23 @@ Note that for a match, at least one positive and no negative is required. - - + + - - - + + + - + @@ -4401,14 +4676,14 @@ Note that for a match, at least one positive and no negative is required. - - + + - + - - + + - - + + - + - + - - - - + + + - - - + + - + @@ -4468,7 +4741,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -4476,24 +4749,35 @@ Note that for a match, at least one positive and no negative is required. - - + + - - - + + + + + + + + + - + - + - - + + - - + + - + - + - - + + @@ -4545,48 +4829,48 @@ Note that for a match, at least one positive and no negative is required. - + - + - - + + - + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - - + + - - + + - - + + - + + - - + + + - + + - + + - + + - - + + + - - + + + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:highway=raceway" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:highway=raceway" /> - + @@ -4730,40 +5052,52 @@ Note that for a match, at least one positive and no negative is required. ru.href="http://wiki.openstreetmap.org/wiki/RU:Key:building" uk.href="http://wiki.openstreetmap.org/wiki/Uk:Key:building" /> - + - + + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Key:building" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Key:building" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Key:building" /> - + - + - + - - + + + @@ -4772,27 +5106,34 @@ Note that for a match, at least one positive and no negative is required. - + - + - + + - - - + + + - - + + - + + - + + + + + + + - + - + - + - + + + + + + - + + - + - - + + - + - - + + - - + + - + - - + + - + - + @@ -4964,15 +5330,16 @@ Note that for a match, at least one positive and no negative is required. - + + + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:man_made=survey_point" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:man_made=survey_point" /> - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -5057,14 +5466,14 @@ Note that for a match, at least one positive and no negative is required. - + - + @@ -5076,42 +5485,42 @@ Note that for a match, at least one positive and no negative is required. - + - + - + - + - + - + @@ -5136,7 +5545,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -5147,7 +5556,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -5160,7 +5569,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -5172,7 +5581,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -5183,31 +5592,31 @@ Note that for a match, at least one positive and no negative is required. - + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + + - - + + - + + - + + + + + + - + @@ -5387,7 +5823,7 @@ Note that for a match, at least one positive and no negative is required. - + - + - + - - + + - + + + - - + + - - + + - + - + - + + - + - + + + + + + + + + - + + + + + + - + + + + + + + + - + - + @@ -5494,9 +5984,9 @@ Note that for a match, at least one positive and no negative is required. ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:shop=supermarket" /> - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - + - - - + + + - - - + + + - + - + - + - + - - + + - + - - + + + de.href="http://wiki.openstreetmap.org/wiki/DE:Key:organic" + es.href="http://wiki.openstreetmap.org/wiki/ES:Key:organic" + ja.href="http://wiki.openstreetmap.org/wiki/JA:Key:organic" /> - + - + - + - - - + + + - - - + + + + + + + + + + @@ -5691,11 +6198,11 @@ Note that for a match, at least one positive and no negative is required. ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:shop=clothes" /> - + - - + + - - - + + + - + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + + - - - + + + - - - + + + @@ -5787,9 +6296,9 @@ Note that for a match, at least one positive and no negative is required. ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:shop=computer" /> - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + + + + + + + + - + - + - - + + + - - - + + + - - + + - - - + + + - + + - - - + + + - - - + + + - - - + + + + + + + + + + - - - + + + - - - - + + + + - - - + + + + + + + + + + - - + + - - - + + + + - - - + + + + - + - - + + + - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + - + - - + + - - - + + + - - - + + + - - - + + + - + - - - + + + - - - + + + - - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + + es.href="http://wiki.openstreetmap.org/wiki/ES:Tag:shop=lottery" + pt.href="http://wiki.openstreetmap.org/wiki/Pt:Tag:shop=lottery" /> - - + + + - - - + + + - - - + + + + + + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + - + + + @@ -6291,14 +6936,15 @@ Note that for a match, at least one positive and no negative is required. - - - + + + - - + + - + + + + + + + + + + + + + + + + + + + + + - - + + + - - + + + - - + + + - - + + + - - + + + - + - + + - - + + - - - - - - - - - + + + - - + + + - - + + + - - + + + + + + + + + + - + + + + + - + + + + - + + + - + + + - + + + + + + + + + + + - + - + + + + - - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:natural=tree" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:natural=tree" /> + - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:natural=tree_row" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:natural=tree_row" /> + - + + + - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:landuse=forest" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Tag:landuse=forest" /> - - + + + + + - + - + + + - + + + - + + + + - + + + + + + - + + + + + + + + + + + + + - + - - + + + - - + + + - - + + - - + + + - + - + - - + + + - + - + + - - + + + - - + + - - + + - - + + + - - + + - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:landuse=brownfield" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:landuse=brownfield" /> - - + + - - + + - - + + - + - + - + - + - + + + + + + + + + + + - - + + + @@ -6974,8 +7878,8 @@ Note that for a match, at least one positive and no negative is required. - - + + - + + + + + + + + + + + + + @@ -7005,16 +7962,17 @@ Note that for a match, at least one positive and no negative is required. it.href="http://wiki.openstreetmap.org/wiki/IT:Relation:multipolygon" ja.href="http://wiki.openstreetmap.org/wiki/JA:Relation:multipolygon" pl.href="http://wiki.openstreetmap.org/wiki/Pl:Relation:multipolygon" - ru.href="http://wiki.openstreetmap.org/wiki/RU:Relation:multipolygon" /> + ru.href="http://wiki.openstreetmap.org/wiki/RU:Relation:multipolygon" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Relation:multipolygon" /> - - + + - + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Relation:boundary" + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Relation:boundary" /> + - - - + + - + - + - + - + - + - - + + uk.href="http://wiki.openstreetmap.org/wiki/Uk:Cycle_routes" /> - + - + + + - - - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Mountain_biking" /> @@ -7134,40 +8095,41 @@ Note that for a match, at least one positive and no negative is required. - + + + - - - + + + ro.href="http://wiki.openstreetmap.org/wiki/Ro:Hiking" + ru.href="http://wiki.openstreetmap.org/wiki/RU:Hiking" /> - + - + - - - + + + fr.href="http://wiki.openstreetmap.org/wiki/FR:Tag:route=piste" /> - + @@ -7181,15 +8143,14 @@ Note that for a match, at least one positive and no negative is required. - - - + + + de.href="http://wiki.openstreetmap.org/wiki/DE:Tag:route=ski" /> - + @@ -7203,12 +8164,11 @@ Note that for a match, at least one positive and no negative is required. - - + - + + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:route=detour" /> @@ -7218,11 +8178,12 @@ Note that for a match, at least one positive and no negative is required. - + - - + + + @@ -7230,12 +8191,13 @@ Note that for a match, at least one positive and no negative is required. - - + + + ru.href="http://wiki.openstreetmap.org/wiki/RU:Tag:route=railway" /> @@ -7243,11 +8205,11 @@ Note that for a match, at least one positive and no negative is required. - - + + + pt_BR.href="http://wiki.openstreetmap.org/wiki/Pt-br:Tag:route=road" /> @@ -7256,15 +8218,13 @@ Note that for a match, at least one positive and no negative is required. - - - - + + + + - - + - @@ -7277,7 +8237,7 @@ Note that for a match, at least one positive and no negative is required. - + - + - + @@ -7307,8 +8267,8 @@ Note that for a match, at least one positive and no negative is required. - - + + - + @@ -7339,7 +8299,7 @@ Note that for a match, at least one positive and no negative is required. - + @@ -7354,7 +8314,7 @@ Note that for a match, at least one positive and no negative is required. - + - + @@ -7387,6 +8347,6 @@ Note that for a match, at least one positive and no negative is required. - + diff --git a/data/el.lang b/data/el.lang index a8ce98c4ee8..d8d4a565a89 100644 Binary files a/data/el.lang and b/data/el.lang differ diff --git a/data/en.lang b/data/en.lang index f79b2e55f46..c1fda1311d2 100644 Binary files a/data/en.lang and b/data/en.lang differ diff --git a/data/en_AU.lang b/data/en_AU.lang index 8a5f64675ab..262bbb4b08f 100644 Binary files a/data/en_AU.lang and b/data/en_AU.lang differ diff --git a/data/en_GB.lang b/data/en_GB.lang index 8c8b865142d..0b317f9c27d 100644 Binary files a/data/en_GB.lang and b/data/en_GB.lang differ diff --git a/data/es.lang b/data/es.lang index 20496627db2..d413bcb7de4 100644 Binary files a/data/es.lang and b/data/es.lang differ diff --git a/data/et.lang b/data/et.lang index 3ad1ed19a39..6c89d7320d9 100644 Binary files a/data/et.lang and b/data/et.lang differ diff --git a/data/fi.lang b/data/fi.lang index 28197a7e3aa..d9e1c90dfeb 100644 Binary files a/data/fi.lang and b/data/fi.lang differ diff --git a/data/fonts/DroidSans-Bold.ttf b/data/fonts/DroidSans-Bold.ttf new file mode 100644 index 00000000000..d065b64eb18 Binary files /dev/null and b/data/fonts/DroidSans-Bold.ttf differ diff --git a/data/fonts/DroidSans.ttf b/data/fonts/DroidSans.ttf new file mode 100644 index 00000000000..ad1efca88ae Binary files /dev/null and b/data/fonts/DroidSans.ttf differ diff --git a/data/fr.lang b/data/fr.lang index e8ad2e725ec..ef0f45c56ff 100644 Binary files a/data/fr.lang and b/data/fr.lang differ diff --git a/data/gl.lang b/data/gl.lang index 451cf5a140d..b6dd21adfee 100644 Binary files a/data/gl.lang and b/data/gl.lang differ diff --git a/data/hu.lang b/data/hu.lang index a8181aabe10..ddbb8ecf2f6 100644 Binary files a/data/hu.lang and b/data/hu.lang differ diff --git a/data/id.lang b/data/id.lang index 92fe9c1ab4e..362603ff5da 100644 Binary files a/data/id.lang and b/data/id.lang differ diff --git a/data/it.lang b/data/it.lang index 11562a2d302..9dbce1a7410 100644 Binary files a/data/it.lang and b/data/it.lang differ diff --git a/data/ja.lang b/data/ja.lang index bf6fc28ab24..2cbbcb9290f 100644 Binary files a/data/ja.lang and b/data/ja.lang differ diff --git a/data/km.lang b/data/km.lang new file mode 100644 index 00000000000..fb92312d389 Binary files /dev/null and b/data/km.lang differ diff --git a/data/left-right-hand-traffic.osm b/data/left-right-hand-traffic.osm index 4c3ee0080b1..1e24fa943f0 100644 --- a/data/left-right-hand-traffic.osm +++ b/data/left-right-hand-traffic.osm @@ -151,8 +151,8 @@ - - + + diff --git a/data/mappaint-style.xsd b/data/mappaint-style.xsd index 7859091fc06..7a97f743007 100644 --- a/data/mappaint-style.xsd +++ b/data/mappaint-style.xsd @@ -21,6 +21,7 @@ + diff --git a/data/maps.xsd b/data/maps.xsd index 772b88f29af..d7b8c5d37d3 100644 --- a/data/maps.xsd +++ b/data/maps.xsd @@ -26,7 +26,7 @@ - + @@ -57,6 +57,22 @@ + + + + + + + + + + + + + + + + @@ -595,52 +611,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/data/nl.lang b/data/nl.lang index 8ae5ffcc2bd..8496a2ab114 100644 Binary files a/data/nl.lang and b/data/nl.lang differ diff --git a/data/pl.lang b/data/pl.lang index a86b21217f5..b59031b3693 100644 Binary files a/data/pl.lang and b/data/pl.lang differ diff --git a/data/projection/epsg b/data/projection/epsg index 86c7cdc7f0a..148eb00409c 100644 --- a/data/projection/epsg +++ b/data/projection/epsg @@ -52,12 +52,36 @@ <3949> +proj=lcc +lat_0=49 +lat_1=48.25 +lat_2=49.75 +lon_0=3 +x_0=1700000 +y_0=8200000 +ellps=GRS80 +nadgrids=null +bounds=-5.5,46.5,10.2,51.1 <> # Lambert CC9 (France) Zone 50 <3950> +proj=lcc +lat_0=50 +lat_1=49.25 +lat_2=50.75 +lon_0=3 +x_0=1700000 +y_0=9200000 +ellps=GRS80 +nadgrids=null +bounds=-5.5,47.5,10.2,51.1 <> +# ETRS89 +<4258> +proj=lonlat +ellps=GRS80 +datum=GRS80 +bounds=-180,-90,180,90 <> # WGS 84 <4326> +proj=lonlat +ellps=WGS84 +datum=WGS84 +bounds=-180,-90,180,90 <> # Swiss Grid (Switzerland) <21781> +proj=somerc +lat_0=46d57'8.66" +lon_0=7d26'22.5" +x_0=600000 +y_0=200000 +ellps=bessel +towgs84=674.374,15.056,405.346 +bounds=5.7,45.7,10.6,47.9 <> # HD72 / EOV (Hungary) <23700> +proj=somerc +lat_0=47.14439372222222 +lon_0=19.04857177777778 +k_0=0.99993 +x_0=650000 +y_0=200000 +ellps=GRS67 +towgs84=52.17,-71.82,-14.9 +units=m +bounds=16.1200,45.7800,22.9100,48.6000 <> +# ETRS89 / UTM zone 28N +<25828> +proj=tmerc +lon_0=-15 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=-20,-5,-10,85 <> +# ETRS89 / UTM zone 29N +<25829> +proj=tmerc +lon_0=-9 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=-14,-5,-4,85 <> +# ETRS89 / UTM zone 30N +<25830> +proj=tmerc +lon_0=-3 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=-8,-5,2,85 <> +# ETRS89 / UTM zone 31N +<25831> +proj=tmerc +lon_0=3 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=-2,-5,8,85 <> +# ETRS89 / UTM zone 32N +<25832> +proj=tmerc +lon_0=9 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=4,-5,14,85 <> +# ETRS89 / UTM zone 33N +<25833> +proj=tmerc +lon_0=15 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=10,-5,20,85 <> +# ETRS89 / UTM zone 34N +<25834> +proj=tmerc +lon_0=21 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=16,-5,26,85 <> +# ETRS89 / UTM zone 35N +<25835> +proj=tmerc +lon_0=27 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=22,-5,32,85 <> +# ETRS89 / UTM zone 36N +<25836> +proj=tmerc +lon_0=33 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=28,-5,38,85 <> +# ETRS89 / UTM zone 37N +<25837> +proj=tmerc +lon_0=39 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=34,-5,44,85 <> +# ETRS89 / UTM zone 38N +<25838> +proj=tmerc +lon_0=45 +k_0=0.9996 +x_0=500000 +ellps=GRS80 +datum=GRS80 +bounds=40,-5,50,85 <> # Lambert 4 Zones France (Nord) <27561> +proj=lcc +lat_0=49.5 +lat_1=48d35'54.682" +lat_2=50d23'45.282" +lon_0=2d20'14.025" +x_0=600000 +y_0=200000 +a=6378249.2 +b=6356515 +nadgrids=ntf_r93_b.gsb +bounds=-4.416666666666665,46.65,9.18,51.300000000000004 <> # Lambert 4 Zones France (Centre) diff --git a/data/pt.lang b/data/pt.lang index 516ab1f6185..e723ac23edc 100644 Binary files a/data/pt.lang and b/data/pt.lang differ diff --git a/data/pt_BR.lang b/data/pt_BR.lang index 52200abbffe..0a40e219501 100644 Binary files a/data/pt_BR.lang and b/data/pt_BR.lang differ diff --git a/data/ru.lang b/data/ru.lang index 5ea1de64a5c..996a6c5b506 100644 Binary files a/data/ru.lang and b/data/ru.lang differ diff --git a/data/sk.lang b/data/sk.lang index 81fb29a2f68..af6eb8b43e5 100644 Binary files a/data/sk.lang and b/data/sk.lang differ diff --git a/data/sv.lang b/data/sv.lang index 4035beb7482..0723cf37a42 100644 Binary files a/data/sv.lang and b/data/sv.lang differ diff --git a/data/tagging-preset.xsd b/data/tagging-preset.xsd index df890d49fe3..90a8b209d16 100644 --- a/data/tagging-preset.xsd +++ b/data/tagging-preset.xsd @@ -85,22 +85,30 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -116,13 +124,19 @@ + + + + + + + - @@ -140,6 +154,8 @@ + + @@ -178,7 +194,7 @@ - + @@ -206,7 +222,7 @@ - + @@ -215,7 +231,7 @@ - + @@ -246,7 +262,7 @@ - + @@ -276,6 +292,7 @@ + diff --git a/data/uk.lang b/data/uk.lang index de66290d896..431d47f0b32 100644 Binary files a/data/uk.lang and b/data/uk.lang differ diff --git a/data/validator/addresses.mapcss b/data/validator/addresses.mapcss index 612fa42089f..9116c4e81a7 100644 --- a/data/validator/addresses.mapcss +++ b/data/validator/addresses.mapcss @@ -1,6 +1,6 @@ -/* see #9667 - Verify interpolation range/values - Matches nodes with a decrease of addr:housenumber within addr:interpolation=even/odd. -*/ -*[tag("addr:housenumber") > child_tag("addr:housenumber")][regexp_test("even|odd", parent_tag("addr:interpolation"))] + *[addr:housenumber] { - throwWarning: tr("Decreasing house numbers in addresses interpolation"); -} +/* see #9667 - Verify interpolation range/values + Matches nodes with a decrease of addr:housenumber within addr:interpolation=even/odd. +*/ +*[tag("addr:housenumber") > child_tag("addr:housenumber")][regexp_test("even|odd", parent_tag("addr:interpolation"))] + *[addr:housenumber] { + throwWarning: tr("Decreasing house numbers in addresses interpolation"); +} diff --git a/data/validator/combinations.mapcss b/data/validator/combinations.mapcss index e6be5018903..754a8527a2d 100644 --- a/data/validator/combinations.mapcss +++ b/data/validator/combinations.mapcss @@ -3,7 +3,7 @@ */ /* {0.key} without {1.key} (warning level) */ -way[junction ][!highway], +way[junction ][!highway][junction!=yes], *[lanes ][!highway], *[lcn ][!highway], *[living_street ][!highway], @@ -13,7 +13,6 @@ way[junction ][!highway], *[ntd_id ][!highway], *[sac_scale ][!highway], *[sidewalk ][!highway], -*[smoothness ][!highway], *[step_count ][!highway], *[tracktype ][!highway], *[trail_visibility ][!highway], @@ -45,6 +44,7 @@ way[junction ][!highway], *[canal ][!waterway], *[have_riverbank ][!waterway], *[border_type ][!boundary], +*[bridge:structure ][!bridge], *[source:date ][!source], *[source:name ][!name], *[source:maxspeed:forward ][!maxspeed:forward], @@ -83,6 +83,8 @@ way[junction ][!highway], } /* {0.key} without {1.key} (info level) */ +*[lanes:forward][!lanes:backward][oneway!=yes][oneway!=-1], +*[lanes:backward][!lanes:forward][oneway!=yes][oneway!=-1], *[leaf_type ][!leaf_cycle], *[leaf_cycle][!leaf_type] { throwOther: tr("{0} without {1}", "{0.key}", "{1.key}"); @@ -91,6 +93,7 @@ way[junction ][!highway], /* {0.key} without {1.tag} */ *[fence_type ][barrier!=fence], *[recycling_type ][amenity!=recycling], +*[information ][tourism!=information], *[board_type ][information!=board], *[shelter_type ][amenity!=shelter], *[lamp_type ][highway!=street_lamp], @@ -99,10 +102,26 @@ way[junction ][!highway], *[artwork_type ][tourism!=artwork], *[castle_type ][historic!=castle], *[reservoir_type ][landuse!=reservoir][water!=reservoir], +*[bridge:movable ][bridge!=movable], +*[parking ][amenity!~/^(parking|parking_space|parking_entrance|motorcycle_parking)$/] *[bunker_type ][military!=bunker] { throwWarning: tr("{0} without {1}", "{0.key}", "{1.tag}"); } +/* {0.tag} without {1.key} (warning level) */ +*[tourism=information][!information], +*[aeroway=terminal][!building], +*[power=generator][!generator:source], +*[amenity=place_of_worship][!religion] { + throwWarning: tr("{0} without {1}", "{0.tag}", "{1.key}"); +} + +/* {0.tag} without {1.key} (info level) */ +*[amenity=parking ][!parking], +*[amenity=parking_entrance ][!parking] { + throwOther: tr("{0} without {1}", "{0.tag}", "{1.key}"); +} + /* {0.key} without {1.key} or {2.key} */ *[incline ][!highway][!railway], *[oneway ][!highway][!railway] { @@ -110,12 +129,17 @@ way[junction ][!highway], } /* {0.key} without {1.key} or {2.tag} */ +*[smoothness ][!highway][amenity!~/^(parking|parking_space|parking_entrance|motorcycle_parking)$/], *[segregated ][!highway][railway!=crossing], -*[boat ][!waterway][natural!=water], -*[intermittent ][!waterway][natural!=water] { +*[boat ][!waterway][natural!=water] { throwWarning: tr("{0} without {1} or {2}", "{0.key}", "{1.key}", "{2.tag}"); } +/* {0.key} without {1.key}, {2.tag} or {3.tag} */ +*[intermittent ][!waterway][natural!~/^(water|spring)$/][ford!=yes] { + throwWarning: tr("{0} without {1}, {2} or {3}", "{0.key}", "{1.key}", "{2.tag}", "{3.tag}"); +} + /* {0.key} without {1.key}, {2.key} or {3.key} */ *[snowplowing ][!highway][!amenity][!leisure] { throwWarning: tr("{0} without {1}, {2} or {3}", "{0.key}", "{1.key}", "{2.key}", "{3.key}"); @@ -126,19 +150,70 @@ way[junction ][!highway], throwWarning: tr("{0} without {1}, {2} or {3}", "{0.key}", "{1.key}", "{2.key}", "{3.tag}"); } +/* {0.key} together with {1.key}, see #10837 */ +*[noname?][name] { + throwWarning: tr("{0} together with {1}", "{0.key}", "{1.key}"); +} + +/* {0.tag} together with {1.key}, see #9389 */ +*[oneway=yes][/:backward/], +*[oneway=yes][/:forward/], +*[oneway=-1 ][/:backward/], +*[oneway=-1 ][/:forward/] { + throwWarning: tr("{0} together with {1}", "{0.tag}", "{1.key}"); +} + +/* {0.tag} together with {1.tag} (info level), see #9696 */ +*[highway=footway][oneway=no] { + throwOther: tr("{0} together with {1}", "{0.tag}", "{1.tag}"); +} + +/*see #11127*/ +way[waterway][bridge=yes] { + throwWarning: tr("{0} together with {1}", "{0.key}", "{1.tag}"); + suggestAlternative: "bridge=aqueduct"; + fixAdd: "bridge=aqueduct"; +} + +/* only {0.key}, see #11104 */ +*[area][eval(number_of_tags()) = 1], +*[name][eval(number_of_tags()) = 1], +*[ref][eval(number_of_tags()) = 1] { + throwWarning: tr("incomplete object: only {0}", "{0.key}"); +} +/* only {0.key} and {1.key}, see #11104 */ +*[name][area][eval(number_of_tags()) = 2], +*[name][ref][eval(number_of_tags()) = 2] { + throwWarning: tr("incomplete object: only {0} and {1}", "{0.key}", "{1.key}"); +} + +/* see #9811 */ +*[place][place!=farm][/^addr:/], +*[boundary][/^addr:/], +*[highway][/^addr:/] { + throwWarning: tr("{0} together with addr:*", "{0.key}"); +} + +/* see #9195 */ +*[highway=footway][cycleway=lane] { + throwWarning: tr("{0} together with {1}", "{0.tag}", "{1.tag}"); + suggestAlternative: "highway=path + foot=designated + bicycle=designated + segregated=yes"; +} + /* {0} on suspicious object */ *[tunnel ][!highway][!railway][!waterway][public_transport != platform][man_made != pipeline], *[bridge ][!highway][!railway][!waterway][!piste:type][public_transport != platform][man_made !~ /^(bridge|pipeline)$/][building != bridge], *[psv ][!highway][!railway][!waterway][amenity !~ /^parking.*/], *[width ][!highway][!railway][!waterway][!aeroway][!cycleway][!footway][!barrier][!man_made][!entrance], *[maxspeed][!highway][!railway][traffic_sign !~ /^(.*;)?maxspeed(;.*)?$/][type != enforcement][waterway !~ /^(river|canal|lock)$/], -*[lit ][!highway][!railway][!piste:type][amenity !~ /^(parking|shelter)/][public_transport!=platform][advertising!=billboard][leisure!=pitch] { +*[lit ][!highway][!railway][!piste:type][!amenity][public_transport!=platform][advertising!=billboard][!leisure][!tourism] { throwWarning: tr("{0} on suspicious object", "{0.key}"); } -node[amenity =~ /^(restaurant|cafe|fast_food)$/][!name] { +node[amenity =~ /^(restaurant|cafe|fast_food)$/][!name][noname!=yes] { throwOther: tr("restaurant without name"); assertMatch: "node amenity=restaurant"; + assertNoMatch: "node amenity=restaurant noname=yes"; assertNoMatch: "node amenity=restaurant name=Foobar"; } @@ -157,8 +232,8 @@ way[highway][natural], fixRemove: "natural"; } -/* see ticket #9593 */ -*[sport][tourism != hotel][highway != raceway][leisure !~ /^(sports_centre|stadium|track|pitch|golf_course|water_park|swimming_pool|recreation_ground)$/][natural !~ /^(beach|bare_rock)$/][amenity !~ /^(pub|restaurant|swimming_pool)$/][landuse !~ /^(recreation_ground|piste|farm|farmland)$/] { +/* see #9593 */ +*[sport][!building][tourism != hotel][highway != raceway][leisure !~ /^(sports_centre|stadium|track|pitch|golf_course|water_park|swimming_pool|recreation_ground)$/][natural !~ /^(beach|bare_rock|peak)$/][amenity !~ /^(pub|restaurant|swimming_pool)$/][landuse !~ /^(recreation_ground|piste|farm|farmland)$/] { throwWarning: tr("sport without physical feature"); assertMatch: "node sport=tennis"; assertNoMatch: "node sport=tennis leisure=pitch"; @@ -172,3 +247,22 @@ way[highway][natural], *[building:levels][!building][!building:part] { throwWarning: tr("{0} without {1} or {2}", "{0.key}", "{1.key}", "{2.key}"); } + +/* see #10471 */ +way[waterway] > node[ford?] { set ford_on_waterway; } +way[highway] > node[ford?] { set ford_on_highway; } +node[ford?]!.ford_on_waterway, node[ford?]!.ford_on_highway { + throwWarning: tr("{0} should be on the node where {1} and {2} intersect", "ford", "highway", "waterway"); +} + +/* any other *_name tag (except old_name, loc_name and uic_name) but not a name , see #10837 */ +*[/_name$/][!name][!old_name][!loc_name][!uic_name] { + throwWarning: tr("alternative name without {0}", "{1.key}"); +} + +/* see #10837 */ +way[destination][!oneway?] { + throwWarning: tr("incomplete usage of {0} on a way without {1}", "{0.key}", "{1.key}"); + suggestAlternative: "destination:forward"; + suggestAlternative: "destination:backward"; +} \ No newline at end of file diff --git a/data/validator/deprecated.mapcss b/data/validator/deprecated.mapcss index 43f081f4abe..e9ee5f1e727 100644 --- a/data/validator/deprecated.mapcss +++ b/data/validator/deprecated.mapcss @@ -81,13 +81,33 @@ suggestAlternative: "generator:output"; } +*[shop=antique] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + fixAdd: "shop=antiques"; +} + +*[shop=bags] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + fixAdd: "shop=bag"; +} + *[shop=organic] { /* from http://wiki.openstreetmap.org/wiki/Tag:shop=organic */ throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); fixAdd: "shop=supermarket"; fixAdd: "organic=only"; } - + +*[shop=pets] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + fixAdd: "shop=pet"; +} + +*[shop=pharmacy] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + fixChangeKey: "shop => amenity"; +} + *[bicycle_parking=sheffield] { /* from http://wiki.openstreetmap.org/wiki/Key:bicycle_parking */ throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); @@ -182,6 +202,11 @@ fixChangeKey: "monitoring:river_level => monitoring:water_level"; } +*[stay] { + throwWarning: tr("{0} is deprecated", "{0.key}"); + fixChangeKey: "stay => maxstay"; +} + *[emergency=aed] { /* see #9554 - http://wiki.openstreetmap.org/wiki/Proposed_features/automated_external_defibrillator */ throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); @@ -216,7 +241,7 @@ assertMatch: "way fixme=yes"; } -/* see #10107, #10108 - http://wiki.openstreetmap.org/wiki/Proposed_features/leaftype#Features.2FPages_affected */ +/* see #10107, #10108, #10929 - http://wiki.openstreetmap.org/wiki/Proposed_features/leaftype#Features.2FPages_affected */ *[wood=deciduous], *[type=broad_leaved], *[type=broad_leafed] { @@ -225,9 +250,11 @@ fixAdd: "leaf_type=broadleaved"; } -*[wood=coniferous] { +*[wood=coniferous], +*[type=coniferous], +*[type=conifer] { throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); - fixRemove: "wood"; + fixRemove: "{0.key}"; fixAdd: "leaf_type=needleleaved"; } @@ -250,3 +277,117 @@ fixRemove: "type"; fixAdd: "leaf_cycle=deciduous"; } + +*[natural=land] { + throwWarning: tr("{0}={1} is deprecated. Please use instead a multipolygon.", "{0.key}", "{0.value}"); + assertMatch: "way natural=land"; +} + +/* see #10447 - https://wiki.openstreetmap.org/wiki/Proposed_features/Bridge_types#Deprecated_bridge_key_values */ +*[bridge=causeway] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + suggestAlternative: "ford=yes"; + suggestAlternative: "bridge=low_water_crossing"; + suggestAlternative: "embankment=yes"; +} + +*[bridge=swing] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + suggestAlternative: "bridge:movable=swing"; + suggestAlternative: "bridge:structure=simple-suspension"; +} + +*[bridge=suspension] { + throwWarning: tr("{0}={1} is deprecated", "{0.key}", "{0.value}"); + fixAdd: "bridge=yes"; + fixAdd: "bridge:structure=suspension"; +} + +/* See #10530 - Incorrect tag proposed in internal preset for years */ +*[fee=interval], +*[lit=interval], +*[supervised=interval] { + throwWarning: tr("{0} is deprecated. Please specify interval by using opening_hours syntax", "{0.tag}"); +} + +/* See #10582 - JOSM supported this tag as "don't upload this" feature, before the introduction of upload flag at layer level */ +*[/josm\/ignore/] { + throwError: tr("{0} is deprecated. Please delete this object and use a private layer instead", "{0.key}"); + fixDeleteObject: this; +} + +/* see #8281 */ +*[sport=diving] { + throwWarning: tr("{0} is deprecated", "{0.tag}"); + suggestAlternative: "sport=scuba_diving"; + suggestAlternative: "sport=cliff_diving"; +} + +/* see #8281 */ +*[parking=park_and_ride] { + throwWarning: tr("{0} is deprecated", "{0.tag}"); + fixRemove: "parking"; + fixAdd: "amenity=parking"; + fixAdd: "park_ride=yes"; +} + +/* see #10661, #10837 */ +*[barrier=yes], +*[aerialway=yes], +*[amenity=yes], +*[place=yes] { + throwWarning: tr("{0}={1} is unspecific. Please replace ''{1}'' by a specific value.", "{0.key}", "{0.value}"); +} + +/* see #10693 - http://wiki.openstreetmap.org/wiki/Proposed_features/drop_recommendation_for_place_name , #10837 */ +*[place_name][!name] { + throwWarning: tr("{0} should be replaced with {1}", "{0.key}", "{1.key}"); + fixChangeKey: "place_name => name"; +} +*[place][place_name = *name] { + throwWarning: tr("{0} = {1}; remove {0}", "{1.key}", "{1.value}"); + fixRemove: "{1.key}"; +} + +way[sidewalk=yes] { + throwWarning: tr("{0}={1} is unspecific. Please replace ''{1}'' by ''left'', ''right'' or ''both''.", "{0.key}", "{0.value}"); +} + +*[waterway=water_point], +*[waterway=waste_disposal] { + throwWarning: tr("{0} is deprecated", "{0.tag}"); + fixChangeKey: "waterway => amenity"; +} +*[waterway=mooring] { + throwWarning: tr("{0} is deprecated", "{0.tag}"); + fixRemove: "waterway"; + fixAdd: "mooring=yes"; +} + +/* see #10837 */ +*[building][levels], +*[building:part=yes][levels] { + throwWarning: tr("{0} is deprecated", "{1.key}"); + suggestAlternative: "building:levels"; + fixChangeKey: "levels => building:levels"; +} + +/* see #10691 */ +*[protected_class] { + throwWarning: tr("{0} is deprecated", "{0.key}"); + suggestAlternative: "protect_class"; + fixChangeKey: "protected_class => protect_class"; +} + +/* see #11070 */ +*[lock=unknown], +*[hide=unknown], +*[shelter=unknown], +*[access=unknown], +*[capacity:parent=unknown], +*[capacity:women=unknown], +*[capacity:disabled=unknown], +*[crossing=unknown], +*[foot=unknown] { + throwWarning: tr("Unspecific tag {0}", "{0.tag}"); +} \ No newline at end of file diff --git a/data/validator/geometry.mapcss b/data/validator/geometry.mapcss index 7cebb2b0b9a..bb23aede48a 100644 --- a/data/validator/geometry.mapcss +++ b/data/validator/geometry.mapcss @@ -1,13 +1,93 @@ -/* {0} on a node */ +/* {0} on a node, should be a way */ node[oneway], -node[bridge?], -node[landuse], -node[source:outline] { - throwWarning: tr("{0} on a node", "{0.key}"); +node[bridge], +node[sidewalk], +node[footway], +node[man_made=embankment], +node[man_made=groyne], +node[aerialway=cable_car], +node[aerialway=gondola], +node[aerialway=chair_lift], +node[aerialway=mixed_lift], +node[aerialway=drag_lift], +node[aerialway=t-bar], +node[aerialway=j-bar], +node[aerialway=platter], +node[aerialway=magic_carpet], +node[aerialway=rope_tow], +node[aerialway=goods], +node[aeroway=taxiway], +node[aeroway=runway], +node[railway=rail], +node[railway=narrow_gauge], +node[railway=monorail], +node[railway=preserved], +node[railway=light_rail], +node[railway=subway], +node[railway=tram], +node[railway=disused], +node[railway=abandoned], +node[waterway=river], +node[waterway=canal], +node[waterway=wadi], +node[waterway=stream], +node[waterway=ditch], +node[waterway=drain], +node[natural=coastline], +node[natural=ridge], +node[natural=tree_row] { + throwWarning: tr("{0} on a node. Should be used on a way.", "{0.tag}"); assertMatch: "node oneway=-1"; assertNoMatch: "way oneway=-1"; assertMatch: "node bridge=yes"; - assertNoMatch: "node bridge=13"; + assertMatch: "node bridge=viaduct"; +} + +/* {0} on a node, should be an area; see ticket #10679 */ +node[landuse], +node[natural=scree], +node[natural=scrub], +node[natural=fell], +node[natural=heath], +node[natural=wood], +node[natural=grassland], +node[natural=wetland], +node[natural=water], +node[natural=mud], +node[natural=beach], +node[natural=sand], +node[natural=wood], +node[natural=bare_rock], +node[waterway=riverbank], +node[man_made=bridge], +node[man_made=breakwater], +node[aeroway=apron], +node[power=plant], +node[source:outline] { + throwWarning: tr("{0} on a node. Should be drawn as an area.", "{0.tag}"); +} + +/* {0} on a node, should be a relation; see #10252, #10769 */ +node[route], +node[restriction] { + throwWarning: tr("{0}=* on a node. Should be used in a relation", "{0.key}"); +} + +/* {0} on a way, should be a node */ +way[entrance], +way[man_made=survey_point], +way[power=pole], +way[amenity=vending_machine], +way[natural=peak], +way[natural=saddle], +way[natural=volcano], +way[natural=tree] { + throwWarning: tr("{0} on a way. Should be used on a node.", "{0.tag}"); +} + +/* {0} on a way, should be a relation; see #10252 */ +way[route=bus] { + throwWarning: tr("{0} on a way. Should be used in a relation", "{0.tag}"); } /* see ticket:#10125 */ @@ -67,3 +147,60 @@ node[tag("shop") = parent_tag("shop")] ∈ *[shop] { node[tag("power") = parent_tag("power")] ∈ *[power] { throwWarning: tr("{0} inside {1}", concat("power=", tag("power")), concat("power=", tag("power"))); } + +/* isolated nodes which should be part of a way, see #10825 */ +node:unconnected[entrance], +node:unconnected[traffic_calming], +node:unconnected[highway=passing_place], +node:unconnected[highway=mini_roundabout], +node:unconnected[highway=motorway_junction], +node:unconnected[highway=turning_loop], +node:unconnected[highway=turning_circle], +node:unconnected[highway=stop], +node:unconnected[highway=give_way], +node:unconnected[highway=traffic_signals], +node:unconnected[highway=crossing], +node:unconnected[crossing], +node:unconnected[railway=crossing], +node:unconnected[railway=level_crossing], +node:unconnected[railway=buffer_stop], +node:unconnected[public_transport=stop_position], +node:unconnected[noexit], +node:unconnected[waterway=dam], +node:unconnected[waterway=weir], +node:unconnected[waterway=waterfall], +node:unconnected[amenity=ferry_terminal], +node:unconnected[mountain_pass=yes], +node:unconnected[barrier=gate], +node:unconnected[barrier=lift_gate], +node:unconnected[barrier=swing_gate], +node:unconnected[barrier=toll_booth], +node:unconnected[barrier=turnstile], +node:unconnected[barrier=full-height_turnstile], +node:unconnected[barrier=motorcycle_barrier], +node:unconnected[barrier=rope], +node:unconnected[barrier=sally_port], +node:unconnected[barrier=spikes], +node:unconnected[barrier=stile], +node:unconnected[barrier=sump_buster], +node:unconnected[barrier=kerb], +node:unconnected[barrier=border_control], +node:unconnected[barrier=bump_gate], +node:unconnected[barrier=bus_trap], +node:unconnected[barrier=cattle_grid], +node:unconnected[barrier=chain], +node:unconnected[barrier=cycle_barrier], +node:unconnected[barrier=hampshire_gate], +node:unconnected[barrier=height_restrictor], +node:unconnected[barrier=debris] { + throwWarning: tr("{0} must be connected to a way", "{1.tag}"); +} + +/*see #11127*/ +way[railway][bridge] > node, +way[highway][bridge] > node { + set node_in_bridge; +} +way[waterway] > node.node_in_bridge { + throwWarning: tr("node connects waterway and bridge"); +} \ No newline at end of file diff --git a/data/validator/highway.mapcss b/data/validator/highway.mapcss index 528fca234d1..9066661a1dc 100644 --- a/data/validator/highway.mapcss +++ b/data/validator/highway.mapcss @@ -16,7 +16,7 @@ way[highway][name =~ /(?i).* (Ave|Blvd|Br|Brg|Cct|Cir|Cl|Cr|Crct|Cres|Crt|Ct|Dr| assertMatch: "way highway=unclassified name=\"Bou blvd.\""; } -node[highway =~ /motorway|trunk|primary|secondary|tertiary|unclassified|residential|service|living_street|pedestrian|track|path|footway/][highway!=motorway_junction][highway!=services] { +node[highway =~ /motorway|trunk|primary|secondary|tertiary|unclassified|residential|service|living_street|pedestrian|track|path|footway|cycleway|bus_guideway|bridleway/][highway!=motorway_junction][highway!=services] { throwWarning: tr("wrong highway tag on a node"); assertMatch: "node highway=primary"; assertMatch: "node highway=primary_link"; @@ -37,15 +37,18 @@ node[highway =~ /motorway|trunk|primary|secondary|tertiary|unclassified|resident assertNoMatch: "node highway=turning_circle"; } -way[highway=crossing], way[railway=crossing] { +way[highway=crossing], +way[railway=crossing], +way[railway=level_crossing] { throwWarning: tr("wrong crossing tag on a way"); assertMatch: "way highway=crossing"; assertNoMatch: "node highway=crossing"; } -way[highway=unclassified][!name] { +way[highway=unclassified][!name][noname!=yes] { throwOther: tr("Unnamed unclassified highway"); assertMatch: "way highway=unclassified"; + assertNoMatch: "way highway=unclassified noname=yes"; assertNoMatch: "way highway=unclassified name=Foo"; } @@ -63,16 +66,12 @@ way[highway=road] { way[highway=footway][maxspeed], way[highway=steps][maxspeed], -way[highway=cycleway][bicycle?!], -way[highway=footway][foot?!], +way[highway=cycleway][bicycle=no], +way[highway=footway][foot=no], way[highway=cycleway][cycleway=lane] { throwWarning: tr("{0} used with {1}", "{0.value}", "{1.tag}"); - assertMatch: "way highway=cycleway bicycle=false"; - assertMatch: "way highway=cycleway bicycle=0"; assertNoMatch: "way highway=cycleway bicycle=yes"; assertNoMatch: "way highway=cycleway"; - assertMatch: "way highway=footway foot=false"; - assertMatch: "way highway=footway foot=0"; assertNoMatch: "way highway=footway foot=yes"; assertNoMatch: "way highway=footway"; assertMatch: "way highway=cycleway cycleway=lane"; @@ -89,3 +88,24 @@ way[highway=cycleway][cycleway=lane] { assertMatch: "way name=Foobar-Strassenweg"; assertNoMatch: "way name=Foobarstraße"; } + +/* footway, see #10851 */ +way[footway=left], +way[footway=right], +way[footway=both], +way[footway=no] { + throwWarning: tr("{0} is deprecated", "{0.tag}"); + suggestAlternative: "sidewalk"; + fixChangeKey: "footway => sidewalk"; + set footway_to_sidewalk; +} +way[footway=none] { + throwWarning: tr("{0} is deprecated", "{0.tag}"); + suggestAlternative: "sidewalk=no"; + fixRemove: "footway"; + fixAdd: "sidewalk=no"; + set footway_to_sidewalk; +} +way[footway][footway!=sidewalk][footway!=crossing]!.footway_to_sidewalk { /* do not trigger this rule if already one of the two previous rules applied */ + throwWarning: tr("Value of ''{0}'' should either be ''{1}'' or ''{2}''. For sidewalks use ''{3}'' instead.", "{0.key}", "{1.value}", "{2.value}", "sidewalk=left|right|both|no"); +} \ No newline at end of file diff --git a/data/validator/ignoretags.cfg b/data/validator/ignoretags.cfg index a47f0ecc939..4b12612c7b2 100644 --- a/data/validator/ignoretags.cfg +++ b/data/validator/ignoretags.cfg @@ -55,6 +55,7 @@ E:opening_hours E:service_times E:collection_times E:maxspeed +E:maxstay E:colour E:level E:brand diff --git a/data/validator/multiple.mapcss b/data/validator/multiple.mapcss index b49d5b641fb..9607beafce4 100644 --- a/data/validator/multiple.mapcss +++ b/data/validator/multiple.mapcss @@ -1,11 +1,44 @@ -/* see #9757 - Better handling of semicolon in values -*/ -*["addr:street" =~ /.+;(.+)?/], -*[highway =~ /.+;(.+)?/], -*[lanes =~ /.+;(.+)?/], -*[maxspeed =~ /.+;(.+)?/], -*[name =~ /.+;(.+)?/], -*[surface =~ /.+;(.+)?/], -*[water =~ /.+;(.+)?/] { - throwWarning: tr("{0} with multiple values", "{0.key}"); -} +/* see #9757, #10869 - Better handling of semicolon in values +*/ +*["addr:street" =~ /.+;(.+)?/], +*[highway =~ /.+;(.+)?/], +*[lanes =~ /.+;(.+)?/], +*[maxspeed =~ /.+;(.+)?/], +*[name =~ /.+;(.+)?/], +*[surface =~ /.+;(.+)?/], +*[water =~ /.+;(.+)?/] { + throwWarning: tr("{0} with multiple values", "{0.key}"); +} + +*[source =~ /^(;.*|.*;;.*|.*;)$/], +*["source:addr" =~ /^(;.*|.*;;.*|.*;)$/], +*["source:maxspeed" =~ /^(;.*|.*;;.*|.*;)$/], +*["source:name" =~ /^(;.*|.*;;.*|.*;)$/], +*["source:position" =~ /^(;.*|.*;;.*|.*;)$/], +*["source:postcode" =~ /^(;.*|.*;;.*|.*;)$/], +*[ref =~ /^(;.*|.*;;.*|.*;)$/], +*[int_ref =~ /^(;.*|.*;;.*|.*;)$/], +*[old_ref =~ /^(;.*|.*;;.*|.*;)$/], +*[source_ref =~ /^(;.*|.*;;.*|.*;)$/], +*[route_ref =~ /^(;.*|.*;;.*|.*;)$/], +*[attribution =~ /^(;.*|.*;;.*|.*;)$/], +*[name =~ /^(;.*|.*;;.*|.*;)$/], +*[alt_name =~ /^(;.*|.*;;.*|.*;)$/], +*[note =~ /^(;.*|.*;;.*|.*;)$/], +*[fixme =~ /^(;.*|.*;;.*|.*;)$/], +*["addr:housenumber" =~ /^(;.*|.*;;.*|.*;)$/], +*[destination =~ /^(;.*|.*;;.*|.*;)$/], +*[exit_to =~ /^(;.*|.*;;.*|.*;)$/], +*[surface =~ /^(;.*|.*;;.*|.*;)$/], +*["building:use" =~ /^(;.*|.*;;.*|.*;)$/], +*[traffic_sign =~ /^(;.*|.*;;.*|.*;)$/], +*[voltage =~ /^(;.*|.*;;.*|.*;)$/], +*[cuisine =~ /^(;.*|.*;;.*|.*;)$/] { + throwWarning: tr("empty value in semicolon-separated ''{0}''", "{0.key}"); + assertMatch: "node ref=;A1"; + assertMatch: "node ref=A1;"; + assertMatch: "node ref=;"; + assertMatch: "node ref=A1;;A2"; + assertNoMatch: "node ref=A1"; + assertNoMatch: "node ref=A1;A2"; +} diff --git a/data/validator/numeric.mapcss b/data/validator/numeric.mapcss index b2a2ee38963..f470546e16d 100644 --- a/data/validator/numeric.mapcss +++ b/data/validator/numeric.mapcss @@ -1,141 +1,157 @@ -/* measurement values and units warnings (ticket #8687) */ - -*[/^[0-9]+$/] { - throwWarning: tr("numerical key"); - assertMatch: "way 123=foo"; - assertNoMatch: "way ref.1=foo"; -} - -*[layer =~ /\+.*/] { - throwWarning: tr("layer tag with + sign"); - fixAdd: concat("layer=", replace(tag("layer"), "+", "")); - assertMatch: "node layer=+1"; - assertNoMatch: "node layer=1"; - assertNoMatch: "node layer=-1"; -} - -*[layer][layer !~ /^0$|^(-|\+)?[1-5]$/] { - throwWarning: tr("layer should be between -5 and 5"); - assertMatch: "node layer=-50"; - assertMatch: "node layer=6"; - assertMatch: "node layer=+100"; - assertNoMatch: "node layer=-5"; - assertNoMatch: "node layer=0"; - assertNoMatch: "node layer=2"; - assertNoMatch: "node layer=+5"; -} - -*[level][level !~ /^((([0-9]|-[1-9])|[1-9][0-9]*)(\.5)?)(;(([0-9]|-[1-9])|[1-9][0-9]*)(\.5)?)*$|^-0\.5$/] { - throwWarning: tr("level should be numbers with optional .5 increments"); - assertMatch: "node level=one"; - assertNoMatch: "node level=0"; - assertNoMatch: "node level=1"; - assertNoMatch: "node level=-1"; - assertNoMatch: "node level=-0.5"; - assertNoMatch: "node level=1.5"; -} - -*[height][height !~ /^(([0-9]+\.?[0-9]*( (m|ft))?)|([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/] { - throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "height"); - assertMatch: "node height=medium"; - assertMatch: "node height=-5"; - assertNoMatch: "node height=2 m"; - assertNoMatch: "node height=5"; - assertNoMatch: "node height=7.8"; - assertNoMatch: "node height=20 ft"; - assertNoMatch: "node height=22'"; -} - -*[maxheight][maxheight !~ /^(([1-9][0-9]*(\.[0-9]+)?( (m|ft))?)|([0-9]+\'(([0-9]|10|11)(\.[0-9]*)?\")?))$/] { - throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "maxheight"); - assertMatch: "node maxheight=something"; - assertMatch: "node maxheight=-5"; - assertMatch: "node maxheight=0"; - assertNoMatch: "node maxheight=4"; - assertNoMatch: "node maxheight=3.5"; - assertNoMatch: "node maxheight=2 m"; - assertNoMatch: "node maxheight=14 ft"; - assertNoMatch: "node maxheight=10'"; - assertNoMatch: "node maxheight=16'3\""; -} - -way[width][width !~ /^(([0-9]+\.?[0-9]*( [a-z]+)?)|([0-9]+\'([0-9]+\.?[0-9]*\")?))$/] { - throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "width"); - assertMatch: "way width=something"; - assertMatch: "way width=-5"; - assertNoMatch: "way width=3"; - assertNoMatch: "way width=0.5"; - assertNoMatch: "way width=1 m"; - assertNoMatch: "way width=10 ft"; - assertNoMatch: "way width=1'"; - assertNoMatch: "way width=10'5\""; -} - -*[maxwidth][maxwidth !~ /^(([0-9]+\.?[0-9]*( (m|ft))?)|([0-9]+\'[0-9]+\.?[0-9]*\"))$/] { - throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "maxwidth"); - assertMatch: "way maxwidth=something"; - assertMatch: "way maxwidth=-5"; - assertNoMatch: "way maxwidth=2"; - assertNoMatch: "way maxwidth=6'6\""; - assertNoMatch: "way maxwidth=2.5"; - assertNoMatch: "way maxwidth=7 ft"; -} - -way[maxspeed][maxspeed !~ /^(signals|none|unposted|unknown|variable|walk|[1-9][0-9]*( [a-z]+)?|[A-Z][A-Z]:(urban|rural|living_street|motorway))$/] { - throwWarning: tr("unusual {0} format", "maxspeed"); - assertMatch: "way maxspeed=something"; - assertMatch: "way maxspeed=-50"; - assertMatch: "way maxspeed=0"; - assertNoMatch: "way maxspeed=50"; - assertNoMatch: "way maxspeed=30 mph"; - assertNoMatch: "way maxspeed=RO:urban"; - assertNoMatch: "way maxspeed=RU:rural"; - assertNoMatch: "way maxspeed=RU:living_street"; - assertNoMatch: "way maxspeed=DE:motorway"; - assertNoMatch: "way maxspeed=signals"; - assertNoMatch: "way maxspeed=none"; - assertNoMatch: "way maxspeed=variable"; -} - -way[voltage][voltage =~ /(.*[A-Za-z].*)|.*,.*|.*( ).*/] { - throwWarning: tr("voltage should be in volts with no units/delimiter/spaces"); - assertMatch: "way voltage=medium"; - assertNoMatch: "way voltage=15000"; -} - -/* some users are using frequency for other purposes (not electromagnetic) - with the values 'perennial' and 'intermittent'; the vast majority are 0, 16.7, 50 and 60 */ -way[frequency][frequency !~ /^(0|[1-9][0-9]*(\.[0-9]+)?)( (kHz|MHz|GHz|THz))?$/] { - throwWarning: tr("unusual {0} specification", "frequency"); - assertMatch: "way frequency=something"; - assertNoMatch: "way frequency=0"; /* DC */ - assertNoMatch: "way frequency=16.7"; - assertNoMatch: "way frequency=50"; - assertNoMatch: "way frequency=680 kHz"; - assertNoMatch: "way frequency=123.5 MHz"; -} - -way[gauge][gauge !~ /^([1-9][0-9]{1,3}(;[1-9][0-9]{1,3})*|broad|standard|narrow)$/] { - throwWarning: tr("unusual train track gauge; use mm with no separator"); - assertMatch: "way gauge=something"; - assertNoMatch: "way gauge=1435"; - assertNoMatch: "way gauge=1000;1435"; - assertNoMatch: "way gauge=standard"; - assertNoMatch: "way gauge=narrow"; -} - -/* the numbers for percentage and degrees include could probably be bracketed a bit more precisely */ -way[incline][incline !~ /^(up|down|-?([0-9]+?(\.[1-9]%)?|100)[%°]?)$/] { - throwWarning: tr("unusual incline; use percentages/degrees or up/down"); - assertMatch: "way incline=extreme"; - assertNoMatch: "way incline=up"; - assertNoMatch: "way incline=down"; - assertNoMatch: "way incline=10%"; - assertNoMatch: "way incline=-5%"; - assertNoMatch: "way incline=10°"; -} - -/* see ticket #9631 */ -*[population][population !~ /^[0-9]+$/ ] { - throwWarning: tr("{0} must be a numeric value", "{0.key}"); -} \ No newline at end of file +/* measurement values and units warnings (ticket #8687) */ + +*[/^[0-9]+$/] { + throwWarning: tr("numerical key"); + assertMatch: "way 123=foo"; + assertNoMatch: "way ref.1=foo"; +} + +*[layer =~ /\+.*/] { + throwWarning: tr("layer tag with + sign"); + fixAdd: concat("layer=", replace(tag("layer"), "+", "")); + assertMatch: "node layer=+1"; + assertNoMatch: "node layer=1"; + assertNoMatch: "node layer=-1"; +} + +*[layer][layer !~ /^0$|^(-|\+)?[1-5]$/] { + throwWarning: tr("layer should be between -5 and 5"); + assertMatch: "node layer=-50"; + assertMatch: "node layer=6"; + assertMatch: "node layer=+100"; + assertNoMatch: "node layer=-5"; + assertNoMatch: "node layer=0"; + assertNoMatch: "node layer=2"; + assertNoMatch: "node layer=+5"; +} + +*[level][level !~ /^((([0-9]|-[1-9])|[1-9][0-9]*)(\.5)?)(;(([0-9]|-[1-9])|[1-9][0-9]*)(\.5)?)*$|^-0\.5$/] { + throwWarning: tr("level should be numbers with optional .5 increments"); + assertMatch: "node level=one"; + assertNoMatch: "node level=0"; + assertNoMatch: "node level=1"; + assertNoMatch: "node level=-1"; + assertNoMatch: "node level=-0.5"; + assertNoMatch: "node level=1.5"; +} + +*[height][height !~ /^(([0-9]+\.?[0-9]*( (m|ft))?)|([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/] { + throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "height"); + assertMatch: "node height=medium"; + assertMatch: "node height=-5"; + assertNoMatch: "node height=2 m"; + assertNoMatch: "node height=5"; + assertNoMatch: "node height=7.8"; + assertNoMatch: "node height=20 ft"; + assertNoMatch: "node height=22'"; +} + +*[maxheight][maxheight !~ /^(([1-9][0-9]*(\.[0-9]+)?( (m|ft))?)|([0-9]+\'(([0-9]|10|11)(\.[0-9]*)?\")?))$/] { + throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "maxheight"); + assertMatch: "node maxheight=something"; + assertMatch: "node maxheight=-5"; + assertMatch: "node maxheight=0"; + assertNoMatch: "node maxheight=4"; + assertNoMatch: "node maxheight=3.5"; + assertNoMatch: "node maxheight=2 m"; + assertNoMatch: "node maxheight=14 ft"; + assertNoMatch: "node maxheight=10'"; + assertNoMatch: "node maxheight=16'3\""; +} + +way[width][width !~ /^(([0-9]+\.?[0-9]*( [a-z]+)?)|([0-9]+\'([0-9]+\.?[0-9]*\")?))$/] { + throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "width"); + assertMatch: "way width=something"; + assertMatch: "way width=-5"; + assertNoMatch: "way width=3"; + assertNoMatch: "way width=0.5"; + assertNoMatch: "way width=1 m"; + assertNoMatch: "way width=10 ft"; + assertNoMatch: "way width=1'"; + assertNoMatch: "way width=10'5\""; +} + +*[maxwidth][maxwidth !~ /^(([0-9]+\.?[0-9]*( (m|ft))?)|([0-9]+\'[0-9]+\.?[0-9]*\"))$/] { + throwWarning: tr("{0}: meters is default; period is separator; if units, put space then unit", "maxwidth"); + assertMatch: "way maxwidth=something"; + assertMatch: "way maxwidth=-5"; + assertNoMatch: "way maxwidth=2"; + assertNoMatch: "way maxwidth=6'6\""; + assertNoMatch: "way maxwidth=2.5"; + assertNoMatch: "way maxwidth=7 ft"; +} + +way[maxspeed][maxspeed !~ /^(signals|none|unposted|unknown|variable|walk|[1-9][0-9]*( [a-z]+)?|[A-Z][A-Z]:(urban|rural|living_street|motorway))$/] { + throwWarning: tr("unusual {0} format", "maxspeed"); + assertMatch: "way maxspeed=something"; + assertMatch: "way maxspeed=-50"; + assertMatch: "way maxspeed=0"; + assertNoMatch: "way maxspeed=50"; + assertNoMatch: "way maxspeed=30 mph"; + assertNoMatch: "way maxspeed=RO:urban"; + assertNoMatch: "way maxspeed=RU:rural"; + assertNoMatch: "way maxspeed=RU:living_street"; + assertNoMatch: "way maxspeed=DE:motorway"; + assertNoMatch: "way maxspeed=signals"; + assertNoMatch: "way maxspeed=none"; + assertNoMatch: "way maxspeed=variable"; +} + +way[voltage][voltage =~ /(.*[A-Za-z].*)|.*,.*|.*( ).*/] { + throwWarning: tr("voltage should be in volts with no units/delimiter/spaces"); + assertMatch: "way voltage=medium"; + assertNoMatch: "way voltage=15000"; +} + +/* some users are using frequency for other purposes (not electromagnetic) + with the values 'perennial' and 'intermittent'; the vast majority are 0, 16.7, 50 and 60 */ +way[frequency][frequency !~ /^(0|[1-9][0-9]*(\.[0-9]+)?)( (kHz|MHz|GHz|THz))?$/] { + throwWarning: tr("unusual {0} specification", "frequency"); + assertMatch: "way frequency=something"; + assertNoMatch: "way frequency=0"; /* DC */ + assertNoMatch: "way frequency=16.7"; + assertNoMatch: "way frequency=50"; + assertNoMatch: "way frequency=680 kHz"; + assertNoMatch: "way frequency=123.5 MHz"; +} + +way[gauge][gauge !~ /^([1-9][0-9]{1,3}(;[1-9][0-9]{1,3})*|broad|standard|narrow)$/] { + throwWarning: tr("unusual train track gauge; use mm with no separator"); + assertMatch: "way gauge=something"; + assertNoMatch: "way gauge=1435"; + assertNoMatch: "way gauge=1000;1435"; + assertNoMatch: "way gauge=standard"; + assertNoMatch: "way gauge=narrow"; +} + +/* the numbers for percentage and degrees include could probably be bracketed a bit more precisely */ +way[incline][incline !~ /^(up|down|-?([0-9]+?(\.[1-9]%)?|100)[%°]?)$/] { + throwWarning: tr("unusual incline; use percentages/degrees or up/down"); + assertMatch: "way incline=extreme"; + assertNoMatch: "way incline=up"; + assertNoMatch: "way incline=down"; + assertNoMatch: "way incline=10%"; + assertNoMatch: "way incline=-5%"; + assertNoMatch: "way incline=10°"; +} + +/* see ticket #9631 */ +*[population][population !~ /^[0-9]+$/ ] { + throwWarning: tr("{0} must be a numeric value", "{0.key}"); +} + +/* must be an integer positive number only and not 0, see #10837 (lanes), #11055 (screen) */ +way[lanes][lanes !~ /^[1-9]([0-9]*)$/][highway], +way["lanes:backward"]["lanes:backward" !~ /^[1-9]([0-9]*)$/][highway], +way["lanes:forward"]["lanes:forward" !~ /^[1-9]([0-9]*)$/][highway], +*[screen][screen !~ /^[1-9]([0-9]*)$/][amenity=cinema] { + throwError: tr("{0} must be a positive integer number", "{0.key}"); + assertMatch: "way highway=residential lanes=-1"; + assertMatch: "way highway=residential lanes=5.5"; + assertMatch: "way highway=residential lanes=1;2"; + assertMatch: "way highway=residential lanes:forward=-1"; + assertMatch: "way highway=residential lanes:backward=-1"; + assertNoMatch: "way highway=residential lanes=1"; + assertMatch: "node amenity=cinema screen=led"; + assertNoMatch: "node amenity=cinema screen=8"; +} diff --git a/data/validator/opening_hours.js b/data/validator/opening_hours.js index c6655807f62..a43e8399aab 100644 --- a/data/validator/opening_hours.js +++ b/data/validator/opening_hours.js @@ -1,34 +1,42 @@ +/* + * For information see https://github.com/ypid/opening_hours.js + * and the doc directory which contains internal documentation and design. + */ +/* jshint laxbreak: true */ +/* jshint boss: true */ +/* jshint loopfunc: true */ + (function (root, factory) { // constants (holidays, error correction) {{{ // holidays {{{ var holidays = { - 'fr': { + 'fr': { // {{{ 'PH': { // http://fr.wikipedia.org/wiki/F%C3%AAtes_et_jours_f%C3%A9ri%C3%A9s_en_France - "Jour de l'an" : [ 1, 1 ], - "Vendredi saint" : [ 'easter', -2, [ 'Moselle', 'Bas-Rhin', 'Haut-Rhin', 'Guadeloupe', 'Martinique', 'Polynésie française' ] ], - "Lundi de Pâques" : [ 'easter', 1 ], - "Saint-Pierre-Chanel" : [ 4, 28, [ 'Wallis-et-Futuna' ] ], - "Fête du Travail" : [ 5, 1 ], - "Fête de la Victoire" : [ 5, 8 ], - "Abolition de l'esclavage" : [ 5, 22, [ 'Martinique' ] ], - "Abolition de l'esclavage" : [ 5, 27, [ 'Guadeloupe' ] ], - "Jeudi de l'Ascension" : [ 'easter', 39 ], - "Lundi de Pentecôte" : [ 'easter', 50 ], - "Abolition de l'esclavage" : [ 6, 10, [ 'Guyane' ] ], - "Fête de l'autonomie" : [ 6, 29, [ 'Polynésie française' ] ], - "Fête nationale" : [ 7, 14 ], - "Fête Victor Schoelcher" : [ 7, 21, [ 'Guadeloupe', 'Martinique' ] ], - "Fête du Territoire" : [ 7, 29, [ 'Wallis-et-Futuna' ] ], - "Assomption" : [ 8, 15 ], - "Fête de la citoyenneté" : [ 9, 24, [ 'Nouvelle-Calédonie' ] ], - "Toussaint" : [ 11, 1 ], - "Armistice" : [ 11, 11 ], - "Abolition de l'esclavage" : [ 12, 20, [ 'Réunion' ] ], - "Noël" : [ 12, 25 ], - "Saint-Étienne " : [ 12, 26, [ 'Moselle', 'Bas-Rhin', 'Haut-Rhin' ] ] + "Jour de l'an" : [ 1, 1 ], + "Vendredi saint" : [ 'easter', -2, [ 'Moselle', 'Bas-Rhin', 'Haut-Rhin', 'Guadeloupe', 'Martinique', 'Polynésie française' ] ], + "Lundi de Pâques" : [ 'easter', 1 ], + "Saint-Pierre-Chanel" : [ 4, 28, [ 'Wallis-et-Futuna' ] ], + "Fête du Travail" : [ 5, 1 ], + "Fête de la Victoire" : [ 5, 8 ], + "Abolition de l'esclavage" : [ 5, 22, [ 'Martinique' ] ], + "Abolition de l'esclavage" : [ 5, 27, [ 'Guadeloupe' ] ], + "Jeudi de l'Ascension" : [ 'easter', 39 ], + "Lundi de Pentecôte" : [ 'easter', 50 ], + "Abolition de l'esclavage" : [ 6, 10, [ 'Guyane' ] ], + "Fête de l'autonomie" : [ 6, 29, [ 'Polynésie française' ] ], + "Fête nationale" : [ 7, 14 ], + "Fête Victor Schoelcher" : [ 7, 21, [ 'Guadeloupe', 'Martinique' ] ], + "Fête du Territoire" : [ 7, 29, [ 'Wallis-et-Futuna' ] ], + "Assomption" : [ 8, 15 ], + "Fête de la citoyenneté" : [ 9, 24, [ 'Nouvelle-Calédonie' ] ], + "Toussaint" : [ 11, 1 ], + "Armistice" : [ 11, 11 ], + "Abolition de l'esclavage" : [ 12, 20, [ 'Réunion' ] ], + "Noël" : [ 12, 25 ], + "Saint-Étienne " : [ 12, 26, [ 'Moselle', 'Bas-Rhin', 'Haut-Rhin' ] ] } - }, - 'de': { + }, // }}} + 'de': { // {{{ 'PH': { // http://de.wikipedia.org/wiki/Feiertage_in_Deutschland 'Neujahrstag' : [ 1, 1 ], // month 1, day 1, whole Germany 'Heilige Drei Könige' : [ 1, 6, [ 'Baden-Württemberg', 'Bayern', 'Sachsen-Anhalt'] ], // only in the specified states @@ -46,15 +54,15 @@ 'Allerheiligen' : [ 11, 1, [ 'Baden-Württemberg', 'Bayern', 'Nordrhein-Westfalen', 'Rheinland-Pfalz', 'Saarland' ] ], '1. Weihnachtstag' : [ 12, 25 ], '2. Weihnachtstag' : [ 12, 26 ], - // 'Silvester' : [ 12, 31 ], // for testing + // 'Silvester' : [ 12, 31 ], // for testing }, 'Baden-Württemberg': { // does only apply in Baden-Württemberg // This more specific rule set overwrites the country wide one (they are just ignored). // You may use this instead of the country wide with some // additional holidays for some states, if one state - // totally disagrees about how to do public holidays … + // totally disagrees about how to do holidays … // 'PH': { - // '2. Weihnachtstag' : [ 12, 26 ], + // '2. Weihnachtstag' : [ 12, 26 ], // }, // school holiday normally variate between states @@ -1075,153 +1083,153 @@ }, ], }, - }, - 'at': { + }, // }}} + 'at': { // {{{ 'PH': { // http://de.wikipedia.org/wiki/Feiertage_in_%C3%96sterreich - 'Neujahrstag' : [ 1, 1 ], - 'Heilige Drei Könige' : [ 1, 6 ], - // 'Josef' : [ 3, 19, [ 'Kärnten', 'Steiermark', 'Tirol', 'Vorarlberg' ] ], - // 'Karfreitag' : [ 'easter', -2 ], - 'Ostermontag' : [ 'easter', 1 ], - 'Staatsfeiertag' : [ 5, 1 ], - // 'Florian' : [ 5, 4, [ 'Oberösterreich' ] ], - 'Christi Himmelfahrt' : [ 'easter', 39 ], - 'Pfingstmontag' : [ 'easter', 50 ], - 'Fronleichnam' : [ 'easter', 60 ], - 'Mariä Himmelfahrt' : [ 8, 15 ], - // 'Rupert' : [ 9, 24, [ 'Salzburg' ] ], - // 'Tag der Volksabstimmung' : [ 10, 10, [ 'Kärnten' ] ], - 'Nationalfeiertag' : [ 10, 26 ], - 'Allerheiligen' : [ 11, 1 ], - // 'Martin' : [ 11, 11, [ 'Burgenland' ] ], - // 'Leopold' : [ 11, 15, [ 'Niederösterreich', 'Wien' ] ], - 'Mariä Empfängnis' : [ 12, 8 ], - // 'Heiliger Abend' : [ 12, 24 ], - 'Christtag' : [ 12, 25 ], - 'Stefanitag' : [ 12, 26 ], - // 'Silvester' : [ 12, 31 ], + 'Neujahrstag' : [ 1, 1 ], + 'Heilige Drei Könige' : [ 1, 6 ], + // 'Josef' : [ 3, 19, [ 'Kärnten', 'Steiermark', 'Tirol', 'Vorarlberg' ] ], + // 'Karfreitag' : [ 'easter', -2 ], + 'Ostermontag' : [ 'easter', 1 ], + 'Staatsfeiertag' : [ 5, 1 ], + // 'Florian' : [ 5, 4, [ 'Oberösterreich' ] ], + 'Christi Himmelfahrt' : [ 'easter', 39 ], + 'Pfingstmontag' : [ 'easter', 50 ], + 'Fronleichnam' : [ 'easter', 60 ], + 'Mariä Himmelfahrt' : [ 8, 15 ], + // 'Rupert' : [ 9, 24, [ 'Salzburg' ] ], + // 'Tag der Volksabstimmung' : [ 10, 10, [ 'Kärnten' ] ], + 'Nationalfeiertag' : [ 10, 26 ], + 'Allerheiligen' : [ 11, 1 ], + // 'Martin' : [ 11, 11, [ 'Burgenland' ] ], + // 'Leopold' : [ 11, 15, [ 'Niederösterreich', 'Wien' ] ], + 'Mariä Empfängnis' : [ 12, 8 ], + // 'Heiliger Abend' : [ 12, 24 ], + 'Christtag' : [ 12, 25 ], + 'Stefanitag' : [ 12, 26 ], + // 'Silvester' : [ 12, 31 ], }, - }, - 'ca': { + }, // }}} + 'ca': { // {{{ 'PH': { // https://en.wikipedia.org/wiki/Public_holidays_in_Canada - "New Year's Day" : [ 1, 1 ], - "Good Friday" : [ 'easter', -2 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Good Friday" : [ 'easter', -2 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Christmas Day" : [ 12, 25 ] }, 'Alberta': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Alberta Family Day" : [ 'firstFebruaryMonday', 14 ], - "Good Friday" : [ 'easter', -2 ], - "Easter Monday" : [ 'easter', 1 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Heritage Day" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ], - "Boxing Day" : [ 12, 26 ] + "New Year's Day" : [ 1, 1 ], + "Alberta Family Day" : [ 'firstFebruaryMonday', 14 ], + "Good Friday" : [ 'easter', -2 ], + "Easter Monday" : [ 'easter', 1 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Heritage Day" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ], + "Boxing Day" : [ 12, 26 ] }, }, 'British Columbia': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Family Day" : [ 'firstFebruaryMonday', 7 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "British Columbia Day" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Family Day" : [ 'firstFebruaryMonday', 7 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "British Columbia Day" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ] }, }, 'Manitoba': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Louis Riel Day" : [ 'firstFebruaryMonday', 14 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Civic Holiday" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Louis Riel Day" : [ 'firstFebruaryMonday', 14 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Civic Holiday" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ] }, }, 'New Brunswick': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "New Brunswick Day" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ], - "Boxing Day" : [ 12, 26 ] + "New Year's Day" : [ 1, 1 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "New Brunswick Day" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ], + "Boxing Day" : [ 12, 26 ] }, }, 'Newfoundland and Labrador': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Saint Patrick's Day" : [ 3, 17 ], - "Good Friday" : [ 'easter', -2 ], - "Saint George's Day" : [ 4, 23 ], - "Discovery Day" : [ 6, 24 ], - "Memorial Day" : [ 7, 1 ], - "Orangemen's Day" : [ 7, 12 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Armistice Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Saint Patrick's Day" : [ 3, 17 ], + "Good Friday" : [ 'easter', -2 ], + "Saint George's Day" : [ 4, 23 ], + "Discovery Day" : [ 6, 24 ], + "Memorial Day" : [ 7, 1 ], + "Orangemen's Day" : [ 7, 12 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Armistice Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ] }, }, 'Northwest Territories': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "National Aboriginal Day" : [ 6, 21 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Civic Holiday" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "National Aboriginal Day" : [ 6, 21 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Civic Holiday" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ] }, }, 'Nova Scotia': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Natal Day" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ], - "Boxing Day" : [ 12, 26 ] + "New Year's Day" : [ 1, 1 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Natal Day" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ], + "Boxing Day" : [ 12, 26 ] }, }, 'Nunavut': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Nunavut Day" : [ 7, 9 ], - "Civic Holiday" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Nunavut Day" : [ 7, 9 ], + "Civic Holiday" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ] }, }, 'Ontario': { @@ -1241,19 +1249,19 @@ }, 'Prince Edward Island': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Islander Day" : [ 'firstFebruaryMonday', 14 ], - "Good Friday" : [ 'easter', -2 ], - "Easter Monday" : [ 'easter', 1 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Civic Holiday" : [ 'firstAugustMonday', 0 ], - "Gold Cup Parade Day" : [ 'firstAugustMonday', 18 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ], - "Boxing Day" : [ 12, 26 ] + "New Year's Day" : [ 1, 1 ], + "Islander Day" : [ 'firstFebruaryMonday', 14 ], + "Good Friday" : [ 'easter', -2 ], + "Easter Monday" : [ 'easter', 1 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Civic Holiday" : [ 'firstAugustMonday', 0 ], + "Gold Cup Parade Day" : [ 'firstAugustMonday', 18 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ], + "Boxing Day" : [ 12, 26 ] }, }, 'Quebec': { @@ -1271,66 +1279,66 @@ }, 'Saskatchewan': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Family Day" : [ 'firstFebruaryMonday', 14 ], - "Good Friday" : [ 'easter', -2 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Saskatchewan Day" : [ 'firstAugustMonday', 0 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ] + "New Year's Day" : [ 1, 1 ], + "Family Day" : [ 'firstFebruaryMonday', 14 ], + "Good Friday" : [ 'easter', -2 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Saskatchewan Day" : [ 'firstAugustMonday', 0 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ] }, }, 'Yukon': { 'PH': { - "New Year's Day" : [ 1, 1 ], - "Heritage Day" : [ 'lastFebruarySunday', -2 ], - "Good Friday" : [ 'easter', -2 ], - "Easter Monday" : [ 'easter', 1 ], - "Victoria Day" : [ 'victoriaDay', 0 ], - "Canada Day" : [ 'canadaDay', 0 ], - "Discovery Day" : [ 'firstAugustMonday', 14 ], - "Labour Day" : [ 'firstSeptemberMonday', 0 ], - "Thanksgiving" : [ 'firstOctoberMonday', 7 ], - "Remembrance Day" : [ 11, 11 ], - "Christmas Day" : [ 12, 25 ], - "Boxing Day" : [ 12, 26 ] + "New Year's Day" : [ 1, 1 ], + "Heritage Day" : [ 'lastFebruarySunday', -2 ], + "Good Friday" : [ 'easter', -2 ], + "Easter Monday" : [ 'easter', 1 ], + "Victoria Day" : [ 'victoriaDay', 0 ], + "Canada Day" : [ 'canadaDay', 0 ], + "Discovery Day" : [ 'firstAugustMonday', 14 ], + "Labour Day" : [ 'firstSeptemberMonday', 0 ], + "Thanksgiving" : [ 'firstOctoberMonday', 7 ], + "Remembrance Day" : [ 11, 11 ], + "Christmas Day" : [ 12, 25 ], + "Boxing Day" : [ 12, 26 ] }, }, - }, - 'ua': { + }, // }}} + 'ua': { // {{{ '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 - "Новий рік" : [ 1, 1 ], - "Різдво" : [ 1, 7 ], - "Міжнародний жіночий день" : [ 3, 8 ], - "Великдень" : [ 'orthodox easter', 1 ], - "День Праці 1" : [ 5, 1 ], - "День Праці 2" : [ 5, 2 ], - "День Перемоги" : [ 5, 9 ], - "День Конституції України" : [ 6, 28 ], - "День Незалежності України" : [ 8, 24 ], + "Новий рік" : [ 1, 1 ], + "Різдво" : [ 1, 7 ], + "Міжнародний жіночий день" : [ 3, 8 ], + "Великдень" : [ 'orthodox easter', 1 ], + "День Праці 1" : [ 5, 1 ], + "День Праці 2" : [ 5, 2 ], + "День Перемоги" : [ 5, 9 ], + "День Конституції України" : [ 6, 28 ], + "День Незалежності України" : [ 8, 24 ], } - }, - 'si': { + }, // }}} + 'si': { // {{{ 'PH': { // http://www.vlada.si/o_sloveniji/politicni_sistem/prazniki/ - 'novo leto' : [ 1, 1 ], - 'Prešernov dan, slovenski kulturni praznik' : [ 2, 8 ], - 'velikonočna nedelja' : [ 'easter', 0 ], - 'velikonočni ponedeljek' : [ 'easter', 1 ], - 'dan upora proti okupatorju' : [ 4, 27 ], - 'praznik dela 1' : [ 5, 1 ], - 'praznik dela 2' : [ 5, 2 ], - 'binkoštna nedelja - binkošti' : [ 'easter', 49 ], - 'dan državnosti' : [ 6, 25 ], - 'Marijino vnebovzetje' : [ 8, 15 ], - 'dan reformacije' : [ 10, 31 ], - 'dan spomina na mrtve' : [ 11, 1 ], - 'božič' : [ 12, 25 ], - 'dan samostojnosti in enotnosti' : [ 12, 26 ], + 'novo leto' : [ 1, 1 ], + 'Prešernov dan, slovenski kulturni praznik' : [ 2, 8 ], + 'velikonočna nedelja' : [ 'easter', 0 ], + 'velikonočni ponedeljek' : [ 'easter', 1 ], + 'dan upora proti okupatorju' : [ 4, 27 ], + 'praznik dela 1' : [ 5, 1 ], + 'praznik dela 2' : [ 5, 2 ], + 'binkoštna nedelja - binkošti' : [ 'easter', 49 ], + 'dan državnosti' : [ 6, 25 ], + 'Marijino vnebovzetje' : [ 8, 15 ], + 'dan reformacije' : [ 10, 31 ], + 'dan spomina na mrtve' : [ 11, 1 ], + 'božič' : [ 12, 25 ], + 'dan samostojnosti in enotnosti' : [ 12, 26 ], }, - }, + }, // }}} }; // }}} @@ -1340,50 +1348,65 @@ // // Key to word_error_correction is the token name except wrong_words var word_error_correction = { - wrong_words: { - 'Assuming "" for ""': { - spring: 'Mar-May', - summer: 'Jun-Aug', - autumn: 'Sep-Nov', - winter: 'Dec-Feb', + wrong_words: { /* {{{ */ + 'Assuming "" for "".': { + 'Frühling': 'Mar-May', + 'Frühjahr': 'Mar-May', + 'Sommer': 'Jun-Aug', + 'Herbst': 'Sep-Nov', + 'winter': 'Dec-Feb', + }, '"" wird als "" interpertiert.': { + 'spring': 'Mar-May', + 'summer': 'Jun-Aug', + 'autumn': 'Sep-Nov', + // 'winter': 'Dec-Feb', // Same as in English. // morning: '08:00-12:00', // evening: '13:00-18:00', '_': '-', 'daytime': 'sunrise-sunset', }, 'Bitte benutze die englische Schreibweise "" für "".': { - sommer: 'summer', + 'sommer': 'summer', 'werktag': 'Mo-Fr', 'werktags': 'Mo-Fr', - }, 'Bitte benutze "" für "". Beispiel: "Mo-Fr 08:00-12:00; Tu off"': { - ruhetag: 'off', - ruhetage: 'off', - geschlossen: 'off', - ausser: 'off', - außer: 'off', + }, 'Bitte benutze "" für "". Beispiel: "Mo-Fr 08:00-12:00; Tu off".': { + 'ruhetag': 'off', + 'ruhetage': 'off', + 'geschlossen': 'off', + 'geschl': 'off', + // 'ausser': 'off', + // 'außer': 'off', }, 'Neem de engelse afkorting "" voor "" alstublieft.': { - 'gesloten': 'off', - 'feestdag': 'PH', + 'gesloten': 'off', + 'feestdag': 'PH', + 'feestdagen': 'PH', }, 'Assuming "" for "". Please avoid using "workday": http://wiki.openstreetmap.org/wiki/Talk:Key:opening_hours#need_syntax_for_holidays_and_workingdays': { - // // Used around 260 times but the problem is, that work day might be different in other countries. - 'wd': 'Mo-Fr', - 'weekday': 'Mo-Fr', - 'weekdays': 'Mo-Fr', - 'vardagar': 'Mo-Fr', - }, 'Please use notation something like "Mo off" instead "".': { - except: 'off', - }, 'Please ommit "" or use a colon instead: "12:00-14:00".': { - h: '', - }, 'Please ommit "".': { - season: '', - hs: '', - hrs: '', - hours: '', - }, 'Please ommit "". The key must not be in the value.': { + // Used around 260 times but the problem is, that work day might be different in other countries. + 'wd': 'Mo-Fr', + 'on work day': 'Mo-Fr', + 'on work days': 'Mo-Fr', + 'weekday': 'Mo-Fr', + 'weekdays': 'Mo-Fr', + 'vardagar': 'Mo-Fr', + }, 'Please use something like "Mo off" instead "".': { + 'except': 'off', + }, 'Please omit "" or use a colon instead: "12:00-14:00".': { + 'h': '', + }, 'Please omit "".': { + 'season': '', + 'hs': '', + 'hrs': '', + 'hours': '', + '·': '', + }, 'Please omit "". The key must not be in the value.': { 'opening_hours=': '', - }, 'Please ommit "". You might want to express open end which can be specified as "12:00+" for example.': { - from: '', - }, 'You can use notation "" for "". You might want to express open end which can be specified as "12:00+" for example.': { + }, 'Please omit "". You might want to express open end which can be specified as "12:00+" for example.': { + 'from': '', + }, 'You can use notation "" for "" in the case that you want to express open end times. Example: "12:00+".': { + 'til late': '+', + 'till late': '+', '-late': '+', + '-open end': '+', + '-openend': '+', }, 'Please use notation "" for "". If the times are unsure or vary consider a comment e.g. 12:00-14:00 "only on sunshine".': { '~': '-', '~': '-', @@ -1391,55 +1414,79 @@ 'otherwise': '||', }, 'You can use notation "" for "" temporally if the syntax will still be valid.': { '?': 'unknown "please add this if known"', - }, 'Please use notation "" for "".': { - '→': '-', + }, 'Please use notation "" for "". Although using "–" is typographical correct, the opening_hours syntax is defined with the normal hyphen. Correct typography should be done on application level …': { '–': '-', - '−': '-', - '=': '-', - 'ー': '-', - to: '-', - 'до': '-', - a: '-', // language unknown - as: '-', // language unknown - 'á': '-', // language unknown - 'ás': '-', // language unknown - 'à': '-', // language unknown - 'às': '-', // language unknown - 'ate': '-', // language unknown - 'till': '-', - 'til': '-', - 'until': '-', - 'through': '-', - and: ',', - '&': ',', - ':': ':', - '°°': ':00', - 'daily': 'Mo-Su', - 'everyday': 'Mo-Su', - 'every day': 'Mo-Su', - always: '24/7', - nonstop: '24/7', - '24x7': '24/7', - 'anytime': '24/7', - 'all day': '24/7', - 'all days': 'Mo-Su', - 'every day': 'Mo-Su', - '7days': 'Mo-Su', - '7j/7': 'Mo-Su', // I guess that it means that - '7/7': 'Mo-Su', // I guess that it means that - '7 days': 'Mo-Su', - '7 days a week': 'Mo-Su', - 'midnight': '00:00', - holiday: 'PH', - holidays: 'PH', + }, 'Please use notation "" for "".': { + '→': '-', + '−': '-', + '=': '-', + 'ー': '-', + 'to': '-', + 'до': '-', + 'a': '-', // language unknown + 'as': '-', // language unknown + 'á': '-', // language unknown + 'ás': '-', // language unknown + 'às': '-', // language unknown + 'ate': '-', // language unknown + 'till': '-', + 'til': '-', + 'until': '-', + 'through': '-', + 'and': ',', + '&': ',', + // '/': ',', // Can not be corrected as / is a valid token + ':': ':', + '°°': ':00', + 'always': '24/7', + 'nonstop': '24/7', + '24x7': '24/7', + 'anytime': '24/7', + 'all day': '24/7', + 'daily': 'Mo-Su', + 'everyday': 'Mo-Su', + 'every day': 'Mo-Su', + 'all days': 'Mo-Su', + '7j/7': 'Mo-Su', // I guess that it means that + '7/7': 'Mo-Su', // I guess that it means that + /* {{{ + * Fixing this causes to ignore the following warning: "There should be no + * reason to differ more than 6 days from a constrained + * weekdays. If so tell us …". + * The following mistake is expected to occur more often. + */ + '7days': 'Mo-Su', + '7 days': 'Mo-Su', + // }}} + '7 days a week': 'Mo-Su', + '7 days/week': 'Mo-Su', + '24 hours 7 days a week': '24/7', + '24 hours': '00:00-24:00', + 'midday': '12:00', + 'midnight': '00:00', + 'holiday': 'PH', + 'holidays': 'PH', 'public holidays': 'PH', - 'public holiday': 'PH', + 'public holiday': 'PH', + 'day after public holiday': 'PH +1 day', + 'one day after public holiday': 'PH +1 day', + 'day before public holiday': 'PH -1 day', + 'one day before public holiday': 'PH -1 day', + 'school holiday': 'SH', + 'school holidays': 'SH', // summerholiday: 'SH', // summerholidays: 'SH', - weekend: 'Sa,Su', - weekends: 'Sa,Su', - 'daylight': 'sunrise-sunset', - 'оff': 'off', // Russian o + /* Not implemented {{{ */ + // 'day after school holiday': 'SH +1 day', + // 'one day after school holiday': 'SH +1 day', + // 'day before school holiday': 'SH -1 day', + // 'one day before school holiday': 'SH -1 day', + /* }}} */ + 'weekend': 'Sa,Su', + 'weekends': 'Sa,Su', + 'daylight': 'sunrise-sunset', + }, 'Please use notation "" for "". Those characters look very similar but are not the same!': { + 'оff': 'off', // Russian o }, 'Please use time format in 24 hours notation (""). If PM is used you might have to convert the hours to the 24 hours notation.': { 'pm': '', 'рм': '', @@ -1448,194 +1495,229 @@ }, 'Bitte verzichte auf "".': { 'uhr': '', 'geöffnet': '', - }, 'Bitte verzichte auf "". Sie möchten eventuell eine Öffnungszeit ohne vorgegebenes Ende angeben. Beispiel: "12:00+"': { - ab: '', - von: '', + 'zwischen': '', + }, 'Bitte verzichte auf "". Sie möchten eventuell eine Öffnungszeit ohne vorgegebenes Ende (Open End) angeben. Beispiel: "12:00+"': { + 'ab': '', + 'von': '', + }, '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 "" für "".': { + 'damen': 'open "Damen"', + 'herren': 'open "Herren"', }, 'Bitte benutze die Schreibweise "" für "".': { - bis: '-', - 'täglich': 'Mo-Su', + 'bis': '-', + 'täglich': 'Mo-Su', + 'schulferien': 'SH', + 'sonn-/feiertag': 'PH,Su', + 'sonn-/feiertags': 'PH,Su', + 'an sonn- und feiertagen': 'PH,Su', + 'nur sonn-/feiertags': 'PH,Su', + }, 'Bitte benutze die Schreibweise "" für "". 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.': { + '„': '"', + '“': '"', + '”': '"', + }, 'Please use notation "" for "". 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.': { + '«': '"', + '»': '"', + '‚': '"', + '‘': '"', + '’': '"', + '「': '"', + '」': '"', + '『': '"', + '』': '"', + }, 'Please use notation "" for "". The used quote signs are not defined in the specification. See http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification.': { + "'": '"', + }, 'You might want to use comments instead of brackets (which are not valid in this context). If you do, replace "" with "".': { + // '(': '"', + // ')': '"', }, 'Bitte benutze die Schreibweise "" als Ersatz für "und" bzw. "u.".': { - und: ',', - u: ',', + 'und': ',', + 'u': ',', }, 'Bitte benutze die englische Abkürzung "" für "".': { - feiertag: 'PH', - feiertags: 'PH', - feiertage: 'PH', - feiertagen: 'PH' + 'feiertag': 'PH', + 'feiertags': 'PH', + 'feiertage': 'PH', + 'feiertagen': 'PH' }, 'S\'il vous plaît utiliser "" pour "".': { - 'fermé': 'off', - 'et': ',', - 'à': '-', + 'fermé': 'off', + 'et': ',', + 'à': '-', 'jours fériés': 'PH', - }, 'Neem de engelse afkorting "" voor "" alstublieft.': { - feestdag: 'PH', - feestdagen: 'PH', } - }, + }, /* }}} */ - month: { + month: { /* {{{ */ 'default': { - jan: 0, - feb: 1, - mar: 2, - apr: 3, - may: 4, - jun: 5, - jul: 6, - aug: 7, - sep: 8, - oct: 9, - nov: 10, - dec: 11, + 'jan': 0, + 'feb': 1, + 'mar': 2, + 'apr': 3, + 'may': 4, + 'jun': 5, + 'jul': 6, + 'aug': 7, + 'sep': 8, + 'oct': 9, + 'nov': 10, + 'dec': 11, }, 'Please use the English abbreviation "" for "".': { 'jänner': 0, // Austria - january: 0, - february: 1, - march: 2, - april: 3, - // may: 4, - june: 5, - july: 6, - august: 7, - september: 8, - sept: 8, - october: 9, - november: 10, - december: 11, + 'january': 0, + 'february': 1, + 'march': 2, + 'april': 3, + // 'may': 4, + 'june': 5, + 'july': 6, + 'august': 7, + 'september': 8, + 'sept': 8, + 'october': 9, + 'november': 10, + 'december': 11, }, 'Bitte benutze die englische Abkürzung "" für "".': { - januar: 0, - februar: 1, + 'januar': 0, + 'februar': 1, 'märz': 2, - maerz: 2, - mai: 4, - juni: 5, - juli: 6, - okt: 9, - oktober: 9, - dez: 11, - dezember: 11, + 'maerz': 2, + 'mai': 4, + 'juni': 5, + 'juli': 6, + 'okt': 9, + 'oktober': 9, + 'dez': 11, + 'dezember': 11, }, 'S\'il vous plaît utiliser l\'abréviation "" pour "".': { - janvier: 0, - février: 1, - fév: 1, - mars: 2, - avril: 3, - avr: 3, - mai: 4, - juin: 5, - juillet: 6, - août: 7, - aoû: 7, - septembre: 8, - octobre: 9, - novembre: 10, - décembre: 11, + 'janvier': 0, + 'février': 1, + 'fév': 1, + 'mars': 2, + 'avril': 3, + 'avr': 3, + 'mai': 4, + 'juin': 5, + 'juillet': 6, + 'août': 7, + 'aoû': 7, + 'septembre': 8, + 'octobre': 9, + 'novembre': 10, + 'décembre': 11, }, 'Neem de engelse afkorting "" voor "" alstublieft.': { - januari: 0, - februari: 1, - maart: 2, - mei: 4, - augustus: 7, + 'januari': 0, + 'februari': 1, + 'maart': 2, + 'mei': 4, + 'augustus': 7, } + }, /* }}} */ + + calcday: { + 'default': { + 'day': 'day', + 'days': 'days', + }, }, - weekday: { // good source: http://www.omniglot.com/language/time/days.htm + weekday: { // {{{ Good source: http://www.omniglot.com/language/time/days.htm */ 'default': { - su: 0, - mo: 1, - tu: 2, - we: 3, - th: 4, - fr: 5, - sa: 6, + 'su': 0, + 'mo': 1, + 'tu': 2, + 'we': 3, + 'th': 4, + 'fr': 5, + 'sa': 6, }, 'Assuming "" for ""': { - m: 1, - w: 3, - f: 5, + 'm': 1, + 'w': 3, + 'f': 5, }, 'Please use the abbreviation "" for "".': { - sun: 0, - sunday: 0, - sundays: 0, - mon: 1, - monday: 1, - mondays: 1, - tue: 2, - tuesday: 2, - tuesdays: 2, - wed: 3, - wednesday: 3, - wednesdays: 3, - thu: 4, - thur: 4, - thursday: 4, - thursdays: 4, - fri: 5, - friday: 5, - fridays: 5, - sat: 6, - saturday: 6, - saturdays: 6, + 'sun': 0, + 'sunday': 0, + 'sundays': 0, + 'mon': 1, + 'monday': 1, + 'mondays': 1, + 'tue': 2, + 'tues': 2, // Used here: http://www.westerhambeauty.co.uk/contact.php + 'tuesday': 2, + 'tuesdays': 2, + 'wed': 3, + 'weds': 3, + 'wednesday': 3, + 'wednesdays': 3, + 'thu': 4, + 'thur': 4, + 'thurs': 4, + 'thursday': 4, + 'thursdays': 4, + 'fri': 5, + 'friday': 5, + 'fridays': 5, + 'sat': 6, + 'saturday': 6, + 'saturdays': 6, }, 'Bitte benutze die englische Abkürzung "" für "". Could also mean Saturday in Polish …': { - so: 0, + 'so': 0, }, 'Bitte benutze die englische Abkürzung "" für "".': { - son: 0, - sonntag: 0, + 'son': 0, + 'sonntag': 0, 'sonn-': 0, - sonntags: 0, - montag: 1, - montags: 1, - di: 2, - die: 2, - dienstag: 2, - dienstags: 2, - mi: 3, - mit: 3, - mittwoch: 3, - mittwochs: 3, + 'sonntags': 0, + 'montag': 1, + 'montags': 1, + 'di': 2, + 'die': 2, + 'dienstag': 2, + 'dienstags': 2, + 'mi': 3, + 'mit': 3, + 'mittwoch': 3, + 'mittwochs': 3, 'do': 4, - don: 4, - donnerstag: 4, - donnerstags: 4, - fre: 5, - freitag: 5, - freitags: 5, - sam: 6, - samstag: 6, - samstags: 6, + 'don': 4, + 'donnerstag': 4, + 'donnerstags': 4, + 'fre': 5, + 'freitag': 5, + 'freitags': 5, + 'sam': 6, + 'samstag': 6, + 'samstags': 6, }, 'S\'il vous plaît utiliser l\'abréviation "" pour "".': { - dim: 0, - dimanche: 0, - lu: 1, - lun: 1, - lundi: 1, - mardi: 2, - mer: 3, - mercredi: 3, - je: 4, - jeu: 4, - jeudi: 4, - ve: 5, - ven: 5, - vendredi: 5, - samedi: 6, + 'dim': 0, + 'dimanche': 0, + 'lu': 1, + 'lun': 1, + 'lundi': 1, + 'mardi': 2, + 'mer': 3, + 'mercredi': 3, + 'je': 4, + 'jeu': 4, + 'jeudi': 4, + 've': 5, + 'ven': 5, + 'vendredi': 5, + 'samedi': 6, }, 'Neem de engelse afkorting "" voor "" alstublieft.': { - zo: 0, - zon: 0, - zontag: 0, // correct? - zondag: 0, - maandag: 1, - din: 2, - dinsdag: 2, - wo: 3, - woe: 3, - woensdag: 3, - donderdag: 4, - vr: 5, - vri: 5, - vrijdag: 5, - za: 6, - zat: 6, - zaterdag: 6, + 'zo': 0, + 'zon': 0, + 'zontag': 0, // correct? + 'zondag': 0, + 'maandag': 1, + 'din': 2, + 'dinsdag': 2, + 'wo': 3, + 'woe': 3, + 'woensdag': 3, + 'donderdag': 4, + 'vr': 5, + 'vri': 5, + 'vrijdag': 5, + 'za': 6, + 'zat': 6, + 'zaterdag': 6, }, 'Please use the English abbreviation "" for "".': { // FIXME: Translate to Czech. 'neděle': 0, 'ne': 0, @@ -1650,8 +1732,7 @@ 'pátek': 5, 'pá': 5, 'sobota': 6, - }, 'Please use the English abbreviation "" for "".': { - // Spanish. + }, 'Please use the English abbreviation "" (Spanish) for "".': { 'martes': 0, 'miércoles': 1, 'jueves': 2, @@ -1659,7 +1740,7 @@ 'sábado': 4, 'domingo': 5, 'lunes': 6, - // Indonesian. + }, 'Please use the English abbreviation "" (Indonesian) for "".': { 'selasa': 0, 'rabu': 1, 'kami': 2, @@ -1667,18 +1748,18 @@ 'sabtu': 4, 'minggu': 5, 'senin': 6, - // Swedish + }, 'Please use the English abbreviation "" (Swedish) for "".': { 'söndag': 0, 'söndagar': 0, 'måndag': 1, 'ma': 1, 'tisdag': 2, - 'onsdag': 3, + 'onsdag': 3, // Same in Danish 'torsdag': 4, 'fredag': 5, 'lördag': 6, 'lördagar': 6, - // Polish + }, 'Please use the English abbreviation "" (Polish) for "".': { 'niedziela': 0, 'niedz': 0, 'n': 0, 'ndz': 0, 'poniedziałek': 1, 'poniedzialek': 1, 'pon': 1, 'pn': 1, 'wtorek': 2, 'wt': 2, @@ -1686,7 +1767,7 @@ 'czwartek': 4, 'czw': 4, 'cz': 4, 'piątek': 5, 'piatek': 5, 'pt': 5, 'sobota': 6, 'sob': 6, // 'so': 6 // abbreviation also used in German - // Russian + }, 'Please use the English abbreviation "" (Russian) for "".': { 'воскресенье' : 0, 'Вс' : 0, "voskresen'ye": 0, @@ -1703,18 +1784,18 @@ 'pyatnitsa' : 5, 'суббота' : 6, 'subbota' : 6, - // Danish + }, 'Please use the English abbreviation "" (Danish) for "".': { 'søndag' : 0, 'mandag' : 1, 'tirsdag': 2, - 'onsdag' : 3, + 'onsdag' : 3, // Same in Swedish 'torsdag': 4, 'fredag' : 5, 'lørdag' : 6, }, - }, + }, /* }}} */ - timevar: { // Special time variables which actual value depends on the date and the position of the facility. + timevar: { /* {{{ Special time variables which actual value depends on the date and the position of the facility. */ 'default': { 'sunrise': 'sunrise', 'sunset': 'sunset', @@ -1728,7 +1809,7 @@ 'sonnenaufgang': 'sunrise', 'sonnenuntergang': 'sunset', }, - }, + }, /* }}} */ 'event': { // variable events 'default': { @@ -1754,53 +1835,34 @@ }(this, function (SunCalc, holidays, word_error_correction) { return function(value, nominatiomJSON, oh_mode) { // short constants {{{ - var word_value_replacement = { // if the correct values can not be calculated + var word_value_replacement = { // If the correct values can not be calculated. dawn : 60 * 5 + 30, sunrise : 60 * 6, sunset : 60 * 18, dusk : 60 * 18 + 30, }; - var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; var weekdays = ['Su','Mo','Tu','We','Th','Fr','Sa']; var default_prettify_conf = { - 'leading_zero_hour': true, // enforce leading zero + // Update README.md if changed. + 'zero_pad_hour': true, // enforce ("%02d", hour) 'one_zero_if_hour_zero': false, // only one zero "0" if hour is zero "0" - 'leave_off_closed': true, // leave keywords of and closed as is + 'leave_off_closed': true, // leave keywords "off" and "closed" as is 'keyword_for_off_closed': 'off', // use given keyword instead of "off" or "closed" - 'block_sep_string': ' ', // separate blocks by string - 'print_semicolon': true, // print token which separates normal blocks + 'rule_sep_string': ' ', // separate rules by string + 'print_semicolon': true, // print token which separates normal rules '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 - 'sep_one_day_between': ',' // separator which should be used + 'sep_one_day_between': ',', // separator which should be used + 'zero_pad_month_and_week_numbers': false, // Format week (e.g. `week 01`) and month day numbers (e.g. `Jan 01`) with "%02d". }; var minutes_in_day = 60 * 24; var msec_in_day = 1000 * 60 * minutes_in_day; var msec_in_week = msec_in_day * 7; - // }}} - // The big picture -- How does this library work? {{{ - //====================================================================== - // Constructor - entry to parsing code - //====================================================================== - // Terminology: - // - // Mo-Fr 10:00-11:00; Th 10:00-12:00 - // \_____block_____/ \____block___/ - // - // The README refers to blocks as rules, which is more intuitive but less clear. - // Because of that only the README uses the term rule in that context. - // In all internal parts of this project, the term block is used. - // - // Mo-Fr Jan 10:00-11:00 - // \__/ \_/ \_________/ - // selectors (left to right: weekday, month, time) - // - // Logic: - // - Tokenize - // Foreach block: - // - Run top-level (block) parser - // - Which calls sub parser for specific selector types - // - Which produce selector functions + var library_name = 'opening_hours.js'; + var repository_url = 'https://github.com/ypid/' + library_name; + var issues_url = repository_url + '/issues?state=open'; // }}} // constructor parameters {{{ @@ -1826,34 +1888,45 @@ // 2: both (time ranges and points in time), tags: collection_times, service_times if (typeof oh_mode == 'undefined') { oh_mode = 0; - } else if (!(typeof oh_mode == 'number' && (oh_mode == 0 || oh_mode == 1 || oh_mode == 2))) { - throw 'The third constructor parameter is oh_mode and must be a number (0, 1 or 2)' + } else if (!(typeof oh_mode == 'number' && (oh_mode === 0 || oh_mode == 1 || oh_mode == 2))) { + throw 'The third constructor parameter is oh_mode and must be a number (0, 1 or 2)'; } // }}} - // put tokenized blocks into list {{{ - if (value.match(/^(\s*;?\s*)+$/)) + // Tokenize value and generate selector functions. {{{ + if (value.match(/^(?:\s*;?\s*)+$/)) throw 'Value contains nothing meaningful which can be parsed'; - var parsing_warnings = []; + var parsing_warnings = []; // Elements are fed into function formatWarnErrorMessage(nrule, at, message) var done_with_warnings = false; // The functions which throw warnings can be called multiple times. - var has_token = {}; + var done_with_selector_reordering = false; + var done_with_selector_reordering_warnings = false; var tokens = tokenize(value); - // console.log(JSON.stringify(tokens, null, '\t')); + // console.log(JSON.stringify(tokens, null, ' ')); var prettified_value = ''; - 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. var week_stable = true; - var blocks = []; - - for (var nblock = 0; nblock < tokens.length; nblock++) { - if (tokens[nblock][0].length == 0) continue; - // Block does contain nothing useful e.g. second block of '10:00-12:00;' (empty) which needs to be handled. + var rules = []; + var new_tokens = []; + + for (var nrule = 0; nrule < tokens.length; nrule++) { + if (tokens[nrule][0].length === 0) { + // Rule does contain nothing useful e.g. second rule of '10:00-12:00;' (empty) which needs to be handled. + parsing_warnings.push([nrule, -1, + 'This rule does not contain anything useful. Please remove this empty rule.' + + (nrule == tokens.length - 1 && nrule > 0 && !tokens[nrule][1] ? + ' Might it be possible that you are a programmer and adding a semicolon after each statement is hardwired in your muscle memory ;) ?' + + ' The thing is that the semicolon in the opening_hours syntax is defined as rule separator.' + + ' So for compatibility reasons you should omit this last semicolon.': '') + ]); + continue; + } var continue_at = 0; + var next_rule_is_additional = false; do { - if (continue_at == tokens[nblock][0].length) break; - // Additional block does contain nothing useful e.g. second block of '10:00-12:00,' (empty) which needs to be handled. + if (continue_at == tokens[nrule][0].length) break; + // Additional rule does contain nothing useful e.g. second rule of '10:00-12:00,' (empty) which needs to be handled. var selectors = { // Time selectors @@ -1873,20 +1946,43 @@ // Array with non-empty date selector types, with most optimal ordering date: [], - fallback: tokens[nblock][1], + fallback: tokens[nrule][1], additional: continue_at ? true : false, meaning: true, unknown: false, comment: undefined, - build_from_token_block: undefined, + build_from_token_rule: undefined, }; - selectors.build_from_token_block = [ nblock, continue_at ]; - continue_at = parseGroup(tokens[nblock][0], continue_at, selectors, nblock); - if (typeof continue_at == 'object') + selectors.build_from_token_rule = [ nrule, continue_at, new_tokens.length ]; + continue_at = parseGroup(tokens[nrule][0], continue_at, selectors, nrule); + if (typeof continue_at == 'object') { continue_at = continue_at[0]; - else + } else { continue_at = 0; + } + + // console.log('Current tokens: ' + JSON.stringify(tokens[nrule], null, ' ')); + + new_tokens.push( + [ + tokens[nrule][0].slice( + selectors.build_from_token_rule[1], + continue_at === 0 + ? tokens[nrule][0].length + : continue_at + ), + tokens[nrule][1], + tokens[nrule][2], + ] + ); + + if (next_rule_is_additional && new_tokens.length > 1) { + // Move 'rule separator' from last token of last rule to first token of this rule. + new_tokens[new_tokens.length - 1][0].unshift(new_tokens[new_tokens.length - 2][0].pop()); + } + + next_rule_is_additional = continue_at === 0 ? false : true; if (selectors.year.length > 0) selectors.date.push(selectors.year); @@ -1902,10 +1998,10 @@ selectors.date.push(selectors.weekday); // console.log('weekday: ' + JSON.stringify(selectors.weekday, null, '\t')); - blocks.push(selectors); + rules.push(selectors); // This handles selectors with time ranges wrapping over midnight (e.g. 10:00-02:00) - // it generates wrappers for all selectors and creates a new block. + // it generates wrappers for all selectors and creates a new rule. if (selectors.wraptime.length > 0) { var wrapselectors = { time: selectors.wraptime, @@ -1916,6 +2012,8 @@ comment: selectors.comment, wrapped: true, + // build_from_token_rule: selectors.build_from_token_rule, + // Not (yet) needed. }; for (var dselg = 0; dselg < selectors.date.length; dselg++) { @@ -1927,111 +2025,239 @@ } } - blocks.push(wrapselectors); + rules.push(wrapselectors); } - } while (continue_at) + } while (continue_at); } + // console.log(JSON.stringify(tokens, null, ' ')); + // console.log(JSON.stringify(new_tokens, null, ' ')); // }}} - /* Tokenization function: Splits string into parts. {{{ + /* Format warning or error message for the user. {{{ + * + * :param nrule: Rule number starting with zero. + * :param at: Token position at which the issue occurred. + * :param message: Human readable string with the message. + * :returns: String with position of the warning or error marked for the user. + */ + function formatWarnErrorMessage(nrule, at, message) { + // FIXME: Change to new_tokens. + if (typeof nrule == 'number') { + var pos = 0; + if (nrule == -1) { // Usage of rule index not required because we do have access to value.length. + pos = value.length - at; + } else { // Issue accrued at a later time, position in string needs to be reconstructed. + if (typeof tokens[nrule][0][at] == 'undefined') { + if (typeof tokens[nrule][0] && at == -1) { + pos = value.length; + if (typeof tokens[nrule+1] == 'object' && typeof tokens[nrule+1][2] == 'number') { + pos -= tokens[nrule+1][2]; + } else if (typeof tokens[nrule][2] == 'number') { + pos -= tokens[nrule][2]; + } + } else { + // Given position is invalid. + // + formatLibraryBugMessage('Bug in warning generation code which could not determine the exact position of the warning or error in value.'); + pos = value.length; + if (typeof tokens[nrule][2] != 'undefined') { + // Fallback: Point to last token in the rule which caused the problem. + // Run real_test regularly to fix the problem before a user is confronted with it. + pos -= tokens[nrule][2]; + console.warn('Last token for rule: ' + tokens[nrule]); + console.log(value.substring(0, pos) + ' <--- (' + message + ')'); + console.log('\n'); + } { + console.warn('tokens[nrule][2] is undefined. This is ok if nrule is the last rule.'); + } + } + } else { + pos = value.length; + if (typeof tokens[nrule][0][at+1] != 'undefined') { + pos -= tokens[nrule][0][at+1][2]; + } else if (typeof tokens[nrule][2] != 'undefined') { + pos -= tokens[nrule][2]; + } + } + } + return value.substring(0, pos) + ' <--- (' + message + ')'; + } else if (typeof nrule == 'string') { + return nrule.substring(0, at) + ' <--- (' + message + ')'; + } + } + // }}} + + /* Format internal library error message. {{{ + * + * :param message: Human readable string with the error message. + * :returns: Error message for the user. + */ + function formatLibraryBugMessage(message) { + if (typeof message == 'undefined') + message = ''; + else + message = ' ' + message; + + message = 'An error occurred during evaluation of the value "' + value + '".' + + ' Please file a bug report here: ' + issues_url + '.' + + message; + console.log(message); + return message; + } + // }}} + + /* Tokenize input stream. {{{ * * :param value: Raw opening_hours value. - * :returns: Tokenized list object. Complex structure. You can print the - * thing as JSON if you would like to know in details. - * The most inner list has the following items: [ internal_value, token_name, value_length ]. + * :returns: Tokenized list object. Complex structure. Check the + * internal documentation in the doc directory for details. */ function tokenize(value) { - var all_tokens = new Array(); - var curr_block_tokens = new Array(); + var all_tokens = []; + var curr_rule_tokens = []; - var last_block_fallback_terminated = false; + var last_rule_fallback_terminated = false; - while (value != '') { + while (value !== '') { + // console.log("Parsing value: " + value); var tmp; - if (tmp = value.match(/^(?:week\b|open\b|unknown\b)/i)) { + if (tmp = value.match(/^week\b/i)) { // Reserved keywords. - curr_block_tokens.push([tmp[0].toLowerCase(), tmp[0].toLowerCase(), value.length ]); + curr_rule_tokens.push([tmp[0].toLowerCase(), tmp[0].toLowerCase(), value.length ]); + value = value.substr(tmp[0].length); + } else if (tmp = value.match(/^(?:off\b|closed\b|open\b|unknown\b)/i)) { + // Reserved keywords. + curr_rule_tokens.push([tmp[0].toLowerCase(), 'state', value.length ]); value = value.substr(tmp[0].length); } else if (tmp = value.match(/^24\/7/i)) { // Reserved keyword. - has_token[tmp[0]] = true; - curr_block_tokens.push([tmp[0], tmp[0], value.length ]); - value = value.substr(tmp[0].length); - } else if (tmp = value.match(/^(?:off|closed)/i)) { - // Reserved keywords. - curr_block_tokens.push([tmp[0].toLowerCase(), 'closed', value.length ]); + curr_rule_tokens.push([tmp[0], tmp[0], value.length ]); value = value.substr(tmp[0].length); } else if (tmp = value.match(/^(?:PH|SH)/i)) { // special day name (holidays) - curr_block_tokens.push([tmp[0].toUpperCase(), 'holiday', value.length ]); + curr_rule_tokens.push([tmp[0].toUpperCase(), 'holiday', value.length ]); value = value.substr(2); - } else if (tmp = value.match(/^days?/i)) { - curr_block_tokens.push([tmp[0].toLowerCase(), 'calcday', value.length ]); - value = value.substr(tmp[0].length); - } 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)) { - // Handle all remaining words with error tolerance. + } 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)) { + /* Handle all remaining words and specific other characters with error tolerance. + * + * à|á: Word boundary does not work with unicode chars: 'test à test'.match(/\bà\b/i) + * https://stackoverflow.com/questions/10590098/javascript-regexp-word-boundaries-unicode-characters + * Order in the regular expression capturing group is important in some cases. + * + * mo|tu|we|th|fr|sa|su|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec: Prefer defended keywords + * if used in cases like 'mo12:00-14:00' (when keyword is followed by number). + */ var correct_val = returnCorrectWordOrToken(tmp[1].toLowerCase(), value.length); + // console.log('Error tolerance for string "' + tmp[1] + '" returned "' + correct_val + '".'); if (typeof correct_val == 'object') { - curr_block_tokens.push([ correct_val[0], correct_val[1], value.length ]); + curr_rule_tokens.push([ correct_val[0], correct_val[1], value.length ]); value = value.substr(tmp[0].length); } else if (typeof correct_val == 'string') { if (tmp[1].toLowerCase() == 'pm') { - var hours_token_at = curr_block_tokens.length - 3; - if (hours_token_at > 0) { - if (matchTokens(curr_block_tokens, hours_token_at, - 'number', 'timesep', 'number') - ) { - var hours_token = curr_block_tokens[hours_token_at]; - } else if (matchTokens(curr_block_tokens, hours_token_at + 2, - 'number') - ) { - hours_token_at += 2; - var hours_token = curr_block_tokens[hours_token_at]; + var hours_token_at = curr_rule_tokens.length - 1; + var hours_token; + if (hours_token_at >= 0) { + if (hours_token_at -2 >= 0 && + matchTokens( + curr_rule_tokens, hours_token_at - 2, + 'number', 'timesep', 'number' + ) + ) { + hours_token_at -= 2; + hours_token = curr_rule_tokens[hours_token_at]; + } else if (matchTokens(curr_rule_tokens, hours_token_at, 'number')) { + hours_token = curr_rule_tokens[hours_token_at]; } - if (hours_token[0] <= 12) { + + if (typeof hours_token == 'object' && hours_token[0] <= 12) { hours_token[0] += 12; - curr_block_tokens[hours_token_at] = hours_token; + curr_rule_tokens[hours_token_at] = hours_token; } } } - value = correct_val + value.substr(tmp[0].length); + var correct_tokens = tokenize(correct_val)[0]; + if (correct_tokens[1] === true) { // last_rule_fallback_terminated + throw formatLibraryBugMessage(); + } + for (var i = 0; i < correct_tokens[0].length; i++) { + curr_rule_tokens.push([correct_tokens[0][i][0], correct_tokens[0][i][1], value.length]); + // value.length - tmp[0].length does not have the desired effect for all test cases. + } + + value = value.substr(tmp[0].length); + // value = correct_val + value.substr(tmp[0].length); + // Does not work because it would generate the wrong length for formatWarnErrorMessage. } else { // other single-character tokens - curr_block_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length - 1 ]); + curr_rule_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length - 1 ]); value = value.substr(1); } } else if (tmp = value.match(/^\d+/)) { // number - if (tmp[0] > 1900) // Assumed to be a year number. - curr_block_tokens.push([tmp[0], 'year', value.length ]); - else - curr_block_tokens.push([+tmp[0], 'number', value.length ]); + if (Number(tmp[0]) > 1900) { // Assumed to be a year number. + curr_rule_tokens.push([tmp[0], 'year', value.length ]); + if (Number(tmp[0]) >= 2100) // Probably an error + parsing_warnings.push([ -1, value.length - 1, + 'The number ' + Number(tmp[0]) + ' will be interpreted as year.' + + ' This is probably not intended. Times can be specified as "12:00".' + ]); + } else { + curr_rule_tokens.push([Number(tmp[0]), 'number', value.length ]); + } + + value = value.substr(tmp[0].length); + } else if (tmp = value.match(/^"([^"]+)"/)) { + // Comment following the specification. + // Any character is allowed inside the comment except " itself. + curr_rule_tokens.push([tmp[1], 'comment', value.length ]); value = value.substr(tmp[0].length); - } else if (tmp = value.match(/^"([^"]*)"/)) { - // comment - curr_block_tokens.push([tmp[1], 'comment', value.length ]); + } else if (tmp = value.match(/^(["'„“‚‘’«「『])([^"'“”‘’»」』;|]*)(["'”“‘’»」』])/)) { + // Comments with error tolerance. + // The comments still have to be somewhat correct meaning + // the start and end quote signs used have to be + // appropriate. So “testing„ will not match as it is not a + // quote but rather something unknown which the user should + // fix first. + // console.log('Matched: ' + JSON.stringify(tmp)); + for (var pos = 1; pos <= 3; pos += 2) { + // console.log('Pos: ' + pos + ', substring: ' + tmp[pos]); + var correct_val = returnCorrectWordOrToken(tmp[pos], + value.length - (pos == 3 ? tmp[1].length + tmp[2].length : 0) + ); + if (typeof correct_val != 'string' && tmp[pos] != '"') { + throw formatLibraryBugMessage( + 'A character for error tolerance was allowed in the regular expression' + + ' but is not covered by word_error_correction' + + ' which is needed to format a proper message for the user.' + ); + } + } + curr_rule_tokens.push([tmp[2], 'comment', value.length ]); value = value.substr(tmp[0].length); } else if (value.match(/^;/)) { - // semicolon terminates block - // next tokens belong to a new block - all_tokens.push([ curr_block_tokens, last_block_fallback_terminated, value.length ]); + // semicolon terminates rule + // next tokens belong to a new rule + all_tokens.push([ curr_rule_tokens, last_rule_fallback_terminated, value.length ]); value = value.substr(1); - curr_block_tokens = []; - last_block_fallback_terminated = false; + curr_rule_tokens = []; + last_rule_fallback_terminated = false; } else if (value.match(/^\|\|/)) { - // || terminates block - // next tokens belong to a fallback block - if (curr_block_tokens.length == 0) + // || terminates rule + // Next tokens belong to a fallback rule. + if (curr_rule_tokens.length === 0) throw formatWarnErrorMessage(-1, value.length - 2, 'Rule before fallback rule does not contain anything useful'); - all_tokens.push([ curr_block_tokens, last_block_fallback_terminated, value.length ]); + all_tokens.push([ curr_rule_tokens, last_rule_fallback_terminated, value.length ]); + curr_rule_tokens = []; + // curr_rule_tokens = [ [ '||', 'rule separator', value.length ] ]; + // FIXME: Use this. Unknown bug needs to be solved in the process. value = value.substr(2); - curr_block_tokens = []; - last_block_fallback_terminated = true; + last_rule_fallback_terminated = true; } else if (value.match(/^(?:␣|\s)/)) { - // Using "␣" as space is not expected to be a normal mistake. Just ignore it to make using taginfo easier. + // Using "␣" as space is not expected to be a normal + // mistake. Just ignore it to make using taginfo easier. value = value.substr(1); } else if (tmp = value.match(/^\s+/)) { // whitespace is ignored @@ -2040,16 +2266,16 @@ // time separator if (value[0] == '.' && !done_with_warnings) parsing_warnings.push([ -1, value.length - 1, 'Please use ":" as hour/minute-separator' ]); - curr_block_tokens.push([ ':', 'timesep', value.length ]); + curr_rule_tokens.push([ ':', 'timesep', value.length ]); value = value.substr(1); } else { // other single-character tokens - curr_block_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length ]); + curr_rule_tokens.push([value[0].toLowerCase(), value[0].toLowerCase(), value.length ]); value = value.substr(1); } } - all_tokens.push([ curr_block_tokens, last_block_fallback_terminated ]); + all_tokens.push([ curr_rule_tokens, last_rule_fallback_terminated ]); return all_tokens; } @@ -2063,7 +2289,7 @@ * :returns: * * (valid) opening_hours sub string. * * object with [ internal_value, token_name ] if value is correct. - * * undefined if word could not be found (and thus not be corrected). + * * undefined if word could not be found (and thus is not be corrected). */ function returnCorrectWordOrToken(word, value_length) { for (var token_name in word_error_correction) { @@ -2088,12 +2314,11 @@ break; } if (typeof correct_abbr == 'undefined') { - throw 'Please file a bug for opening_hours.js.' - + ' Including the stacktrace.' + throw formatLibraryBugMessage('Including the stacktrace.'); } if (token_name != 'timevar') { // Everything else than timevar: - // E.g. 'Mo' are start with a upper case letter. + // E.g. 'Mo' start with a upper case letter. // It just looks better. correct_abbr = correct_abbr.charAt(0).toUpperCase() + correct_abbr.slice(1); @@ -2117,9 +2342,177 @@ * :returns: Warnings as list with one warning per element. */ function getWarnings(it) { - if (typeof it == 'object') { // getWarnings was called in a state without critical errors. We can do extended tests. + if (!done_with_warnings && typeof it == 'object') { + /* getWarnings was called in a state without critical errors. + * We can do extended tests. + */ + + /* Place all tests in this function if an additional (high + * level) test is added and this does not require to rewrite + * big parts of (sub) selector parsers only to get the + * position. If that is the case, then rather place the test + * code in the (sub) selector parser function directly. + */ + + var wide_range_selectors = [ 'year', 'month', 'week', 'holiday' ]; + var small_range_selectors = [ 'weekday', 'time', '24/7', 'state', 'comment']; + + // How many times was a selector_type used per rule? {{{ + var used_selectors = []; + var used_selectors_types_array = []; + var has_token = {}; + + for (var nrule = 0; nrule < new_tokens.length; nrule++) { + if (new_tokens[nrule][0].length === 0) continue; + // Rule does contain nothing useful e.g. second rule of '10:00-12:00;' (empty) which needs to be handled. + + var selector_start_end_type = [ 0, 0, undefined ], + prettified_group_value = []; + // console.log(new_tokens[nrule][0]); + + used_selectors[nrule] = {}; + used_selectors_types_array[nrule] = []; + + do { + selector_start_end_type = getSelectorRange(new_tokens[nrule][0], selector_start_end_type[1]); + // console.log(selector_start_end_type, new_tokens[nrule][0].length); + + if (selector_start_end_type[0] == selector_start_end_type[1] && + new_tokens[nrule][0][selector_start_end_type[0]][0] == '24/7' + ) { + has_token['24/7'] = true; + } + + if (typeof used_selectors[nrule][selector_start_end_type[2]] != 'object') { + used_selectors[nrule][selector_start_end_type[2]] = [ selector_start_end_type[1] ]; + } else { + used_selectors[nrule][selector_start_end_type[2]].push(selector_start_end_type[1]); + } + used_selectors_types_array[nrule].push(selector_start_end_type[2]); + + selector_start_end_type[1]++; + } while (selector_start_end_type[1] < new_tokens[nrule][0].length); + } + // console.log('used_selectors: ' + JSON.stringify(used_selectors, null, ' ')); + // }}} + + for (var nrule = 0; nrule < used_selectors.length; nrule++) { + + /* Check if more than one not connected selector of the same type is used in one rule {{{ */ + for (var selector_type in used_selectors[nrule]) { + // console.log(selector_type + ' use at: ' + used_selectors[nrule][selector_type].length); + if (used_selectors[nrule][selector_type].length > 1) { + parsing_warnings.push([nrule, used_selectors[nrule][selector_type][used_selectors[nrule][selector_type].length - 1], + 'You have used ' + used_selectors[nrule][selector_type].length + + (selector_type.match(/^(?:comment|state)/) ? + ' ' + selector_type + + (selector_type == 'state' ? ' keywords' : 's') + + ' in one rule.' + + ' You may only use one in one rule.' + : + ' not connected ' + selector_type + + (selector_type.match(/^(?:month|weekday)$/) ? 's' : ' ranges') + + ' in one rule.' + + ' This is probably an error.' + + ' Equal selector types can (and should) always be written in conjunction separated by comma or something.' + + ' Example for time ranges "12:00-13:00,15:00-18:00".' + + ' Example for weekdays "Mo-We,Fr".' + ) + + ' Rules can be separated by ";".' ] + ); + done_with_selector_reordering = true; // Correcting the selector order makes no sense if this kind of issue exists. + } + } + /* }}} */ + + /* Check if change default state rule is not the first rule {{{ */ + if ( typeof used_selectors[nrule].state === 'object' + && Object.keys(used_selectors[nrule]).length === 1 + ) { + + if (nrule !== 0) { + parsing_warnings.push([nrule, new_tokens[nrule][0].length - 1, + "This rule which changes the default state (which is closed) for all following rules is not the first rule." + + " The rule will overwrite all previous rules." + + " It can be legitimate to change the default state to open for example" + + " and then only specify for which times the facility is closed." + ]); + } + /* }}} */ + /* Check if a rule (with state open) has no time selector {{{ */ + } else if (typeof used_selectors[nrule].time === 'undefined') { + if ( ( typeof used_selectors[nrule].state === 'object' + && new_tokens[nrule][0][used_selectors[nrule].state[0]][0] === 'open' + && typeof used_selectors[nrule].comment === 'undefined' + ) || ( typeof used_selectors[nrule].comment === 'undefined' + && typeof used_selectors[nrule].state === 'undefined' + ) && + typeof used_selectors[nrule]['24/7'] === 'undefined' + ) { + + parsing_warnings.push([nrule, new_tokens[nrule][0].length - 1, + "This rule is not very explicit because there is no time selector being used." + + " Please add a time selector to this rule or use a comment to make it more explicit." + ]); + } + } + /* }}} */ + + /* Check if empty comment was given {{{ */ + if (typeof used_selectors[nrule].comment === 'object' + && new_tokens[nrule][0][used_selectors[nrule].comment[0]][0].length === 0 + ) { + + parsing_warnings.push([nrule, used_selectors[nrule].comment[0], + "You have used an empty comment." + + " Please either write something in the comment or use the keyword unknown instead." + ]); + } + /* }}} */ + + /* Check if rule with closed|off modifier is additional {{{ */ + /* FIXME: Enable this test. */ + if (typeof new_tokens[nrule][0][0] === 'object' + && new_tokens[nrule][0][0][0] === ',' + && new_tokens[nrule][0][0][1] === 'rule separator' + && typeof used_selectors[nrule].state === 'object' + && ( + new_tokens[nrule][0][used_selectors[nrule].state[0]][0] === 'closed' + || new_tokens[nrule][0][used_selectors[nrule].state[0]][0] === 'off' + ) + ) { + + // parsing_warnings.push([nrule, new_tokens[nrule][0].length - 1, + // "This rule will be evaluated as closed but it was specified as additional rule." + // + " It is enough to specify this rule as normal rule using the \";\" character." + // + " See https://wiki.openstreetmap.org/wiki/Key:opening_hours:specification#explain:rule_modifier:closed." + // ]); + } + /* }}} */ + + /* Check for valid use of {{{ */ + for (var i = 0; i < used_selectors_types_array[nrule].length - 1; i++) { + var selector_type = used_selectors_types_array[nrule][i]; + var next_selector_type = used_selectors_types_array[nrule][i+1]; + if ( ( wide_range_selectors.indexOf(selector_type) != -1 + && wide_range_selectors.indexOf(next_selector_type) != -1 + ) || ( small_range_selectors.indexOf(selector_type) != -1 + && small_range_selectors.indexOf(next_selector_type) != -1) + ) { + + if (new_tokens[nrule][0][used_selectors[nrule][selector_type][0]][0] == ':') { + parsing_warnings.push([nrule, used_selectors[nrule][selector_type][0], + "You have used the optional symbol in the wrong place." + + " Please check the syntax specification to see where it could be used or remove it." + ]); + } + } + } + /* }}} */ + + } - // Check if 24/7 is used and it does not mean 24/7 because there are other blocks. + /* Check if 24/7 is used and it does not mean 24/7 because there are other rules {{{ */ var has_advanced = it.advance(); if (has_advanced === true && has_token['24/7'] && !done_with_warnings) { @@ -2129,17 +2522,234 @@ + ' for this rule and then write your exceptions which should achieve the same goal and is more clear' + ' e.g. "open; Mo 12:00-14:00 off".']); } + /* }}} */ + + prettifyValue(); } + done_with_warnings = true; var warnings = []; + // FIXME: Sort based on parsing_warnings[1], tricky … for (var i = 0; i < parsing_warnings.length; i++) { warnings.push( formatWarnErrorMessage(parsing_warnings[i][0], parsing_warnings[i][1], parsing_warnings[i][2]) ); } return warnings; } + + /* Helpers for getWarnings {{{ */ + + /* Check if token is the begin of a selector and why. {{{ + * + * :param tokens: List of token objects. + * :param at: Position where to start. + * :returns: + * * false the current token is not the begin of a selector. + * * Position in token array from where the decision was made that + * the token is the start of a selector. + */ + function tokenIsTheBeginOfSelector(tokens, at) { + if (typeof tokens[at][3] == 'string') { + return 3; + } else if (tokens[at][1] == 'comment' + || tokens[at][1] == 'state' + || tokens[at][1] == '24/7' + || tokens[at][1] == 'rule separator' + ){ + + return 1; + } else { + return false; + } + } + /* }}} */ + + /* Get start and end position of a selector. {{{ + * For example this value 'Mo-We,Fr' will return the position of the + * token lexeme 'Mo' and 'Fr' e.g. there indexes [ 0, 4 ] in the + * selector array of tokens. + * + * :param tokens: List of token objects. + * :param at: Position where to start. + * :returns: Array: + * 0. Index of first token in selector array of tokens. + * 1. Index of last token in selector array of tokens. + * 2. Selector type. + */ + function getSelectorRange(tokens, at) { + var selector_start = at, + selector_end, + pos_in_token_array; + + for (; selector_start >= 0; selector_start--) { + pos_in_token_array = tokenIsTheBeginOfSelector(tokens, selector_start); + if (pos_in_token_array) + break; + } + selector_end = selector_start; + + if (pos_in_token_array === 1) { + // Selector consists of a single token. + + // Include tailing colon. + if (selector_end + 1 < tokens.length && tokens[selector_end + 1][0] == ':') + selector_end++; + + return [ selector_start, selector_end, tokens[selector_start][pos_in_token_array] ]; + } + + for (selector_end++; selector_end < tokens.length ; selector_end++) { + if (tokenIsTheBeginOfSelector(tokens, selector_end)) + return [ selector_start, selector_end - 1, tokens[selector_start][pos_in_token_array] ]; + } + + return [ selector_start, selector_end - 1, tokens[selector_start][pos_in_token_array] ]; + } + /* }}} */ + /* }}} */ + /* }}} */ + + /* Prettify raw value from user. {{{ + * The value is generated by putting the tokens back together to a string. + * + * :param argument_hash: Hash which can contain: + * 'conf': Configuration hash. + * 'get_internals: If true export internal data structures. + * 'rule_index: Only prettify the rule with this index. + * :returns: Prettified value string or object if get_internals is true. + */ + function prettifyValue(argument_hash) { + var user_conf = {}, + get_internals = false, + rule_index; + if (typeof argument_hash != 'undefined') { + + if (typeof argument_hash.conf === 'object') + user_conf = argument_hash.conf; + + if (typeof argument_hash.rule_index === 'number') + rule_index = argument_hash.rule_index; + + if (argument_hash.get_internals === true) + get_internals = true; + } + + for (var key in default_prettify_conf) { + if (typeof user_conf[key] == 'undefined') + user_conf[key] = default_prettify_conf[key]; + } + + prettified_value = ''; + var prettified_value_array = []; + + for (var nrule = 0; nrule < new_tokens.length; nrule++) { + if (new_tokens[nrule][0].length === 0) continue; + // Rule does contain nothing useful e.g. second rule of '10:00-12:00;' (empty) which needs to be handled. + + if (typeof rule_index == 'number') { + if (rule_index != nrule) continue; + } else { + if (nrule !== 0) + prettified_value += ( + new_tokens[nrule][1] + ? user_conf.rule_sep_string + '|| ' + : ( + new_tokens[nrule][0][0][1] == 'rule separator' + ? ',' + : ( + user_conf.print_semicolon + ? ';' + : '' + ) + ) + + user_conf.rule_sep_string); + } + + var selector_start_end_type = [ 0, 0, undefined ], + prettified_group_value = []; + // console.log(new_tokens[nrule][0]); + var count = 0; + + + do { + selector_start_end_type = getSelectorRange(new_tokens[nrule][0], selector_start_end_type[1]); + // console.log(selector_start_end_type, new_tokens[nrule][0].length, count); + + if (count > 50) { + throw formatLibraryBugMessage('infinite loop'); + } + + if (selector_start_end_type[2] != 'rule separator') { + prettified_group_value.push( + [ + selector_start_end_type, + prettifySelector( + new_tokens[nrule][0], + selector_start_end_type[0], + selector_start_end_type[1], + selector_start_end_type[2], + user_conf + ), + ] + ); + } + + selector_start_end_type[1]++; + count++; + // console.log(selector_start_end_type, new_tokens[nrule][0].length, count); + } while (selector_start_end_type[1] < new_tokens[nrule][0].length); + // console.log('Prettified value: ' + JSON.stringify(prettified_group_value, null, ' ')); + var not_sorted_prettified_group_value = prettified_group_value.slice(); + + if (!done_with_selector_reordering) { + prettified_group_value.sort( + function (a, b) { + var selector_order = [ 'year', 'month', 'week', 'holiday', 'weekday', 'time', '24/7', 'state', 'comment']; + return selector_order.indexOf(a[0][2]) - selector_order.indexOf(b[0][2]); + } + ); + } + var old_prettified_value_length = prettified_value.length; + + prettified_value += prettified_group_value.map( + function (array) { + return array[1]; + } + ).join(' '); + + prettified_value_array.push( prettified_group_value ); + + if (!done_with_selector_reordering_warnings) { + for (var i = 0, l = not_sorted_prettified_group_value.length; i < l; i++) { + if (not_sorted_prettified_group_value[i] != prettified_group_value[i]) { + // console.log(i + ': ' + prettified_group_value[i][0][2]); + var length = i + old_prettified_value_length; // i: Number of spaces in string. + for (var x = 0; x <= i; x++) { + length += prettified_group_value[x][1].length; + // console.log('Length: ' + length + ' ' + prettified_group_value[x][1]); + } + // console.log(length); + parsing_warnings.push([ prettified_value, length, + 'The selector "' + prettified_group_value[i][0][2] + '" was switched with' + + ' the selector "' + not_sorted_prettified_group_value[i][0][2] + '"' + + ' for readablitity and compatibiltity reasons.' + ]); + } + } + } + } + + done_with_selector_reordering_warnings = true; + // console.log(JSON.stringify(prettified_value_array, null, ' ')); + + if (get_internals) { + return [ prettified_value_array, new_tokens ]; + } else { + return prettified_value; + } + } // }}} - /* Function to check token array for specific pattern {{{ + /* Check selector array of tokens for specific token name pattern. {{{ * * :param tokens: List of token objects. * :param at: Position at which the matching should begin. @@ -2172,26 +2782,23 @@ if (typeof res[1] === 'undefined') return res; return [ res[0], new Date(res[1].getTime() - shift) ]; - } + }; } // }}} /* Top-level parser {{{ * * :param tokens: List of token objects. - * :param at: Position at which the matching should begin. + * :param at: Position where to start. * :param selectors: Reference to selector object. - * :param nblock: Block number starting with 0. - * :param conf: Configuration for prettifyValue. + * :param nrule: Rule number starting with 0. * :returns: See selector code. */ - function parseGroup(tokens, at, selectors, nblock, conf) { - var prettified_group_value = ''; - used_subparsers = { 'time ranges': [ ] }; + function parseGroup(tokens, at, selectors, nrule) { + var rule_modifier_specified = false; // console.log(tokens); // useful for debugging of tokenize while (at < tokens.length) { - var old_at = at; // console.log('Parsing at position', at +':', tokens[at]); if (matchTokens(tokens, at, 'weekday')) { at = parseWeekdayRange(tokens, at, selectors); @@ -2211,143 +2818,99 @@ || matchTokens(tokens, at, 'year', 'month', 'number') || matchTokens(tokens, at, 'year', 'event') || matchTokens(tokens, at, 'event')) { - at = parseMonthdayRange(tokens, at, nblock); + + at = parseMonthdayRange(tokens, at, nrule); week_stable = false; } else if (matchTokens(tokens, at, 'year')) { at = parseYearRange(tokens, at); week_stable = false; } else if (matchTokens(tokens, at, 'month')) { at = parseMonthRange(tokens, at); - // week_stable = false; // decided based on actual values + // week_stable = false; // Decided based on the actual value/tokens. } else if (matchTokens(tokens, at, 'week')) { - at = parseWeekRange(tokens, at + 1); - week_stable = false; + tokens[at][3] = 'week'; + at = parseWeekRange(tokens, at); + + } else if (at !== 0 && at != tokens.length - 1 && tokens[at][0] == ':') { + /* Ignore colon if they appear somewhere else than as time separator. + * Except the start or end of the value. + * This provides compatibility with the syntax proposed by Netzwolf: + * http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification#separator_for_readability + * Check for valid use of is implemented in function getWarnings(). + */ + + if (!done_with_warnings && matchTokens(tokens, at-1, 'holiday')) + parsing_warnings.push([nrule, at, 'Please don’t use ":" after ' + tokens[at-1][1] + '.']); - // if (prettified_group_value[-1] != ' ') - // prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 1); - } else if (at != 0 && at != tokens.length - 1 && tokens[at][0] == ':') { - // Ignore colon if they appear somewhere else than as time separator. - // Except the start or end of the value. - // This provides compatibility with the syntax proposed by Netzwolf: - // http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification - if (!done_with_warnings && (matchTokens(tokens, at-1, 'weekday') || matchTokens(tokens, at-1, 'holiday'))) - parsing_warnings.push([nblock, at, 'Please don’t use ":" after ' + tokens[at-1][1] + '.']); - - if (prettified_group_value[-1] != ' ') - prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 1); at++; } else if (matchTokens(tokens, at, 'number', 'timesep') || matchTokens(tokens, at, 'timevar') || matchTokens(tokens, at, '(', 'timevar') || matchTokens(tokens, at, 'number', '-')) { + at = parseTimeRange(tokens, at, selectors, false); - used_subparsers['time ranges'].push(at); - } else if (matchTokens(tokens, at, 'closed')) { - selectors.meaning = false; - at++; - if (matchTokens(tokens, at, ',')) // additional block - at = [ at + 1 ]; + } else if (matchTokens(tokens, at, 'state')) { - if (typeof used_subparsers['state keywords'] != 'object') - used_subparsers['state keywords'] = [ at ]; - else - used_subparsers['state keywords'].push(at); - } else if (matchTokens(tokens, at, 'open')) { - selectors.meaning = true; - at++; - if (matchTokens(tokens, at, ',')) // additional block - at = [ at + 1 ]; + if (tokens[at][0] == 'open') { + selectors.meaning = true; + } else if (tokens[at][0] == 'closed' || tokens[at][0] == 'off') { + selectors.meaning = false; + } else { + selectors.meaning = false; + selectors.unknown = true; + } - if (typeof used_subparsers['state keywords'] != 'object') - used_subparsers['state keywords'] = [ at ]; - else - used_subparsers['state keywords'].push(at); - } else if (matchTokens(tokens, at, 'unknown')) { - selectors.meaning = false; - selectors.unknown = true; + rule_modifier_specified = true; at++; - if (matchTokens(tokens, at, ',')) // additional block + if (typeof tokens[at] == 'object' && tokens[at][0] == ',') // additional rule at = [ at + 1 ]; - if (typeof used_subparsers['state keywords'] != 'object') - used_subparsers['state keywords'] = [ at ]; - else - used_subparsers['state keywords'].push(at); } else if (matchTokens(tokens, at, 'comment')) { selectors.comment = tokens[at][0]; - if (at > 0) { - if (!matchTokens(tokens, at - 1, 'open') - && !matchTokens(tokens, at - 1, 'closed')) { - // Then it is unknown. Either with unknown explicitly - // specified or just a comment behind. - selectors.meaning = false; - selectors.unknown = true; - } - } else { // block starts with comment - selectors.time.push(function(date) { return [true]; }); - // Not needed. If there is no selector it automatically matches everything. - // WRONG: This only works if there is no other selector in this selector group ... + if (!rule_modifier_specified) { + // Then it is unknown. Either with unknown explicitly + // specified or just a comment. selectors.meaning = false; selectors.unknown = true; } + + rule_modifier_specified = true; at++; - if (matchTokens(tokens, at, ',')) // additional block + if (typeof tokens[at] == 'object' && tokens[at][0] == ',') // additional rule at = [ at + 1 ]; - - if (typeof used_subparsers['comments'] != 'object') - used_subparsers['comments'] = [ at ]; - else - used_subparsers['comments'].push(at); + } else if ((at === 0 || at == tokens.length - 1) && matchTokens(tokens, at, 'rule separator')) { + at++; + console.log("value: " + nrule); + // throw formatLibraryBugMessage('Not implemented yet.'); } else { var warnings = getWarnings(); - throw formatWarnErrorMessage(nblock, at, 'Unexpected token: "' + tokens[at][1] + throw formatWarnErrorMessage(nrule, at, 'Unexpected token: "' + tokens[at][1] + '" This means that the syntax is not valid at that point or it is currently not supported.') + (warnings ? ' ' + warnings.join('; ') : ''); } - if (typeof conf != 'undefined') { - // 'Mo: 12:00-13:00' -> 'Mo 12:00-13:00' - if (used_subparsers['time ranges'] && old_at > 1 && tokens[old_at-1][0] == ':' - && matchTokens(tokens, old_at - 2, 'weekday')) - prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 2) + ' '; - - // 'week 1, week 3' -> 'week 1,week 3' - if (prettified_group_value.substr(prettified_group_value.length -2, 2) == ', ' - && matchTokens(tokens, old_at, 'week')) - prettified_group_value = prettified_group_value.substring(0, prettified_group_value.length - 1); - - prettified_group_value += prettifySelector(tokens, old_at, at, conf, used_subparsers['time ranges'].length); - } - - if (typeof at == 'object') // additional block + if (typeof at == 'object') { // additional rule + tokens[at[0] - 1][1] = 'rule separator'; break; + } } - prettified_value += prettified_group_value.replace(/\s+$/, ''); + return at; + } - if (!done_with_warnings) { - for (var subparser_name in used_subparsers) { - if (used_subparsers[subparser_name].length > 1) { - parsing_warnings.push([nblock, used_subparsers[subparser_name][used_subparsers[subparser_name].length - 1] - 1, - 'You have used ' + used_subparsers[subparser_name].length - + (subparser_name.match(/^(?:comments|state keywords)/) ? - ' ' + subparser_name + ' in one rule.' - + ' You may only use one in one rule.' - : - ' not connected ' + subparser_name + ' in one rule.' - + ' This is probably an error.' - + ' Equal selector types can (and should) always be written in conjunction separated by comma or something.' - + ' Example for time ranges "12:00-13:00,15:00-18:00".' - + ' Example for weekdays "Mo-We,Fr".' - ) - + ' Rules can be separated by ";".' ] - ); + function get_last_token_pos_in_token_group(tokens, at, last_at) { + for (at++; at < last_at; at++) { + if (typeof tokens[at] != 'undefined') { + if (typeof tokens[at][3] == 'string' + || tokens[at][1] == 'comment' + || tokens[at][1] == 'state'){ + + return at - 1; } } } - - return at; + return last_at; } // }}} @@ -2369,11 +2932,11 @@ /* For given date, returns date moved to the specific day of week {{{ * * :param date: Date object. - * :param day: Integer number for day of week. Starting with zero (Sunday). + * :param weekday: Integer number for day of week. Starting with zero (Sunday). * :returns: Moved date object. */ - function dateAtNextWeekday(date, day) { - var delta = day - date.getDay(); + function dateAtNextWeekday(date, weekday) { + var delta = weekday - date.getDay(); return new Date(date.getFullYear(), date.getMonth(), date.getDate() + delta + (delta < 0 ? 7 : 0)); } // }}} @@ -2420,13 +2983,13 @@ } else if (matchTokens(tokens, at, '-', 'number')) { // Negative number func(-tokens[at+1][0], -tokens[at+1][0], at); - at += 2 + at += 2; } else if (matchTokens(tokens, at, 'number')) { // Single number func(tokens[at][0], tokens[at][0], at); at++; } else { - throw formatWarnErrorMessage(nblock, at + matchTokens(tokens, at, '-'), + throw formatWarnErrorMessage(nrule, at + matchTokens(tokens, at, '-'), 'Unexpected token in number range: ' + tokens[at][1]); } @@ -2452,56 +3015,56 @@ var endat = parseNumRange(tokens, at, function(from, to, at) { // bad number - if (from == 0 || from < -5 || from > 5) - throw formatWarnErrorMessage(nblock, at, + if (from === 0 || from < -5 || from > 5) + throw formatWarnErrorMessage(nrule, at, 'Number between -5 and 5 (except 0) expected'); if (from == to) { - if (number != 0) - throw formatWarnErrorMessage(nblock, at, + if (number !== 0) + throw formatWarnErrorMessage(nrule, at, 'You can not use more than one constrained weekday in a month range'); number = from; } else { - throw formatWarnErrorMessage(nblock, at+2, + throw formatWarnErrorMessage(nrule, at+2, 'You can not use a range of constrained weekdays in a month range'); } }); if (!matchTokens(tokens, endat, ']')) - throw formatWarnErrorMessage(nblock, endat, '"]" expected.'); + throw formatWarnErrorMessage(nrule, endat, '"]" expected.'); return [ number, endat + 1 ]; } // }}} // Check if period is ok. Period 0 or 1 don’t make much sense. - /* List parser for constrained weekdays in month range {{{ - * e.g. Su[-1] which selects the last Sunday of the month. - * - * :param tokens: List of token objects. - * :param at: Position where to start. - * :returns: Array: - * 0. Constrained weekday number. - * 1. Position at which the token does not belong to the list any more (after ']' token). - */ function checkPeriod(at, period, period_type, parm_string) { if (done_with_warnings) return; if (period === 0) { - throw formatWarnErrorMessage(nblock, at, + throw formatWarnErrorMessage(nrule, at, 'You can not use '+ period_type +' ranges with period equals zero.'); } else if (period === 1) { if (typeof parm_string == 'string' && parm_string == 'no_end_year') - parsing_warnings.push([nblock, at, + parsing_warnings.push([nrule, at, 'Please don’t use '+ period_type +' ranges with period equals one.' + ' If you want to express that a facility is open starting from a year without limit use "+".']); else - parsing_warnings.push([nblock, at, + parsing_warnings.push([nrule, at, 'Please don’t use '+ period_type +' ranges with period equals one.']); } } + /* Get date moved to constrained weekday (and moved for add_days. {{{ + * E.g. used for 'Aug Su[-1] -1 day'. + * + * :param year: Year as integer. + * :param month: Month as integer starting with zero. + * :param weekday: Integer number for day of week. Starting with zero (Sunday). + * :param constrained_weekday: Position where to start. + * :returns: Date object. + */ function getDateForConstrainedWeekday(year, month, weekday, constrained_weekday, add_days) { var tmp_date = dateAtNextWeekday( new Date(year, month + (constrained_weekday[0] > 0 ? 0 : 1), 1), weekday); @@ -2513,36 +3076,16 @@ return tmp_date; } + // }}} - function formatWarnErrorMessage(nblock, at, message) { - var pos = 0; - if (nblock == -1) { // Usage of block index not required because we do have access to value.length. - pos = value.length - at; - } else { // Issue accrued at a later time, position in string needs to be reconstructed. - if (typeof tokens[nblock][0][at] == 'undefined') { - pos = value.length; - if (typeof tokens[nblock][0][tokens[nblock][0].length-1] != 'undefined') { - // pos -= tokens[nblock][0][tokens[nblock][0].length-1][2]; - console.warn("FIXME"); - } - } else { - pos = value.length; - if (typeof tokens[nblock][0][at+1] != 'undefined') { - pos -= tokens[nblock][0][at+1][2]; - } else if (typeof tokens[nblock][2] != 'undefined') { - pos -= tokens[nblock][2]; - } else { - } - } - } - return value.substring(0, pos) + ' <--- (' + message + ')'; - } - - // check if date is valid - function isValidDate(month, day, nblock, at) { - // month == 0 is Jan - - // May use this instead. Does not say, what is wrong as good was implementation below. + /* Check if date is valid. {{{ + * + * :param month: Month as integer starting with zero. + * :param date: Day of month as integer. + * :returns: undefined. There is no real return value. This function just throws an exception if something is wrong. + */ + function checkIfDateIsValid(month, day, nrule, at) { + // May use this instead. The problem is that this does not give feedback as precise as the code which is used in this function. // var testDate = new Date(year, month, day); // if (testDate.getDate() != day || testDate.getMonth() != month || testDate.getFullYear() != year) { // console.error('date not valid'); @@ -2550,23 +3093,35 @@ // https://en.wikipedia.org/wiki/Month#Julian_and_Gregorian_calendars if (day < 1 || day > 31) - throw formatWarnErrorMessage(nblock, at, 'Day must be between 1 and 31.'); + throw formatWarnErrorMessage(nrule, at, 'Day must be between 1 and 31.'); if ((month==3 || month==5 || month==8 || month==10) && day==31) - throw formatWarnErrorMessage(nblock, at, 'Month ' + months[month] + " doesn't have 31 days.!"); + throw formatWarnErrorMessage(nrule, at, 'Month ' + months[month] + " doesn't have 31 days.!"); if (month == 1 && day == 30) - throw formatWarnErrorMessage(nblock, at, 'Month ' + months[1]+ " either has 28 or 29 days (leap years)."); + throw formatWarnErrorMessage(nrule, at, 'Month ' + months[1]+ " either has 28 or 29 days (leap years)."); } // }}} + // }}} - // Time range parser (10:00-12:00,14:00-16:00) {{{ - // - // extended_open_end: