Quellcode durchsuchen

Merge branch 'master' into lib-change

Romulus Tsai 蔡仲明 vor 5 Jahren
Ursprung
Commit
c3458855bd
100 geänderte Dateien mit 3069 neuen und 9756 gelöschten Zeilen
  1. 5 0
      .babelrc
  2. 3 3
      .devcontainer/Dockerfile
  3. 1 0
      .eslintrc.json
  4. 18 5
      .future-snap/broken-snapcraft.yaml
  5. 1 1
      .future-snap/snapcraft.yaml
  6. 10 0
      .gitpod.Dockerfile
  7. 4 0
      .gitpod.yml
  8. 9 9
      .meteor/packages
  9. 1 1
      .meteor/release
  10. 25 26
      .meteor/versions
  11. 0 20
      .snap-meteor-1.8/.meteor/.finished-upgraders
  12. 0 2
      .snap-meteor-1.8/.meteor/.gitignore
  13. 0 7
      .snap-meteor-1.8/.meteor/.id
  14. 0 0
      .snap-meteor-1.8/.meteor/cordova-plugins
  15. 0 99
      .snap-meteor-1.8/.meteor/packages
  16. 0 2
      .snap-meteor-1.8/.meteor/platforms
  17. 0 1
      .snap-meteor-1.8/.meteor/release
  18. 0 198
      .snap-meteor-1.8/.meteor/versions
  19. 0 914
      .snap-meteor-1.8/cfs_access-point.txt
  20. 0 238
      .snap-meteor-1.8/export.js
  21. 0 584
      .snap-meteor-1.8/ldap.js
  22. 0 149
      .snap-meteor-1.8/oidc_server.js
  23. 0 5184
      .snap-meteor-1.8/package-lock.json
  24. 0 73
      .snap-meteor-1.8/package.json
  25. 0 853
      .snap-meteor-1.8/wekanCreator.js
  26. 2 2
      .travis.yml
  27. 501 1
      CHANGELOG.md
  28. 11 3
      Dockerfile
  29. 77 0
      Dockerfile.arm64v8
  30. 4 2
      README.md
  31. 1 1
      Stackerfile.yml
  32. 6 0
      client/00-startup.js
  33. 157 190
      client/components/activities/activities.jade
  34. 74 41
      client/components/activities/activities.js
  35. 1 1
      client/components/boards/boardArchive.js
  36. 3 7
      client/components/boards/boardBody.js
  37. 0 14
      client/components/boards/boardHeader.jade
  38. 0 20
      client/components/boards/boardHeader.js
  39. 4 4
      client/components/boards/boardsList.jade
  40. 72 8
      client/components/boards/boardsList.js
  41. 17 3
      client/components/boards/boardsList.styl
  42. 1 1
      client/components/cards/attachments.jade
  43. 68 5
      client/components/cards/cardDetails.jade
  44. 90 19
      client/components/cards/cardDetails.js
  45. 14 3
      client/components/cards/cardDetails.styl
  46. 2 1
      client/components/cards/checklists.jade
  47. 20 11
      client/components/cards/checklists.js
  48. 4 1
      client/components/cards/checklists.styl
  49. 2 0
      client/components/cards/labels.styl
  50. 6 2
      client/components/cards/minicard.jade
  51. 1 1
      client/components/cards/minicard.styl
  52. 16 1
      client/components/cards/subtasks.js
  53. 0 3
      client/components/import/import.jade
  54. 4 18
      client/components/lists/list.js
  55. 3 3
      client/components/lists/list.styl
  56. 19 3
      client/components/lists/listBody.js
  57. 1 2
      client/components/lists/listHeader.jade
  58. 64 128
      client/components/main/editor.js
  59. 7 0
      client/components/main/header.jade
  60. 3 3
      client/components/main/header.styl
  61. 10 4
      client/components/main/layouts.jade
  62. 7 0
      client/components/main/layouts.js
  63. 5 0
      client/components/main/popup.styl
  64. 10 0
      client/components/notifications/notification.jade
  65. 28 0
      client/components/notifications/notification.js
  66. 57 0
      client/components/notifications/notification.styl
  67. 53 0
      client/components/notifications/notificationIcon.jade
  68. 5 0
      client/components/notifications/notifications.jade
  69. 32 0
      client/components/notifications/notifications.js
  70. 17 0
      client/components/notifications/notifications.styl
  71. 20 0
      client/components/notifications/notificationsDrawer.jade
  72. 53 0
      client/components/notifications/notificationsDrawer.js
  73. 69 0
      client/components/notifications/notificationsDrawer.styl
  74. 1 1
      client/components/rules/actions/boardActions.js
  75. 53 1
      client/components/settings/peopleBody.jade
  76. 95 0
      client/components/settings/peopleBody.js
  77. 1 1
      client/components/settings/settingBody.js
  78. 21 12
      client/components/sidebar/sidebar.jade
  79. 16 2
      client/components/sidebar/sidebar.js
  80. 18 0
      client/components/sidebar/sidebarFilters.jade
  81. 5 0
      client/components/sidebar/sidebarFilters.js
  82. 6 32
      client/components/swimlanes/swimlanes.js
  83. 17 8
      client/components/users/userHeader.jade
  84. 37 3
      client/components/users/userHeader.js
  85. 10 0
      client/lib/datepicker.js
  86. 9 1
      client/lib/filter.js
  87. 21 11
      client/lib/keyboard.js
  88. 21 0
      config/router.js
  89. 20 3
      docker-compose.yml
  90. 7 0
      helm/wekan/README.md
  91. 6 0
      helm/wekan/templates/deployment.yaml
  92. 23 0
      helm/wekan/templates/route.yaml
  93. 4 0
      helm/wekan/templates/serviceaccount.yaml
  94. 8 2
      helm/wekan/values.yaml
  95. 58 30
      i18n/ar.i18n.json
  96. 33 5
      i18n/bg.i18n.json
  97. 33 5
      i18n/br.i18n.json
  98. 46 18
      i18n/ca.i18n.json
  99. 41 13
      i18n/cs.i18n.json
  100. 761 733
      i18n/da.i18n.json

+ 5 - 0
.babelrc

@@ -0,0 +1,5 @@
+{ 
+  "presets": [ 
+    "@babel/preset-stage-3" 
+  ]
+}

+ 3 - 3
.devcontainer/Dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:disco
+FROM ubuntu:rolling
 LABEL maintainer="sgr"
 LABEL maintainer="sgr"
 
 
 ENV BUILD_DEPS="gnupg gosu bsdtar wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
 ENV BUILD_DEPS="gnupg gosu bsdtar wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
@@ -6,8 +6,8 @@ ENV DEBIAN_FRONTEND=noninteractive
 
 
 ENV \
 ENV \
     DEBUG=false \
     DEBUG=false \
-    NODE_VERSION=8.17.0 \
-    METEOR_RELEASE=1.8.1 \
+    NODE_VERSION=12.16.3 \
+    METEOR_RELEASE=1.10.2 \
     USE_EDGE=false \
     USE_EDGE=false \
     METEOR_EDGE=1.5-beta.17 \
     METEOR_EDGE=1.5-beta.17 \
     NPM_VERSION=latest \
     NPM_VERSION=latest \

+ 1 - 0
.eslintrc.json

@@ -11,6 +11,7 @@
     "browser": true,
     "browser": true,
     "meteor": true
     "meteor": true
   },
   },
+  "parser": "babel-eslint",
   "parserOptions": {
   "parserOptions": {
     "ecmaVersion": 2018,
     "ecmaVersion": 2018,
     "sourceType": "module"
     "sourceType": "module"

+ 18 - 5
.snap-meteor-1.8/snapcraft.yaml → .future-snap/broken-snapcraft.yaml

@@ -65,9 +65,9 @@ apps:
 
 
 parts:
 parts:
     mongodb:
     mongodb:
-        source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
+        source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.6.tgz
         plugin: dump
         plugin: dump
-        stage-packages: [libssl1.0.0]
+        stage-packages: [libssl1.0.0, libcurl3]
         filesets:
         filesets:
             mongo:
             mongo:
                 - usr
                 - usr
@@ -81,19 +81,20 @@ parts:
     wekan:
     wekan:
         source: .
         source: .
         plugin: nodejs
         plugin: nodejs
-        node-engine: 8.17.0
+        node-engine: 12.16.3
         node-packages:
         node-packages:
             - node-gyp
             - node-gyp
             - node-pre-gyp
             - node-pre-gyp
-            - fibers@2.0.0
+            - fibers
         build-packages:
         build-packages:
             - ca-certificates
             - ca-certificates
             - apt-utils
             - apt-utils
             - python
             - python
-#            - python3
+            - python3
             - g++
             - g++
             - capnproto
             - capnproto
             - curl
             - curl
+            - libcurl3
             - execstack
             - execstack
             - nodejs
             - nodejs
             - npm
             - npm
@@ -104,6 +105,18 @@ parts:
             rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
             rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
             # Create the OpenAPI specification
             # Create the OpenAPI specification
             rm -rf .build
             rm -rf .build
+            ## Use Meteor 1.8.x on Snap
+            #rm -rf .meteor
+            #mv .snap-meteor-1.8/.meteor .
+            #mv .snap-meteor-1.8/package.json .
+            #mv .snap-meteor-1.8/package-lock.json .
+            ## Meteor 1.9.x has changes to Buffer() => Buffer.alloc(), so reverting those
+            #mv .snap-meteor-1.8/cfs_access-point.txt fix-download-unicode/
+            #mv .snap-meteor-1.8/export.js models/
+            #mv .snap-meteor-1.8/wekanCreator.js models/
+            #mv .snap-meteor-1.8/ldap.js packages/wekan-ldap/server/ldap.js
+            #mv .snap-meteor-1.8/oidc_server.js packages/wekan-oidc/oidc_server.js
+            rm -rf .snap-meteor-1.8
             #mkdir -p .build/python
             #mkdir -p .build/python
             #cd .build/python
             #cd .build/python
             #git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
             #git clone --depth 1 -b master https://github.com/Kronuz/esprima-python

+ 1 - 1
.snap-meteor-1.8/future/snapcraft.yaml → .future-snap/snapcraft.yaml

@@ -83,7 +83,7 @@ parts:
     wekan:
     wekan:
         source: .
         source: .
         plugin: nodejs
         plugin: nodejs
-        node-engine: 12.14.1
+        node-engine: 12.14.3
         node-packages:
         node-packages:
             - node-gyp
             - node-gyp
             - node-pre-gyp
             - node-pre-gyp

+ 10 - 0
.gitpod.Dockerfile

@@ -0,0 +1,10 @@
+FROM gitpod/workspace-mongodb
+                    
+USER gitpod
+
+# Install custom tools, runtime, etc. using apt-get
+# For example, the command below would install "bastet" - a command line tetris clone:
+#
+# RUN sudo apt-get -q update && #     sudo apt-get install -yq bastet && #     sudo rm -rf /var/lib/apt/lists/*
+#
+# More information: https://www.gitpod.io/docs/config-docker/

+ 4 - 0
.gitpod.yml

@@ -0,0 +1,4 @@
+tasks:
+  - init: npm install
+image:
+  file: .gitpod.Dockerfile

+ 9 - 9
.meteor/packages

@@ -6,10 +6,11 @@
 meteor-base@1.4.0
 meteor-base@1.4.0
 
 
 # Build system
 # Build system
-ecmascript@0.14.2
+ecmascript@0.14.3
 standard-minifier-css@1.6.0
 standard-minifier-css@1.6.0
 standard-minifier-js@2.6.0
 standard-minifier-js@2.6.0
 mquandalle:jade
 mquandalle:jade
+coffeescript@2.4.1!
 
 
 # Polyfills
 # Polyfills
 es5-shim@4.8.0
 es5-shim@4.8.0
@@ -22,7 +23,7 @@ dburles:collection-helpers
 idmontie:migrations
 idmontie:migrations
 matb33:collection-hooks
 matb33:collection-hooks
 matteodem:easy-search
 matteodem:easy-search
-mongo@1.8.0
+mongo@1.10.0
 mquandalle:collection-mutations
 mquandalle:collection-mutations
 
 
 # Account system
 # Account system
@@ -37,13 +38,12 @@ wekan-accounts-oidc
 # Utilities
 # Utilities
 check@1.3.1
 check@1.3.1
 jquery@1.11.10
 jquery@1.11.10
-random@1.1.0
+random@1.2.0
 reactive-dict@1.3.0
 reactive-dict@1.3.0
 session@1.2.0
 session@1.2.0
 tracker@1.2.0
 tracker@1.2.0
 underscore@1.0.10
 underscore@1.0.10
 3stack:presence
 3stack:presence
-alethes:pages
 arillo:flow-router-helpers
 arillo:flow-router-helpers
 audit-argument-checks@1.0.7
 audit-argument-checks@1.0.7
 kadira:blaze-layout
 kadira:blaze-layout
@@ -67,15 +67,15 @@ templates:tabs
 verron:autosize
 verron:autosize
 simple:json-routes
 simple:json-routes
 rajit:bootstrap3-datepicker
 rajit:bootstrap3-datepicker
-shell-server@0.4.0
+shell-server@0.5.0
 simple:rest-accounts-password
 simple:rest-accounts-password
 useraccounts:core
 useraccounts:core
 email@1.2.3
 email@1.2.3
 horka:swipebox
 horka:swipebox
-dynamic-import@0.5.1
+dynamic-import@0.5.2
 staringatlights:fast-render
 staringatlights:fast-render
 
 
-accounts-password@1.5.2
+accounts-password@1.6.0
 cfs:gridfs
 cfs:gridfs
 rzymek:fullcalendar
 rzymek:fullcalendar
 momentjs:moment@2.22.2
 momentjs:moment@2.22.2
@@ -85,7 +85,8 @@ msavin:usercache
 wekan-scrollbar
 wekan-scrollbar
 mquandalle:perfect-scrollbar
 mquandalle:perfect-scrollbar
 mdg:meteor-apm-agent@3.2.0-rc.0!
 mdg:meteor-apm-agent@3.2.0-rc.0!
-coagmano:stylus@1.1.0
+# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
+coagmano:stylus@1.1.0!
 lucasantoniassi:accounts-lockout
 lucasantoniassi:accounts-lockout
 meteorhacks:subs-manager
 meteorhacks:subs-manager
 meteorhacks:picker
 meteorhacks:picker
@@ -96,5 +97,4 @@ konecty:mongo-counter
 percolate:synced-cron
 percolate:synced-cron
 easylogic:summernote
 easylogic:summernote
 cfs:filesystem
 cfs:filesystem
-ostrio:files
 ostrio:cookies
 ostrio:cookies

+ 1 - 1
.meteor/release

@@ -1 +1 @@
-METEOR@1.9.2
+METEOR@1.10.2

+ 25 - 26
.meteor/versions

@@ -1,27 +1,26 @@
 3stack:presence@1.1.2
 3stack:presence@1.1.2
-accounts-base@1.5.0
-accounts-oauth@1.1.16
-accounts-password@1.5.3
+accounts-base@1.6.0
+accounts-oauth@1.2.0
+accounts-password@1.6.0
 aldeed:collection2@2.10.0
 aldeed:collection2@2.10.0
 aldeed:collection2-core@1.2.0
 aldeed:collection2-core@1.2.0
 aldeed:schema-deny@1.1.0
 aldeed:schema-deny@1.1.0
 aldeed:schema-index@1.1.1
 aldeed:schema-index@1.1.1
 aldeed:simple-schema@1.5.4
 aldeed:simple-schema@1.5.4
-alethes:pages@1.8.6
 allow-deny@1.1.0
 allow-deny@1.1.0
 arillo:flow-router-helpers@0.5.2
 arillo:flow-router-helpers@0.5.2
 audit-argument-checks@1.0.7
 audit-argument-checks@1.0.7
 autoupdate@1.6.0
 autoupdate@1.6.0
-babel-compiler@7.5.2
+babel-compiler@7.5.3
 babel-runtime@1.5.0
 babel-runtime@1.5.0
 base64@1.0.12
 base64@1.0.12
 binary-heap@1.0.11
 binary-heap@1.0.11
 blaze@2.3.4
 blaze@2.3.4
 blaze-tools@1.0.10
 blaze-tools@1.0.10
-boilerplate-generator@1.6.0
+boilerplate-generator@1.7.0
 browser-policy-common@1.0.11
 browser-policy-common@1.0.11
 browser-policy-framing@1.1.0
 browser-policy-framing@1.1.0
-caching-compiler@1.2.1
+caching-compiler@1.2.2
 caching-html-compiler@1.1.3
 caching-html-compiler@1.1.3
 callback-hook@1.3.0
 callback-hook@1.3.0
 cfs:access-point@0.1.49
 cfs:access-point@0.1.49
@@ -45,7 +44,8 @@ cfs:worker@0.1.5
 check@1.3.1
 check@1.3.1
 chuangbo:cookie@1.1.0
 chuangbo:cookie@1.1.0
 coagmano:stylus@1.1.0
 coagmano:stylus@1.1.0
-coffeescript@1.0.17
+coffeescript@2.4.1
+coffeescript-compiler@2.4.1
 cottz:publish-relations@2.0.8
 cottz:publish-relations@2.0.8
 dburles:collection-helpers@1.1.0
 dburles:collection-helpers@1.1.0
 ddp@1.4.0
 ddp@1.4.0
@@ -55,9 +55,9 @@ ddp-rate-limiter@1.0.7
 ddp-server@2.3.1
 ddp-server@2.3.1
 deps@1.0.12
 deps@1.0.12
 diff-sequence@1.1.1
 diff-sequence@1.1.1
-dynamic-import@0.5.1
+dynamic-import@0.5.2
 easylogic:summernote@0.8.8
 easylogic:summernote@0.8.8
-ecmascript@0.14.2
+ecmascript@0.14.3
 ecmascript-runtime@0.7.0
 ecmascript-runtime@0.7.0
 ecmascript-runtime-client@0.10.0
 ecmascript-runtime-client@0.10.0
 ecmascript-runtime-server@0.9.0
 ecmascript-runtime-server@0.9.0
@@ -75,7 +75,7 @@ htmljs@1.0.11
 http@1.4.2
 http@1.4.2
 id-map@1.1.0
 id-map@1.1.0
 idmontie:migrations@1.0.3
 idmontie:migrations@1.0.3
-inter-process-messaging@0.1.0
+inter-process-messaging@0.1.1
 jquery@1.11.11
 jquery@1.11.11
 kadira:blaze-layout@2.3.0
 kadira:blaze-layout@2.3.0
 kadira:dochead@1.5.0
 kadira:dochead@1.5.0
@@ -84,7 +84,7 @@ kenton:accounts-sandstorm@0.7.0
 konecty:mongo-counter@0.0.5_3
 konecty:mongo-counter@0.0.5_3
 lamhieu:meteorx@2.1.1
 lamhieu:meteorx@2.1.1
 lamhieu:unblock@1.0.0
 lamhieu:unblock@1.0.0
-launch-screen@1.1.1
+launch-screen@1.2.0
 livedata@1.0.18
 livedata@1.0.18
 localstorage@1.2.0
 localstorage@1.2.0
 logging@1.1.20
 logging@1.1.20
@@ -104,13 +104,13 @@ meteorspark:util@0.2.0
 minifier-css@1.5.0
 minifier-css@1.5.0
 minifier-js@2.6.0
 minifier-js@2.6.0
 minifiers@1.1.8-faster-rebuild.0
 minifiers@1.1.8-faster-rebuild.0
-minimongo@1.4.5
-mobile-status-bar@1.0.14
+minimongo@1.6.0
+mobile-status-bar@1.1.0
 modern-browsers@0.1.5
 modern-browsers@0.1.5
 modules@0.15.0
 modules@0.15.0
 modules-runtime@0.12.0
 modules-runtime@0.12.0
 momentjs:moment@2.24.0
 momentjs:moment@2.24.0
-mongo@1.8.1
+mongo@1.10.0
 mongo-decimal@0.1.1
 mongo-decimal@0.1.1
 mongo-dev-server@1.1.0
 mongo-dev-server@1.1.0
 mongo-id@1.0.7
 mongo-id@1.0.7
@@ -127,14 +127,13 @@ mquandalle:mousetrap-bindglobal@0.0.1
 mquandalle:perfect-scrollbar@0.6.5_2
 mquandalle:perfect-scrollbar@0.6.5_2
 msavin:usercache@1.8.0
 msavin:usercache@1.8.0
 npm-bcrypt@0.9.3
 npm-bcrypt@0.9.3
-npm-mongo@3.3.0
-oauth@1.2.8
-oauth2@1.2.1
+npm-mongo@3.7.0
+oauth@1.3.0
+oauth2@1.3.0
 observe-sequence@1.0.16
 observe-sequence@1.0.16
 ongoworks:speakingurl@1.1.0
 ongoworks:speakingurl@1.1.0
 ordered-dict@1.1.0
 ordered-dict@1.1.0
-ostrio:cookies@2.5.0
-ostrio:files@1.13.0
+ostrio:cookies@2.6.0
 peerlibrary:assert@0.3.0
 peerlibrary:assert@0.3.0
 peerlibrary:base-component@0.16.0
 peerlibrary:base-component@0.16.0
 peerlibrary:blaze-components@0.15.1
 peerlibrary:blaze-components@0.15.1
@@ -145,7 +144,7 @@ promise@0.11.2
 raix:eventemitter@0.1.3
 raix:eventemitter@0.1.3
 raix:handlebar-helpers@0.2.5
 raix:handlebar-helpers@0.2.5
 rajit:bootstrap3-datepicker@1.7.1_1
 rajit:bootstrap3-datepicker@1.7.1_1
-random@1.1.0
+random@1.2.0
 rate-limit@1.0.9
 rate-limit@1.0.9
 reactive-dict@1.3.0
 reactive-dict@1.3.0
 reactive-var@1.0.11
 reactive-var@1.0.11
@@ -157,17 +156,17 @@ server-render@0.3.1
 service-configuration@1.0.11
 service-configuration@1.0.11
 session@1.2.0
 session@1.2.0
 sha@1.0.9
 sha@1.0.9
-shell-server@0.4.0
+shell-server@0.5.0
 simple:authenticate-user-by-token@1.0.1
 simple:authenticate-user-by-token@1.0.1
 simple:json-routes@2.1.0
 simple:json-routes@2.1.0
 simple:rest-accounts-password@1.1.2
 simple:rest-accounts-password@1.1.2
 simple:rest-bearer-token-parser@1.0.1
 simple:rest-bearer-token-parser@1.0.1
 simple:rest-json-error-handler@1.0.1
 simple:rest-json-error-handler@1.0.1
-socket-stream-client@0.2.3
+socket-stream-client@0.3.0
 softwarerero:accounts-t9n@1.3.11
 softwarerero:accounts-t9n@1.3.11
 spacebars@1.0.15
 spacebars@1.0.15
 spacebars-compiler@1.1.3
 spacebars-compiler@1.1.3
-srp@1.0.12
+srp@1.1.0
 standard-minifier-css@1.6.0
 standard-minifier-css@1.6.0
 standard-minifier-js@2.6.0
 standard-minifier-js@2.6.0
 staringatlights:fast-render@3.2.0
 staringatlights:fast-render@3.2.0
@@ -182,12 +181,12 @@ tracker@1.2.0
 twbs:bootstrap@3.3.6
 twbs:bootstrap@3.3.6
 ui@1.0.13
 ui@1.0.13
 underscore@1.0.10
 underscore@1.0.10
-url@1.2.0
+url@1.3.0
 useraccounts:core@1.14.2
 useraccounts:core@1.14.2
 useraccounts:flow-routing@1.14.2
 useraccounts:flow-routing@1.14.2
 useraccounts:unstyled@1.14.2
 useraccounts:unstyled@1.14.2
 verron:autosize@3.0.8
 verron:autosize@3.0.8
-webapp@1.8.2
+webapp@1.9.1
 webapp-hashing@1.0.9
 webapp-hashing@1.0.9
 wekan-accounts-cas@0.1.0
 wekan-accounts-cas@0.1.0
 wekan-accounts-oidc@1.0.10
 wekan-accounts-oidc@1.0.10

+ 0 - 20
.snap-meteor-1.8/.meteor/.finished-upgraders

@@ -1,20 +0,0 @@
-# This file contains information which helps Meteor properly upgrade your
-# app when you run 'meteor update'. You should check it into version control
-# with your project.
-
-notices-for-0.9.0
-notices-for-0.9.1
-0.9.4-platform-file
-notices-for-facebook-graph-api-2
-1.2.0-standard-minifiers-package
-1.2.0-meteor-platform-split
-1.2.0-cordova-changes
-1.2.0-breaking-changes
-1.3.0-split-minifiers-package
-1.3.5-remove-old-dev-bundle-link
-1.4.0-remove-old-dev-bundle-link
-1.4.1-add-shell-server-package
-1.4.3-split-account-service-packages
-1.5-add-dynamic-import-package
-1.7-split-underscore-from-meteor-base
-1.8.3-split-jquery-from-blaze

+ 0 - 2
.snap-meteor-1.8/.meteor/.gitignore

@@ -1,2 +0,0 @@
-dev_bundle
-local

+ 0 - 7
.snap-meteor-1.8/.meteor/.id

@@ -1,7 +0,0 @@
-# This file contains a token that is unique to your project.
-# Check it into your repository along with the rest of this directory.
-# It can be used for purposes such as:
-#   - ensuring you don't accidentally deploy one app on top of another
-#   - providing package authors with aggregated statistics
-
-dvyihgykyzec6y1dpg

+ 0 - 0
.snap-meteor-1.8/.meteor/cordova-plugins


+ 0 - 99
.snap-meteor-1.8/.meteor/packages

@@ -1,99 +0,0 @@
-# Meteor packages used by this project, one per line.
-#
-# 'meteor add' and 'meteor remove' will edit this file for you,
-# but you can also edit it by hand.
-
-meteor-base@1.4.0
-
-# Build system
-ecmascript@0.13.2
-standard-minifier-css@1.5.4
-standard-minifier-js@2.5.2
-mquandalle:jade
-
-# Polyfills
-es5-shim@4.8.0
-
-# Collections
-aldeed:collection2
-cfs:standard-packages
-cottz:publish-relations
-dburles:collection-helpers
-idmontie:migrations
-matb33:collection-hooks
-matteodem:easy-search
-mongo@1.7.0
-mquandalle:collection-mutations
-
-# Account system
-kenton:accounts-sandstorm
-service-configuration@1.0.11
-useraccounts:unstyled
-useraccounts:flow-routing
-wekan-ldap
-wekan-accounts-cas
-wekan-accounts-oidc
-
-# Utilities
-check@1.3.1
-jquery@1.11.10
-random@1.1.0
-reactive-dict@1.3.0
-session@1.2.0
-tracker@1.2.0
-underscore@1.0.10
-3stack:presence
-alethes:pages
-arillo:flow-router-helpers
-audit-argument-checks@1.0.7
-kadira:blaze-layout
-kadira:dochead
-mquandalle:autofocus
-ongoworks:speakingurl
-raix:handlebar-helpers
-tap:i18n
-http@1.4.2
-
-# UI components
-blaze
-reactive-var@1.0.11
-fortawesome:fontawesome
-mousetrap:mousetrap
-mquandalle:jquery-textcomplete
-mquandalle:jquery-ui-drag-drop-sort
-mquandalle:mousetrap-bindglobal
-peerlibrary:blaze-components@=0.15.1
-templates:tabs
-verron:autosize
-simple:json-routes
-rajit:bootstrap3-datepicker
-shell-server@0.4.0
-simple:rest-accounts-password
-useraccounts:core
-email@1.2.3
-horka:swipebox
-dynamic-import@0.5.1
-staringatlights:fast-render
-
-accounts-password@1.5.2
-cfs:gridfs
-rzymek:fullcalendar
-momentjs:moment@2.22.2
-browser-policy-framing@1.1.0
-mquandalle:moment
-msavin:usercache
-wekan-scrollbar
-mquandalle:perfect-scrollbar
-mdg:meteor-apm-agent@3.2.0-rc.0!
-coagmano:stylus
-lucasantoniassi:accounts-lockout
-meteorhacks:subs-manager
-meteorhacks:picker
-lamhieu:unblock
-meteorhacks:aggregate@1.3.0
-wekan-markdown
-konecty:mongo-counter
-percolate:synced-cron
-easylogic:summernote
-cfs:filesystem
-ostrio:cookies

+ 0 - 2
.snap-meteor-1.8/.meteor/platforms

@@ -1,2 +0,0 @@
-server
-browser

+ 0 - 1
.snap-meteor-1.8/.meteor/release

@@ -1 +0,0 @@
-METEOR@1.8.3

+ 0 - 198
.snap-meteor-1.8/.meteor/versions

@@ -1,198 +0,0 @@
-3stack:presence@1.1.2
-accounts-base@1.4.5
-accounts-oauth@1.1.16
-accounts-password@1.5.2
-aldeed:collection2@2.10.0
-aldeed:collection2-core@1.2.0
-aldeed:schema-deny@1.1.0
-aldeed:schema-index@1.1.1
-aldeed:simple-schema@1.5.4
-alethes:pages@1.8.6
-allow-deny@1.1.0
-arillo:flow-router-helpers@0.5.2
-audit-argument-checks@1.0.7
-autoupdate@1.6.0
-babel-compiler@7.4.2
-babel-runtime@1.4.0
-base64@1.0.12
-binary-heap@1.0.11
-blaze@2.3.4
-blaze-tools@1.0.10
-boilerplate-generator@1.6.0
-browser-policy-common@1.0.11
-browser-policy-framing@1.1.0
-caching-compiler@1.2.1
-caching-html-compiler@1.1.3
-callback-hook@1.2.0
-cfs:access-point@0.1.49
-cfs:base-package@0.0.30
-cfs:collection@0.5.5
-cfs:collection-filters@0.2.4
-cfs:data-man@0.0.6
-cfs:file@0.1.17
-cfs:filesystem@0.1.2
-cfs:gridfs@0.0.34
-cfs:http-methods@0.0.32
-cfs:http-publish@0.0.13
-cfs:power-queue@0.9.11
-cfs:reactive-list@0.0.9
-cfs:reactive-property@0.0.4
-cfs:standard-packages@0.5.10
-cfs:storage-adapter@0.2.4
-cfs:tempstore@0.1.6
-cfs:upload-http@0.0.20
-cfs:worker@0.1.5
-check@1.3.1
-chuangbo:cookie@1.1.0
-coagmano:stylus@2.0.0
-coffeescript@1.0.17
-cottz:publish-relations@2.0.8
-dburles:collection-helpers@1.1.0
-ddp@1.4.0
-ddp-client@2.3.3
-ddp-common@1.4.0
-ddp-rate-limiter@1.0.7
-ddp-server@2.3.0
-deps@1.0.12
-diff-sequence@1.1.1
-dynamic-import@0.5.1
-easylogic:summernote@0.8.8
-ecmascript@0.13.2
-ecmascript-runtime@0.7.0
-ecmascript-runtime-client@0.9.0
-ecmascript-runtime-server@0.8.0
-ejson@1.1.1
-email@1.2.3
-es5-shim@4.8.0
-fastclick@1.0.13
-fetch@0.1.1
-fortawesome:fontawesome@4.7.0
-geojson-utils@1.0.10
-horka:swipebox@1.0.2
-hot-code-push@1.0.4
-html-tools@1.0.11
-htmljs@1.0.11
-http@1.4.2
-id-map@1.1.0
-idmontie:migrations@1.0.3
-inter-process-messaging@0.1.0
-jquery@1.11.11
-kadira:blaze-layout@2.3.0
-kadira:dochead@1.5.0
-kadira:flow-router@2.12.1
-kenton:accounts-sandstorm@0.7.0
-konecty:mongo-counter@0.0.5_3
-lamhieu:meteorx@2.1.1
-lamhieu:unblock@1.0.0
-launch-screen@1.1.1
-livedata@1.0.18
-localstorage@1.2.0
-logging@1.1.20
-lucasantoniassi:accounts-lockout@1.0.0
-matb33:collection-hooks@0.9.1
-matteodem:easy-search@1.6.4
-mdg:meteor-apm-agent@3.2.5
-mdg:validation-error@0.5.1
-meteor@1.9.3
-meteor-base@1.4.0
-meteor-platform@1.2.6
-meteorhacks:aggregate@1.3.0
-meteorhacks:collection-utils@1.2.0
-meteorhacks:picker@1.0.3
-meteorhacks:subs-manager@1.6.4
-meteorspark:util@0.2.0
-minifier-css@1.4.3
-minifier-js@2.5.1
-minifiers@1.1.8-faster-rebuild.0
-minimongo@1.4.5
-mobile-status-bar@1.0.14
-modern-browsers@0.1.4
-modules@0.14.0
-modules-runtime@0.11.0
-momentjs:moment@2.24.0
-mongo@1.7.0
-mongo-decimal@0.1.1
-mongo-dev-server@1.1.0
-mongo-id@1.0.7
-mongo-livedata@1.0.12
-mousetrap:mousetrap@1.4.6_1
-mquandalle:autofocus@1.0.0
-mquandalle:collection-mutations@0.1.0
-mquandalle:jade@0.4.9
-mquandalle:jade-compiler@0.4.5
-mquandalle:jquery-textcomplete@0.8.0_1
-mquandalle:jquery-ui-drag-drop-sort@0.2.0
-mquandalle:moment@1.0.1
-mquandalle:mousetrap-bindglobal@0.0.1
-mquandalle:perfect-scrollbar@0.6.5_2
-msavin:usercache@1.8.0
-npm-bcrypt@0.9.3
-npm-mongo@3.2.0
-oauth@1.2.8
-oauth2@1.2.1
-observe-sequence@1.0.16
-ongoworks:speakingurl@1.1.0
-ordered-dict@1.1.0
-ostrio:cookies@2.5.0
-peerlibrary:assert@0.3.0
-peerlibrary:base-component@0.16.0
-peerlibrary:blaze-components@0.15.1
-peerlibrary:computed-field@0.10.0
-peerlibrary:reactive-field@0.6.0
-percolate:synced-cron@1.3.2
-promise@0.11.2
-raix:eventemitter@0.1.3
-raix:handlebar-helpers@0.2.5
-rajit:bootstrap3-datepicker@1.7.1_1
-random@1.1.0
-rate-limit@1.0.9
-reactive-dict@1.3.0
-reactive-var@1.0.11
-reload@1.3.0
-retry@1.1.0
-routepolicy@1.1.0
-rzymek:fullcalendar@3.8.0
-server-render@0.3.1
-service-configuration@1.0.11
-session@1.2.0
-sha@1.0.9
-shell-server@0.4.0
-simple:authenticate-user-by-token@1.0.1
-simple:json-routes@2.1.0
-simple:rest-accounts-password@1.1.2
-simple:rest-bearer-token-parser@1.0.1
-simple:rest-json-error-handler@1.0.1
-socket-stream-client@0.2.2
-softwarerero:accounts-t9n@1.3.11
-spacebars@1.0.15
-spacebars-compiler@1.1.3
-srp@1.0.12
-standard-minifier-css@1.5.4
-standard-minifier-js@2.5.2
-staringatlights:fast-render@3.2.0
-staringatlights:inject-data@2.3.0
-tap:i18n@1.8.2
-templates:tabs@2.3.0
-templating@1.3.2
-templating-compiler@1.3.3
-templating-runtime@1.3.2
-templating-tools@1.1.2
-tracker@1.2.0
-twbs:bootstrap@3.3.6
-ui@1.0.13
-underscore@1.0.10
-url@1.2.0
-useraccounts:core@1.14.2
-useraccounts:flow-routing@1.14.2
-useraccounts:unstyled@1.14.2
-verron:autosize@3.0.8
-webapp@1.7.5
-webapp-hashing@1.0.9
-wekan-accounts-cas@0.1.0
-wekan-accounts-oidc@1.0.10
-wekan-ldap@0.0.2
-wekan-markdown@1.0.7
-wekan-oidc@1.0.12
-wekan-scrollbar@3.1.3
-yasaricli:slugify@0.0.7
-zimme:active-route@2.3.2

+ 0 - 914
.snap-meteor-1.8/cfs_access-point.txt

@@ -1,914 +0,0 @@
-(function () {
-
-/* Imports */
-var Meteor = Package.meteor.Meteor;
-var global = Package.meteor.global;
-var meteorEnv = Package.meteor.meteorEnv;
-var FS = Package['cfs:base-package'].FS;
-var check = Package.check.check;
-var Match = Package.check.Match;
-var EJSON = Package.ejson.EJSON;
-var HTTP = Package['cfs:http-methods'].HTTP;
-
-/* Package-scope variables */
-var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMountPoints, mountUrls;
-
-(function(){
-
-///////////////////////////////////////////////////////////////////////
-//                                                                   //
-// packages/cfs_access-point/packages/cfs_access-point.js            //
-//                                                                   //
-///////////////////////////////////////////////////////////////////////
-                                                                     //
-(function () {
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                                                                                                    //
-// packages/cfs:access-point/access-point-common.js                                                                   //
-//                                                                                                                    //
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-                                                                                                                      //
-rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || "";                                             // 1
-// Adjust the rootUrlPathPrefix if necessary                                                                          // 2
-if (rootUrlPathPrefix.length > 0) {                                                                                   // 3
-  if (rootUrlPathPrefix.slice(0, 1) !== '/') {                                                                        // 4
-    rootUrlPathPrefix = '/' + rootUrlPathPrefix;                                                                      // 5
-  }                                                                                                                   // 6
-  if (rootUrlPathPrefix.slice(-1) === '/') {                                                                          // 7
-    rootUrlPathPrefix = rootUrlPathPrefix.slice(0, -1);                                                               // 8
-  }                                                                                                                   // 9
-}                                                                                                                     // 10
-                                                                                                                      // 11
-// prepend ROOT_URL when isCordova                                                                                    // 12
-if (Meteor.isCordova) {                                                                                               // 13
-  rootUrlPathPrefix = Meteor.absoluteUrl(rootUrlPathPrefix.replace(/^\/+/, '')).replace(/\/+$/, '');                  // 14
-}                                                                                                                     // 15
-                                                                                                                      // 16
-baseUrl = '/cfs';                                                                                                     // 17
-FS.HTTP = FS.HTTP || {};                                                                                              // 18
-                                                                                                                      // 19
-// Note the upload URL so that client uploader packages know what it is                                               // 20
-FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files';                                                           // 21
-                                                                                                                      // 22
-/**                                                                                                                   // 23
- * @method FS.HTTP.setBaseUrl                                                                                         // 24
- * @public                                                                                                            // 25
- * @param {String} newBaseUrl - Change the base URL for the HTTP GET and DELETE endpoints.                            // 26
- * @returns {undefined}                                                                                               // 27
- */                                                                                                                   // 28
-FS.HTTP.setBaseUrl = function setBaseUrl(newBaseUrl) {                                                                // 29
-                                                                                                                      // 30
-  // Adjust the baseUrl if necessary                                                                                  // 31
-  if (newBaseUrl.slice(0, 1) !== '/') {                                                                               // 32
-    newBaseUrl = '/' + newBaseUrl;                                                                                    // 33
-  }                                                                                                                   // 34
-  if (newBaseUrl.slice(-1) === '/') {                                                                                 // 35
-    newBaseUrl = newBaseUrl.slice(0, -1);                                                                             // 36
-  }                                                                                                                   // 37
-                                                                                                                      // 38
-  // Update the base URL                                                                                              // 39
-  baseUrl = newBaseUrl;                                                                                               // 40
-                                                                                                                      // 41
-  // Change the upload URL so that client uploader packages know what it is                                           // 42
-  FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files';                                                         // 43
-                                                                                                                      // 44
-  // Remount URLs with the new baseUrl, unmounting the old, on the server only.                                       // 45
-  // If existingMountPoints is empty, then we haven't run the server startup                                          // 46
-  // code yet, so this new URL will be used at that point for the initial mount.                                      // 47
-  if (Meteor.isServer && !FS.Utility.isEmpty(_existingMountPoints)) {                                                 // 48
-    mountUrls();                                                                                                      // 49
-  }                                                                                                                   // 50
-};                                                                                                                    // 51
-                                                                                                                      // 52
-/*                                                                                                                    // 53
- * FS.File extensions                                                                                                 // 54
- */                                                                                                                   // 55
-                                                                                                                      // 56
-/**                                                                                                                   // 57
- * @method FS.File.prototype.url Construct the file url                                                               // 58
- * @public                                                                                                            // 59
- * @param {Object} [options]                                                                                          // 60
- * @param {String} [options.store] Name of the store to get from. If not defined, the first store defined in `options.stores` for the collection on the client is used.
- * @param {Boolean} [options.auth=null] Add authentication token to the URL query string? By default, a token for the current logged in user is added on the client. Set this to `false` to omit the token. Set this to a string to provide your own token. Set this to a number to specify an expiration time for the token in seconds.
- * @param {Boolean} [options.download=false] Should headers be set to force a download? Typically this means that clicking the link with this URL will download the file to the user's Downloads folder instead of displaying the file in the browser.
- * @param {Boolean} [options.brokenIsFine=false] Return the URL even if we know it's currently a broken link because the file hasn't been saved in the requested store yet.
- * @param {Boolean} [options.metadata=false] Return the URL for the file metadata access point rather than the file itself.
- * @param {String} [options.uploading=null] A URL to return while the file is being uploaded.                         // 66
- * @param {String} [options.storing=null] A URL to return while the file is being stored.                             // 67
- * @param {String} [options.filename=null] Override the filename that should appear at the end of the URL. By default it is the name of the file in the requested store.
- *                                                                                                                    // 69
- * Returns the HTTP URL for getting the file or its metadata.                                                         // 70
- */                                                                                                                   // 71
-FS.File.prototype.url = function(options) {                                                                           // 72
-  var self = this;                                                                                                    // 73
-  options = options || {};                                                                                            // 74
-  options = FS.Utility.extend({                                                                                       // 75
-    store: null,                                                                                                      // 76
-    auth: null,                                                                                                       // 77
-    download: false,                                                                                                  // 78
-    metadata: false,                                                                                                  // 79
-    brokenIsFine: false,                                                                                              // 80
-    uploading: null, // return this URL while uploading                                                               // 81
-    storing: null, // return this URL while storing                                                                   // 82
-    filename: null // override the filename that is shown to the user                                                 // 83
-  }, options.hash || options); // check for "hash" prop if called as helper                                           // 84
-                                                                                                                      // 85
-  // Primarily useful for displaying a temporary image while uploading an image                                       // 86
-  if (options.uploading && !self.isUploaded()) {                                                                      // 87
-    return options.uploading;                                                                                         // 88
-  }                                                                                                                   // 89
-                                                                                                                      // 90
-  if (self.isMounted()) {                                                                                             // 91
-    // See if we've stored in the requested store yet                                                                 // 92
-    var storeName = options.store || self.collection.primaryStore.name;                                               // 93
-    if (!self.hasStored(storeName)) {                                                                                 // 94
-      if (options.storing) {                                                                                          // 95
-        return options.storing;                                                                                       // 96
-      } else if (!options.brokenIsFine) {                                                                             // 97
-        // We want to return null if we know the URL will be a broken                                                 // 98
-        // link because then we can avoid rendering broken links, broken                                              // 99
-        // images, etc.                                                                                               // 100
-        return null;                                                                                                  // 101
-      }                                                                                                               // 102
-    }                                                                                                                 // 103
-                                                                                                                      // 104
-    // Add filename to end of URL if we can determine one                                                             // 105
-    var filename = options.filename || self.name({store: storeName});                                                 // 106
-    if (typeof filename === "string" && filename.length) {                                                            // 107
-      filename = '/' + filename;                                                                                      // 108
-    } else {                                                                                                          // 109
-      filename = '';                                                                                                  // 110
-    }                                                                                                                 // 111
-                                                                                                                      // 112
-    // TODO: Could we somehow figure out if the collection requires login?                                            // 113
-    var authToken = '';                                                                                               // 114
-    if (Meteor.isClient && typeof Accounts !== "undefined" && typeof Accounts._storedLoginToken === "function") {     // 115
-      if (options.auth !== false) {                                                                                   // 116
-        // Add reactive deps on the user                                                                              // 117
-        Meteor.userId();                                                                                              // 118
-                                                                                                                      // 119
-        var authObject = {                                                                                            // 120
-          authToken: Accounts._storedLoginToken() || ''                                                               // 121
-        };                                                                                                            // 122
-                                                                                                                      // 123
-        // If it's a number, we use that as the expiration time (in seconds)                                          // 124
-        if (options.auth === +options.auth) {                                                                         // 125
-          authObject.expiration = FS.HTTP.now() + options.auth * 1000;                                                // 126
-        }                                                                                                             // 127
-                                                                                                                      // 128
-        // Set the authToken                                                                                          // 129
-        var authString = JSON.stringify(authObject);                                                                  // 130
-        authToken = FS.Utility.btoa(authString);                                                                      // 131
-      }                                                                                                               // 132
-    } else if (typeof options.auth === "string") {                                                                    // 133
-      // If the user supplies auth token the user will be responsible for                                             // 134
-      // updating                                                                                                     // 135
-      authToken = options.auth;                                                                                       // 136
-    }                                                                                                                 // 137
-                                                                                                                      // 138
-    // Construct query string                                                                                         // 139
-    var params = {};                                                                                                  // 140
-    if (authToken !== '') {                                                                                           // 141
-      params.token = authToken;                                                                                       // 142
-    }                                                                                                                 // 143
-    if (options.download) {                                                                                           // 144
-      params.download = true;                                                                                         // 145
-    }                                                                                                                 // 146
-    if (options.store) {                                                                                              // 147
-      // We use options.store here instead of storeName because we want to omit the queryString                       // 148
-      // whenever possible, allowing users to have "clean" URLs if they want. The server will                         // 149
-      // assume the first store defined on the server, which means that we are assuming that                          // 150
-      // the first on the client is also the first on the server. If that's not the case, the                         // 151
-      // store option should be supplied.                                                                             // 152
-      params.store = options.store;                                                                                   // 153
-    }                                                                                                                 // 154
-    var queryString = FS.Utility.encodeParams(params);                                                                // 155
-    if (queryString.length) {                                                                                         // 156
-      queryString = '?' + queryString;                                                                                // 157
-    }                                                                                                                 // 158
-                                                                                                                      // 159
-    // Determine which URL to use                                                                                     // 160
-    var area;                                                                                                         // 161
-    if (options.metadata) {                                                                                           // 162
-      area = '/record';                                                                                               // 163
-    } else {                                                                                                          // 164
-      area = '/files';                                                                                                // 165
-    }                                                                                                                 // 166
-                                                                                                                      // 167
-    // Construct and return the http method url                                                                       // 168
-    return rootUrlPathPrefix + baseUrl + area + '/' + self.collection.name + '/' + self._id + filename + queryString; // 169
-  }                                                                                                                   // 170
-                                                                                                                      // 171
-};                                                                                                                    // 172
-                                                                                                                      // 173
-                                                                                                                      // 174
-                                                                                                                      // 175
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-}).call(this);
-
-
-
-
-
-
-(function () {
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                                                                                                    //
-// packages/cfs:access-point/access-point-handlers.js                                                                 //
-//                                                                                                                    //
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-                                                                                                                      //
-getHeaders = [];                                                                                                      // 1
-getHeadersByCollection = {};                                                                                          // 2
-                                                                                                                      // 3
-FS.HTTP.Handlers = {};                                                                                                // 4
-                                                                                                                      // 5
-/**                                                                                                                   // 6
- * @method FS.HTTP.Handlers.Del                                                                                       // 7
- * @public                                                                                                            // 8
- * @returns {any} response                                                                                            // 9
- *                                                                                                                    // 10
- * HTTP DEL request handler                                                                                           // 11
- */                                                                                                                   // 12
-FS.HTTP.Handlers.Del = function httpDelHandler(ref) {                                                                 // 13
-  var self = this;                                                                                                    // 14
-  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 15
-                                                                                                                      // 16
-  // If DELETE request, validate with 'remove' allow/deny, delete the file, and return                                // 17
-  FS.Utility.validateAction(ref.collection.files._validators['remove'], ref.file, self.userId);                       // 18
-                                                                                                                      // 19
-  /*                                                                                                                  // 20
-   * From the DELETE spec:                                                                                            // 21
-   * A successful response SHOULD be 200 (OK) if the response includes an                                             // 22
-   * entity describing the status, 202 (Accepted) if the action has not                                               // 23
-   * yet been enacted, or 204 (No Content) if the action has been enacted                                             // 24
-   * but the response does not include an entity.                                                                     // 25
-   */                                                                                                                 // 26
-  self.setStatusCode(200);                                                                                            // 27
-                                                                                                                      // 28
-  return {                                                                                                            // 29
-    deleted: !!ref.file.remove()                                                                                      // 30
-  };                                                                                                                  // 31
-};                                                                                                                    // 32
-                                                                                                                      // 33
-/**                                                                                                                   // 34
- * @method FS.HTTP.Handlers.GetList                                                                                   // 35
- * @public                                                                                                            // 36
- * @returns {Object} response                                                                                         // 37
- *                                                                                                                    // 38
- * HTTP GET file list request handler                                                                                 // 39
- */                                                                                                                   // 40
-FS.HTTP.Handlers.GetList = function httpGetListHandler() {                                                            // 41
-  // Not Yet Implemented                                                                                              // 42
-  // Need to check publications and return file list based on                                                         // 43
-  // what user is allowed to see                                                                                      // 44
-};                                                                                                                    // 45
-                                                                                                                      // 46
-/*                                                                                                                    // 47
-  requestRange will parse the range set in request header - if not possible it                                        // 48
-  will throw fitting errors and autofill range for both partial and full ranges                                       // 49
-                                                                                                                      // 50
-  throws error or returns the object:                                                                                 // 51
-  {                                                                                                                   // 52
-    start                                                                                                             // 53
-    end                                                                                                               // 54
-    length                                                                                                            // 55
-    unit                                                                                                              // 56
-    partial                                                                                                           // 57
-  }                                                                                                                   // 58
-*/                                                                                                                    // 59
-var requestRange = function(req, fileSize) {                                                                          // 60
-  if (req) {                                                                                                          // 61
-    if (req.headers) {                                                                                                // 62
-      var rangeString = req.headers.range;                                                                            // 63
-                                                                                                                      // 64
-      // Make sure range is a string                                                                                  // 65
-      if (rangeString === ''+rangeString) {                                                                           // 66
-                                                                                                                      // 67
-        // range will be in the format "bytes=0-32767"                                                                // 68
-        var parts = rangeString.split('=');                                                                           // 69
-        var unit = parts[0];                                                                                          // 70
-                                                                                                                      // 71
-        // Make sure parts consists of two strings and range is of type "byte"                                        // 72
-        if (parts.length == 2 && unit == 'bytes') {                                                                   // 73
-          // Parse the range                                                                                          // 74
-          var range = parts[1].split('-');                                                                            // 75
-          var start = Number(range[0]);                                                                               // 76
-          var end = Number(range[1]);                                                                                 // 77
-                                                                                                                      // 78
-          // Fix invalid ranges?                                                                                      // 79
-          if (range[0] != start) start = 0;                                                                           // 80
-          if (range[1] != end || !end) end = fileSize - 1;                                                            // 81
-                                                                                                                      // 82
-          // Make sure range consists of a start and end point of numbers and start is less than end                  // 83
-          if (start < end) {                                                                                          // 84
-                                                                                                                      // 85
-            var partSize = 0 - start + end + 1;                                                                       // 86
-                                                                                                                      // 87
-            // Return the parsed range                                                                                // 88
-            return {                                                                                                  // 89
-              start: start,                                                                                           // 90
-              end: end,                                                                                               // 91
-              length: partSize,                                                                                       // 92
-              size: fileSize,                                                                                         // 93
-              unit: unit,                                                                                             // 94
-              partial: (partSize < fileSize)                                                                          // 95
-            };                                                                                                        // 96
-                                                                                                                      // 97
-          } else {                                                                                                    // 98
-            throw new Meteor.Error(416, "Requested Range Not Satisfiable");                                           // 99
-          }                                                                                                           // 100
-                                                                                                                      // 101
-        } else {                                                                                                      // 102
-          // The first part should be bytes                                                                           // 103
-          throw new Meteor.Error(416, "Requested Range Unit Not Satisfiable");                                        // 104
-        }                                                                                                             // 105
-                                                                                                                      // 106
-      } else {                                                                                                        // 107
-        // No range found                                                                                             // 108
-      }                                                                                                               // 109
-                                                                                                                      // 110
-    } else {                                                                                                          // 111
-      // throw new Error('No request headers set for _parseRange function');                                          // 112
-    }                                                                                                                 // 113
-  } else {                                                                                                            // 114
-    throw new Error('No request object passed to _parseRange function');                                              // 115
-  }                                                                                                                   // 116
-                                                                                                                      // 117
-  return {                                                                                                            // 118
-    start: 0,                                                                                                         // 119
-    end: fileSize - 1,                                                                                                // 120
-    length: fileSize,                                                                                                 // 121
-    size: fileSize,                                                                                                   // 122
-    unit: 'bytes',                                                                                                    // 123
-    partial: false                                                                                                    // 124
-  };                                                                                                                  // 125
-};                                                                                                                    // 126
-                                                                                                                      // 127
-/**                                                                                                                   // 128
- * @method FS.HTTP.Handlers.Get                                                                                       // 129
- * @public                                                                                                            // 130
- * @returns {any} response                                                                                            // 131
- *                                                                                                                    // 132
- * HTTP GET request handler                                                                                           // 133
- */                                                                                                                   // 134
-FS.HTTP.Handlers.Get = function httpGetHandler(ref) {                                                                 // 135
-  var self = this;                                                                                                    // 136
-  // Once we have the file, we can test allow/deny validators                                                         // 137
-  // XXX: pass on the "share" query eg. ?share=342hkjh23ggj for shared url access?                                    // 138
-  FS.Utility.validateAction(ref.collection._validators['download'], ref.file, self.userId /*, self.query.shareId*/);  // 139
-                                                                                                                      // 140
-  var storeName = ref.storeName;                                                                                      // 141
-                                                                                                                      // 142
-  // If no storeName was specified, use the first defined storeName                                                   // 143
-  if (typeof storeName !== "string") {                                                                                // 144
-    // No store handed, we default to primary store                                                                   // 145
-    storeName = ref.collection.primaryStore.name;                                                                     // 146
-  }                                                                                                                   // 147
-                                                                                                                      // 148
-  // Get the storage reference                                                                                        // 149
-  var storage = ref.collection.storesLookup[storeName];                                                               // 150
-                                                                                                                      // 151
-  if (!storage) {                                                                                                     // 152
-    throw new Meteor.Error(404, "Not Found", 'There is no store "' + storeName + '"');                                // 153
-  }                                                                                                                   // 154
-                                                                                                                      // 155
-  // Get the file                                                                                                     // 156
-  var copyInfo = ref.file.copies[storeName];                                                                          // 157
-                                                                                                                      // 158
-  if (!copyInfo) {                                                                                                    // 159
-    throw new Meteor.Error(404, "Not Found", 'This file was not stored in the ' + storeName + ' store');              // 160
-  }                                                                                                                   // 161
-                                                                                                                      // 162
-  // Set the content type for file                                                                                    // 163
-  if (typeof copyInfo.type === "string") {                                                                            // 164
-    self.setContentType(copyInfo.type);                                                                               // 165
-  } else {                                                                                                            // 166
-    self.setContentType('application/octet-stream');                                                                  // 167
-  }                                                                                                                   // 168
-                                                                                                                      // 169
-  // Add 'Content-Disposition' header if requested a download/attachment URL                                          // 170
-  if (typeof ref.download !== "undefined") {                                                                          // 171
-    var filename = ref.filename || copyInfo.name;                                                                     // 172
-    self.addHeader('Content-Disposition', 'attachment; filename="' + filename + '"');                                 // 173
-  } else {                                                                                                            // 174
-    self.addHeader('Content-Disposition', 'inline');                                                                  // 175
-  }                                                                                                                   // 176
-                                                                                                                      // 177
-  // Get the contents range from request                                                                              // 178
-  var range = requestRange(self.request, copyInfo.size);                                                              // 179
-                                                                                                                      // 180
-  // Some browsers cope better if the content-range header is                                                         // 181
-  // still included even for the full file being returned.                                                            // 182
-  self.addHeader('Content-Range', range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);               // 183
-                                                                                                                      // 184
-  // If a chunk/range was requested instead of the whole file, serve that'                                            // 185
-  if (range.partial) {                                                                                                // 186
-    self.setStatusCode(206, 'Partial Content');                                                                       // 187
-  } else {                                                                                                            // 188
-    self.setStatusCode(200, 'OK');                                                                                    // 189
-  }                                                                                                                   // 190
-                                                                                                                      // 191
-  // Add any other global custom headers and collection-specific custom headers                                       // 192
-  FS.Utility.each(getHeaders.concat(getHeadersByCollection[ref.collection.name] || []), function(header) {            // 193
-    self.addHeader(header[0], header[1]);                                                                             // 194
-  });                                                                                                                 // 195
-                                                                                                                      // 196
-  // Inform clients about length (or chunk length in case of ranges)                                                  // 197
-  self.addHeader('Content-Length', range.length);                                                                     // 198
-                                                                                                                      // 199
-  // Last modified header (updatedAt from file info)                                                                  // 200
-  self.addHeader('Last-Modified', copyInfo.updatedAt.toUTCString());                                                  // 201
-                                                                                                                      // 202
-  // Inform clients that we accept ranges for resumable chunked downloads                                             // 203
-  self.addHeader('Accept-Ranges', range.unit);                                                                        // 204
-                                                                                                                      // 205
-  if (FS.debug) console.log('Read file "' + (ref.filename || copyInfo.name) + '" ' + range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
-                                                                                                                      // 207
-  var readStream = storage.adapter.createReadStream(ref.file, {start: range.start, end: range.end});                  // 208
-                                                                                                                      // 209
-  readStream.on('error', function(err) {                                                                              // 210
-    // Send proper error message on get error                                                                         // 211
-    if (err.message && err.statusCode) {                                                                              // 212
-      self.Error(new Meteor.Error(err.statusCode, err.message));                                                      // 213
-    } else {                                                                                                          // 214
-      self.Error(new Meteor.Error(503, 'Service unavailable'));                                                       // 215
-    }                                                                                                                 // 216
-  });                                                                                                                 // 217
-                                                                                                                      // 218
-  readStream.pipe(self.createWriteStream());                                                                          // 219
-};                                                                                                                    // 220
-
-const originalHandler = FS.HTTP.Handlers.Get;
-FS.HTTP.Handlers.Get = function (ref) {
-//console.log(ref.filename);
-  try {
-     var userAgent = (this.requestHeaders['user-agent']||'').toLowerCase();
-
-        if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
-            ref.filename =  encodeURIComponent(ref.filename);
-        } else if(userAgent.indexOf('firefox') >= 0) {
-            ref.filename = new Buffer(ref.filename).toString('binary');
-        } else {
-            /* safari*/
-            ref.filename = new Buffer(ref.filename).toString('binary');
-        }   
-   } catch (ex){
-        ref.filename = 'tempfix';
-   } 
-   return originalHandler.call(this, ref);
-};
-                                                                                                                      // 221
-/**                                                                                                                   // 222
- * @method FS.HTTP.Handlers.PutInsert                                                                                 // 223
- * @public                                                                                                            // 224
- * @returns {Object} response object with _id property                                                                // 225
- *                                                                                                                    // 226
- * HTTP PUT file insert request handler                                                                               // 227
- */                                                                                                                   // 228
-FS.HTTP.Handlers.PutInsert = function httpPutInsertHandler(ref) {                                                     // 229
-  var self = this;                                                                                                    // 230
-  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 231
-                                                                                                                      // 232
-  FS.debug && console.log("HTTP PUT (insert) handler");                                                               // 233
-                                                                                                                      // 234
-  // Create the nice FS.File                                                                                          // 235
-  var fileObj = new FS.File();                                                                                        // 236
-                                                                                                                      // 237
-  // Set its name                                                                                                     // 238
-  fileObj.name(opts.filename || null);                                                                                // 239
-                                                                                                                      // 240
-  // Attach the readstream as the file's data                                                                         // 241
-  fileObj.attachData(self.createReadStream(), {type: self.requestHeaders['content-type'] || 'application/octet-stream'});
-                                                                                                                      // 243
-  // Validate with insert allow/deny                                                                                  // 244
-  FS.Utility.validateAction(ref.collection.files._validators['insert'], fileObj, self.userId);                        // 245
-                                                                                                                      // 246
-  // Insert file into collection, triggering readStream storage                                                       // 247
-  ref.collection.insert(fileObj);                                                                                     // 248
-                                                                                                                      // 249
-  // Send response                                                                                                    // 250
-  self.setStatusCode(200);                                                                                            // 251
-                                                                                                                      // 252
-  // Return the new file id                                                                                           // 253
-  return {_id: fileObj._id};                                                                                          // 254
-};                                                                                                                    // 255
-                                                                                                                      // 256
-/**                                                                                                                   // 257
- * @method FS.HTTP.Handlers.PutUpdate                                                                                 // 258
- * @public                                                                                                            // 259
- * @returns {Object} response object with _id and chunk properties                                                    // 260
- *                                                                                                                    // 261
- * HTTP PUT file update chunk request handler                                                                         // 262
- */                                                                                                                   // 263
-FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) {                                                     // 264
-  var self = this;                                                                                                    // 265
-  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 266
-                                                                                                                      // 267
-  var chunk = parseInt(opts.chunk, 10);                                                                               // 268
-  if (isNaN(chunk)) chunk = 0;                                                                                        // 269
-                                                                                                                      // 270
-  FS.debug && console.log("HTTP PUT (update) handler received chunk: ", chunk);                                       // 271
-                                                                                                                      // 272
-  // Validate with insert allow/deny; also mounts and retrieves the file                                              // 273
-  FS.Utility.validateAction(ref.collection.files._validators['insert'], ref.file, self.userId);                       // 274
-                                                                                                                      // 275
-  self.createReadStream().pipe( FS.TempStore.createWriteStream(ref.file, chunk) );                                    // 276
-                                                                                                                      // 277
-  // Send response                                                                                                    // 278
-  self.setStatusCode(200);                                                                                            // 279
-                                                                                                                      // 280
-  return { _id: ref.file._id, chunk: chunk };                                                                         // 281
-};                                                                                                                    // 282
-                                                                                                                      // 283
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-}).call(this);
-
-
-
-
-
-
-(function () {
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                                                                                                    //
-// packages/cfs:access-point/access-point-server.js                                                                   //
-//                                                                                                                    //
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-                                                                                                                      //
-var path = Npm.require("path");                                                                                       // 1
-                                                                                                                      // 2
-HTTP.publishFormats({                                                                                                 // 3
-  fileRecordFormat: function (input) {                                                                                // 4
-    // Set the method scope content type to json                                                                      // 5
-    this.setContentType('application/json');                                                                          // 6
-    if (FS.Utility.isArray(input)) {                                                                                  // 7
-      return EJSON.stringify(FS.Utility.map(input, function (obj) {                                                   // 8
-        return FS.Utility.cloneFileRecord(obj);                                                                       // 9
-      }));                                                                                                            // 10
-    } else {                                                                                                          // 11
-      return EJSON.stringify(FS.Utility.cloneFileRecord(input));                                                      // 12
-    }                                                                                                                 // 13
-  }                                                                                                                   // 14
-});                                                                                                                   // 15
-                                                                                                                      // 16
-/**                                                                                                                   // 17
- * @method FS.HTTP.setHeadersForGet                                                                                   // 18
- * @public                                                                                                            // 19
- * @param {Array} headers - List of headers, where each is a two-item array in which item 1 is the header name and item 2 is the header value.
- * @param {Array|String} [collections] - Which collections the headers should be added for. Omit this argument to add the header for all collections.
- * @returns {undefined}                                                                                               // 22
- */                                                                                                                   // 23
-FS.HTTP.setHeadersForGet = function setHeadersForGet(headers, collections) {                                          // 24
-  if (typeof collections === "string") {                                                                              // 25
-    collections = [collections];                                                                                      // 26
-  }                                                                                                                   // 27
-  if (collections) {                                                                                                  // 28
-    FS.Utility.each(collections, function(collectionName) {                                                           // 29
-      getHeadersByCollection[collectionName] = headers || [];                                                         // 30
-    });                                                                                                               // 31
-  } else {                                                                                                            // 32
-    getHeaders = headers || [];                                                                                       // 33
-  }                                                                                                                   // 34
-};                                                                                                                    // 35
-                                                                                                                      // 36
-/**                                                                                                                   // 37
- * @method FS.HTTP.publish                                                                                            // 38
- * @public                                                                                                            // 39
- * @param {FS.Collection} collection                                                                                  // 40
- * @param {Function} func - Publish function that returns a cursor.                                                   // 41
- * @returns {undefined}                                                                                               // 42
- *                                                                                                                    // 43
- * Publishes all documents returned by the cursor at a GET URL                                                        // 44
- * with the format baseUrl/record/collectionName. The publish                                                         // 45
- * function `this` is similar to normal `Meteor.publish`.                                                             // 46
- */                                                                                                                   // 47
-FS.HTTP.publish = function fsHttpPublish(collection, func) {                                                          // 48
-  var name = baseUrl + '/record/' + collection.name;                                                                  // 49
-  // Mount collection listing URL using http-publish package                                                          // 50
-  HTTP.publish({                                                                                                      // 51
-    name: name,                                                                                                       // 52
-    defaultFormat: 'fileRecordFormat',                                                                                // 53
-    collection: collection,                                                                                           // 54
-    collectionGet: true,                                                                                              // 55
-    collectionPost: false,                                                                                            // 56
-    documentGet: true,                                                                                                // 57
-    documentPut: false,                                                                                               // 58
-    documentDelete: false                                                                                             // 59
-  }, func);                                                                                                           // 60
-                                                                                                                      // 61
-  FS.debug && console.log("Registered HTTP method GET URLs:\n\n" + name + '\n' + name + '/:id\n');                    // 62
-};                                                                                                                    // 63
-                                                                                                                      // 64
-/**                                                                                                                   // 65
- * @method FS.HTTP.unpublish                                                                                          // 66
- * @public                                                                                                            // 67
- * @param {FS.Collection} collection                                                                                  // 68
- * @returns {undefined}                                                                                               // 69
- *                                                                                                                    // 70
- * Unpublishes a restpoint created by a call to `FS.HTTP.publish`                                                     // 71
- */                                                                                                                   // 72
-FS.HTTP.unpublish = function fsHttpUnpublish(collection) {                                                            // 73
-  // Mount collection listing URL using http-publish package                                                          // 74
-  HTTP.unpublish(baseUrl + '/record/' + collection.name);                                                             // 75
-};                                                                                                                    // 76
-                                                                                                                      // 77
-_existingMountPoints = {};                                                                                            // 78
-                                                                                                                      // 79
-/**                                                                                                                   // 80
- * @method defaultSelectorFunction                                                                                    // 81
- * @private                                                                                                           // 82
- * @returns { collection, file }                                                                                      // 83
- *                                                                                                                    // 84
- * This is the default selector function                                                                              // 85
- */                                                                                                                   // 86
-var defaultSelectorFunction = function() {                                                                            // 87
-  var self = this;                                                                                                    // 88
-  // Selector function                                                                                                // 89
-  //                                                                                                                  // 90
-  // This function will have to return the collection and the                                                         // 91
-  // file. If file not found undefined is returned - if null is returned the                                          // 92
-  // search was not possible                                                                                          // 93
-  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 94
-                                                                                                                      // 95
-  // Get the collection name from the url                                                                             // 96
-  var collectionName = opts.collectionName;                                                                           // 97
-                                                                                                                      // 98
-  // Get the id from the url                                                                                          // 99
-  var id = opts.id;                                                                                                   // 100
-                                                                                                                      // 101
-  // Get the collection                                                                                               // 102
-  var collection = FS._collections[collectionName];                                                                   // 103
-                                                                                                                      // 104
-  // Get the file if possible else return null                                                                        // 105
-  var file = (id && collection)? collection.findOne({ _id: id }): null;                                               // 106
-                                                                                                                      // 107
-  // Return the collection and the file                                                                               // 108
-  return {                                                                                                            // 109
-    collection: collection,                                                                                           // 110
-    file: file,                                                                                                       // 111
-    storeName: opts.store,                                                                                            // 112
-    download: opts.download,                                                                                          // 113
-    filename: opts.filename                                                                                           // 114
-  };                                                                                                                  // 115
-};                                                                                                                    // 116
-                                                                                                                      // 117
-/*                                                                                                                    // 118
- * @method FS.HTTP.mount                                                                                              // 119
- * @public                                                                                                            // 120
- * @param {array of string} mountPoints mount points to map rest functinality on                                      // 121
- * @param {function} selector_f [selector] function returns `{ collection, file }` for mount points to work with      // 122
- *                                                                                                                    // 123
-*/                                                                                                                    // 124
-FS.HTTP.mount = function(mountPoints, selector_f) {                                                                   // 125
-  // We take mount points as an array and we get a selector function                                                  // 126
-  var selectorFunction = selector_f || defaultSelectorFunction;                                                       // 127
-                                                                                                                      // 128
-  var accessPoint = {                                                                                                 // 129
-    'stream': true,                                                                                                   // 130
-    'auth': expirationAuth,                                                                                           // 131
-    'post': function(data) {                                                                                          // 132
-      // Use the selector for finding the collection and file reference                                               // 133
-      var ref = selectorFunction.call(this);                                                                          // 134
-                                                                                                                      // 135
-      // We dont support post - this would be normal insert eg. of filerecord?                                        // 136
-      throw new Meteor.Error(501, "Not implemented", "Post is not supported");                                        // 137
-    },                                                                                                                // 138
-    'put': function(data) {                                                                                           // 139
-      // Use the selector for finding the collection and file reference                                               // 140
-      var ref = selectorFunction.call(this);                                                                          // 141
-                                                                                                                      // 142
-      // Make sure we have a collection reference                                                                     // 143
-      if (!ref.collection)                                                                                            // 144
-        throw new Meteor.Error(404, "Not Found", "No collection found");                                              // 145
-                                                                                                                      // 146
-      // Make sure we have a file reference                                                                           // 147
-      if (ref.file === null) {                                                                                        // 148
-        // No id supplied so we will create a new FS.File instance and                                                // 149
-        // insert the supplied data.                                                                                  // 150
-        return FS.HTTP.Handlers.PutInsert.apply(this, [ref]);                                                         // 151
-      } else {                                                                                                        // 152
-        if (ref.file) {                                                                                               // 153
-          return FS.HTTP.Handlers.PutUpdate.apply(this, [ref]);                                                       // 154
-        } else {                                                                                                      // 155
-          throw new Meteor.Error(404, "Not Found", 'No file found');                                                  // 156
-        }                                                                                                             // 157
-      }                                                                                                               // 158
-    },                                                                                                                // 159
-    'get': function(data) {                                                                                           // 160
-      // Use the selector for finding the collection and file reference                                               // 161
-      var ref = selectorFunction.call(this);                                                                          // 162
-                                                                                                                      // 163
-      // Make sure we have a collection reference                                                                     // 164
-      if (!ref.collection)                                                                                            // 165
-        throw new Meteor.Error(404, "Not Found", "No collection found");                                              // 166
-                                                                                                                      // 167
-      // Make sure we have a file reference                                                                           // 168
-      if (ref.file === null) {                                                                                        // 169
-        // No id supplied so we will return the published list of files ala                                           // 170
-        // http.publish in json format                                                                                // 171
-        return FS.HTTP.Handlers.GetList.apply(this, [ref]);                                                           // 172
-      } else {                                                                                                        // 173
-        if (ref.file) {                                                                                               // 174
-          return FS.HTTP.Handlers.Get.apply(this, [ref]);                                                             // 175
-        } else {                                                                                                      // 176
-          throw new Meteor.Error(404, "Not Found", 'No file found');                                                  // 177
-        }                                                                                                             // 178
-      }                                                                                                               // 179
-    },                                                                                                                // 180
-    'delete': function(data) {                                                                                        // 181
-      // Use the selector for finding the collection and file reference                                               // 182
-      var ref = selectorFunction.call(this);                                                                          // 183
-                                                                                                                      // 184
-      // Make sure we have a collection reference                                                                     // 185
-      if (!ref.collection)                                                                                            // 186
-        throw new Meteor.Error(404, "Not Found", "No collection found");                                              // 187
-                                                                                                                      // 188
-      // Make sure we have a file reference                                                                           // 189
-      if (ref.file) {                                                                                                 // 190
-        return FS.HTTP.Handlers.Del.apply(this, [ref]);                                                               // 191
-      } else {                                                                                                        // 192
-        throw new Meteor.Error(404, "Not Found", 'No file found');                                                    // 193
-      }                                                                                                               // 194
-    }                                                                                                                 // 195
-  };                                                                                                                  // 196
-                                                                                                                      // 197
-  var accessPoints = {};                                                                                              // 198
-                                                                                                                      // 199
-  // Add debug message                                                                                                // 200
-  FS.debug && console.log('Registered HTTP method URLs:');                                                            // 201
-                                                                                                                      // 202
-  FS.Utility.each(mountPoints, function(mountPoint) {                                                                 // 203
-    // Couple mountpoint and accesspoint                                                                              // 204
-    accessPoints[mountPoint] = accessPoint;                                                                           // 205
-    // Remember our mountpoints                                                                                       // 206
-    _existingMountPoints[mountPoint] = mountPoint;                                                                    // 207
-    // Add debug message                                                                                              // 208
-    FS.debug && console.log(mountPoint);                                                                              // 209
-  });                                                                                                                 // 210
-                                                                                                                      // 211
-  // XXX: HTTP:methods should unmount existing mounts in case of overwriting?                                         // 212
-  HTTP.methods(accessPoints);                                                                                         // 213
-                                                                                                                      // 214
-};                                                                                                                    // 215
-                                                                                                                      // 216
-/**                                                                                                                   // 217
- * @method FS.HTTP.unmount                                                                                            // 218
- * @public                                                                                                            // 219
- * @param {string | array of string} [mountPoints] Optional, if not specified all mountpoints are unmounted           // 220
- *                                                                                                                    // 221
- */                                                                                                                   // 222
-FS.HTTP.unmount = function(mountPoints) {                                                                             // 223
-  // The mountPoints is optional, can be string or array if undefined then                                            // 224
-  // _existingMountPoints will be used                                                                                // 225
-  var unmountList;                                                                                                    // 226
-  // Container for the mount points to unmount                                                                        // 227
-  var unmountPoints = {};                                                                                             // 228
-                                                                                                                      // 229
-  if (typeof mountPoints === 'undefined') {                                                                           // 230
-    // Use existing mount points - unmount all                                                                        // 231
-    unmountList = _existingMountPoints;                                                                               // 232
-  } else if (mountPoints === ''+mountPoints) {                                                                        // 233
-    // Got a string                                                                                                   // 234
-    unmountList = [mountPoints];                                                                                      // 235
-  } else if (mountPoints.length) {                                                                                    // 236
-    // Got an array                                                                                                   // 237
-    unmountList = mountPoints;                                                                                        // 238
-  }                                                                                                                   // 239
-                                                                                                                      // 240
-  // If we have a list to unmount                                                                                     // 241
-  if (unmountList) {                                                                                                  // 242
-    // Iterate over each item                                                                                         // 243
-    FS.Utility.each(unmountList, function(mountPoint) {                                                               // 244
-      // Check _existingMountPoints to make sure the mount point exists in our                                        // 245
-      // context / was created by the FS.HTTP.mount                                                                   // 246
-      if (_existingMountPoints[mountPoint]) {                                                                         // 247
-        // Mark as unmount                                                                                            // 248
-        unmountPoints[mountPoint] = false;                                                                            // 249
-        // Release                                                                                                    // 250
-        delete _existingMountPoints[mountPoint];                                                                      // 251
-      }                                                                                                               // 252
-    });                                                                                                               // 253
-    FS.debug && console.log('FS.HTTP.unmount:');                                                                      // 254
-    FS.debug && console.log(unmountPoints);                                                                           // 255
-    // Complete unmount                                                                                               // 256
-    HTTP.methods(unmountPoints);                                                                                      // 257
-  }                                                                                                                   // 258
-};                                                                                                                    // 259
-                                                                                                                      // 260
-// ### FS.Collection maps on HTTP pr. default on the following restpoints:                                            // 261
-// *                                                                                                                  // 262
-//    baseUrl + '/files/:collectionName/:id/:filename',                                                               // 263
-//    baseUrl + '/files/:collectionName/:id',                                                                         // 264
-//    baseUrl + '/files/:collectionName'                                                                              // 265
-//                                                                                                                    // 266
-// Change/ replace the existing mount point by:                                                                       // 267
-// ```js                                                                                                              // 268
-//   // unmount all existing                                                                                          // 269
-//   FS.HTTP.unmount();                                                                                               // 270
-//   // Create new mount point                                                                                        // 271
-//   FS.HTTP.mount([                                                                                                  // 272
-//    '/cfs/files/:collectionName/:id/:filename',                                                                     // 273
-//    '/cfs/files/:collectionName/:id',                                                                               // 274
-//    '/cfs/files/:collectionName'                                                                                    // 275
-//  ]);                                                                                                               // 276
-//  ```                                                                                                               // 277
-//                                                                                                                    // 278
-mountUrls = function mountUrls() {                                                                                    // 279
-  // We unmount first in case we are calling this a second time                                                       // 280
-  FS.HTTP.unmount();                                                                                                  // 281
-                                                                                                                      // 282
-  FS.HTTP.mount([                                                                                                     // 283
-    baseUrl + '/files/:collectionName/:id/:filename',                                                                 // 284
-    baseUrl + '/files/:collectionName/:id',                                                                           // 285
-    baseUrl + '/files/:collectionName'                                                                                // 286
-  ]);                                                                                                                 // 287
-};                                                                                                                    // 288
-                                                                                                                      // 289
-// Returns the userId from URL token                                                                                  // 290
-var expirationAuth = function expirationAuth() {                                                                      // 291
-  var self = this;                                                                                                    // 292
-                                                                                                                      // 293
-  // Read the token from '/hello?token=base64'                                                                        // 294
-  var encodedToken = self.query.token;                                                                                // 295
-                                                                                                                      // 296
-  FS.debug && console.log("token: "+encodedToken);                                                                    // 297
-                                                                                                                      // 298
-  if (!encodedToken || !Meteor.users) return false;                                                                   // 299
-                                                                                                                      // 300
-  // Check the userToken before adding it to the db query                                                             // 301
-  // Set the this.userId                                                                                              // 302
-  var tokenString = FS.Utility.atob(encodedToken);                                                                    // 303
-                                                                                                                      // 304
-  var tokenObject;                                                                                                    // 305
-  try {                                                                                                               // 306
-    tokenObject = JSON.parse(tokenString);                                                                            // 307
-  } catch(err) {                                                                                                      // 308
-    throw new Meteor.Error(400, 'Bad Request');                                                                       // 309
-  }                                                                                                                   // 310
-                                                                                                                      // 311
-  // XXX: Do some check here of the object                                                                            // 312
-  var userToken = tokenObject.authToken;                                                                              // 313
-  if (userToken !== ''+userToken) {                                                                                   // 314
-    throw new Meteor.Error(400, 'Bad Request');                                                                       // 315
-  }                                                                                                                   // 316
-                                                                                                                      // 317
-  // If we have an expiration token we should check that it's still valid                                             // 318
-  if (tokenObject.expiration != null) {                                                                               // 319
-    // check if its too old                                                                                           // 320
-    var now = Date.now();                                                                                             // 321
-    if (tokenObject.expiration < now) {                                                                               // 322
-      FS.debug && console.log('Expired token: ' + tokenObject.expiration + ' is less than ' + now);                   // 323
-      throw new Meteor.Error(500, 'Expired token');                                                                   // 324
-    }                                                                                                                 // 325
-  }                                                                                                                   // 326
-                                                                                                                      // 327
-  // We are not on a secure line - so we have to look up the user...                                                  // 328
-  var user = Meteor.users.findOne({                                                                                   // 329
-    $or: [                                                                                                            // 330
-      {'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(userToken)},                               // 331
-      {'services.resume.loginTokens.token': userToken}                                                                // 332
-    ]                                                                                                                 // 333
-  });                                                                                                                 // 334
-                                                                                                                      // 335
-  // Set the userId in the scope                                                                                      // 336
-  return user && user._id;                                                                                            // 337
-};                                                                                                                    // 338
-                                                                                                                      // 339
-HTTP.methods(                                                                                                         // 340
-  {'/cfs/servertime': {                                                                                               // 341
-    get: function(data) {                                                                                             // 342
-      return Date.now().toString();                                                                                   // 343
-    }                                                                                                                 // 344
-  }                                                                                                                   // 345
-});                                                                                                                   // 346
-                                                                                                                      // 347
-// Unify client / server api                                                                                          // 348
-FS.HTTP.now = function() {                                                                                            // 349
-  return Date.now();                                                                                                  // 350
-};                                                                                                                    // 351
-                                                                                                                      // 352
-// Start up the basic mount points                                                                                    // 353
-Meteor.startup(function () {                                                                                          // 354
-  mountUrls();                                                                                                        // 355
-});                                                                                                                   // 356
-                                                                                                                      // 357
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-}).call(this);
-
-///////////////////////////////////////////////////////////////////////
-
-}).call(this);
-
-
-/* Exports */
-if (typeof Package === 'undefined') Package = {};
-Package['cfs:access-point'] = {};
-
-})();

+ 0 - 238
.snap-meteor-1.8/export.js

@@ -1,238 +0,0 @@
-/* global JsonRoutes */
-if (Meteor.isServer) {
-  // todo XXX once we have a real API in place, move that route there
-  // todo XXX also  share the route definition between the client and the server
-  // so that we could use something like
-  // `ApiRoutes.path('boards/export', boardId)``
-  // on the client instead of copy/pasting the route path manually between the
-  // client and the server.
-  /**
-   * @operation export
-   * @tag Boards
-   *
-   * @summary This route is used to export the board.
-   *
-   * @description If user is already logged-in, pass loginToken as param
-   * "authToken": '/api/boards/:boardId/export?authToken=:token'
-   *
-   * See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
-   * for detailed explanations
-   *
-   * @param {string} boardId the ID of the board we are exporting
-   * @param {string} authToken the loginToken
-   */
-  JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) {
-    const boardId = req.params.boardId;
-    let user = null;
-
-    const loginToken = req.query.authToken;
-    if (loginToken) {
-      const hashToken = Accounts._hashLoginToken(loginToken);
-      user = Meteor.users.findOne({
-        'services.resume.loginTokens.hashedToken': hashToken,
-      });
-    } else if (!Meteor.settings.public.sandstorm) {
-      Authentication.checkUserId(req.userId);
-      user = Users.findOne({ _id: req.userId, isAdmin: true });
-    }
-
-    const exporter = new Exporter(boardId);
-    if (exporter.canExport(user)) {
-      JsonRoutes.sendResult(res, {
-        code: 200,
-        data: exporter.build(),
-      });
-    } else {
-      // we could send an explicit error message, but on the other hand the only
-      // way to get there is by hacking the UI so let's keep it raw.
-      JsonRoutes.sendResult(res, 403);
-    }
-  });
-}
-
-// exporter maybe is broken since Gridfs introduced, add fs and path
-
-export class Exporter {
-  constructor(boardId) {
-    this._boardId = boardId;
-  }
-
-  build() {
-    const fs = Npm.require('fs');
-    const os = Npm.require('os');
-    const path = Npm.require('path');
-
-    const byBoard = { boardId: this._boardId };
-    const byBoardNoLinked = {
-      boardId: this._boardId,
-      linkedId: { $in: ['', null] },
-    };
-    // we do not want to retrieve boardId in related elements
-    const noBoardId = {
-      fields: {
-        boardId: 0,
-      },
-    };
-    const result = {
-      _format: 'wekan-board-1.0.0',
-    };
-    _.extend(
-      result,
-      Boards.findOne(this._boardId, {
-        fields: {
-          stars: 0,
-        },
-      }),
-    );
-    result.lists = Lists.find(byBoard, noBoardId).fetch();
-    result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
-    result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
-    result.customFields = CustomFields.find(
-      { boardIds: { $in: [this.boardId] } },
-      { fields: { boardId: 0 } },
-    ).fetch();
-    result.comments = CardComments.find(byBoard, noBoardId).fetch();
-    result.activities = Activities.find(byBoard, noBoardId).fetch();
-    result.rules = Rules.find(byBoard, noBoardId).fetch();
-    result.checklists = [];
-    result.checklistItems = [];
-    result.subtaskItems = [];
-    result.triggers = [];
-    result.actions = [];
-    result.cards.forEach(card => {
-      result.checklists.push(
-        ...Checklists.find({
-          cardId: card._id,
-        }).fetch(),
-      );
-      result.checklistItems.push(
-        ...ChecklistItems.find({
-          cardId: card._id,
-        }).fetch(),
-      );
-      result.subtaskItems.push(
-        ...Cards.find({
-          parentId: card._id,
-        }).fetch(),
-      );
-    });
-    result.rules.forEach(rule => {
-      result.triggers.push(
-        ...Triggers.find(
-          {
-            _id: rule.triggerId,
-          },
-          noBoardId,
-        ).fetch(),
-      );
-      result.actions.push(
-        ...Actions.find(
-          {
-            _id: rule.actionId,
-          },
-          noBoardId,
-        ).fetch(),
-      );
-    });
-
-    // [Old] for attachments we only export IDs and absolute url to original doc
-    // [New] Encode attachment to base64
-    const getBase64Data = function(doc, callback) {
-      let buffer = new Buffer(0);
-      // callback has the form function (err, res) {}
-      const tmpFile = path.join(
-        os.tmpdir(),
-        `tmpexport${process.pid}${Math.random()}`,
-      );
-      const tmpWriteable = fs.createWriteStream(tmpFile);
-      const readStream = doc.createReadStream();
-      readStream.on('data', function(chunk) {
-        buffer = Buffer.concat([buffer, chunk]);
-      });
-      readStream.on('error', function(err) {
-        callback(err, null);
-      });
-      readStream.on('end', function() {
-        // done
-        fs.unlink(tmpFile, () => {
-          //ignored
-        });
-        callback(null, buffer.toString('base64'));
-      });
-      readStream.pipe(tmpWriteable);
-    };
-    const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
-    result.attachments = Attachments.find(byBoard)
-      .fetch()
-      .map(attachment => {
-        return {
-          _id: attachment._id,
-          cardId: attachment.cardId,
-          // url: FlowRouter.url(attachment.url()),
-          file: getBase64DataSync(attachment),
-          name: attachment.original.name,
-          type: attachment.original.type,
-        };
-      });
-
-    // we also have to export some user data - as the other elements only
-    // include id but we have to be careful:
-    // 1- only exports users that are linked somehow to that board
-    // 2- do not export any sensitive information
-    const users = {};
-    result.members.forEach(member => {
-      users[member.userId] = true;
-    });
-    result.lists.forEach(list => {
-      users[list.userId] = true;
-    });
-    result.cards.forEach(card => {
-      users[card.userId] = true;
-      if (card.members) {
-        card.members.forEach(memberId => {
-          users[memberId] = true;
-        });
-      }
-    });
-    result.comments.forEach(comment => {
-      users[comment.userId] = true;
-    });
-    result.activities.forEach(activity => {
-      users[activity.userId] = true;
-    });
-    result.checklists.forEach(checklist => {
-      users[checklist.userId] = true;
-    });
-    const byUserIds = {
-      _id: {
-        $in: Object.getOwnPropertyNames(users),
-      },
-    };
-    // we use whitelist to be sure we do not expose inadvertently
-    // some secret fields that gets added to User later.
-    const userFields = {
-      fields: {
-        _id: 1,
-        username: 1,
-        'profile.fullname': 1,
-        'profile.initials': 1,
-        'profile.avatarUrl': 1,
-      },
-    };
-    result.users = Users.find(byUserIds, userFields)
-      .fetch()
-      .map(user => {
-        // user avatar is stored as a relative url, we export absolute
-        if ((user.profile || {}).avatarUrl) {
-          user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
-        }
-        return user;
-      });
-    return result;
-  }
-
-  canExport(user) {
-    const board = Boards.findOne(this._boardId);
-    return board && board.isVisibleBy(user);
-  }
-}

+ 0 - 584
.snap-meteor-1.8/ldap.js

@@ -1,584 +0,0 @@
-import ldapjs from 'ldapjs';
-import util from 'util';
-import Bunyan from 'bunyan';
-import {log_debug, log_info, log_warn, log_error} from './logger';
-
-
-export default class LDAP {
-  constructor() {
-    this.ldapjs = ldapjs;
-
-    this.connected = false;
-
-    this.options = {
-      host                               : this.constructor.settings_get('LDAP_HOST'),
-      port                               : this.constructor.settings_get('LDAP_PORT'),
-      Reconnect                          : this.constructor.settings_get('LDAP_RECONNECT'),
-      timeout                            : this.constructor.settings_get('LDAP_TIMEOUT'),
-      connect_timeout                    : this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
-      idle_timeout                       : this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
-      encryption                         : this.constructor.settings_get('LDAP_ENCRYPTION'),
-      ca_cert                            : this.constructor.settings_get('LDAP_CA_CERT'),
-      reject_unauthorized                : this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
-      Authentication                     : this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
-      Authentication_UserDN              : this.constructor.settings_get('LDAP_AUTHENTIFICATION_USERDN'),
-      Authentication_Password            : this.constructor.settings_get('LDAP_AUTHENTIFICATION_PASSWORD'),
-      Authentication_Fallback            : this.constructor.settings_get('LDAP_LOGIN_FALLBACK'),
-      BaseDN                             : this.constructor.settings_get('LDAP_BASEDN'),
-      Internal_Log_Level                 : this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
-      User_Authentication                : this.constructor.settings_get('LDAP_USER_AUTHENTICATION'),
-      User_Authentication_Field          : this.constructor.settings_get('LDAP_USER_AUTHENTICATION_FIELD'),
-      User_Attributes                    : this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
-      User_Search_Filter                 : this.constructor.settings_get('LDAP_USER_SEARCH_FILTER'),
-      User_Search_Scope                  : this.constructor.settings_get('LDAP_USER_SEARCH_SCOPE'),
-      User_Search_Field                  : this.constructor.settings_get('LDAP_USER_SEARCH_FIELD'),
-      Search_Page_Size                   : this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
-      Search_Size_Limit                  : this.constructor.settings_get('LDAP_SEARCH_SIZE_LIMIT'),
-      group_filter_enabled               : this.constructor.settings_get('LDAP_GROUP_FILTER_ENABLE'),
-      group_filter_object_class          : this.constructor.settings_get('LDAP_GROUP_FILTER_OBJECTCLASS'),
-      group_filter_group_id_attribute    : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE'),
-      group_filter_group_member_attribute: this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE'),
-      group_filter_group_member_format   : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT'),
-      group_filter_group_name            : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_NAME'),
-    };
-  }
-
-  static settings_get(name, ...args) {
-    let value = process.env[name];
-    if (value !== undefined) {
-      if (value === 'true' || value === 'false') {
-        value = JSON.parse(value);
-      } else if (value !== '' && !isNaN(value)) {
-        value = Number(value);
-      }
-      return value;
-    } else {
-      log_warn(`Lookup for unset variable: ${name}`);
-    }
-  }
-
-  connectSync(...args) {
-     if (!this._connectSync) {
-      this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
-    }
-    return this._connectSync(...args);
-  }
-
-  searchAllSync(...args) {
-
-    if (!this._searchAllSync) {
-      this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
-    }
-    return this._searchAllSync(...args);
-  }
-
-  connectAsync(callback) {
-    log_info('Init setup');
-
-    let replied = false;
-
-    const connectionOptions = {
-      url           : `${this.options.host}:${this.options.port}`,
-      timeout       : this.options.timeout,
-      connectTimeout: this.options.connect_timeout,
-      idleTimeout   : this.options.idle_timeout,
-      reconnect     : this.options.Reconnect,
-    };
-
-    if (this.options.Internal_Log_Level !== 'disabled') {
-      connectionOptions.log = new Bunyan({
-        name     : 'ldapjs',
-        component: 'client',
-        stream   : process.stderr,
-        level    : this.options.Internal_Log_Level,
-      });
-    }
-
-    const tlsOptions = {
-      rejectUnauthorized: this.options.reject_unauthorized,
-    };
-
-    if (this.options.ca_cert && this.options.ca_cert !== '') {
-      // Split CA cert into array of strings
-      const chainLines = this.constructor.settings_get('LDAP_CA_CERT').split('\n');
-      let cert         = [];
-      const ca         = [];
-      chainLines.forEach((line) => {
-        cert.push(line);
-        if (line.match(/-END CERTIFICATE-/)) {
-          ca.push(cert.join('\n'));
-          cert = [];
-        }
-      });
-      tlsOptions.ca = ca;
-    }
-
-    if (this.options.encryption === 'ssl') {
-      connectionOptions.url        = `ldaps://${connectionOptions.url}`;
-      connectionOptions.tlsOptions = tlsOptions;
-    } else {
-      connectionOptions.url = `ldap://${connectionOptions.url}`;
-    }
-
-    log_info('Connecting', connectionOptions.url);
-    log_debug(`connectionOptions${util.inspect(connectionOptions)}`);
-
-    this.client = ldapjs.createClient(connectionOptions);
-
-    this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
-
-    this.client.on('error', (error) => {
-      log_error('connection', error);
-      if (replied === false) {
-        replied = true;
-        callback(error, null);
-      }
-    });
-
-    this.client.on('idle', () => {
-      log_info('Idle');
-      this.disconnect();
-    });
-
-    this.client.on('close', () => {
-      log_info('Closed');
-    });
-
-    if (this.options.encryption === 'tls') {
-      // Set host parameter for tls.connect which is used by ldapjs starttls. This shouldn't be needed in newer nodejs versions (e.g v5.6.0).
-      // https://github.com/RocketChat/Rocket.Chat/issues/2035
-      // https://github.com/mcavage/node-ldapjs/issues/349
-      tlsOptions.host = this.options.host;
-
-      log_info('Starting TLS');
-      log_debug('tlsOptions', tlsOptions);
-
-      this.client.starttls(tlsOptions, null, (error, response) => {
-        if (error) {
-          log_error('TLS connection', error);
-          if (replied === false) {
-            replied = true;
-            callback(error, null);
-          }
-          return;
-        }
-
-        log_info('TLS connected');
-        this.connected = true;
-        if (replied === false) {
-          replied = true;
-          callback(null, response);
-        }
-      });
-    } else {
-      this.client.on('connect', (response) => {
-        log_info('LDAP connected');
-        this.connected = true;
-        if (replied === false) {
-          replied = true;
-          callback(null, response);
-        }
-      });
-    }
-
-    setTimeout(() => {
-      if (replied === false) {
-        log_error('connection time out', connectionOptions.connectTimeout);
-        replied = true;
-        callback(new Error('Timeout'));
-      }
-    }, connectionOptions.connectTimeout);
-  }
-
-  getUserFilter(username) {
-    const filter = [];
-
-    if (this.options.User_Search_Filter !== '') {
-      if (this.options.User_Search_Filter[0] === '(') {
-        filter.push(`${this.options.User_Search_Filter}`);
-      } else {
-        filter.push(`(${this.options.User_Search_Filter})`);
-      }
-    }
-
-    const usernameFilter = this.options.User_Search_Field.split(',').map((item) => `(${item}=${username})`);
-
-    if (usernameFilter.length === 0) {
-      log_error('LDAP_LDAP_User_Search_Field not defined');
-    } else if (usernameFilter.length === 1) {
-      filter.push(`${usernameFilter[0]}`);
-    } else {
-      filter.push(`(|${usernameFilter.join('')})`);
-    }
-
-    return `(&${filter.join('')})`;
-  }
-
-  bindUserIfNecessary(username, password) {
-
-    if (this.domainBinded === true) {
-      return;
-    }
-
-    if (!this.options.User_Authentication) {
-      return;
-    }
-
-
-    if (!this.options.BaseDN) throw new Error('BaseDN is not provided');
-
-    const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`;
-
-    this.bindSync(userDn, password);
-    this.domainBinded = true;
-  }
-
-  bindIfNecessary() {
-    if (this.domainBinded === true) {
-      return;
-    }
-
-    if (this.options.Authentication !== true) {
-      return;
-    }
-
-    log_info('Binding UserDN', this.options.Authentication_UserDN);
-
-    this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password);
-    this.domainBinded = true;
-  }
-
-  searchUsersSync(username, page) {
-    this.bindIfNecessary();
-    const searchOptions = {
-      filter   : this.getUserFilter(username),
-      scope    : this.options.User_Search_Scope || 'sub',
-      sizeLimit: this.options.Search_Size_Limit,
-    };
-
-    if (!!this.options.User_Attributes) searchOptions.attributes = this.options.User_Attributes.split(',');
-
-    if (this.options.Search_Page_Size > 0) {
-      searchOptions.paged = {
-        pageSize : this.options.Search_Page_Size,
-        pagePause: !!page,
-      };
-    }
-
-    log_info('Searching user', username);
-    log_debug('searchOptions', searchOptions);
-    log_debug('BaseDN', this.options.BaseDN);
-
-    if (page) {
-      return this.searchAllPaged(this.options.BaseDN, searchOptions, page);
-    }
-
-    return this.searchAllSync(this.options.BaseDN, searchOptions);
-  }
-
-  getUserByIdSync(id, attribute) {
-    this.bindIfNecessary();
-
-    const Unique_Identifier_Field = this.constructor.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD').split(',');
-
-    let filter;
-
-    if (attribute) {
-      filter = new this.ldapjs.filters.EqualityFilter({
-        attribute,
-        value: new Buffer(id, 'hex'),
-      });
-    } else {
-      const filters = [];
-      Unique_Identifier_Field.forEach((item) => {
-        filters.push(new this.ldapjs.filters.EqualityFilter({
-          attribute: item,
-          value    : new Buffer(id, 'hex'),
-        }));
-      });
-
-      filter = new this.ldapjs.filters.OrFilter({ filters });
-    }
-
-    const searchOptions = {
-      filter,
-      scope: 'sub',
-    };
-
-    log_info('Searching by id', id);
-    log_debug('search filter', searchOptions.filter.toString());
-    log_debug('BaseDN', this.options.BaseDN);
-
-    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
-
-    if (!Array.isArray(result) || result.length === 0) {
-      return;
-    }
-
-    if (result.length > 1) {
-      log_error('Search by id', id, 'returned', result.length, 'records');
-    }
-
-    return result[0];
-  }
-
-  getUserByUsernameSync(username) {
-    this.bindIfNecessary();
-
-    const searchOptions = {
-      filter: this.getUserFilter(username),
-      scope : this.options.User_Search_Scope || 'sub',
-    };
-
-    log_info('Searching user', username);
-    log_debug('searchOptions', searchOptions);
-    log_debug('BaseDN', this.options.BaseDN);
-
-    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
-
-    if (!Array.isArray(result) || result.length === 0) {
-      return;
-    }
-
-    if (result.length > 1) {
-      log_error('Search by username', username, 'returned', result.length, 'records');
-    }
-
-    return result[0];
-  }
-
-  getUserGroups(username, ldapUser) {
-    if (!this.options.group_filter_enabled) {
-      return true;
-    }
-
-    const filter = ['(&'];
-
-    if (this.options.group_filter_object_class !== '') {
-      filter.push(`(objectclass=${this.options.group_filter_object_class})`);
-    }
-
-    if (this.options.group_filter_group_member_attribute !== '') {
-      const format_value = ldapUser[this.options.group_filter_group_member_format];
-      if (format_value) {
-        filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
-      }
-    }
-
-    filter.push(')');
-
-    const searchOptions = {
-      filter: filter.join('').replace(/#{username}/g, username),
-      scope : 'sub',
-    };
-
-    log_debug('Group list filter LDAP:', searchOptions.filter);
-
-    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
-
-    if (!Array.isArray(result) || result.length === 0) {
-      return [];
-    }
-
-    const grp_identifier = this.options.group_filter_group_id_attribute || 'cn';
-    const groups         = [];
-    result.map((item) => {
-      groups.push(item[grp_identifier]);
-    });
-    log_debug(`Groups: ${groups.join(', ')}`);
-    return groups;
-
-  }
-
-  isUserInGroup(username, ldapUser) {
-    if (!this.options.group_filter_enabled) {
-      return true;
-    }
-
-    const grps = this.getUserGroups(username, ldapUser);
-
-    const filter = ['(&'];
-
-    if (this.options.group_filter_object_class !== '') {
-      filter.push(`(objectclass=${this.options.group_filter_object_class})`);
-    }
-
-    if (this.options.group_filter_group_member_attribute !== '') {
-      const format_value = ldapUser[this.options.group_filter_group_member_format];
-      if (format_value) {
-        filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
-      }
-    }
-
-    if (this.options.group_filter_group_id_attribute !== '') {
-      filter.push(`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`);
-    }
-    filter.push(')');
-
-    const searchOptions = {
-      filter: filter.join('').replace(/#{username}/g, username),
-      scope : 'sub',
-    };
-
-    log_debug('Group filter LDAP:', searchOptions.filter);
-
-    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
-
-    if (!Array.isArray(result) || result.length === 0) {
-      return false;
-    }
-    return true;
-  }
-
-  extractLdapEntryData(entry) {
-    const values = {
-      _raw: entry.raw,
-    };
-
-    Object.keys(values._raw).forEach((key) => {
-      const value = values._raw[key];
-
-      if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) {
-        if (value instanceof Buffer) {
-          values[key] = value.toString();
-        } else {
-          values[key] = value;
-        }
-      }
-    });
-
-    return values;
-  }
-
-  searchAllPaged(BaseDN, options, page) {
-    this.bindIfNecessary();
-
-    const processPage = ({ entries, title, end, next }) => {
-      log_info(title);
-      // Force LDAP idle to wait the record processing
-      this.client._updateIdle(true);
-      page(null, entries, {
-        end, next: () => {
-          // Reset idle timer
-          this.client._updateIdle();
-          next && next();
-        }
-      });
-    };
-
-    this.client.search(BaseDN, options, (error, res) => {
-      if (error) {
-        log_error(error);
-        page(error);
-        return;
-      }
-
-      res.on('error', (error) => {
-        log_error(error);
-        page(error);
-        return;
-      });
-
-      let entries = [];
-
-      const internalPageSize = options.paged && options.paged.pageSize > 0 ? options.paged.pageSize * 2 : 500;
-
-      res.on('searchEntry', (entry) => {
-        entries.push(this.extractLdapEntryData(entry));
-
-        if (entries.length >= internalPageSize) {
-          processPage({
-            entries,
-            title: 'Internal Page',
-            end  : false,
-          });
-          entries = [];
-        }
-      });
-
-      res.on('page', (result, next) => {
-        if (!next) {
-          this.client._updateIdle(true);
-          processPage({
-            entries,
-            title: 'Final Page',
-            end  : true,
-          });
-        } else if (entries.length) {
-          log_info('Page');
-          processPage({
-            entries,
-            title: 'Page',
-            end  : false,
-            next,
-          });
-          entries = [];
-        }
-      });
-
-      res.on('end', () => {
-        if (entries.length) {
-          processPage({
-            entries,
-            title: 'Final Page',
-            end  : true,
-          });
-          entries = [];
-        }
-      });
-    });
-  }
-
-  searchAllAsync(BaseDN, options, callback) {
-    this.bindIfNecessary();
-
-    this.client.search(BaseDN, options, (error, res) => {
-      if (error) {
-        log_error(error);
-        callback(error);
-        return;
-      }
-
-      res.on('error', (error) => {
-        log_error(error);
-        callback(error);
-        return;
-      });
-
-      const entries = [];
-
-      res.on('searchEntry', (entry) => {
-        entries.push(this.extractLdapEntryData(entry));
-      });
-
-      res.on('end', () => {
-        log_info('Search result count', entries.length);
-        callback(null, entries);
-      });
-    });
-  }
-
-  authSync(dn, password) {
-    log_info('Authenticating', dn);
-
-    try {
-      if (password === '') {
-        throw new Error('Password is not provided');
-      }
-      this.bindSync(dn, password);
-      log_info('Authenticated', dn);
-      return true;
-    } catch (error) {
-      log_info('Not authenticated', dn);
-      log_debug('error', error);
-      return false;
-    }
-  }
-
-  disconnect() {
-    this.connected    = false;
-    this.domainBinded = false;
-    log_info('Disconecting');
-    this.client.unbind();
-  }
-}

+ 0 - 149
.snap-meteor-1.8/oidc_server.js

@@ -1,149 +0,0 @@
-Oidc = {};
-
-OAuth.registerService('oidc', 2, null, function (query) {
-
-  var debug = process.env.DEBUG || false;
-  var token = getToken(query);
-  if (debug) console.log('XXX: register token:', token);
-
-  var accessToken = token.access_token || token.id_token;
-  var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10));
-
-  var userinfo = getUserInfo(accessToken);
-  if (debug) console.log('XXX: userinfo:', userinfo);
-
-  var serviceData = {};
-  serviceData.id = userinfo[process.env.OAUTH2_ID_MAP]; // || userinfo["id"];
-  serviceData.username = userinfo[process.env.OAUTH2_USERNAME_MAP]; // || userinfo["uid"];
-  serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
-  serviceData.accessToken = accessToken;
-  serviceData.expiresAt = expiresAt;
-  serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
-
-  if (accessToken) {
-    var tokenContent = getTokenContent(accessToken);
-    var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
-    _.extend(serviceData, fields);
-  }
-
-  if (token.refresh_token)
-    serviceData.refreshToken = token.refresh_token;
-  if (debug) console.log('XXX: serviceData:', serviceData);
-
-  var profile = {};
-  profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
-  profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
-  if (debug) console.log('XXX: profile:', profile);
-
-  return {
-    serviceData: serviceData,
-    options: { profile: profile }
-  };
-});
-
-var userAgent = "Meteor";
-if (Meteor.release) {
-  userAgent += "/" + Meteor.release;
-}
-
-var getToken = function (query) {
-  var debug = process.env.DEBUG || false;
-  var config = getConfiguration();
-  if(config.tokenEndpoint.includes('https://')){
-    var serverTokenEndpoint = config.tokenEndpoint;
-  }else{
-    var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
-  }
-  var requestPermissions = config.requestPermissions;
-  var response;
-
-  try {
-    response = HTTP.post(
-      serverTokenEndpoint,
-      {
-        headers: {
-          Accept: 'application/json',
-          "User-Agent": userAgent
-        },
-        params: {
-          code: query.code,
-          client_id: config.clientId,
-          client_secret: OAuth.openSecret(config.secret),
-          redirect_uri: OAuth._redirectUri('oidc', config),
-          grant_type: 'authorization_code',
-          scope: requestPermissions,
-          state: query.state
-        }
-      }
-    );
-  } catch (err) {
-    throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
-      { response: err.response });
-  }
-  if (response.data.error) {
-    // if the http response was a json object with an error attribute
-    throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
-  } else {
-    if (debug) console.log('XXX: getToken response: ', response.data);
-    return response.data;
-  }
-};
-
-var getUserInfo = function (accessToken) {
-  var debug = process.env.DEBUG || false;
-  var config = getConfiguration();
-  // Some userinfo endpoints use a different base URL than the authorization or token endpoints.
-  // This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
-  if (config.userinfoEndpoint.includes("https://")) {
-    var serverUserinfoEndpoint = config.userinfoEndpoint;
-  } else {
-    var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
-  }
-  var response;
-  try {
-    response = HTTP.get(
-      serverUserinfoEndpoint,
-      {
-        headers: {
-          "User-Agent": userAgent,
-          "Authorization": "Bearer " + accessToken
-        }
-      }
-    );
-  } catch (err) {
-    throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
-                   {response: err.response});
-  }
-  if (debug) console.log('XXX: getUserInfo response: ', response.data);
-  return response.data;
-};
-
-var getConfiguration = function () {
-  var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
-  if (!config) {
-    throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
-  }
-  return config;
-};
-
-var getTokenContent = function (token) {
-  var content = null;
-  if (token) {
-    try {
-      var parts = token.split('.');
-      var header = JSON.parse(new Buffer(parts[0], 'base64').toString());
-      content = JSON.parse(new Buffer(parts[1], 'base64').toString());
-      var signature = new Buffer(parts[2], 'base64');
-      var signed = parts[0] + '.' + parts[1];
-    } catch (err) {
-      this.content = {
-        exp: 0
-      };
-    }
-  }
-  return content;
-}
-
-Oidc.retrieveCredential = function (credentialToken, credentialSecret) {
-  return OAuth.retrieveCredential(credentialToken, credentialSecret);
-};

+ 0 - 5184
.snap-meteor-1.8/package-lock.json

@@ -1,5184 +0,0 @@
-{
-  "name": "wekan",
-  "version": "v3.78.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "@babel/code-frame": {
-      "version": "7.5.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
-      "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
-      "dev": true,
-      "requires": {
-        "@babel/highlight": "^7.0.0"
-      }
-    },
-    "@babel/highlight": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
-      "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.0.0",
-        "esutils": "^2.0.2",
-        "js-tokens": "^4.0.0"
-      }
-    },
-    "@babel/runtime": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz",
-      "integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==",
-      "requires": {
-        "regenerator-runtime": "^0.13.2"
-      }
-    },
-    "@samverschueren/stream-to-observable": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
-      "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==",
-      "dev": true,
-      "requires": {
-        "any-observable": "^0.3.0"
-      }
-    },
-    "abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
-    },
-    "acorn": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
-      "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
-      "dev": true
-    },
-    "acorn-jsx": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
-      "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
-      "dev": true
-    },
-    "ajv": {
-      "version": "5.5.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-      "requires": {
-        "co": "^4.6.0",
-        "fast-deep-equal": "^1.0.0",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.3.0"
-      }
-    },
-    "ajv-keywords": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
-      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
-      "dev": true
-    },
-    "ansi-escapes": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz",
-      "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==",
-      "dev": true,
-      "requires": {
-        "type-fest": "^0.5.2"
-      }
-    },
-    "ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
-    },
-    "ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "requires": {
-        "color-convert": "^1.9.0"
-      }
-    },
-    "any-observable": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
-      "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==",
-      "dev": true
-    },
-    "aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
-    },
-    "are-we-there-yet": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
-      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
-      "requires": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
-    "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dev": true,
-      "requires": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "arr-diff": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-      "dev": true
-    },
-    "arr-flatten": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-      "dev": true
-    },
-    "arr-union": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
-      "dev": true
-    },
-    "array-includes": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
-      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "es-abstract": "^1.7.0"
-      }
-    },
-    "array-unique": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-      "dev": true
-    },
-    "asn1": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
-      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
-    },
-    "assign-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
-      "dev": true
-    },
-    "astral-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
-      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
-      "dev": true
-    },
-    "atob": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-      "dev": true
-    },
-    "babel-code-frame": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "esutils": "^2.0.2",
-        "js-tokens": "^3.0.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "js-tokens": {
-          "version": "3.0.2",
-          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-          "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
-    "babel-runtime": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
-      "requires": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.11.0"
-      },
-      "dependencies": {
-        "regenerator-runtime": {
-          "version": "0.11.1",
-          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
-          "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
-        }
-      }
-    },
-    "backoff": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
-      "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=",
-      "requires": {
-        "precond": "0.2"
-      }
-    },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
-    },
-    "base": {
-      "version": "0.11.2",
-      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
-      "dev": true,
-      "requires": {
-        "cache-base": "^1.0.1",
-        "class-utils": "^0.3.5",
-        "component-emitter": "^1.2.1",
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.1",
-        "mixin-deep": "^1.2.0",
-        "pascalcase": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "base64-js": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
-      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
-    },
-    "bcrypt": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.6.tgz",
-      "integrity": "sha512-taA5bCTfXe7FUjKroKky9EXpdhkVvhE5owfxfLYodbrAR1Ul3juLmIQmIQBK4L9a5BuUcE6cqmwT+Da20lF9tg==",
-      "requires": {
-        "nan": "2.13.2",
-        "node-pre-gyp": "0.12.0"
-      }
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "braces": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-      "dev": true,
-      "requires": {
-        "arr-flatten": "^1.1.0",
-        "array-unique": "^0.3.2",
-        "extend-shallow": "^2.0.1",
-        "fill-range": "^4.0.0",
-        "isobject": "^3.0.1",
-        "repeat-element": "^1.1.2",
-        "snapdragon": "^0.8.1",
-        "snapdragon-node": "^2.0.1",
-        "split-string": "^3.0.2",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "bson": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/bson/-/bson-4.0.2.tgz",
-      "integrity": "sha512-rBdCxMBCg2aR420e1oKUejjcuPZLTibA7zEhWAlliFWEwzuBCC9Dkp5r7VFFIQB2t1WVsvTbohry575mc7Xw5A==",
-      "requires": {
-        "buffer": "^5.1.0",
-        "long": "^4.0.0"
-      }
-    },
-    "buffer": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.3.0.tgz",
-      "integrity": "sha512-XykNc84nIOC32vZ9euOKbmGAP69JUkXDtBQfLq88c8/6J/gZi/t14A+l/p/9EM2TcT5xNC1MKPCrvO3LVUpVPw==",
-      "requires": {
-        "base64-js": "^1.0.2",
-        "ieee754": "^1.1.4"
-      }
-    },
-    "buffer-from": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
-    },
-    "bunyan": {
-      "version": "1.8.12",
-      "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz",
-      "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=",
-      "requires": {
-        "dtrace-provider": "~0.8",
-        "moment": "^2.10.6",
-        "mv": "~2",
-        "safe-json-stringify": "~1"
-      }
-    },
-    "cache-base": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
-      "dev": true,
-      "requires": {
-        "collection-visit": "^1.0.0",
-        "component-emitter": "^1.2.1",
-        "get-value": "^2.0.6",
-        "has-value": "^1.0.0",
-        "isobject": "^3.0.1",
-        "set-value": "^2.0.0",
-        "to-object-path": "^0.3.0",
-        "union-value": "^1.0.0",
-        "unset-value": "^1.0.0"
-      }
-    },
-    "caller-callsite": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
-      "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
-      "dev": true,
-      "requires": {
-        "callsites": "^2.0.0"
-      },
-      "dependencies": {
-        "callsites": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-          "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
-          "dev": true
-        }
-      }
-    },
-    "caller-path": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
-      "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
-      "dev": true,
-      "requires": {
-        "caller-callsite": "^2.0.0"
-      }
-    },
-    "callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true
-    },
-    "chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      }
-    },
-    "chardet": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
-      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
-      "dev": true
-    },
-    "chownr": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
-      "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
-    },
-    "circular-json": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
-      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
-      "dev": true
-    },
-    "class-utils": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
-      "dev": true,
-      "requires": {
-        "arr-union": "^3.1.0",
-        "define-property": "^0.2.5",
-        "isobject": "^3.0.0",
-        "static-extend": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "cli-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
-      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
-      "dev": true,
-      "requires": {
-        "restore-cursor": "^3.1.0"
-      }
-    },
-    "cli-truncate": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
-      "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
-      "dev": true,
-      "requires": {
-        "slice-ansi": "0.0.4",
-        "string-width": "^1.0.1"
-      },
-      "dependencies": {
-        "slice-ansi": {
-          "version": "0.0.4",
-          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
-          "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
-          "dev": true
-        }
-      }
-    },
-    "cli-width": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
-      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
-      "dev": true
-    },
-    "co": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
-    },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
-    },
-    "collection-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
-      "dev": true,
-      "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
-      }
-    },
-    "color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "requires": {
-        "color-name": "1.1.3"
-      }
-    },
-    "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
-    },
-    "commander": {
-      "version": "2.20.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
-      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
-    },
-    "common-tags": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
-      "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
-      "dev": true
-    },
-    "component-emitter": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
-      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
-    },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
-      }
-    },
-    "console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
-    },
-    "contains-path": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
-      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
-      "dev": true
-    },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "core-js": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
-      "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A=="
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
-    },
-    "cosmiconfig": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
-      "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
-      "dev": true,
-      "requires": {
-        "import-fresh": "^2.0.0",
-        "is-directory": "^0.3.1",
-        "js-yaml": "^3.13.1",
-        "parse-json": "^4.0.0"
-      },
-      "dependencies": {
-        "import-fresh": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
-          "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
-          "dev": true,
-          "requires": {
-            "caller-path": "^2.0.0",
-            "resolve-from": "^3.0.0"
-          }
-        },
-        "parse-json": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
-          "dev": true,
-          "requires": {
-            "error-ex": "^1.3.1",
-            "json-parse-better-errors": "^1.0.1"
-          }
-        },
-        "resolve-from": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
-          "dev": true
-        }
-      }
-    },
-    "cross-spawn": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-      "dev": true,
-      "requires": {
-        "nice-try": "^1.0.4",
-        "path-key": "^2.0.1",
-        "semver": "^5.5.0",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
-      }
-    },
-    "cssfilter": {
-      "version": "0.0.10",
-      "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
-      "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4="
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "date-fns": {
-      "version": "1.30.1",
-      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
-      "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
-      "dev": true
-    },
-    "debug": {
-      "version": "3.2.6",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-      "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
-      "requires": {
-        "ms": "^2.1.1"
-      }
-    },
-    "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
-      "dev": true
-    },
-    "dedent": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
-      "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
-      "dev": true
-    },
-    "deep-extend": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
-      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
-    },
-    "deep-is": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
-      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
-      "dev": true
-    },
-    "define-properties": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
-      "dev": true,
-      "requires": {
-        "object-keys": "^1.0.12"
-      }
-    },
-    "define-property": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-      "dev": true,
-      "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
-    },
-    "detect-libc": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
-      "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
-    },
-    "dlv": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
-      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
-      "dev": true
-    },
-    "doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
-      "dev": true,
-      "requires": {
-        "esutils": "^2.0.2"
-      }
-    },
-    "dtrace-provider": {
-      "version": "0.8.7",
-      "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz",
-      "integrity": "sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ=",
-      "optional": true,
-      "requires": {
-        "nan": "^2.10.0"
-      }
-    },
-    "elegant-spinner": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
-      "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=",
-      "dev": true
-    },
-    "emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true
-    },
-    "error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
-      "requires": {
-        "is-arrayish": "^0.2.1"
-      }
-    },
-    "es-abstract": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
-      "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
-      "dev": true,
-      "requires": {
-        "es-to-primitive": "^1.2.0",
-        "function-bind": "^1.1.1",
-        "has": "^1.0.3",
-        "is-callable": "^1.1.4",
-        "is-regex": "^1.0.4",
-        "object-keys": "^1.0.12"
-      }
-    },
-    "es-to-primitive": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
-      "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
-      "dev": true,
-      "requires": {
-        "is-callable": "^1.1.4",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.2"
-      }
-    },
-    "es6-promise": {
-      "version": "4.2.8",
-      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
-      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
-    },
-    "eslint": {
-      "version": "5.16.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz",
-      "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "ajv": "^6.9.1",
-        "chalk": "^2.1.0",
-        "cross-spawn": "^6.0.5",
-        "debug": "^4.0.1",
-        "doctrine": "^3.0.0",
-        "eslint-scope": "^4.0.3",
-        "eslint-utils": "^1.3.1",
-        "eslint-visitor-keys": "^1.0.0",
-        "espree": "^5.0.1",
-        "esquery": "^1.0.1",
-        "esutils": "^2.0.2",
-        "file-entry-cache": "^5.0.1",
-        "functional-red-black-tree": "^1.0.1",
-        "glob": "^7.1.2",
-        "globals": "^11.7.0",
-        "ignore": "^4.0.6",
-        "import-fresh": "^3.0.0",
-        "imurmurhash": "^0.1.4",
-        "inquirer": "^6.2.2",
-        "js-yaml": "^3.13.0",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.3.0",
-        "lodash": "^4.17.11",
-        "minimatch": "^3.0.4",
-        "mkdirp": "^0.5.1",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.8.2",
-        "path-is-inside": "^1.0.2",
-        "progress": "^2.0.0",
-        "regexpp": "^2.0.1",
-        "semver": "^5.5.1",
-        "strip-ansi": "^4.0.0",
-        "strip-json-comments": "^2.0.1",
-        "table": "^5.2.3",
-        "text-table": "^0.2.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "6.10.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
-          "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
-          "dev": true,
-          "requires": {
-            "fast-deep-equal": "^2.0.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.2"
-          }
-        },
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "debug": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "0.4.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        }
-      }
-    },
-    "eslint-config-meteor": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/eslint-config-meteor/-/eslint-config-meteor-0.0.9.tgz",
-      "integrity": "sha1-a+IZQguko+oCPbMKhm5g70h2Uvo=",
-      "dev": true
-    },
-    "eslint-config-prettier": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-3.6.0.tgz",
-      "integrity": "sha512-ixJ4U3uTLXwJts4rmSVW/lMXjlGwCijhBJHk8iVqKKSifeI0qgFEfWl8L63isfc8Od7EiBALF6BX3jKLluf/jQ==",
-      "dev": true,
-      "requires": {
-        "get-stdin": "^6.0.0"
-      }
-    },
-    "eslint-import-resolver-meteor": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.4.0.tgz",
-      "integrity": "sha1-yGhjhAghIIz4EzxczlGQnCamFWk=",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.0.1",
-        "resolve": "^1.1.6"
-      }
-    },
-    "eslint-import-resolver-node": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
-      "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
-      "dev": true,
-      "requires": {
-        "debug": "^2.6.9",
-        "resolve": "^1.5.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
-      }
-    },
-    "eslint-module-utils": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
-      "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
-      "dev": true,
-      "requires": {
-        "debug": "^2.6.8",
-        "pkg-dir": "^2.0.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
-      }
-    },
-    "eslint-plugin-import": {
-      "version": "2.18.2",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz",
-      "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==",
-      "dev": true,
-      "requires": {
-        "array-includes": "^3.0.3",
-        "contains-path": "^0.1.0",
-        "debug": "^2.6.9",
-        "doctrine": "1.5.0",
-        "eslint-import-resolver-node": "^0.3.2",
-        "eslint-module-utils": "^2.4.0",
-        "has": "^1.0.3",
-        "minimatch": "^3.0.4",
-        "object.values": "^1.1.0",
-        "read-pkg-up": "^2.0.0",
-        "resolve": "^1.11.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "doctrine": {
-          "version": "1.5.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
-          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
-          "dev": true,
-          "requires": {
-            "esutils": "^2.0.2",
-            "isarray": "^1.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
-      }
-    },
-    "eslint-plugin-meteor": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-meteor/-/eslint-plugin-meteor-5.2.0.tgz",
-      "integrity": "sha512-bHzs/0BwHdKcBbX7tYrSnBaMG+1i2f1wy8k6H/sBBsERD/yifmBUrNLiPyZkIvyVUeI8OaZw8U9fsMvLP5GhIg==",
-      "dev": true,
-      "requires": {
-        "invariant": "2.2.4"
-      }
-    },
-    "eslint-plugin-prettier": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz",
-      "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==",
-      "dev": true,
-      "requires": {
-        "prettier-linter-helpers": "^1.0.0"
-      }
-    },
-    "eslint-scope": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
-      "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
-      "dev": true,
-      "requires": {
-        "esrecurse": "^4.1.0",
-        "estraverse": "^4.1.1"
-      }
-    },
-    "eslint-utils": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
-      "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
-      "dev": true,
-      "requires": {
-        "eslint-visitor-keys": "^1.0.0"
-      }
-    },
-    "eslint-visitor-keys": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
-      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
-      "dev": true
-    },
-    "espree": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
-      "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
-      "dev": true,
-      "requires": {
-        "acorn": "^6.0.7",
-        "acorn-jsx": "^5.0.0",
-        "eslint-visitor-keys": "^1.0.0"
-      }
-    },
-    "esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true
-    },
-    "esquery": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
-      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^4.0.0"
-      }
-    },
-    "esrecurse": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
-      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^4.1.0"
-      }
-    },
-    "estraverse": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
-      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
-      "dev": true
-    },
-    "esutils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
-      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
-      "dev": true
-    },
-    "execa": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-0.9.0.tgz",
-      "integrity": "sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^5.0.1",
-        "get-stream": "^3.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
-          "dev": true,
-          "requires": {
-            "lru-cache": "^4.0.1",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        }
-      }
-    },
-    "expand-brackets": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-      "dev": true,
-      "requires": {
-        "debug": "^2.3.3",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "posix-character-classes": "^0.1.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
-      }
-    },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
-      "dev": true,
-      "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "external-editor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
-      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
-      "dev": true,
-      "requires": {
-        "chardet": "^0.7.0",
-        "iconv-lite": "^0.4.24",
-        "tmp": "^0.0.33"
-      }
-    },
-    "extglob": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-      "dev": true,
-      "requires": {
-        "array-unique": "^0.3.2",
-        "define-property": "^1.0.0",
-        "expand-brackets": "^2.1.4",
-        "extend-shallow": "^2.0.1",
-        "fragment-cache": "^0.2.1",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "extsprintf": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz",
-      "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk="
-    },
-    "fast-deep-equal": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
-      "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
-    },
-    "fast-diff": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
-      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
-      "dev": true
-    },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
-    },
-    "fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
-      "dev": true
-    },
-    "figures": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-3.0.0.tgz",
-      "integrity": "sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==",
-      "dev": true,
-      "requires": {
-        "escape-string-regexp": "^1.0.5"
-      }
-    },
-    "file-entry-cache": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
-      "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
-      "dev": true,
-      "requires": {
-        "flat-cache": "^2.0.1"
-      }
-    },
-    "fill-range": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1",
-        "to-regex-range": "^2.1.0"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "find-parent-dir": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
-      "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=",
-      "dev": true
-    },
-    "find-up": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
-      "dev": true,
-      "requires": {
-        "locate-path": "^2.0.0"
-      }
-    },
-    "flat-cache": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
-      "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
-      "dev": true,
-      "requires": {
-        "flatted": "^2.0.0",
-        "rimraf": "2.6.3",
-        "write": "1.0.3"
-      }
-    },
-    "flatted": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
-      "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
-      "dev": true
-    },
-    "for-in": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
-      "dev": true
-    },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
-      "dev": true,
-      "requires": {
-        "map-cache": "^0.2.2"
-      }
-    },
-    "fs-minipass": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz",
-      "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==",
-      "requires": {
-        "minipass": "^2.2.1"
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
-      "dev": true
-    },
-    "gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
-      "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
-      }
-    },
-    "get-own-enumerable-property-symbols": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz",
-      "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==",
-      "dev": true
-    },
-    "get-stdin": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
-      "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
-      "dev": true
-    },
-    "get-stream": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
-      "dev": true
-    },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
-      "dev": true
-    },
-    "glob": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
-      "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "globals": {
-      "version": "11.12.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
-      "dev": true
-    },
-    "graceful-fs": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
-      "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
-      "dev": true
-    },
-    "gridfs-stream": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/gridfs-stream/-/gridfs-stream-0.5.3.tgz",
-      "integrity": "sha1-wIlnKPo+qD9fo8nO1GGvt6A20Uk="
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
-      "dev": true
-    },
-    "has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
-    },
-    "has-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
-      "dev": true,
-      "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
-      }
-    },
-    "has-values": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
-      "dev": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "hosted-git-info": {
-      "version": "2.8.4",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz",
-      "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==",
-      "dev": true
-    },
-    "iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-      "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
-      }
-    },
-    "ieee754": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
-      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
-    },
-    "ignore": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
-      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
-      "dev": true
-    },
-    "ignore-walk": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
-      "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
-      "requires": {
-        "minimatch": "^3.0.4"
-      }
-    },
-    "import-fresh": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
-      "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==",
-      "dev": true,
-      "requires": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "dependencies": {
-        "resolve-from": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-          "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-          "dev": true
-        }
-      }
-    },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
-      "dev": true
-    },
-    "indent-string": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
-      "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
-    },
-    "ini": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
-    },
-    "inquirer": {
-      "version": "6.5.1",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz",
-      "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==",
-      "dev": true,
-      "requires": {
-        "ansi-escapes": "^4.2.1",
-        "chalk": "^2.4.2",
-        "cli-cursor": "^3.1.0",
-        "cli-width": "^2.0.0",
-        "external-editor": "^3.0.3",
-        "figures": "^3.0.0",
-        "lodash": "^4.17.15",
-        "mute-stream": "0.0.8",
-        "run-async": "^2.2.0",
-        "rxjs": "^6.4.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^5.1.0",
-        "through": "^2.3.6"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz",
-          "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^5.2.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        }
-      }
-    },
-    "invariant": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
-      "dev": true,
-      "requires": {
-        "loose-envify": "^1.0.0"
-      }
-    },
-    "is-accessor-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
-    },
-    "is-buffer": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
-      "dev": true
-    },
-    "is-callable": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
-      "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
-      "dev": true
-    },
-    "is-data-descriptor": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "is-date-object": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
-      "dev": true
-    },
-    "is-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-      "dev": true,
-      "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "is-directory": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
-      "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
-      "dev": true
-    },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
-      "dev": true
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-fullwidth-code-point": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-      "requires": {
-        "number-is-nan": "^1.0.0"
-      }
-    },
-    "is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-      "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-number": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "is-obj": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
-      "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
-      "dev": true
-    },
-    "is-observable": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz",
-      "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==",
-      "dev": true,
-      "requires": {
-        "symbol-observable": "^1.1.0"
-      }
-    },
-    "is-plain-object": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
-      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
-    },
-    "is-promise": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
-      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
-      "dev": true
-    },
-    "is-regex": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.1"
-      }
-    },
-    "is-regexp": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
-      "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
-      "dev": true
-    },
-    "is-resolvable": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
-      "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
-      "dev": true
-    },
-    "is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-      "dev": true
-    },
-    "is-symbol": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
-      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
-      "dev": true,
-      "requires": {
-        "has-symbols": "^1.0.0"
-      }
-    },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
-      "dev": true
-    },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
-    },
-    "isobject": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-      "dev": true
-    },
-    "jest-get-type": {
-      "version": "22.4.3",
-      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
-      "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
-      "dev": true
-    },
-    "jest-validate": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.6.0.tgz",
-      "integrity": "sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.0.1",
-        "jest-get-type": "^22.1.0",
-        "leven": "^2.1.0",
-        "pretty-format": "^23.6.0"
-      }
-    },
-    "js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "dev": true
-    },
-    "js-yaml": {
-      "version": "3.13.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
-      "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
-      "dev": true,
-      "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
-      }
-    },
-    "json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true
-    },
-    "json-schema-traverse": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
-    },
-    "json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
-      "dev": true
-    },
-    "kind-of": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-      "dev": true
-    },
-    "ldap-filter": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz",
-      "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=",
-      "requires": {
-        "assert-plus": "0.1.5"
-      },
-      "dependencies": {
-        "assert-plus": {
-          "version": "0.1.5",
-          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
-          "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA="
-        }
-      }
-    },
-    "ldapjs": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz",
-      "integrity": "sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=",
-      "requires": {
-        "asn1": "0.2.3",
-        "assert-plus": "^1.0.0",
-        "backoff": "^2.5.0",
-        "bunyan": "^1.8.3",
-        "dashdash": "^1.14.0",
-        "dtrace-provider": "~0.8",
-        "ldap-filter": "0.2.2",
-        "once": "^1.4.0",
-        "vasync": "^1.6.4",
-        "verror": "^1.8.1"
-      }
-    },
-    "leven": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
-      "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
-      "dev": true
-    },
-    "levn": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
-      "dev": true,
-      "requires": {
-        "prelude-ls": "~1.1.2",
-        "type-check": "~0.3.2"
-      }
-    },
-    "lint-staged": {
-      "version": "7.3.0",
-      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-7.3.0.tgz",
-      "integrity": "sha512-AXk40M9DAiPi7f4tdJggwuKIViUplYtVj1os1MVEteW7qOkU50EOehayCfO9TsoGK24o/EsWb41yrEgfJDDjCw==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.3.1",
-        "commander": "^2.14.1",
-        "cosmiconfig": "^5.0.2",
-        "debug": "^3.1.0",
-        "dedent": "^0.7.0",
-        "execa": "^0.9.0",
-        "find-parent-dir": "^0.3.0",
-        "is-glob": "^4.0.0",
-        "is-windows": "^1.0.2",
-        "jest-validate": "^23.5.0",
-        "listr": "^0.14.1",
-        "lodash": "^4.17.5",
-        "log-symbols": "^2.2.0",
-        "micromatch": "^3.1.8",
-        "npm-which": "^3.0.1",
-        "p-map": "^1.1.1",
-        "path-is-inside": "^1.0.2",
-        "pify": "^3.0.0",
-        "please-upgrade-node": "^3.0.2",
-        "staged-git-files": "1.1.1",
-        "string-argv": "^0.0.2",
-        "stringify-object": "^3.2.2"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        }
-      }
-    },
-    "listr": {
-      "version": "0.14.3",
-      "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
-      "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==",
-      "dev": true,
-      "requires": {
-        "@samverschueren/stream-to-observable": "^0.3.0",
-        "is-observable": "^1.1.0",
-        "is-promise": "^2.1.0",
-        "is-stream": "^1.1.0",
-        "listr-silent-renderer": "^1.1.1",
-        "listr-update-renderer": "^0.5.0",
-        "listr-verbose-renderer": "^0.5.0",
-        "p-map": "^2.0.0",
-        "rxjs": "^6.3.3"
-      },
-      "dependencies": {
-        "p-map": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
-          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
-          "dev": true
-        }
-      }
-    },
-    "listr-silent-renderer": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz",
-      "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=",
-      "dev": true
-    },
-    "listr-update-renderer": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz",
-      "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "cli-truncate": "^0.2.1",
-        "elegant-spinner": "^1.0.1",
-        "figures": "^1.7.0",
-        "indent-string": "^3.0.0",
-        "log-symbols": "^1.0.2",
-        "log-update": "^2.3.0",
-        "strip-ansi": "^3.0.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "figures": {
-          "version": "1.7.0",
-          "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
-          "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
-          "dev": true,
-          "requires": {
-            "escape-string-regexp": "^1.0.5",
-            "object-assign": "^4.1.0"
-          }
-        },
-        "log-symbols": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
-          "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
-    "listr-verbose-renderer": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz",
-      "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.4.1",
-        "cli-cursor": "^2.1.0",
-        "date-fns": "^1.27.2",
-        "figures": "^2.0.0"
-      },
-      "dependencies": {
-        "cli-cursor": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
-          "dev": true,
-          "requires": {
-            "restore-cursor": "^2.0.0"
-          }
-        },
-        "figures": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
-          "dev": true,
-          "requires": {
-            "escape-string-regexp": "^1.0.5"
-          }
-        },
-        "mimic-fn": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
-          "dev": true
-        },
-        "onetime": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
-          "dev": true,
-          "requires": {
-            "mimic-fn": "^1.0.0"
-          }
-        },
-        "restore-cursor": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
-          "dev": true,
-          "requires": {
-            "onetime": "^2.0.0",
-            "signal-exit": "^3.0.2"
-          }
-        }
-      }
-    },
-    "load-json-file": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
-      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^2.2.0",
-        "pify": "^2.0.0",
-        "strip-bom": "^3.0.0"
-      }
-    },
-    "locate-path": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
-      "dev": true,
-      "requires": {
-        "p-locate": "^2.0.0",
-        "path-exists": "^3.0.0"
-      }
-    },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
-      "dev": true
-    },
-    "lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true
-    },
-    "lodash.unescape": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
-      "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
-      "dev": true
-    },
-    "log-symbols": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
-      "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.0.1"
-      }
-    },
-    "log-update": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz",
-      "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=",
-      "dev": true,
-      "requires": {
-        "ansi-escapes": "^3.0.0",
-        "cli-cursor": "^2.0.0",
-        "wrap-ansi": "^3.0.1"
-      },
-      "dependencies": {
-        "ansi-escapes": {
-          "version": "3.2.0",
-          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
-          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
-          "dev": true
-        },
-        "cli-cursor": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
-          "dev": true,
-          "requires": {
-            "restore-cursor": "^2.0.0"
-          }
-        },
-        "mimic-fn": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
-          "dev": true
-        },
-        "onetime": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
-          "dev": true,
-          "requires": {
-            "mimic-fn": "^1.0.0"
-          }
-        },
-        "restore-cursor": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
-          "dev": true,
-          "requires": {
-            "onetime": "^2.0.0",
-            "signal-exit": "^3.0.2"
-          }
-        }
-      }
-    },
-    "loglevel": {
-      "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz",
-      "integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==",
-      "dev": true
-    },
-    "loglevel-colored-level-prefix": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
-      "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "loglevel": "^1.4.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
-    "long": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
-      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
-    },
-    "loose-envify": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
-      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
-      "dev": true,
-      "requires": {
-        "js-tokens": "^3.0.0 || ^4.0.0"
-      }
-    },
-    "lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
-      "dev": true,
-      "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
-      },
-      "dependencies": {
-        "yallist": {
-          "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
-          "dev": true
-        }
-      }
-    },
-    "map-cache": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
-      "dev": true
-    },
-    "map-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
-      "dev": true,
-      "requires": {
-        "object-visit": "^1.0.0"
-      }
-    },
-    "memory-pager": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
-      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
-      "optional": true
-    },
-    "meteor-node-stubs": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-0.4.1.tgz",
-      "integrity": "sha512-UO2OStvLOKoApmOdIP5eCqoLaa/ritMXRg4ffJVdkNLEsczzPvTjgC0Mxk4cM4R8MZkwll90FYgjDf5qUTJdMA==",
-      "requires": {
-        "assert": "^1.4.1",
-        "browserify-zlib": "^0.1.4",
-        "buffer": "^4.9.1",
-        "console-browserify": "^1.1.0",
-        "constants-browserify": "^1.0.0",
-        "crypto-browserify": "^3.11.0",
-        "domain-browser": "^1.1.7",
-        "events": "^1.1.1",
-        "https-browserify": "0.0.1",
-        "os-browserify": "^0.2.1",
-        "path-browserify": "0.0.0",
-        "process": "^0.11.9",
-        "punycode": "^1.4.1",
-        "querystring-es3": "^0.2.1",
-        "readable-stream": "^2.3.6",
-        "stream-browserify": "^2.0.1",
-        "stream-http": "^2.8.0",
-        "string_decoder": "^1.1.0",
-        "timers-browserify": "^1.4.2",
-        "tty-browserify": "0.0.0",
-        "url": "^0.11.0",
-        "util": "^0.10.3",
-        "vm-browserify": "0.0.4"
-      },
-      "dependencies": {
-        "asn1.js": {
-          "version": "4.10.1",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.0.0",
-            "inherits": "^2.0.1",
-            "minimalistic-assert": "^1.0.0"
-          }
-        },
-        "assert": {
-          "version": "1.4.1",
-          "bundled": true,
-          "requires": {
-            "util": "0.10.3"
-          }
-        },
-        "base64-js": {
-          "version": "1.3.0",
-          "bundled": true
-        },
-        "bn.js": {
-          "version": "4.11.8",
-          "bundled": true
-        },
-        "brorand": {
-          "version": "1.1.0",
-          "bundled": true
-        },
-        "browserify-aes": {
-          "version": "1.2.0",
-          "bundled": true,
-          "requires": {
-            "buffer-xor": "^1.0.3",
-            "cipher-base": "^1.0.0",
-            "create-hash": "^1.1.0",
-            "evp_bytestokey": "^1.0.3",
-            "inherits": "^2.0.1",
-            "safe-buffer": "^5.0.1"
-          }
-        },
-        "browserify-cipher": {
-          "version": "1.0.1",
-          "bundled": true,
-          "requires": {
-            "browserify-aes": "^1.0.4",
-            "browserify-des": "^1.0.0",
-            "evp_bytestokey": "^1.0.0"
-          }
-        },
-        "browserify-des": {
-          "version": "1.0.1",
-          "bundled": true,
-          "requires": {
-            "cipher-base": "^1.0.1",
-            "des.js": "^1.0.0",
-            "inherits": "^2.0.1"
-          }
-        },
-        "browserify-rsa": {
-          "version": "4.0.1",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.1.0",
-            "randombytes": "^2.0.1"
-          }
-        },
-        "browserify-sign": {
-          "version": "4.0.4",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.1.1",
-            "browserify-rsa": "^4.0.0",
-            "create-hash": "^1.1.0",
-            "create-hmac": "^1.1.2",
-            "elliptic": "^6.0.0",
-            "inherits": "^2.0.1",
-            "parse-asn1": "^5.0.0"
-          }
-        },
-        "browserify-zlib": {
-          "version": "0.1.4",
-          "bundled": true,
-          "requires": {
-            "pako": "~0.2.0"
-          }
-        },
-        "buffer": {
-          "version": "4.9.1",
-          "bundled": true,
-          "requires": {
-            "base64-js": "^1.0.2",
-            "ieee754": "^1.1.4",
-            "isarray": "^1.0.0"
-          }
-        },
-        "buffer-xor": {
-          "version": "1.0.3",
-          "bundled": true
-        },
-        "builtin-status-codes": {
-          "version": "3.0.0",
-          "bundled": true
-        },
-        "cipher-base": {
-          "version": "1.0.4",
-          "bundled": true,
-          "requires": {
-            "inherits": "^2.0.1",
-            "safe-buffer": "^5.0.1"
-          }
-        },
-        "console-browserify": {
-          "version": "1.1.0",
-          "bundled": true,
-          "requires": {
-            "date-now": "^0.1.4"
-          }
-        },
-        "constants-browserify": {
-          "version": "1.0.0",
-          "bundled": true
-        },
-        "core-util-is": {
-          "version": "1.0.2",
-          "bundled": true
-        },
-        "create-ecdh": {
-          "version": "4.0.3",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.1.0",
-            "elliptic": "^6.0.0"
-          }
-        },
-        "create-hash": {
-          "version": "1.2.0",
-          "bundled": true,
-          "requires": {
-            "cipher-base": "^1.0.1",
-            "inherits": "^2.0.1",
-            "md5.js": "^1.3.4",
-            "ripemd160": "^2.0.1",
-            "sha.js": "^2.4.0"
-          }
-        },
-        "create-hmac": {
-          "version": "1.1.7",
-          "bundled": true,
-          "requires": {
-            "cipher-base": "^1.0.3",
-            "create-hash": "^1.1.0",
-            "inherits": "^2.0.1",
-            "ripemd160": "^2.0.0",
-            "safe-buffer": "^5.0.1",
-            "sha.js": "^2.4.8"
-          }
-        },
-        "crypto-browserify": {
-          "version": "3.12.0",
-          "bundled": true,
-          "requires": {
-            "browserify-cipher": "^1.0.0",
-            "browserify-sign": "^4.0.0",
-            "create-ecdh": "^4.0.0",
-            "create-hash": "^1.1.0",
-            "create-hmac": "^1.1.0",
-            "diffie-hellman": "^5.0.0",
-            "inherits": "^2.0.1",
-            "pbkdf2": "^3.0.3",
-            "public-encrypt": "^4.0.0",
-            "randombytes": "^2.0.0",
-            "randomfill": "^1.0.3"
-          }
-        },
-        "date-now": {
-          "version": "0.1.4",
-          "bundled": true
-        },
-        "des.js": {
-          "version": "1.0.0",
-          "bundled": true,
-          "requires": {
-            "inherits": "^2.0.1",
-            "minimalistic-assert": "^1.0.0"
-          }
-        },
-        "diffie-hellman": {
-          "version": "5.0.3",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.1.0",
-            "miller-rabin": "^4.0.0",
-            "randombytes": "^2.0.0"
-          }
-        },
-        "domain-browser": {
-          "version": "1.2.0",
-          "bundled": true
-        },
-        "elliptic": {
-          "version": "6.4.0",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.4.0",
-            "brorand": "^1.0.1",
-            "hash.js": "^1.0.0",
-            "hmac-drbg": "^1.0.0",
-            "inherits": "^2.0.1",
-            "minimalistic-assert": "^1.0.0",
-            "minimalistic-crypto-utils": "^1.0.0"
-          }
-        },
-        "events": {
-          "version": "1.1.1",
-          "bundled": true
-        },
-        "evp_bytestokey": {
-          "version": "1.0.3",
-          "bundled": true,
-          "requires": {
-            "md5.js": "^1.3.4",
-            "safe-buffer": "^5.1.1"
-          }
-        },
-        "hash-base": {
-          "version": "3.0.4",
-          "bundled": true,
-          "requires": {
-            "inherits": "^2.0.1",
-            "safe-buffer": "^5.0.1"
-          }
-        },
-        "hash.js": {
-          "version": "1.1.3",
-          "bundled": true,
-          "requires": {
-            "inherits": "^2.0.3",
-            "minimalistic-assert": "^1.0.0"
-          },
-          "dependencies": {
-            "inherits": {
-              "version": "2.0.3",
-              "bundled": true
-            }
-          }
-        },
-        "hmac-drbg": {
-          "version": "1.0.1",
-          "bundled": true,
-          "requires": {
-            "hash.js": "^1.0.3",
-            "minimalistic-assert": "^1.0.0",
-            "minimalistic-crypto-utils": "^1.0.1"
-          }
-        },
-        "https-browserify": {
-          "version": "0.0.1",
-          "bundled": true
-        },
-        "ieee754": {
-          "version": "1.1.11",
-          "bundled": true
-        },
-        "indexof": {
-          "version": "0.0.1",
-          "bundled": true
-        },
-        "inherits": {
-          "version": "2.0.1",
-          "bundled": true
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "bundled": true
-        },
-        "md5.js": {
-          "version": "1.3.4",
-          "bundled": true,
-          "requires": {
-            "hash-base": "^3.0.0",
-            "inherits": "^2.0.1"
-          }
-        },
-        "miller-rabin": {
-          "version": "4.0.1",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.0.0",
-            "brorand": "^1.0.1"
-          }
-        },
-        "minimalistic-assert": {
-          "version": "1.0.1",
-          "bundled": true
-        },
-        "minimalistic-crypto-utils": {
-          "version": "1.0.1",
-          "bundled": true
-        },
-        "os-browserify": {
-          "version": "0.2.1",
-          "bundled": true
-        },
-        "pako": {
-          "version": "0.2.9",
-          "bundled": true
-        },
-        "parse-asn1": {
-          "version": "5.1.1",
-          "bundled": true,
-          "requires": {
-            "asn1.js": "^4.0.0",
-            "browserify-aes": "^1.0.0",
-            "create-hash": "^1.1.0",
-            "evp_bytestokey": "^1.0.0",
-            "pbkdf2": "^3.0.3"
-          }
-        },
-        "path-browserify": {
-          "version": "0.0.0",
-          "bundled": true
-        },
-        "pbkdf2": {
-          "version": "3.0.16",
-          "bundled": true,
-          "requires": {
-            "create-hash": "^1.1.2",
-            "create-hmac": "^1.1.4",
-            "ripemd160": "^2.0.1",
-            "safe-buffer": "^5.0.1",
-            "sha.js": "^2.4.8"
-          }
-        },
-        "process": {
-          "version": "0.11.10",
-          "bundled": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "bundled": true
-        },
-        "public-encrypt": {
-          "version": "4.0.2",
-          "bundled": true,
-          "requires": {
-            "bn.js": "^4.1.0",
-            "browserify-rsa": "^4.0.0",
-            "create-hash": "^1.1.0",
-            "parse-asn1": "^5.0.0",
-            "randombytes": "^2.0.1"
-          }
-        },
-        "punycode": {
-          "version": "1.4.1",
-          "bundled": true
-        },
-        "querystring": {
-          "version": "0.2.0",
-          "bundled": true
-        },
-        "querystring-es3": {
-          "version": "0.2.1",
-          "bundled": true
-        },
-        "randombytes": {
-          "version": "2.0.6",
-          "bundled": true,
-          "requires": {
-            "safe-buffer": "^5.1.0"
-          }
-        },
-        "randomfill": {
-          "version": "1.0.4",
-          "bundled": true,
-          "requires": {
-            "randombytes": "^2.0.5",
-            "safe-buffer": "^5.1.0"
-          }
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "bundled": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          },
-          "dependencies": {
-            "inherits": {
-              "version": "2.0.3",
-              "bundled": true
-            }
-          }
-        },
-        "ripemd160": {
-          "version": "2.0.2",
-          "bundled": true,
-          "requires": {
-            "hash-base": "^3.0.0",
-            "inherits": "^2.0.1"
-          }
-        },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "bundled": true
-        },
-        "sha.js": {
-          "version": "2.4.11",
-          "bundled": true,
-          "requires": {
-            "inherits": "^2.0.1",
-            "safe-buffer": "^5.0.1"
-          }
-        },
-        "stream-browserify": {
-          "version": "2.0.1",
-          "bundled": true,
-          "requires": {
-            "inherits": "~2.0.1",
-            "readable-stream": "^2.0.2"
-          }
-        },
-        "stream-http": {
-          "version": "2.8.1",
-          "bundled": true,
-          "requires": {
-            "builtin-status-codes": "^3.0.0",
-            "inherits": "^2.0.1",
-            "readable-stream": "^2.3.3",
-            "to-arraybuffer": "^1.0.0",
-            "xtend": "^4.0.0"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "bundled": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        },
-        "timers-browserify": {
-          "version": "1.4.2",
-          "bundled": true,
-          "requires": {
-            "process": "~0.11.0"
-          }
-        },
-        "to-arraybuffer": {
-          "version": "1.0.1",
-          "bundled": true
-        },
-        "tty-browserify": {
-          "version": "0.0.0",
-          "bundled": true
-        },
-        "url": {
-          "version": "0.11.0",
-          "bundled": true,
-          "requires": {
-            "punycode": "1.3.2",
-            "querystring": "0.2.0"
-          },
-          "dependencies": {
-            "punycode": {
-              "version": "1.3.2",
-              "bundled": true
-            }
-          }
-        },
-        "util": {
-          "version": "0.10.3",
-          "bundled": true,
-          "requires": {
-            "inherits": "2.0.1"
-          }
-        },
-        "util-deprecate": {
-          "version": "1.0.2",
-          "bundled": true
-        },
-        "vm-browserify": {
-          "version": "0.0.4",
-          "bundled": true,
-          "requires": {
-            "indexof": "0.0.1"
-          }
-        },
-        "xtend": {
-          "version": "4.0.1",
-          "bundled": true
-        }
-      }
-    },
-    "micromatch": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "braces": "^2.3.1",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "extglob": "^2.0.4",
-        "fragment-cache": "^0.2.1",
-        "kind-of": "^6.0.2",
-        "nanomatch": "^1.2.9",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.2"
-      }
-    },
-    "mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "dev": true
-    },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "minimist": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
-    },
-    "minipass": {
-      "version": "2.3.5",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
-      "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
-      "requires": {
-        "safe-buffer": "^5.1.2",
-        "yallist": "^3.0.0"
-      }
-    },
-    "minizlib": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
-      "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
-      "requires": {
-        "minipass": "^2.2.1"
-      }
-    },
-    "mixin-deep": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "mkdirp": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
-      "requires": {
-        "minimist": "0.0.8"
-      }
-    },
-    "moment": {
-      "version": "2.24.0",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
-      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
-      "optional": true
-    },
-    "mongodb": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.3.tgz",
-      "integrity": "sha512-MdRnoOjstmnrKJsK8PY0PjP6fyF/SBS4R8coxmhsfEU7tQ46/J6j+aSHF2n4c2/H8B+Hc/Klbfp8vggZfI0mmA==",
-      "requires": {
-        "bson": "^1.1.1",
-        "require_optional": "^1.0.1",
-        "safe-buffer": "^5.1.2",
-        "saslprep": "^1.0.0"
-      },
-      "dependencies": {
-        "bson": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz",
-          "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg=="
-        }
-      }
-    },
-    "ms": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
-    },
-    "mute-stream": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
-      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
-      "dev": true
-    },
-    "mv": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
-      "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
-      "optional": true,
-      "requires": {
-        "mkdirp": "~0.5.1",
-        "ncp": "~2.0.0",
-        "rimraf": "~2.4.0"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "6.0.4",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
-          "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
-          "optional": true,
-          "requires": {
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "2 || 3",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "rimraf": {
-          "version": "2.4.5",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
-          "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
-          "optional": true,
-          "requires": {
-            "glob": "^6.0.1"
-          }
-        }
-      }
-    },
-    "nan": {
-      "version": "2.13.2",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
-      "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw=="
-    },
-    "nanomatch": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "fragment-cache": "^0.2.1",
-        "is-windows": "^1.0.2",
-        "kind-of": "^6.0.2",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      }
-    },
-    "natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
-      "dev": true
-    },
-    "ncp": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
-      "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
-      "optional": true
-    },
-    "needle": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
-      "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
-      "requires": {
-        "debug": "^3.2.6",
-        "iconv-lite": "^0.4.4",
-        "sax": "^1.2.4"
-      }
-    },
-    "nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-      "dev": true
-    },
-    "node-pre-gyp": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz",
-      "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
-      "requires": {
-        "detect-libc": "^1.0.2",
-        "mkdirp": "^0.5.1",
-        "needle": "^2.2.1",
-        "nopt": "^4.0.1",
-        "npm-packlist": "^1.1.6",
-        "npmlog": "^4.0.2",
-        "rc": "^1.2.7",
-        "rimraf": "^2.6.1",
-        "semver": "^5.3.0",
-        "tar": "^4"
-      }
-    },
-    "nopt": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
-      "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
-      "requires": {
-        "abbrev": "1",
-        "osenv": "^0.1.4"
-      }
-    },
-    "normalize-package-data": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
-      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
-      "dev": true,
-      "requires": {
-        "hosted-git-info": "^2.1.4",
-        "resolve": "^1.10.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
-      }
-    },
-    "npm-bundled": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
-      "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
-    },
-    "npm-packlist": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz",
-      "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==",
-      "requires": {
-        "ignore-walk": "^3.0.1",
-        "npm-bundled": "^1.0.1"
-      }
-    },
-    "npm-path": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz",
-      "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==",
-      "dev": true,
-      "requires": {
-        "which": "^1.2.10"
-      }
-    },
-    "npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
-      "dev": true,
-      "requires": {
-        "path-key": "^2.0.0"
-      }
-    },
-    "npm-which": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz",
-      "integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=",
-      "dev": true,
-      "requires": {
-        "commander": "^2.9.0",
-        "npm-path": "^2.0.2",
-        "which": "^1.2.10"
-      }
-    },
-    "npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
-      "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
-      }
-    },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
-    },
-    "object-copy": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
-      "dev": true,
-      "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true
-    },
-    "object-visit": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.0"
-      }
-    },
-    "object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
-    },
-    "object.values": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
-      "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.12.0",
-        "function-bind": "^1.1.1",
-        "has": "^1.0.3"
-      }
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "onetime": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
-      "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
-      "dev": true,
-      "requires": {
-        "mimic-fn": "^2.1.0"
-      }
-    },
-    "optionator": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
-      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
-      "dev": true,
-      "requires": {
-        "deep-is": "~0.1.3",
-        "fast-levenshtein": "~2.0.4",
-        "levn": "~0.3.0",
-        "prelude-ls": "~1.1.2",
-        "type-check": "~0.3.2",
-        "wordwrap": "~1.0.0"
-      }
-    },
-    "os": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz",
-      "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M="
-    },
-    "os-homedir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
-    },
-    "os-shim": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
-      "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=",
-      "dev": true
-    },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
-    },
-    "osenv": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
-      "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.0"
-      }
-    },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
-    },
-    "p-limit": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
-      "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
-      "dev": true,
-      "requires": {
-        "p-try": "^1.0.0"
-      }
-    },
-    "p-locate": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
-      "dev": true,
-      "requires": {
-        "p-limit": "^1.1.0"
-      }
-    },
-    "p-map": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
-      "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
-      "dev": true
-    },
-    "p-try": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
-      "dev": true
-    },
-    "page": {
-      "version": "1.11.4",
-      "resolved": "https://registry.npmjs.org/page/-/page-1.11.4.tgz",
-      "integrity": "sha512-8JMZzcE5W4qk+/DtmogN57cI+Yscy7xTYCpfSO7s3Tx6LjZuAfHFQY1+cKIAy60NaXdzVD6nOc3objaVbE0HJg==",
-      "requires": {
-        "path-to-regexp": "~1.2.1"
-      }
-    },
-    "parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "requires": {
-        "callsites": "^3.0.0"
-      }
-    },
-    "parse-json": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
-      "dev": true,
-      "requires": {
-        "error-ex": "^1.2.0"
-      }
-    },
-    "pascalcase": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
-      "dev": true
-    },
-    "path-exists": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-      "dev": true
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
-    },
-    "path-is-inside": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
-      "dev": true
-    },
-    "path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
-    },
-    "path-to-regexp": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.2.1.tgz",
-      "integrity": "sha1-szcFwUAjTYc8hyHHuf2LVB7Tr/k=",
-      "requires": {
-        "isarray": "0.0.1"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
-        }
-      }
-    },
-    "path-type": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
-      "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
-      "dev": true,
-      "requires": {
-        "pify": "^2.0.0"
-      }
-    },
-    "pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-      "dev": true
-    },
-    "pkg-dir": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
-      "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
-      "dev": true,
-      "requires": {
-        "find-up": "^2.1.0"
-      }
-    },
-    "please-upgrade-node": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
-      "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==",
-      "dev": true,
-      "requires": {
-        "semver-compare": "^1.0.0"
-      }
-    },
-    "pluralize": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
-      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
-      "dev": true
-    },
-    "posix-character-classes": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
-      "dev": true
-    },
-    "pre-commit": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
-      "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^5.0.1",
-        "spawn-sync": "^1.0.15",
-        "which": "1.2.x"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
-          "dev": true,
-          "requires": {
-            "lru-cache": "^4.0.1",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "which": {
-          "version": "1.2.14",
-          "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
-          "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
-          "dev": true,
-          "requires": {
-            "isexe": "^2.0.0"
-          }
-        }
-      }
-    },
-    "precond": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
-      "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw="
-    },
-    "prelude-ls": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
-      "dev": true
-    },
-    "prettier": {
-      "version": "1.18.2",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
-      "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==",
-      "dev": true
-    },
-    "prettier-eslint": {
-      "version": "8.8.2",
-      "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.8.2.tgz",
-      "integrity": "sha512-2UzApPuxi2yRoyMlXMazgR6UcH9DKJhNgCviIwY3ixZ9THWSSrUww5vkiZ3C48WvpFl1M1y/oU63deSy1puWEA==",
-      "dev": true,
-      "requires": {
-        "babel-runtime": "^6.26.0",
-        "common-tags": "^1.4.0",
-        "dlv": "^1.1.0",
-        "eslint": "^4.0.0",
-        "indent-string": "^3.2.0",
-        "lodash.merge": "^4.6.0",
-        "loglevel-colored-level-prefix": "^1.0.0",
-        "prettier": "^1.7.0",
-        "pretty-format": "^23.0.1",
-        "require-relative": "^0.8.7",
-        "typescript": "^2.5.1",
-        "typescript-eslint-parser": "^16.0.0",
-        "vue-eslint-parser": "^2.0.2"
-      },
-      "dependencies": {
-        "acorn": {
-          "version": "5.7.3",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
-          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
-          "dev": true
-        },
-        "acorn-jsx": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
-          "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
-          "dev": true,
-          "requires": {
-            "acorn": "^3.0.4"
-          },
-          "dependencies": {
-            "acorn": {
-              "version": "3.3.0",
-              "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-              "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
-              "dev": true
-            }
-          }
-        },
-        "ansi-escapes": {
-          "version": "3.2.0",
-          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
-          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
-          "dev": true
-        },
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "chardet": {
-          "version": "0.4.2",
-          "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
-          "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
-          "dev": true
-        },
-        "cli-cursor": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
-          "dev": true,
-          "requires": {
-            "restore-cursor": "^2.0.0"
-          }
-        },
-        "cross-spawn": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
-          "dev": true,
-          "requires": {
-            "lru-cache": "^4.0.1",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "doctrine": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
-          "dev": true,
-          "requires": {
-            "esutils": "^2.0.2"
-          }
-        },
-        "eslint": {
-          "version": "4.19.1",
-          "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
-          "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
-          "dev": true,
-          "requires": {
-            "ajv": "^5.3.0",
-            "babel-code-frame": "^6.22.0",
-            "chalk": "^2.1.0",
-            "concat-stream": "^1.6.0",
-            "cross-spawn": "^5.1.0",
-            "debug": "^3.1.0",
-            "doctrine": "^2.1.0",
-            "eslint-scope": "^3.7.1",
-            "eslint-visitor-keys": "^1.0.0",
-            "espree": "^3.5.4",
-            "esquery": "^1.0.0",
-            "esutils": "^2.0.2",
-            "file-entry-cache": "^2.0.0",
-            "functional-red-black-tree": "^1.0.1",
-            "glob": "^7.1.2",
-            "globals": "^11.0.1",
-            "ignore": "^3.3.3",
-            "imurmurhash": "^0.1.4",
-            "inquirer": "^3.0.6",
-            "is-resolvable": "^1.0.0",
-            "js-yaml": "^3.9.1",
-            "json-stable-stringify-without-jsonify": "^1.0.1",
-            "levn": "^0.3.0",
-            "lodash": "^4.17.4",
-            "minimatch": "^3.0.2",
-            "mkdirp": "^0.5.1",
-            "natural-compare": "^1.4.0",
-            "optionator": "^0.8.2",
-            "path-is-inside": "^1.0.2",
-            "pluralize": "^7.0.0",
-            "progress": "^2.0.0",
-            "regexpp": "^1.0.1",
-            "require-uncached": "^1.0.3",
-            "semver": "^5.3.0",
-            "strip-ansi": "^4.0.0",
-            "strip-json-comments": "~2.0.1",
-            "table": "4.0.2",
-            "text-table": "~0.2.0"
-          }
-        },
-        "eslint-scope": {
-          "version": "3.7.3",
-          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
-          "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
-          "dev": true,
-          "requires": {
-            "esrecurse": "^4.1.0",
-            "estraverse": "^4.1.1"
-          }
-        },
-        "espree": {
-          "version": "3.5.4",
-          "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
-          "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
-          "dev": true,
-          "requires": {
-            "acorn": "^5.5.0",
-            "acorn-jsx": "^3.0.0"
-          }
-        },
-        "external-editor": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
-          "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
-          "dev": true,
-          "requires": {
-            "chardet": "^0.4.0",
-            "iconv-lite": "^0.4.17",
-            "tmp": "^0.0.33"
-          }
-        },
-        "figures": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
-          "dev": true,
-          "requires": {
-            "escape-string-regexp": "^1.0.5"
-          }
-        },
-        "file-entry-cache": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
-          "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
-          "dev": true,
-          "requires": {
-            "flat-cache": "^1.2.1",
-            "object-assign": "^4.0.1"
-          }
-        },
-        "flat-cache": {
-          "version": "1.3.4",
-          "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
-          "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
-          "dev": true,
-          "requires": {
-            "circular-json": "^0.3.1",
-            "graceful-fs": "^4.1.2",
-            "rimraf": "~2.6.2",
-            "write": "^0.2.1"
-          }
-        },
-        "ignore": {
-          "version": "3.3.10",
-          "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
-          "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
-          "dev": true
-        },
-        "inquirer": {
-          "version": "3.3.0",
-          "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
-          "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
-          "dev": true,
-          "requires": {
-            "ansi-escapes": "^3.0.0",
-            "chalk": "^2.0.0",
-            "cli-cursor": "^2.1.0",
-            "cli-width": "^2.0.0",
-            "external-editor": "^2.0.4",
-            "figures": "^2.0.0",
-            "lodash": "^4.3.0",
-            "mute-stream": "0.0.7",
-            "run-async": "^2.2.0",
-            "rx-lite": "^4.0.8",
-            "rx-lite-aggregates": "^4.0.8",
-            "string-width": "^2.1.0",
-            "strip-ansi": "^4.0.0",
-            "through": "^2.3.6"
-          }
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "mimic-fn": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
-          "dev": true
-        },
-        "mute-stream": {
-          "version": "0.0.7",
-          "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
-          "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
-          "dev": true
-        },
-        "onetime": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
-          "dev": true,
-          "requires": {
-            "mimic-fn": "^1.0.0"
-          }
-        },
-        "regexpp": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
-          "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
-          "dev": true
-        },
-        "restore-cursor": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
-          "dev": true,
-          "requires": {
-            "onetime": "^2.0.0",
-            "signal-exit": "^3.0.2"
-          }
-        },
-        "slice-ansi": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
-          "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
-          "dev": true,
-          "requires": {
-            "is-fullwidth-code-point": "^2.0.0"
-          }
-        },
-        "string-width": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-          "dev": true,
-          "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
-        "table": {
-          "version": "4.0.2",
-          "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
-          "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
-          "dev": true,
-          "requires": {
-            "ajv": "^5.2.3",
-            "ajv-keywords": "^2.1.0",
-            "chalk": "^2.1.0",
-            "lodash": "^4.17.4",
-            "slice-ansi": "1.0.0",
-            "string-width": "^2.1.1"
-          }
-        },
-        "write": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
-          "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
-          "dev": true,
-          "requires": {
-            "mkdirp": "^0.5.1"
-          }
-        }
-      }
-    },
-    "prettier-linter-helpers": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
-      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
-      "dev": true,
-      "requires": {
-        "fast-diff": "^1.1.2"
-      }
-    },
-    "pretty-format": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz",
-      "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^3.0.0",
-        "ansi-styles": "^3.2.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        }
-      }
-    },
-    "process-nextick-args": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
-    },
-    "progress": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
-      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
-      "dev": true
-    },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
-      "dev": true
-    },
-    "punycode": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-      "dev": true
-    },
-    "qs": {
-      "version": "6.8.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.8.0.tgz",
-      "integrity": "sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w=="
-    },
-    "rc": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
-      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
-      "requires": {
-        "deep-extend": "^0.6.0",
-        "ini": "~1.3.0",
-        "minimist": "^1.2.0",
-        "strip-json-comments": "~2.0.1"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
-        }
-      }
-    },
-    "read-pkg": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
-      "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
-      "dev": true,
-      "requires": {
-        "load-json-file": "^2.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^2.0.0"
-      }
-    },
-    "read-pkg-up": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
-      "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
-      "dev": true,
-      "requires": {
-        "find-up": "^2.0.0",
-        "read-pkg": "^2.0.0"
-      }
-    },
-    "readable-stream": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      }
-    },
-    "regenerator-runtime": {
-      "version": "0.13.3",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
-      "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
-    },
-    "regex-not": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
-      }
-    },
-    "regexpp": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
-      "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
-      "dev": true
-    },
-    "repeat-element": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
-      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
-      "dev": true
-    },
-    "repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
-      "dev": true
-    },
-    "require-relative": {
-      "version": "0.8.7",
-      "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
-      "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
-      "dev": true
-    },
-    "require-uncached": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
-      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
-      "dev": true,
-      "requires": {
-        "caller-path": "^0.1.0",
-        "resolve-from": "^1.0.0"
-      },
-      "dependencies": {
-        "caller-path": {
-          "version": "0.1.0",
-          "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
-          "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
-          "dev": true,
-          "requires": {
-            "callsites": "^0.2.0"
-          }
-        },
-        "callsites": {
-          "version": "0.2.0",
-          "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
-          "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
-          "dev": true
-        },
-        "resolve-from": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
-          "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
-          "dev": true
-        }
-      }
-    },
-    "require_optional": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
-      "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
-      "requires": {
-        "resolve-from": "^2.0.0",
-        "semver": "^5.1.0"
-      }
-    },
-    "resolve": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
-      "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
-      "dev": true,
-      "requires": {
-        "path-parse": "^1.0.6"
-      }
-    },
-    "resolve-from": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
-      "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
-    },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
-      "dev": true
-    },
-    "restore-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
-      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
-      "dev": true,
-      "requires": {
-        "onetime": "^5.1.0",
-        "signal-exit": "^3.0.2"
-      }
-    },
-    "ret": {
-      "version": "0.1.15",
-      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-      "dev": true
-    },
-    "rimraf": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
-      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
-      "requires": {
-        "glob": "^7.1.3"
-      }
-    },
-    "run-async": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
-      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
-      "dev": true,
-      "requires": {
-        "is-promise": "^2.1.0"
-      }
-    },
-    "rx-lite": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
-      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
-      "dev": true
-    },
-    "rx-lite-aggregates": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
-      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
-      "dev": true,
-      "requires": {
-        "rx-lite": "*"
-      }
-    },
-    "rxjs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
-      "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==",
-      "dev": true,
-      "requires": {
-        "tslib": "^1.9.0"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
-    },
-    "safe-json-stringify": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
-      "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
-      "optional": true
-    },
-    "safe-regex": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
-      "dev": true,
-      "requires": {
-        "ret": "~0.1.10"
-      }
-    },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
-    },
-    "saslprep": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
-      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
-      "optional": true,
-      "requires": {
-        "sparse-bitfield": "^3.0.3"
-      }
-    },
-    "sax": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
-    },
-    "semver": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
-    },
-    "semver-compare": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
-      "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
-      "dev": true
-    },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
-    },
-    "set-value": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
-      "dev": true,
-      "requires": {
-        "shebang-regex": "^1.0.0"
-      }
-    },
-    "shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
-      "dev": true
-    },
-    "signal-exit": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
-    },
-    "slice-ansi": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
-      "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.0",
-        "astral-regex": "^1.0.0",
-        "is-fullwidth-code-point": "^2.0.0"
-      },
-      "dependencies": {
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        }
-      }
-    },
-    "snapdragon": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
-      "dev": true,
-      "requires": {
-        "base": "^0.11.1",
-        "debug": "^2.2.0",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "map-cache": "^0.2.2",
-        "source-map": "^0.5.6",
-        "source-map-resolve": "^0.5.0",
-        "use": "^3.1.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        }
-      }
-    },
-    "snapdragon-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
-      "dev": true,
-      "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "snapdragon-util": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.2.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
-    },
-    "source-map-resolve": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
-      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
-      "dev": true,
-      "requires": {
-        "atob": "^2.1.1",
-        "decode-uri-component": "^0.2.0",
-        "resolve-url": "^0.2.1",
-        "source-map-url": "^0.4.0",
-        "urix": "^0.1.0"
-      }
-    },
-    "source-map-support": {
-      "version": "0.5.13",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
-      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "source-map-url": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
-      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
-      "dev": true
-    },
-    "sparse-bitfield": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-      "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
-      "optional": true,
-      "requires": {
-        "memory-pager": "^1.0.2"
-      }
-    },
-    "spawn-sync": {
-      "version": "1.0.15",
-      "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
-      "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=",
-      "dev": true,
-      "requires": {
-        "concat-stream": "^1.4.7",
-        "os-shim": "^0.1.2"
-      }
-    },
-    "spdx-correct": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
-      "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
-      "dev": true,
-      "requires": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "spdx-exceptions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
-      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
-      "dev": true
-    },
-    "spdx-expression-parse": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
-      "dev": true,
-      "requires": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "spdx-license-ids": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
-      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
-      "dev": true
-    },
-    "split-string": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.0"
-      }
-    },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-      "dev": true
-    },
-    "staged-git-files": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.1.tgz",
-      "integrity": "sha512-H89UNKr1rQJvI1c/PIR3kiAMBV23yvR7LItZiV74HWZwzt7f3YHuujJ9nJZlt58WlFox7XQsOahexwk7nTe69A==",
-      "dev": true
-    },
-    "static-extend": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
-      "dev": true,
-      "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "string-argv": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz",
-      "integrity": "sha1-2sMECGkMIfPDYwo/86BYd73L1zY=",
-      "dev": true
-    },
-    "string-width": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-      "requires": {
-        "code-point-at": "^1.0.0",
-        "is-fullwidth-code-point": "^1.0.0",
-        "strip-ansi": "^3.0.0"
-      }
-    },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "requires": {
-        "safe-buffer": "~5.1.0"
-      }
-    },
-    "stringify-object": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
-      "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
-      "dev": true,
-      "requires": {
-        "get-own-enumerable-property-symbols": "^3.0.0",
-        "is-obj": "^1.0.1",
-        "is-regexp": "^1.0.0"
-      }
-    },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-      "dev": true
-    },
-    "strip-eof": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
-      "dev": true
-    },
-    "strip-json-comments": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
-    },
-    "supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "requires": {
-        "has-flag": "^3.0.0"
-      }
-    },
-    "symbol-observable": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
-      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
-      "dev": true
-    },
-    "table": {
-      "version": "5.4.5",
-      "resolved": "https://registry.npmjs.org/table/-/table-5.4.5.tgz",
-      "integrity": "sha512-oGa2Hl7CQjfoaogtrOHEJroOcYILTx7BZWLGsJIlzoWmB2zmguhNfPJZsWPKYek/MgCxfco54gEi31d1uN2hFA==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.10.2",
-        "lodash": "^4.17.14",
-        "slice-ansi": "^2.1.0",
-        "string-width": "^3.0.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "6.10.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
-          "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
-          "dev": true,
-          "requires": {
-            "fast-deep-equal": "^2.0.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.2"
-          }
-        },
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "emoji-regex": {
-          "version": "7.0.3",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
-          "dev": true
-        },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "0.4.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        }
-      }
-    },
-    "tar": {
-      "version": "4.4.10",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
-      "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
-      "requires": {
-        "chownr": "^1.1.1",
-        "fs-minipass": "^1.2.5",
-        "minipass": "^2.3.5",
-        "minizlib": "^1.2.1",
-        "mkdirp": "^0.5.0",
-        "safe-buffer": "^5.1.2",
-        "yallist": "^3.0.3"
-      }
-    },
-    "text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
-      "dev": true
-    },
-    "through": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
-      "dev": true
-    },
-    "tmp": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
-      "dev": true,
-      "requires": {
-        "os-tmpdir": "~1.0.2"
-      }
-    },
-    "to-object-path": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "to-regex": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
-      "dev": true,
-      "requires": {
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "regex-not": "^1.0.2",
-        "safe-regex": "^1.1.0"
-      }
-    },
-    "to-regex-range": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
-      "dev": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1"
-      }
-    },
-    "tslib": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
-      "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
-      "dev": true
-    },
-    "type-check": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
-      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
-      "dev": true,
-      "requires": {
-        "prelude-ls": "~1.1.2"
-      }
-    },
-    "type-fest": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz",
-      "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==",
-      "dev": true
-    },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
-      "dev": true
-    },
-    "typescript": {
-      "version": "2.9.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
-      "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
-      "dev": true
-    },
-    "typescript-eslint-parser": {
-      "version": "16.0.1",
-      "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-16.0.1.tgz",
-      "integrity": "sha512-IKawLTu4A2xN3aN/cPLxvZ0bhxZHILGDKTZWvWNJ3sLNhJ3PjfMEDQmR2VMpdRPrmWOadgWXRwjLBzSA8AGsaQ==",
-      "dev": true,
-      "requires": {
-        "lodash.unescape": "4.0.1",
-        "semver": "5.5.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
-          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
-          "dev": true
-        }
-      }
-    },
-    "union-value": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
-      "dev": true,
-      "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^2.0.1"
-      }
-    },
-    "unset-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
-      "dev": true,
-      "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
-      },
-      "dependencies": {
-        "has-value": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
-          "dev": true,
-          "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
-          },
-          "dependencies": {
-            "isobject": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-              "dev": true,
-              "requires": {
-                "isarray": "1.0.0"
-              }
-            }
-          }
-        },
-        "has-values": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
-          "dev": true
-        }
-      }
-    },
-    "uri-js": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
-      "dev": true,
-      "requires": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "urix": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
-      "dev": true
-    },
-    "use": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
-      "dev": true
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
-    },
-    "validate-npm-package-license": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
-      "dev": true,
-      "requires": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
-      }
-    },
-    "vasync": {
-      "version": "1.6.4",
-      "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz",
-      "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=",
-      "requires": {
-        "verror": "1.6.0"
-      },
-      "dependencies": {
-        "verror": {
-          "version": "1.6.0",
-          "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz",
-          "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=",
-          "requires": {
-            "extsprintf": "1.2.0"
-          }
-        }
-      }
-    },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
-      }
-    },
-    "vue-eslint-parser": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
-      "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==",
-      "dev": true,
-      "requires": {
-        "debug": "^3.1.0",
-        "eslint-scope": "^3.7.1",
-        "eslint-visitor-keys": "^1.0.0",
-        "espree": "^3.5.2",
-        "esquery": "^1.0.0",
-        "lodash": "^4.17.4"
-      },
-      "dependencies": {
-        "acorn": {
-          "version": "5.7.3",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
-          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
-          "dev": true
-        },
-        "acorn-jsx": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
-          "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
-          "dev": true,
-          "requires": {
-            "acorn": "^3.0.4"
-          },
-          "dependencies": {
-            "acorn": {
-              "version": "3.3.0",
-              "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-              "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
-              "dev": true
-            }
-          }
-        },
-        "eslint-scope": {
-          "version": "3.7.3",
-          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
-          "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
-          "dev": true,
-          "requires": {
-            "esrecurse": "^4.1.0",
-            "estraverse": "^4.1.1"
-          }
-        },
-        "espree": {
-          "version": "3.5.4",
-          "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
-          "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
-          "dev": true,
-          "requires": {
-            "acorn": "^5.5.0",
-            "acorn-jsx": "^3.0.0"
-          }
-        }
-      }
-    },
-    "which": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
-      "dev": true,
-      "requires": {
-        "isexe": "^2.0.0"
-      }
-    },
-    "wide-align": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
-      "requires": {
-        "string-width": "^1.0.2 || 2"
-      }
-    },
-    "wordwrap": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
-      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
-      "dev": true
-    },
-    "wrap-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz",
-      "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=",
-      "dev": true,
-      "requires": {
-        "string-width": "^2.1.1",
-        "strip-ansi": "^4.0.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "string-width": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-          "dev": true,
-          "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        }
-      }
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
-    },
-    "write": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
-      "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
-      "dev": true,
-      "requires": {
-        "mkdirp": "^0.5.1"
-      }
-    },
-    "xss": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.6.tgz",
-      "integrity": "sha512-6Q9TPBeNyoTRxgZFk5Ggaepk/4vUOYdOsIUYvLehcsIZTFjaavbVnsuAkLA5lIFuug5hw8zxcB9tm01gsjph2A==",
-      "requires": {
-        "commander": "^2.9.0",
-        "cssfilter": "0.0.10"
-      }
-    },
-    "yallist": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
-      "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
-    }
-  }
-}

+ 0 - 73
.snap-meteor-1.8/package.json

@@ -1,73 +0,0 @@
-{
-  "name": "wekan",
-  "version": "v3.78.0",
-  "description": "Open-Source kanban",
-  "private": true,
-  "scripts": {
-    "lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
-    "lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
-    "lint:staged": "lint-staged",
-    "prettify": "prettier --write '**/*.js' '**/*.jsx'",
-    "test": "npm run lint"
-  },
-  "lint-staged": {
-    "*.js": [
-      "meteor npm run prettify",
-      "meteor npm run lint:eslint:fix",
-      "git add --force"
-    ],
-    "*.jsx": [
-      "meteor npm run prettify",
-      "meteor npm run lint:eslint:fix",
-      "git add --force"
-    ],
-    "*.json": [
-      "prettier --write",
-      "git add --force"
-    ]
-  },
-  "pre-commit": "lint:staged",
-  "eslintConfig": {
-    "extends": "@meteorjs/eslint-config-meteor"
-  },
-  "repository": {
-    "type": "git",
-    "url": "git+https://github.com/wekan/wekan.git"
-  },
-  "license": "MIT",
-  "bugs": {
-    "url": "https://github.com/wekan/wekan/issues"
-  },
-  "homepage": "https://wekan.github.io",
-  "devDependencies": {
-    "eslint": "^5.16.0",
-    "eslint-config-meteor": "0.0.9",
-    "eslint-config-prettier": "^3.6.0",
-    "eslint-import-resolver-meteor": "^0.4.0",
-    "eslint-plugin-import": "^2.18.0",
-    "eslint-plugin-meteor": "^5.1.0",
-    "eslint-plugin-prettier": "^3.1.0",
-    "lint-staged": "^7.3.0",
-    "pre-commit": "^1.2.2",
-    "prettier": "^1.18.2",
-    "prettier-eslint": "^8.8.2"
-  },
-  "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "ajv": "^5.0.0",
-    "babel-runtime": "^6.26.0",
-    "bcrypt": "^3.0.2",
-    "bson": "^4.0.0",
-    "bunyan": "^1.8.12",
-    "es6-promise": "^4.2.4",
-    "gridfs-stream": "^0.5.3",
-    "ldapjs": "^1.0.2",
-    "meteor-node-stubs": "^0.4.1",
-    "mongodb": "^3.3.3",
-    "os": "^0.1.1",
-    "page": "^1.8.6",
-    "qs": "^6.8.0",
-    "source-map-support": "^0.5.12",
-    "xss": "^1.0.6"
-  }
-}

+ 0 - 853
.snap-meteor-1.8/wekanCreator.js

@@ -1,853 +0,0 @@
-const DateString = Match.Where(function(dateAsString) {
-  check(dateAsString, String);
-  return moment(dateAsString, moment.ISO_8601).isValid();
-});
-
-export class WekanCreator {
-  constructor(data) {
-    // we log current date, to use the same timestamp for all our actions.
-    // this helps to retrieve all elements performed by the same import.
-    this._nowDate = new Date();
-    // The object creation dates, indexed by Wekan id
-    // (so we only parse actions once!)
-    this.createdAt = {
-      board: null,
-      cards: {},
-      lists: {},
-      swimlanes: {},
-    };
-    // The object creator Wekan Id, indexed by the object Wekan id
-    // (so we only parse actions once!)
-    this.createdBy = {
-      cards: {}, // only cards have a field for that
-    };
-
-    // Map of labels Wekan ID => Wekan ID
-    this.labels = {};
-    // Map of swimlanes Wekan ID => Wekan ID
-    this.swimlanes = {};
-    // Map of lists Wekan ID => Wekan ID
-    this.lists = {};
-    // Map of cards Wekan ID => Wekan ID
-    this.cards = {};
-    // Map of comments Wekan ID => Wekan ID
-    this.commentIds = {};
-    // Map of attachments Wekan ID => Wekan ID
-    this.attachmentIds = {};
-    // Map of checklists Wekan ID => Wekan ID
-    this.checklists = {};
-    // Map of checklistItems Wekan ID => Wekan ID
-    this.checklistItems = {};
-    // The comments, indexed by Wekan card id (to map when importing cards)
-    this.comments = {};
-    // Map of rules Wekan ID => Wekan ID
-    this.rules = {};
-    // the members, indexed by Wekan member id => Wekan user ID
-    this.members = data.membersMapping ? data.membersMapping : {};
-    // Map of triggers Wekan ID => Wekan ID
-    this.triggers = {};
-    // Map of actions Wekan ID => Wekan ID
-    this.actions = {};
-
-    // maps a wekanCardId to an array of wekanAttachments
-    this.attachments = {};
-  }
-
-  /**
-   * If dateString is provided,
-   * return the Date it represents.
-   * If not, will return the date when it was first called.
-   * This is useful for us, as we want all import operations to
-   * have the exact same date for easier later retrieval.
-   *
-   * @param {String} dateString a properly formatted Date
-   */
-  _now(dateString) {
-    if (dateString) {
-      return new Date(dateString);
-    }
-    if (!this._nowDate) {
-      this._nowDate = new Date();
-    }
-    return this._nowDate;
-  }
-
-  /**
-   * if wekanUserId is provided and we have a mapping,
-   * return it.
-   * Otherwise return current logged user.
-   * @param wekanUserId
-   * @private
-   */
-  _user(wekanUserId) {
-    if (wekanUserId && this.members[wekanUserId]) {
-      return this.members[wekanUserId];
-    }
-    return Meteor.userId();
-  }
-
-  checkActivities(wekanActivities) {
-    check(wekanActivities, [
-      Match.ObjectIncluding({
-        activityType: String,
-        createdAt: DateString,
-      }),
-    ]);
-    // XXX we could perform more thorough checks based on action type
-  }
-
-  checkBoard(wekanBoard) {
-    check(
-      wekanBoard,
-      Match.ObjectIncluding({
-        archived: Boolean,
-        title: String,
-        // XXX refine control by validating 'color' against a list of
-        // allowed values (is it worth the maintenance?)
-        color: String,
-        permission: Match.Where(value => {
-          return ['private', 'public'].indexOf(value) >= 0;
-        }),
-      }),
-    );
-  }
-
-  checkCards(wekanCards) {
-    check(wekanCards, [
-      Match.ObjectIncluding({
-        archived: Boolean,
-        dateLastActivity: DateString,
-        labelIds: [String],
-        title: String,
-        sort: Number,
-      }),
-    ]);
-  }
-
-  checkLabels(wekanLabels) {
-    check(wekanLabels, [
-      Match.ObjectIncluding({
-        // XXX refine control by validating 'color' against a list of allowed
-        // values (is it worth the maintenance?)
-        color: String,
-      }),
-    ]);
-  }
-
-  checkLists(wekanLists) {
-    check(wekanLists, [
-      Match.ObjectIncluding({
-        archived: Boolean,
-        title: String,
-      }),
-    ]);
-  }
-
-  checkSwimlanes(wekanSwimlanes) {
-    check(wekanSwimlanes, [
-      Match.ObjectIncluding({
-        archived: Boolean,
-        title: String,
-      }),
-    ]);
-  }
-
-  checkChecklists(wekanChecklists) {
-    check(wekanChecklists, [
-      Match.ObjectIncluding({
-        cardId: String,
-        title: String,
-      }),
-    ]);
-  }
-
-  checkChecklistItems(wekanChecklistItems) {
-    check(wekanChecklistItems, [
-      Match.ObjectIncluding({
-        cardId: String,
-        title: String,
-      }),
-    ]);
-  }
-
-  checkRules(wekanRules) {
-    check(wekanRules, [
-      Match.ObjectIncluding({
-        triggerId: String,
-        actionId: String,
-        title: String,
-      }),
-    ]);
-  }
-
-  checkTriggers(wekanTriggers) {
-    // XXX More check based on trigger type
-    check(wekanTriggers, [
-      Match.ObjectIncluding({
-        activityType: String,
-        desc: String,
-      }),
-    ]);
-  }
-
-  getMembersToMap(data) {
-    // we will work on the list itself (an ordered array of objects) when a
-    // mapping is done, we add a 'wekan' field to the object representing the
-    // imported member
-    const membersToMap = data.members;
-    const users = data.users;
-    // auto-map based on username
-    membersToMap.forEach(importedMember => {
-      importedMember.id = importedMember.userId;
-      delete importedMember.userId;
-      const user = users.filter(user => {
-        return user._id === importedMember.id;
-      })[0];
-      if (user.profile && user.profile.fullname) {
-        importedMember.fullName = user.profile.fullname;
-      }
-      importedMember.username = user.username;
-      const wekanUser = Users.findOne({ username: importedMember.username });
-      if (wekanUser) {
-        importedMember.wekanId = wekanUser._id;
-      }
-    });
-    return membersToMap;
-  }
-
-  checkActions(wekanActions) {
-    // XXX More check based on action type
-    check(wekanActions, [
-      Match.ObjectIncluding({
-        actionType: String,
-        desc: String,
-      }),
-    ]);
-  }
-
-  // You must call parseActions before calling this one.
-  createBoardAndLabels(boardToImport) {
-    const boardToCreate = {
-      archived: boardToImport.archived,
-      color: boardToImport.color,
-      // very old boards won't have a creation activity so no creation date
-      createdAt: this._now(boardToImport.createdAt),
-      labels: [],
-      members: [
-        {
-          userId: Meteor.userId(),
-          wekanId: Meteor.userId(),
-          isActive: true,
-          isAdmin: true,
-          isNoComments: false,
-          isCommentOnly: false,
-          swimlaneId: false,
-        },
-      ],
-      // Standalone Export has modifiedAt missing, adding modifiedAt to fix it
-      modifiedAt: this._now(boardToImport.modifiedAt),
-      permission: boardToImport.permission,
-      slug: getSlug(boardToImport.title) || 'board',
-      stars: 0,
-      title: boardToImport.title,
-    };
-    // now add other members
-    if (boardToImport.members) {
-      boardToImport.members.forEach(wekanMember => {
-        // do we already have it in our list?
-        if (
-          !boardToCreate.members.some(
-            member => member.wekanId === wekanMember.wekanId,
-          )
-        )
-          boardToCreate.members.push({
-            ...wekanMember,
-            userId: wekanMember.wekanId,
-          });
-      });
-    }
-    boardToImport.labels.forEach(label => {
-      const labelToCreate = {
-        _id: Random.id(6),
-        color: label.color,
-        name: label.name,
-      };
-      // We need to remember them by Wekan ID, as this is the only ref we have
-      // when importing cards.
-      this.labels[label._id] = labelToCreate._id;
-      boardToCreate.labels.push(labelToCreate);
-    });
-    const boardId = Boards.direct.insert(boardToCreate);
-    Boards.direct.update(boardId, {
-      $set: {
-        modifiedAt: this._now(),
-      },
-    });
-    // log activity
-    Activities.direct.insert({
-      activityType: 'importBoard',
-      boardId,
-      createdAt: this._now(),
-      source: {
-        id: boardToImport.id,
-        system: 'Wekan',
-      },
-      // We attribute the import to current user,
-      // not the author from the original object.
-      userId: this._user(),
-    });
-    return boardId;
-  }
-
-  /**
-   * Create the Wekan cards corresponding to the supplied Wekan cards,
-   * as well as all linked data: activities, comments, and attachments
-   * @param wekanCards
-   * @param boardId
-   * @returns {Array}
-   */
-  createCards(wekanCards, boardId) {
-    const result = [];
-    wekanCards.forEach(card => {
-      const cardToCreate = {
-        archived: card.archived,
-        boardId,
-        // very old boards won't have a creation activity so no creation date
-        createdAt: this._now(this.createdAt.cards[card._id]),
-        dateLastActivity: this._now(),
-        description: card.description,
-        listId: this.lists[card.listId],
-        swimlaneId: this.swimlanes[card.swimlaneId],
-        sort: card.sort,
-        title: card.title,
-        // we attribute the card to its creator if available
-        userId: this._user(this.createdBy.cards[card._id]),
-        isOvertime: card.isOvertime || false,
-        startAt: card.startAt ? this._now(card.startAt) : null,
-        dueAt: card.dueAt ? this._now(card.dueAt) : null,
-        spentTime: card.spentTime || null,
-      };
-      // add labels
-      if (card.labelIds) {
-        cardToCreate.labelIds = card.labelIds.map(wekanId => {
-          return this.labels[wekanId];
-        });
-      }
-      // add members {
-      if (card.members) {
-        const wekanMembers = [];
-        // we can't just map, as some members may not have been mapped
-        card.members.forEach(sourceMemberId => {
-          if (this.members[sourceMemberId]) {
-            const wekanId = this.members[sourceMemberId];
-            // we may map multiple Wekan members to the same wekan user
-            // in which case we risk adding the same user multiple times
-            if (!wekanMembers.find(wId => wId === wekanId)) {
-              wekanMembers.push(wekanId);
-            }
-          }
-          return true;
-        });
-        if (wekanMembers.length > 0) {
-          cardToCreate.members = wekanMembers;
-        }
-      }
-      // set color
-      if (card.color) {
-        cardToCreate.color = card.color;
-      }
-      // insert card
-      const cardId = Cards.direct.insert(cardToCreate);
-      // keep track of Wekan id => Wekan id
-      this.cards[card._id] = cardId;
-      // // log activity
-      // Activities.direct.insert({
-      //   activityType: 'importCard',
-      //   boardId,
-      //   cardId,
-      //   createdAt: this._now(),
-      //   listId: cardToCreate.listId,
-      //   source: {
-      //     id: card._id,
-      //     system: 'Wekan',
-      //   },
-      //   // we attribute the import to current user,
-      //   // not the author of the original card
-      //   userId: this._user(),
-      // });
-      // add comments
-      const comments = this.comments[card._id];
-      if (comments) {
-        comments.forEach(comment => {
-          const commentToCreate = {
-            boardId,
-            cardId,
-            createdAt: this._now(comment.createdAt),
-            text: comment.text,
-            // we attribute the comment to the original author, default to current user
-            userId: this._user(comment.userId),
-          };
-          // dateLastActivity will be set from activity insert, no need to
-          // update it ourselves
-          const commentId = CardComments.direct.insert(commentToCreate);
-          this.commentIds[comment._id] = commentId;
-          // Activities.direct.insert({
-          //   activityType: 'addComment',
-          //   boardId: commentToCreate.boardId,
-          //   cardId: commentToCreate.cardId,
-          //   commentId,
-          //   createdAt: this._now(commentToCreate.createdAt),
-          //   // we attribute the addComment (not the import)
-          //   // to the original author - it is needed by some UI elements.
-          //   userId: commentToCreate.userId,
-          // });
-        });
-      }
-      const attachments = this.attachments[card._id];
-      const wekanCoverId = card.coverId;
-      if (attachments) {
-        attachments.forEach(att => {
-          const file = new FS.File();
-          // Simulating file.attachData on the client generates multiple errors
-          // - HEAD returns null, which causes exception down the line
-          // - the template then tries to display the url to the attachment which causes other errors
-          // so we make it server only, and let UI catch up once it is done, forget about latency comp.
-          const self = this;
-          if (Meteor.isServer) {
-            if (att.url) {
-              file.attachData(att.url, function(error) {
-                file.boardId = boardId;
-                file.cardId = cardId;
-                file.userId = self._user(att.userId);
-                // The field source will only be used to prevent adding
-                // attachments' related activities automatically
-                file.source = 'import';
-                if (error) {
-                  throw error;
-                } else {
-                  const wekanAtt = Attachments.insert(file, () => {
-                    // we do nothing
-                  });
-                  self.attachmentIds[att._id] = wekanAtt._id;
-                  //
-                  if (wekanCoverId === att._id) {
-                    Cards.direct.update(cardId, {
-                      $set: {
-                        coverId: wekanAtt._id,
-                      },
-                    });
-                  }
-                }
-              });
-            } else if (att.file) {
-              file.attachData(
-                new Buffer(att.file, 'base64'),
-                {
-                  type: att.type,
-                },
-                error => {
-                  file.name(att.name);
-                  file.boardId = boardId;
-                  file.cardId = cardId;
-                  file.userId = self._user(att.userId);
-                  // The field source will only be used to prevent adding
-                  // attachments' related activities automatically
-                  file.source = 'import';
-                  if (error) {
-                    throw error;
-                  } else {
-                    const wekanAtt = Attachments.insert(file, () => {
-                      // we do nothing
-                    });
-                    this.attachmentIds[att._id] = wekanAtt._id;
-                    //
-                    if (wekanCoverId === att._id) {
-                      Cards.direct.update(cardId, {
-                        $set: {
-                          coverId: wekanAtt._id,
-                        },
-                      });
-                    }
-                  }
-                },
-              );
-            }
-          }
-          // todo XXX set cover - if need be
-        });
-      }
-      result.push(cardId);
-    });
-    return result;
-  }
-
-  // Create labels if they do not exist and load this.labels.
-  createLabels(wekanLabels, board) {
-    wekanLabels.forEach(label => {
-      const color = label.color;
-      const name = label.name;
-      const existingLabel = board.getLabel(name, color);
-      if (existingLabel) {
-        this.labels[label.id] = existingLabel._id;
-      } else {
-        const idLabelCreated = board.pushLabel(name, color);
-        this.labels[label.id] = idLabelCreated;
-      }
-    });
-  }
-
-  createLists(wekanLists, boardId) {
-    wekanLists.forEach((list, listIndex) => {
-      const listToCreate = {
-        archived: list.archived,
-        boardId,
-        // We are being defensing here by providing a default date (now) if the
-        // creation date wasn't found on the action log. This happen on old
-        // Wekan boards (eg from 2013) that didn't log the 'createList' action
-        // we require.
-        createdAt: this._now(this.createdAt.lists[list.id]),
-        title: list.title,
-        sort: list.sort ? list.sort : listIndex,
-      };
-      const listId = Lists.direct.insert(listToCreate);
-      Lists.direct.update(listId, {
-        $set: {
-          updatedAt: this._now(),
-        },
-      });
-      this.lists[list._id] = listId;
-      // // log activity
-      // Activities.direct.insert({
-      //   activityType: 'importList',
-      //   boardId,
-      //   createdAt: this._now(),
-      //   listId,
-      //   source: {
-      //     id: list._id,
-      //     system: 'Wekan',
-      //   },
-      //   // We attribute the import to current user,
-      //   // not the creator of the original object
-      //   userId: this._user(),
-      // });
-    });
-  }
-
-  createSwimlanes(wekanSwimlanes, boardId) {
-    wekanSwimlanes.forEach((swimlane, swimlaneIndex) => {
-      const swimlaneToCreate = {
-        archived: swimlane.archived,
-        boardId,
-        // We are being defensing here by providing a default date (now) if the
-        // creation date wasn't found on the action log. This happen on old
-        // Wekan boards (eg from 2013) that didn't log the 'createList' action
-        // we require.
-        createdAt: this._now(this.createdAt.swimlanes[swimlane._id]),
-        title: swimlane.title,
-        sort: swimlane.sort ? swimlane.sort : swimlaneIndex,
-      };
-      // set color
-      if (swimlane.color) {
-        swimlaneToCreate.color = swimlane.color;
-      }
-      const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
-      Swimlanes.direct.update(swimlaneId, {
-        $set: {
-          updatedAt: this._now(),
-        },
-      });
-      this.swimlanes[swimlane._id] = swimlaneId;
-    });
-  }
-
-  createChecklists(wekanChecklists) {
-    const result = [];
-    wekanChecklists.forEach((checklist, checklistIndex) => {
-      // Create the checklist
-      const checklistToCreate = {
-        cardId: this.cards[checklist.cardId],
-        title: checklist.title,
-        createdAt: checklist.createdAt,
-        sort: checklist.sort ? checklist.sort : checklistIndex,
-      };
-      const checklistId = Checklists.direct.insert(checklistToCreate);
-      this.checklists[checklist._id] = checklistId;
-      result.push(checklistId);
-    });
-    return result;
-  }
-
-  createTriggers(wekanTriggers, boardId) {
-    wekanTriggers.forEach(trigger => {
-      if (trigger.hasOwnProperty('labelId')) {
-        trigger.labelId = this.labels[trigger.labelId];
-      }
-      if (trigger.hasOwnProperty('memberId')) {
-        trigger.memberId = this.members[trigger.memberId];
-      }
-      trigger.boardId = boardId;
-      const oldId = trigger._id;
-      delete trigger._id;
-      this.triggers[oldId] = Triggers.direct.insert(trigger);
-    });
-  }
-
-  createActions(wekanActions, boardId) {
-    wekanActions.forEach(action => {
-      if (action.hasOwnProperty('labelId')) {
-        action.labelId = this.labels[action.labelId];
-      }
-      if (action.hasOwnProperty('memberId')) {
-        action.memberId = this.members[action.memberId];
-      }
-      action.boardId = boardId;
-      const oldId = action._id;
-      delete action._id;
-      this.actions[oldId] = Actions.direct.insert(action);
-    });
-  }
-
-  createRules(wekanRules, boardId) {
-    wekanRules.forEach(rule => {
-      // Create the rule
-      rule.boardId = boardId;
-      rule.triggerId = this.triggers[rule.triggerId];
-      rule.actionId = this.actions[rule.actionId];
-      delete rule._id;
-      Rules.direct.insert(rule);
-    });
-  }
-
-  createChecklistItems(wekanChecklistItems) {
-    wekanChecklistItems.forEach((checklistitem, checklistitemIndex) => {
-      // Create the checklistItem
-      const checklistItemTocreate = {
-        title: checklistitem.title,
-        checklistId: this.checklists[checklistitem.checklistId],
-        cardId: this.cards[checklistitem.cardId],
-        sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
-        isFinished: checklistitem.isFinished,
-      };
-      const checklistItemId = ChecklistItems.direct.insert(
-        checklistItemTocreate,
-      );
-      this.checklistItems[checklistitem._id] = checklistItemId;
-    });
-  }
-
-  parseActivities(wekanBoard) {
-    wekanBoard.activities.forEach(activity => {
-      switch (activity.activityType) {
-        case 'addAttachment': {
-          // We have to be cautious, because the attachment could have been removed later.
-          // In that case Wekan still reports its addition, but removes its 'url' field.
-          // So we test for that
-          const wekanAttachment = wekanBoard.attachments.filter(attachment => {
-            return attachment._id === activity.attachmentId;
-          })[0];
-
-          if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
-            if (wekanAttachment.url || wekanAttachment.file) {
-              // we cannot actually create the Wekan attachment, because we don't yet
-              // have the cards to attach it to, so we store it in the instance variable.
-              const wekanCardId = activity.cardId;
-              if (!this.attachments[wekanCardId]) {
-                this.attachments[wekanCardId] = [];
-              }
-              this.attachments[wekanCardId].push(wekanAttachment);
-            }
-          }
-          break;
-        }
-        case 'addComment': {
-          const wekanComment = wekanBoard.comments.filter(comment => {
-            return comment._id === activity.commentId;
-          })[0];
-          const id = activity.cardId;
-          if (!this.comments[id]) {
-            this.comments[id] = [];
-          }
-          this.comments[id].push(wekanComment);
-          break;
-        }
-        case 'createBoard': {
-          this.createdAt.board = activity.createdAt;
-          break;
-        }
-        case 'createCard': {
-          const cardId = activity.cardId;
-          this.createdAt.cards[cardId] = activity.createdAt;
-          this.createdBy.cards[cardId] = activity.userId;
-          break;
-        }
-        case 'createList': {
-          const listId = activity.listId;
-          this.createdAt.lists[listId] = activity.createdAt;
-          break;
-        }
-        case 'createSwimlane': {
-          const swimlaneId = activity.swimlaneId;
-          this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
-          break;
-        }
-      }
-    });
-  }
-
-  importActivities(activities, boardId) {
-    activities.forEach(activity => {
-      switch (activity.activityType) {
-        // Board related activities
-        // TODO: addBoardMember, removeBoardMember
-        case 'createBoard': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            type: 'board',
-            activityTypeId: boardId,
-            activityType: activity.activityType,
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        // List related activities
-        // TODO: removeList, archivedList
-        case 'createList': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            type: 'list',
-            activityType: activity.activityType,
-            listId: this.lists[activity.listId],
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        // Card related activities
-        // TODO: archivedCard, restoredCard, joinMember, unjoinMember
-        case 'createCard': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            activityType: activity.activityType,
-            listId: this.lists[activity.listId],
-            cardId: this.cards[activity.cardId],
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        case 'moveCard': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            oldListId: this.lists[activity.oldListId],
-            activityType: activity.activityType,
-            listId: this.lists[activity.listId],
-            cardId: this.cards[activity.cardId],
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        // Comment related activities
-        case 'addComment': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            activityType: activity.activityType,
-            cardId: this.cards[activity.cardId],
-            commentId: this.commentIds[activity.commentId],
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        // Attachment related activities
-        case 'addAttachment': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            type: 'card',
-            activityType: activity.activityType,
-            attachmentId: this.attachmentIds[activity.attachmentId],
-            cardId: this.cards[activity.cardId],
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        // Checklist related activities
-        case 'addChecklist': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            activityType: activity.activityType,
-            cardId: this.cards[activity.cardId],
-            checklistId: this.checklists[activity.checklistId],
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-        case 'addChecklistItem': {
-          Activities.direct.insert({
-            userId: this._user(activity.userId),
-            activityType: activity.activityType,
-            cardId: this.cards[activity.cardId],
-            checklistId: this.checklists[activity.checklistId],
-            checklistItemId: activity.checklistItemId.replace(
-              activity.checklistId,
-              this.checklists[activity.checklistId],
-            ),
-            boardId,
-            createdAt: this._now(activity.createdAt),
-          });
-          break;
-        }
-      }
-    });
-  }
-
-  //check(board) {
-  check() {
-    //try {
-    // check(data, {
-    //   membersMapping: Match.Optional(Object),
-    // });
-    // this.checkActivities(board.activities);
-    // this.checkBoard(board);
-    // this.checkLabels(board.labels);
-    // this.checkLists(board.lists);
-    // this.checkSwimlanes(board.swimlanes);
-    // this.checkCards(board.cards);
-    //this.checkChecklists(board.checklists);
-    // this.checkRules(board.rules);
-    // this.checkActions(board.actions);
-    //this.checkTriggers(board.triggers);
-    //this.checkChecklistItems(board.checklistItems);
-    //} catch (e) {
-    //  throw new Meteor.Error('error-json-schema');
-    // }
-  }
-
-  create(board, currentBoardId) {
-    // TODO : Make isSandstorm variable global
-    const isSandstorm =
-      Meteor.settings &&
-      Meteor.settings.public &&
-      Meteor.settings.public.sandstorm;
-    if (isSandstorm && currentBoardId) {
-      const currentBoard = Boards.findOne(currentBoardId);
-      currentBoard.archive();
-    }
-    this.parseActivities(board);
-    const boardId = this.createBoardAndLabels(board);
-    this.createLists(board.lists, boardId);
-    this.createSwimlanes(board.swimlanes, boardId);
-    this.createCards(board.cards, boardId);
-    this.createChecklists(board.checklists);
-    this.createChecklistItems(board.checklistItems);
-    this.importActivities(board.activities, boardId);
-    this.createTriggers(board.triggers, boardId);
-    this.createActions(board.actions, boardId);
-    this.createRules(board.rules, boardId);
-    // XXX add members
-    return boardId;
-  }
-}

+ 2 - 2
.travis.yml

@@ -1,9 +1,9 @@
-dist: eoan
+dist: focal
 sudo: required
 sudo: required
 
 
 env:
 env:
   TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
   TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
-  TRAVIS_NODE_VERSION: 12.15.0
+  TRAVIS_NODE_VERSION: 12.16.3
   TRAVIS_NPM_VERSION: latest
   TRAVIS_NPM_VERSION: latest
 
 
 before_install:
 before_install:

+ 501 - 1
CHANGELOG.md

@@ -1,3 +1,503 @@
+# Upcoming Wekan release
+
+This release adds the following server platforms:
+
+- [Android arm64/x64](https://github.com/wekan/wekan/wiki/Android).
+  Thanks to xet7.
+
+and adds the following features:
+
+- [Install Wekan to mobile homescreen icon and use fullscreen
+  PWA](https://github.com/commit/8d5adc04645e3e71423f16869f39b8d79969bccd).
+  [Docs for iOS and Android at wiki PWA page](https://github.com/wekan/wekan/wiki/PWA).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix getStartDayOfWeek once again](https://github.com/wekan/wekan/pull/3061).
+  Thanks to marc1006.
+- [Fix shortcuts list and support card shortcuts when hovering
+  a card](https://github.com/wekan/wekan/pull/3066).
+  Thanks to marc1006.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v4.01 2020-04-28 Wekan release
+
+This release adds the following updates:
+
+- [Upgrade to Node v12.16.3](https://github.com/wekan/wekan/commit/1d89e96dd101c11913f1acdd6d16b5650eaf18a7).
+  Thanks to Node developers and xet7.
+
+and fixes the following bugs:
+
+- [Fix Docker builds](https://github.com/wekan/wekan/commit/280e66947e3afa878c41e876cf827ebcec81a2c6).
+  Thanks to xet7.
+- [Fix Cards and Users API docs at https://wekan.github.io/api/ not generated because of
+  syntax error and new Javascript syntax](https://github.com/wekan/wekan/commit/9ae20a3f51e63c29f536e2f5b3e66a2c7d88c691).
+  Wekan uses wekan/releases/generate-docs*.sh Python code to generate OpenAPI docs,
+  it did not show any errors while generating docs, only left out parts of API docs.
+  This affected Wekan versions v3.94-v4.00.
+  Thanks to pvcon13 and xet7.
+- [Fix list header height when cards count is shown](https://github.com/wekan/wekan/pull/3056).
+  Thanks to marc1006.
+- [Smaller height for Add Board button](https://github.com/wekan/wekan/commit/6afc9259f084717a0cc3ce6d66979fd7c1471939).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v4.00 2020-04-27 Wekan release
+
+This release fixes the following bugs:
+
+- [Make sure that the board header buttons fit into one line even for devices with 360px width
+  resolution](https://github.com/wekan/wekan/pull/3052).
+  Thanks to marc1006.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.99 2020-04-27 Wekan release
+
+This release fixes the following bugs:
+
+- [Fix Boards are very hard to tap in mobile](https://github.com/wekan/wekan/pull/3051).
+  Thanks to marc1006.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.98 2020-04-25 Wekan release
+
+News:
+
+- There is now many mobile and desktop webbrowser fixes. Please test does your
+  favourite Javascript enabled webbrowser work, and add issues if something
+  does not work, and there is no existing issue about that yet.
+- Desktop browser mode has setting for Show/Hide drag handles:
+  top right click username / Change Settings / Show desktop drag handles.
+  You can request desktop website also at mobile webbrowsers on Android.
+  At iOS requesting desktop website did not seem to work yet.
+- At iOS Safari and Chrome, to see swimlane buttons you need to scroll to right.
+  Fixes to this and other issues are welcome as pull request.
+
+This release adds the following new features:
+
+- [Pre-fill the title of checklists (Trello-style)](https://github.com/wekan/wekan/pull/3030).
+  Thanks to boeserwolf.
+- [Implement option to change the first day of the week in user settings](https://github.com/wekan/wekan/pull/3032).
+  Thanks to marc1006.
+- [Add babel to build chain and linter. Enables fancy Javascript language
+  features like optional chaining, for developer happiness](https://github.com/wekan/wekan/pull/3034).
+  Thanks to boeserwolf.
+- [Use only one 'Apply' button for applying the user settings](https://github.com/wekan/wekan/pull/3039).
+  Thanks to marc1006.
+- [Allow variable height for board list items. Allow words in title/description to be able to break
+  and wrap onto the next line](https://github.com/wekan/wekan/pull/3046).
+  Thanks to marc1006.
+
+and adds the following updates:
+
+- [Upgrade to Meteor 1.10.2](https://github.com/wekan/wekan/commit/d1f98d0c472fb41e25fb29a9a6f6dae7db003f6f).
+  Thanks to Meteor developers and xet7.
+- [Set Snap MongoDB compatibility to 4.2 according to Meteor ChangeLog](https://github.com/wekan/wekan/commit/7de18eccea3854db3be6197bf21afbfd3ddb65a6).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Multiple lint issue fixes](https://github.com/wekan/wekan/pull/3031).
+  Thanks to marc1006.
+- [Fix lint errors in lint error fix](https://github.com/wekan/wekan/commit/9e95c06415e614e587d684ff9660cc53c5f8c8d3).
+  Thanks to xet7.
+- [Fix getStartDayOfWeek function](https://github.com/wekan/wekan/pull/3038).
+  Thanks to marc1006 and boeserwolf.
+- Improve mobile devices support [Part1](https://github.com/wekan/wekan/pull/3040) and [Part2](https://github.com/wekan/wekan/pull/3045).
+  Thanks to marc1006.
+- [Fix Wekan not load at all in Firefox v.68 for Android](https://github.com/wekan/wekan/commit/1235363465b824d26129d4aa74a4445f362c1a73).
+  Thanks to xet7.
+- [Fix comment typo in docker-compose.yml](https://github.com/wekan/wekan/pull/3044).
+  Thanks to VictorioBerra.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.97 2020-04-19 Wekan release
+
+This release adds the following new features:
+
+- [Sortable boards](https://github.com/wekan/wekan/pull/3027).
+  Thanks to boeserwolf.
+- [Added dockerfiles for multi-arch builds and manifest](https://github.com/wekan/wekan/pull/3023).
+  [In Progress](https://github.com/wekan/wekan/issues/2999).
+  Thanks to brokencode64.
+- [Make linked card clickable](https://github.com/wekan/wekan/pull/3025).
+  Thanks to boeserwolf.
+
+and fixes the following bugs:
+
+- [Fix using checklists on mobile and iPad](https://github.com/wekan/wekan/pull/3019).
+  Thanks to devinsm.
+- [Improve card layout on mobile devices](https://github.com/wekan/wekan/pull/3024).
+  Thanks to marc1006.
+- [Make OCP OAuth work with Openshift 4.x](https://github.com/wekan/wekan/pull/3020).
+  Thanks to ckavili.
+- [Remove old warning from Sandstorm import board data loss, because bug has been already
+  fixed](https://github.com/wekan/wekan/commit/960fe5163b6a2f7c3dca03b5e31d69611b49f079).
+  Thanks to aputsiaq and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.96 2020-04-15 Wekan release
+
+This release adds the following Sandstorm updates:
+
+- This is the first Sandstorm Wekan release that uses newest Meteor 1.10.1 and Node 12.x.
+  Now all Wekan platforms use newest Meteor and Node 12.x LTS.
+  Thanks to kentonv and xet7.
+- [Fix capnp workaround to work with newest Meteor and
+  Node 12.x](https://github.com/wekan/wekan/commit/b2d546579c4957352c29b36c0c8a4a08b944dbb4).
+  Thanks to kentonv.
+- [Update Sandstorm release script for newest Meteor and
+  Node 12.x](https://github.com/wekan/wekan/commit/c5f782976b971fa3f2323e80a013bbf6a49c0596).
+  Thanks to xet7.
+- [Remove Meteor 1.8.x files because Sandstorm Wekan now uses newest
+  Meteor](https://github.com/wekan/wekan/commit/1a836969e10215bad47ac56a9b0d9de801b66fd2).
+  Thanks to xet7.
+
+and adds the following new features:
+
+- [Hide password auth with environment variable PASSWORD_LOGIN_ENABLED=false](https://github.com/wekan/wekan/pull/3014).
+  Snap example: `sudo snap set wekan password-login-enabled='false'` .
+  Thanks to salleman33.
+
+and fixes the following bugs:
+
+- [Fix Board admins can not clone or archive their boards at All Boards
+  page](https://github.com/wekan/wekan/pull/3013).
+  Thanks to salleman33.
+- [Fix `<p>` margin in card labels](https://github.com/wekan/wekan/pull/3015).
+  Thanks to boeserwolf.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.95 2020-04-12 Wekan release
+
+This release adds the following new features:
+
+- [Add gitpod config](https://github.com/wekan/wekan/pull/3009).
+  This adds support for Gitpod.io, a free automated
+  dev environment that makes contributing and generally working on GitHub
+  projects much easier. It allows anyone to start a ready-to-code dev
+  environment for any branch, issue and pull request with a single click.
+  Thanks to juniormendonca.
+- [Public boards overview](https://github.com/wekan/wekan/pull/3008).
+  Thanks to NicoP-S.
+
+and fixes the following bugs:
+
+- [Fix styling issue in notifications drawer](https://github.com/wekan/wekan/pull/3012).
+  Thanks to boeserwolf.
+- [Fix error in notifications cleanup cron](https://github.com/wekan/wekan/pull/3010).
+  Thanks to jtbairdsr.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.94 2020-04-12 Wekan release
+
+This release adds the following new features:
+
+- [Public vote](https://github.com/wekan/wekan/pull/3006).
+  Thanks to NicoP-S.
+- [Add robots.txt disallow all](https://github.com/wekan/wekan/commit/3fae5355d40055757bf4a5f0c503581195609720).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.93 2020-04-10 Wekan release
+
+This release adds the following new features:
+
+- [Trello vote import & hide export button if with_api is
+  disabled](https://github.com/wekan/wekan/pull/3000).
+  Thanks to NicoP-S.
+- [When adding a user to a board that has subtasks, also add user to the subtask
+  board](https://github.com/wekan/wekan/pull/3004).
+  Thanks to slvrpdr.
+
+and adds the following updates:
+
+- Upgrade to Node v12.16.2 [Part1](https://github.com/wekan/wekan/commit/6db717b9b384fe1491063e507b80e67791a07e3a)
+  and [Part2](https://github.com/wekan/wekan/commit/268d7fcb32186a902a84e7f6d80c50b1f3790bad).
+  Thanks to Node developers and xet7.
+
+and fixes the following bugs:
+
+- [Fix bug that prevents editing or deleting
+  comments](https://github.com/wekan/wekan/pull/3005).
+  Thanks to jtbairdsr.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.92 2020-04-09 Wekan release
+
+This release adds the following new features:
+
+- [Scheduler to clean up read notifications. Also added a button to manually remove all
+  read notifications, and a fix to prevent users form getting notifications for their own
+  actions](https://github.com/wekan/wekan/pull/2998).
+  Thanks to jtbairdsr.
+- [Add setting](https://github.com/wekan/wekan/commit/5ebb47cb0ec7272894a37d99579ede872251f55c)
+  default [NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE=2](https://github.com/wekan/wekan/pull/2998)
+  to all Wekan platforms.
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.91 2020-04-08 Wekan release
+
+This release adds the following new features:
+
+- [OpenShift: Route template added to helm chart for Openshift v4x
+  cluster](https://github.com/wekan/wekan/pull/2996).
+  Thanks to ckavili.
+- [Filter by Assignee](https://github.com/wekan/wekan/pull/2997).
+  Thanks to daniel-eder.
+- [Vote on Card](https://github.com/wekan/wekan/pull/2994).
+  Thanks to NicoP-S and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.90 2020-04-06 Wekan release
+
+This release makes the following updates:
+
+- [Update dependencies](https://github.com/wekan/wekan/commit/d798f6e3ef09595ce4f1d1fbc053eec70fc91fb9).
+
+and updates the following translations:
+
+- [Update layouts.js for zh-TW language name](https://github.com/wekan/wekan/pull/2988).
+  Thanks to doggy8088.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.89 2020-04-05 Wekan release
+
+This release adds the following new features:
+
+- [Create subtasks in parenttask swimlane](https://github.com/wekan/wekan/issues/1953).
+  Thanks to TOSCom-DanielEder.
+- [When searching cards in a board, also search from Custom Fields](https://github.com/wekan/wekan/pull/2985).
+  Thanks to slvrpdr.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.88 2020-04-02 Wekan release
+
+This release adds the following new features:
+
+- [Notification drawer](https://github.com/wekan/wekan/pull/2975) [like Trello](https://github.com/wekan/wekan/issues/2471).
+  Thanks to jtbairdsr and xet7.
+
+and makes the following UI changes:
+
+- [Minicard labels on the top and title on bottom](https://github.com/wekan/wekan/issues/2980).
+  Thanks to helioguardabaxo and xet7.
+
+and fixes the following bugs:
+
+- [Fix start-wekan.sh MongoDB port to 27017](https://github.com/wekan/wekan/commit/c60a092fc0ed9fe15c417bcb443b1e3e3aaedf7e).
+  Thanks to Keelan and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.87 2020-04-01 Wekan release
+
+This release makes the following UI changes:
+
+- [Move "Rules" from "Board View" to "Board Settings"](https://github.com/wekan/wekan/issues/2973).
+  Thanks to helioguardabaxo and xet7.
+- [Improvements on card details visualization](https://github.com/wekan/wekan/issues/2974).
+  Thanks to helioguardabaxo and xet7.
+- [Hide duplicate "Hide system messages" at Change Settings/Member Settings, because it's also on card
+  slider](https://github.com/wekan/wekan/issues/2837).
+  Thanks to notohiro and xet7.
+
+and fixes the following bugs:
+
+- [Fix Browser always reload the whole page when I change one of the card
+  color](https://github.com/wekan/wekan/commit/3546d7aa02bc65cf1183cb493adeb543ba51945d).
+  Fixed by making label colors and text again editable.
+  Regression from [Wekan v3.86 2)](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325).
+  Thanks to javen9881 and xet7.
+- [Fix richer editor submit did not clear edit area](https://github.com/wekan/wekan/commit/033d6710470b2ecd7a0ec0b2f0741ff459e68b32).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.86 2020-03-24 Wekan release
+
+This release fixes the following bugs:
+
+- [Fix Rich editor can not be disabled, regression from changes yesterday at Wekan v3.85](https://github.com/wekan/wekan/commit/12ab8fac5db9c5ac8069d0ca2bca340d6004a25b).
+  Thanks to uusijani, vjrj and xet7.
+- [1) Fix Pasting text into a card is adding a line before and after
+      (and multiplies by pasting more) by changing paste "p" to "br".
+   2) Fixes to summernote and markdown comment editors, related
+       to keeping them open when adding comments, having
+       @member mention not close card, and disabling clicking of
+       @member mention](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325).
+  Thanks to xet7 !
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.85 2020-03-23 Wekan release
+
+This release fixes the following CRITICAL SECURITY VULNERABILITIES:
+
+- [Fix XSS bug reported today 4 hours ago by Cyb3rjunky](https://github.com/wekan/wekan/commit/482682e50079d70c5113169020d6834013b57c11).
+  Logged in users could run javascript in input fields.
+  This affects Wekan versions v3.12-v3.84.
+  In [Wekan v3.12](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v312-2019-08-09-wekan-release)
+  there was [changes for XSS filter to allow inserting images, videos etc
+  on comment WYSIWYG editor](https://github.com/wekan/wekan/pull/2593)
+  so features related to that are now removed.
+  After this fix, Javascript in input fields is not executed.
+  Thanks to Cyb3rjunky and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.84 2020-03-16 Wekan release
+
+This release adds the following features:
+
+- Add settings for mouse wheel scroll inertia and scroll
+  amount [Part1](https://github.com/wekan/wekan/commit/9d13001b903f9ec50f5fa3a4bdbacae32b27ac65)
+  and [Part2](https://github.com/wekan/wekan/commit/aaecac091209e90c0c2123830728f5e7a835ccb4).
+  For example: sudo snap set wekan scrollinertia='200' , sudo snap set wekan scrollamount='200' .
+  Thanks to danger89 and xet7.
+
+and adds the following updates:
+
+- [Upgrade to Meteor 1.10.1](https://github.com/wekan/wekan/commit/e16c65babc1f021c35a3d46bc61e649ec94d1e82).
+  Thanks to xet7.
+- [Update markdown](https://github.com/wekan/wekan/commit/6e0fa78022ea487176eb0a32ec5a4a441f8e0c3c).
+  Thanks to xet7.
+- [Update minimist](https://github.com/wekan/wekan/commit/ea6baa5c2b956ee28b0a7e63f988e2fc1998201a).
+  Thanks to xet7.
+- [Update acorn](https://github.com/wekan/wekan/commit/369a29707bbec3bf89717c16e8b698fb4666087a).
+  Thanks to xet7.
+- [Update prettier-eslint](https://github.com/wekan/wekan/commit/8183b7bdaa01d2ce53ac7215beafd5efe21373e8).
+  Thanks to xet7.
+- [Update ostrio:cookies](https://github.com/wekan/wekan/commit/14b8610837117616d436e2bac6a9dc653e315662).
+  Thanks to xet7.
+- [Add build time profiling to build script](https://github.com/wekan/wekan/commit/f968109e7390139e50375ee29bc7bc3cf1e1ab41).
+  Thanks to zodern.
+
+and fixes the following bugs:
+
+- [Downgrade stylus to v1.1.0 to speed up building Wekan](https://github.com/wekan/wekan/commit/fca4cdcebf1cc6642aefeb78b911cb5b95ebe473).
+  This is because building newer stylus v2 takes 52 minutes. After this change, building Wekan takes 3 minutes.
+  Thanks to zodern.
+- [Fix: Error when retrieve token from some OIDC due to not necessary scope
+  parameter](https://github.com/wekan/wekan/pull/2955).
+  Thanks to benoitm76.
+- [Fix: img tag did not allow width and height. Removed swipebox from markdown editor
+  img tag and updated marked markdown to newest version](https://github.com/wekan/wekan/commit/2b26bbe78a1a2b8b427963a6c44c3853efdb737e).
+  Thanks to hradec and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.83 2020-03-01 Wekan release
+
+This release tries to revert remaining the following changes:
+
+- [Revert](https://github.com/wekan/wekan/88573ad2cdb8596b795a82ef40a0662180e8a7d7) change made at Wekan v3.81,
+  because building did not work: [Try to make Meteor build time shorter
+  by excluding legacy and cordova. This was made possible by
+  Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.82 2020-03-01 Wekan release
+
+This release reverts the following changes:
+
+- Revert change made at Wekan v3.81, because building did not work: [Try to make Meteor build time shorter
+  by excluding legacy and cordova. This was made possible by
+  Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.81 2020-03-01 Wekan release
+
+This release [fixes](https://github.com/wekan/wekan/commit/aac7c380c8c389b0683b2bd64e2cc856993f0e30) the following CRITICAL SECURITY VULNERABILITIES and other bugs:
+
+- Fix critical and moderate security vulnerabilities reported at 2020-02-26 with
+  responsible disclosure by [Dejan Zelic](https://twitter.com/dejandayoff),
+  Justin Benjamin and others at [Offensive Security](https://twitter.com/offsectraining),
+  that follow standard 90 days before public disclosure.
+  Thanks to xet7.
+- Fix webhook error that prevented some card etc deleting from web UI of board.
+  Thanks to xet7.
+- Add missing Font Awesome icon to Board Settings Menu.
+  Thanks to xet7.
+- Remove autofocus from many form input boxes so that they would not cause warnings.
+  Thanks to xet7.
+
+and does the following upgrades:
+
+- [Upgrade Meteor to 1.10-rc.2](https://github.com/wekan/wekan/commit/26b521e86e6ac40b7ba25bbe8dac7bf4d48d43ce).
+  Thanks to xet7.
+- [Try to make Meteor build time shorter by excluding legacy and cordova. This was made possible by
+  Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Try to fix afterwards loading of cards by adding fallback when requestIdleCallback is not
+  available](https://github.com/wekan/wekan/commit/2b9540ce02de604bf84ea082f2dcb1d01673708c).
+  Thanks to xet7.
+- [Make profile.initials available in publications](https://github.com/wekan/wekan/pull/2948).
+  Thanks to NicoP-S.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.80 2020-02-22 Wekan release
+
+This release adds the following features:
+
+- [Create New User in Admin Panel](https://github.com/wekan/wekan/commit/e0ca960a35cf006880019ba28fc82aa30f289a71).
+  Works, but does not save fullname yet, so currently it's needed to edit add fullname later.
+  Thanks to xet7.
+
+and adds the following updates:
+
+- [Update to Meteor 1.9.1, Node 12.16.1 etc newest dependencies](https://github.com/wekan/wekan/commit/cbbb5deff7d84a91c40becc9caaf70f5b6738b63).
+  Thanks to xet7.
+- [Update to Meteor 1.9.2](https://github.com/wekan/wekan/commit/9be3f3714ae680ff9fc1855c960c9831e84c2b07).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Update Sandstorm release build script](https://github.com/wekan/wekan/commit/a4ff6cc0af8545ca4d3e97fa2cabbe7981c025b2).
+  Thanks to xet7.
+- [Fix docker-compose link](https://github.com/wekan/wekan/pull/2937).
+  Thanks to pbek.
+- [Remove alethes:pages package, that had some indentation error.
+  Package is about pagination, but I did not find any pagination related code in Wekan
+  yet](https://github.com/wekan/wekan/commit/ec012060305bc16fbf8d2ac218f5c847e02c4301).
+  Thanks to xet7 !
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.79 2020-02-13 Wekan release
+
+This release fixes the following bugs:
+
+- [Fix Card Opened Webhook can not be disabled](https://github.com/wekan/wekan/commit/178f376e2138b5522c2e92ddfd2babb113df8d9f).
+  Thanks to mvanvoorden and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
 # v3.78 2020-02-12 Wekan release
 # v3.78 2020-02-12 Wekan release
 
 
 This release adds the following features:
 This release adds the following features:
@@ -25,7 +525,7 @@ and fixes the following bugs:
   work [Part 1](https://github.com/wekan/wekan/commit/9a21b0a1c933e7f778e4e57a8258e150ccea1620)
   work [Part 1](https://github.com/wekan/wekan/commit/9a21b0a1c933e7f778e4e57a8258e150ccea1620)
   and [Part2](https://github.com/wekan/wekan/commit/4467a68b97a3fbf0fbae7f05177d978f2aa80287).
   and [Part2](https://github.com/wekan/wekan/commit/4467a68b97a3fbf0fbae7f05177d978f2aa80287).
   Thanks to 2020product and xet7.
   Thanks to 2020product and xet7.
-    
+
 Thanks to above GitHub users for their contributions and translators for their translations.
 Thanks to above GitHub users for their contributions and translators for their translations.
 
 
 # v3.76 2020-02-07 Wekan release
 # v3.76 2020-02-07 Wekan release

+ 11 - 3
Dockerfile

@@ -4,10 +4,12 @@ LABEL maintainer="wekan"
 # Set the environment variables (defaults where required)
 # Set the environment variables (defaults where required)
 # DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
 # DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
 # ENV BUILD_DEPS="paxctl"
 # ENV BUILD_DEPS="paxctl"
+ARG DEBIAN_FRONTEND=noninteractive
+
 ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
 ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
     DEBUG=false \
     DEBUG=false \
-    NODE_VERSION=v12.15.0 \
-    METEOR_RELEASE=1.9.0 \
+    NODE_VERSION=v12.16.3 \
+    METEOR_RELEASE=1.10.2 \
     USE_EDGE=false \
     USE_EDGE=false \
     METEOR_EDGE=1.5-beta.17 \
     METEOR_EDGE=1.5-beta.17 \
     NPM_VERSION=latest \
     NPM_VERSION=latest \
@@ -26,6 +28,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
     ATTACHMENTS_STORE_PATH="" \
     ATTACHMENTS_STORE_PATH="" \
     MAX_IMAGE_PIXEL="" \
     MAX_IMAGE_PIXEL="" \
     IMAGE_COMPRESS_RATIO="" \
     IMAGE_COMPRESS_RATIO="" \
+    NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE="" \
     BIGEVENTS_PATTERN=NONE \
     BIGEVENTS_PATTERN=NONE \
     NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
     NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
     NOTIFY_DUE_AT_HOUR_OF_DAY="" \
     NOTIFY_DUE_AT_HOUR_OF_DAY="" \
@@ -110,7 +113,10 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
     CORS="" \
     CORS="" \
     CORS_ALLOW_HEADERS="" \
     CORS_ALLOW_HEADERS="" \
     CORS_EXPOSE_HEADERS="" \
     CORS_EXPOSE_HEADERS="" \
-    DEFAULT_AUTHENTICATION_METHOD=""
+    DEFAULT_AUTHENTICATION_METHOD="" \
+    SCROLLINERTIA="0" \
+    SCROLLAMOUNT="auto" \
+    PASSWORD_LOGIN_ENABLED=true
 
 
 # Copy the app to the image
 # Copy the app to the image
 COPY ${SRC_PATH} /home/wekan/app
 COPY ${SRC_PATH} /home/wekan/app
@@ -267,6 +273,8 @@ RUN \
     cd /home/wekan/app_build/bundle/programs/server/ && \
     cd /home/wekan/app_build/bundle/programs/server/ && \
     gosu wekan:wekan npm install && \
     gosu wekan:wekan npm install && \
     #gosu wekan:wekan npm install bcrypt && \
     #gosu wekan:wekan npm install bcrypt && \
+    # Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
+		rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
     mv /home/wekan/app_build/bundle /build && \
     mv /home/wekan/app_build/bundle /build && \
     \
     \
     # Put back the original tar
     # Put back the original tar

+ 77 - 0
Dockerfile.arm64v8

@@ -0,0 +1,77 @@
+FROM amd64/alpine:3.7 AS builder
+
+# Set the environment variables for builder
+ENV QEMU_VERSION=v4.2.0-6 \
+    QEMU_ARCHITECTURE=aarch64 \
+    NODE_ARCHITECTURE=linux-arm64 \
+    NODE_VERSION=v12.16.3 \
+    WEKAN_VERSION=3.96  \
+    WEKAN_ARCHITECTURE=arm64
+
+     # Install dependencies
+RUN  apk update && apk add ca-certificates outils-sha1 && \
+     \
+     # Download qemu static for our architecture
+     wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
+     \
+    # Download wekan and shasum
+    wget https://releases.wekan.team/raspi3/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
+    wget https://releases.wekan.team/raspi3/SHA256SUMS.txt && \
+    # Verify wekan
+    grep wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip SHA256SUMS.txt | sha256sum -c - && \
+    \
+    # Unzip wekan
+    unzip wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
+    \
+    # Download node and shasums
+    wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
+    wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
+    \
+    # Verify nodejs authenticity
+    grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | sha256sum -c - && \
+    \
+    # Extract node and remove tar.gz
+    tar xvzf node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz
+
+# Build wekan dockerfile
+FROM arm64v8/ubuntu:19.10
+LABEL maintainer="wekan"
+
+# Set the environment variables (defaults where required)
+ENV QEMU_ARCHITECTURE=aarch64 \
+    NODE_ARCHITECTURE=linux-arm64 \
+    NODE_VERSION=v12.16.1 \
+    NODE_ENV=production \
+    NPM_VERSION=latest \
+    WITH_API=true \
+    PORT=8080 \
+    ROOT_URL=http://localhost \
+    MONGO_URL=mongodb://127.0.0.1:27017/wekan
+
+# Copy qemu-static to image
+COPY --from=builder qemu-${QEMU_ARCHITECTURE}-static /usr/bin
+
+# Copy the app to the image
+COPY --from=builder bundle /home/wekan/bundle
+
+# Copy
+COPY --from=builder node-${NODE_VERSION}-${NODE_ARCHITECTURE} /opt/nodejs
+
+RUN \
+    set -o xtrace && \
+    # Add non-root user wekan
+    useradd --user-group --system --home-dir /home/wekan wekan && \
+    \
+    # Install Node
+    ln -s /opt/nodejs/bin/node /usr/bin/node && \
+    ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
+    mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
+    chown wekan --recursive /home/wekan/.config && \
+    \
+    # Install Node dependencies
+    npm install -g npm@${NPM_VERSION}
+
+EXPOSE $PORT
+USER wekan
+
+CMD ["node", "/home/wekan/bundle/main.js"]

+ 4 - 2
README.md

@@ -1,3 +1,5 @@
+[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/wekan/wekan) 
+
 # Wekan - Open Source kanban
 # Wekan - Open Source kanban
 
 
 [![Contributors](https://img.shields.io/github/contributors/wekan/wekan.svg "Contributors")](https://github.com/wekan/wekan/graphs/contributors)
 [![Contributors](https://img.shields.io/github/contributors/wekan/wekan.svg "Contributors")](https://github.com/wekan/wekan/graphs/contributors)
@@ -32,7 +34,7 @@ and PWA app that can be added as icon on Android and bookmark on iOS, used like
 
 
 **NOTE**: 
 **NOTE**: 
 - Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
 - Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
-- Please don't feed the trolls and spammers that are mentioned in the FAQ :)
+- Please don't feed the [trolls](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
 
 
 ## About Wekan
 ## About Wekan
 
 
@@ -61,7 +63,7 @@ that by providing one-click installation on various platforms.
   [Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
   [Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
   [More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
   [More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
 - 1 GB RAM minimum free for Wekan. Production server should have minimum total 4 GB RAM.
 - 1 GB RAM minimum free for Wekan. Production server should have minimum total 4 GB RAM.
-  For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/devel/docker-compose.yml): 3 frontend servers,
+  For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/master/docker-compose.yml): 3 frontend servers,
   each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.  
   each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.  
 - Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
 - Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
 - SECURITY: Updating to newest Wekan version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
 - SECURITY: Updating to newest Wekan version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.

+ 1 - 1
Stackerfile.yml

@@ -1,5 +1,5 @@
 appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
 appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
-appVersion: "v3.78.0"
+appVersion: "v4.01.0"
 files:
 files:
   userUploads:
   userUploads:
     - README.md
     - README.md

+ 6 - 0
client/00-startup.js

@@ -0,0 +1,6 @@
+// PWA
+if ('serviceWorker' in navigator) {
+  window.addEventListener('load', function() {
+    navigator.serviceWorker.register('/pwa-service-worker.js');
+  });
+}

+ 157 - 190
client/components/activities/activities.jade

@@ -8,234 +8,201 @@ template(name="activities")
       +cardActivities
       +cardActivities
 
 
 template(name="boardActivities")
 template(name="boardActivities")
-  each currentBoard.activities
-    .activity
-      +userAvatar(userId=user._id)
-      p.activity-desc
-        +memberName(user=user)
+  each activityData in currentBoard.activities
+    +activity(activity=activityData card=card mode=mode)
 
 
-        if($eq activityType 'deleteAttachment')
-          | {{{_ 'activity-delete-attach' cardLink}}}.
+template(name="cardActivities")
+  each activityData in currentCard.activities
+    +activity(activity=activityData card=card mode=mode)
+
+template(name="activity")
+  .activity
+    +userAvatar(userId=activity.user._id)
+    p.activity-desc
+      +memberName(user=activity.user)
+
+      //- attachment activity -------------------------------------------------
+      if($eq activity.activityType 'deleteAttachment')
+        | {{{_ 'activity-delete-attach' cardLink}}}.
+
+      if($eq activity.activityType 'addAttachment')
+        | {{{_ 'activity-attached' attachmentLink cardLink}}}.
+        if($neq mode 'board')
+          if activity.attachment.isImage
+            img.attachment-image-preview(src=activity.attachment.url)
+
+      //- board activity ------------------------------------------------------
+      if($eq mode 'board')
+        if($eq activity.activityType 'createBoard')
+          | {{_ 'activity-created' boardLabel}}.
 
 
-        if($eq activityType 'addAttachment')
-          | {{{_ 'activity-attached' attachmentLink cardLink}}}.
+        if($eq activity.activityType 'importBoard')
+          | {{{_ 'activity-imported-board' boardLabel sourceLink}}}.
 
 
-        if($eq activityType 'addBoardMember')
+        if($eq activity.activityType 'addBoardMember')
           | {{{_ 'activity-added' memberLink boardLabel}}}.
           | {{{_ 'activity-added' memberLink boardLabel}}}.
 
 
-        if($eq activityType 'addComment')
-          | {{{_ 'activity-on' cardLink}}}
-          a.activity-comment(href="{{ card.absoluteUrl }}")
-            +viewer
-              = comment.text
-
-        if($eq activityType 'addChecklist')
-          | {{{_ 'activity-checklist-added' cardLink}}}.
-          .activity-checklist(href="{{ card.absoluteUrl }}")
-            +viewer
-              = checklist.title
-        if($eq activityType 'removeChecklist')
-          | {{{_ 'activity-checklist-removed' cardLink}}}.
-
-        if($eq activityType 'checkedItem')
-          | {{{_ 'activity-checked-item' checkItem checklist.title cardLink}}}.
-
-        if($eq activityType 'uncheckedItem')
-          | {{{_ 'activity-unchecked-item' checkItem checklist.title cardLink}}}.
-
-        if($eq activityType 'checklistCompleted')
-          | {{{_ 'activity-checklist-completed' checklist.title cardLink}}}.
+        if($eq activity.activityType 'removeBoardMember')
+          | {{{_ 'activity-excluded' memberLink boardLabel}}}.
 
 
-        if($eq activityType 'checklistUncompleted')
-          | {{{_ 'activity-checklist-uncompleted' checklist.title cardLink}}}.
+      //- card activity -------------------------------------------------------
+      if($eq activity.activityType 'createCard')
+        if($eq mode 'card')
+          | {{{_ 'activity-added' cardLabel activity.listName}}}.
+        else
+          | {{{_ 'activity-added' cardLabel boardLabel}}}.
 
 
-        if($eq activityType 'addChecklistItem')
-          | {{{_ 'activity-checklist-item-added' checklist.title cardLink}}}.
-          .activity-checklist(href="{{ card.absoluteUrl }}")
-            +viewer
-              = checklistItem.title
-        if($eq activityType 'removedChecklistItem')
-          | {{{_ 'activity-checklist-item-removed' checklist.title cardLink}}}.
+      if($eq activity.activityType 'importCard')
+        | {{{_ 'activity-imported' cardLink boardLabel sourceLink}}}.
 
 
-        if($eq activityType 'archivedCard')
-          | {{{_ 'activity-archived' cardLink}}}.
+      if($eq activity.activityType 'moveCard')
+        | {{{_ 'activity-moved' cardLabel activity.oldList.title activity.list.title}}}.
 
 
-        if($eq activityType 'archivedList')
-          | {{_ 'activity-archived' list.title}}.
+      if($eq activity.activityType 'moveCardBoard')
+        | {{{_ 'activity-moved' cardLink activity.oldBoardName activity.boardName}}}.
 
 
-        if($eq activityType 'archivedSwimlane')
-          | {{_ 'activity-archived' swimlane.title}}.
+      if($eq activity.activityType 'archivedCard')
+        | {{{_ 'activity-archived' cardLink}}}.
 
 
-        if($eq activityType 'createBoard')
-          | {{_ 'activity-created' boardLabel}}.
+      if($eq activity.activityType 'restoredCard')
+        | {{{_ 'activity-sent' cardLink boardLabel}}}.
 
 
-        if($eq activityType 'createCard')
-          | {{{_ 'activity-added' cardLink boardLabel}}}.
+      //- checklist activity --------------------------------------------------
+      if($eq activity.activityType 'addChecklist')
+        | {{{_ 'activity-checklist-added' cardLink}}}.
+        if($eq mode 'card')
+          .activity-checklist
+            +viewer
+              = activity.checklist.title
+        else
+          a.activity-checklist(href="{{ activity.card.absoluteUrl }}")
+            +viewer
+              = activity.checklist.title
 
 
-        if($eq activityType 'createCustomField')
-          | {{_ 'activity-customfield-created' customField}}.
+      if($eq activity.activityType 'removedChecklist')
+        | {{{_ 'activity-checklist-removed' cardLink}}}.
 
 
-        if($eq activityType 'createList')
-          | {{_ 'activity-added' list.title boardLabel}}.
+      if($eq activity.activityType 'completeChecklist')
+        | {{{_ 'activity-checklist-completed' activity.checklist.title cardLink}}}.
 
 
-        if($eq activityType 'createSwimlane')
-          | {{_ 'activity-added' swimlane.title boardLabel}}.
+      if($eq activity.activityType 'uncompleteChecklist')
+        | {{{_ 'activity-checklist-uncompleted' activity.checklist.title cardLink}}}.
 
 
-        if($eq activityType 'removeList')
-          | {{_ 'activity-removed' title boardLabel}}.
+      if($eq activity.activityType 'checkedItem')
+        | {{{_ 'activity-checked-item' checkItem activity.checklist.title cardLink}}}.
 
 
-        if($eq activityType 'importBoard')
-          | {{{_ 'activity-imported-board' boardLabel sourceLink}}}.
+      if($eq activity.activityType 'uncheckedItem')
+        | {{{_ 'activity-unchecked-item' checkItem activity.checklist.title cardLink}}}.
 
 
-        if($eq activityType 'importCard')
-          | {{{_ 'activity-imported' cardLink boardLabel sourceLink}}}.
+      if($eq activity.activityType 'addChecklistItem')
+        | {{{_ 'activity-checklist-item-added' activity.checklist.title cardLink}}}.
+        .activity-checklist(href="{{ activity.card.absoluteUrl }}")
+          +viewer
+            = activity.checklistItem.title
 
 
-        if($eq activityType 'importList')
-          | {{{_ 'activity-imported' listLabel boardLabel sourceLink}}}.
+      if($eq activity.activityType 'removedChecklistItem')
+        | {{{_ 'activity-checklist-item-removed' activity.checklist.title cardLink}}}.
 
 
-        if($eq activityType 'joinMember')
-          if($eq user._id member._id)
-            | {{{_ 'activity-joined' cardLink}}}.
+      //- comment activity ----------------------------------------------------
+      if($eq mode 'card')
+        //- if we are in card mode we display the comment in a way that it
+        //- can be edited by the owner
+        if($eq activity.activityType 'addComment')
+          +inlinedForm(classNames='js-edit-comment')
+            +editor(autofocus=true)
+              = activity.comment.text
+            .edit-controls
+              button.primary(type="submit") {{_ 'edit'}}
           else
           else
-            | {{{_ 'activity-added' memberLink cardLink}}}.
-
-        if($eq activityType 'moveCardBoard')
-          | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}.
-
-        if($eq activityType 'moveCard')
-          | {{{_ 'activity-moved' cardLink oldList.title list.title}}}.
-
-        if($eq activityType 'removeBoardMember')
-          | {{{_ 'activity-excluded' memberLink boardLabel}}}.
+            .activity-comment
+              +viewer
+                = activity.comment.text
+            span(title=activity.createdAt).activity-meta {{ moment activity.createdAt }}
+              if ($eq currentUser._id activity.comment.userId)
+                = ' - '
+                a.js-open-inlined-form {{_ "edit"}}
+                = ' - '
+                a.js-delete-comment {{_ "delete"}}
 
 
-        if($eq activityType 'restoredCard')
-          | {{{_ 'activity-sent' cardLink boardLabel}}}.
+        if($eq activity.activityType 'deleteComment')
+          | {{{_ 'activity-deleteComment' currentData.commentId}}}.
 
 
-        if($eq activityType 'addedLabel')
-          | {{{_ 'activity-added-label' lastLabel cardLink}}}.
+        if($eq activity.activityType 'editComment')
+          | {{{_ 'activity-editComment' currentData.commentId}}}.
+      else
+        //- if we are not in card mode we only display a summary of the comment
+        if($eq activity.activityType 'addComment')
+          | {{{_ 'activity-on' cardLink}}}
+          a.activity-comment(href="{{ activity.card.absoluteUrl }}")
+            +viewer
+              = activity.comment.text
 
 
-        if($eq activityType 'removedLabel')
-          | {{{_ 'activity-removed-label' lastLabel cardLink}}}.
+      //- customField activity ------------------------------------------------
+      if($eq mode 'board')
+        if($eq activity.activityType 'createCustomField')
+          | {{_ 'activity-customfield-created' customField}}.
 
 
-        if($eq activityType 'setCustomField')
+        if($eq activity.activityType 'setCustomField')
           | {{{_ 'activity-set-customfield' lastCustomField lastCustomFieldValue cardLink}}}.
           | {{{_ 'activity-set-customfield' lastCustomField lastCustomFieldValue cardLink}}}.
 
 
-        if($eq activityType 'unsetCustomField')
+        if($eq activity.activityType 'unsetCustomField')
           | {{{_ 'activity-unset-customfield' lastCustomField cardLink}}}.
           | {{{_ 'activity-unset-customfield' lastCustomField cardLink}}}.
 
 
-        if($eq activityType 'unjoinMember')
-          if($eq user._id member._id)
-            | {{{_ 'activity-unjoined' cardLink}}}.
-          else
-            | {{{_ 'activity-removed' memberLink cardLink}}}.
+      //- label activity ------------------------------------------------------
+      if($eq activity.activityType 'addedLabel')
+        | {{{_ 'activity-added-label' lastLabel cardLink}}}.
 
 
-        span(title=createdAt).activity-meta {{ moment createdAt }}
+      if($eq activity.activityType 'removedLabel')
+        | {{{_ 'activity-removed-label' lastLabel cardLink}}}.
 
 
-template(name="cardActivities")
-  each currentCard.activities
-    .activity
-      +userAvatar(userId=user._id)
-      p.activity-desc
-        +memberName(user=user)
-        if($eq activityType 'createCard')
-          | {{_ 'activity-added' cardLabel listName}}.
-        if($eq activityType 'importCard')
-          | {{{_ 'activity-imported' cardLabel list.title sourceLink}}}.
-        if($eq activityType 'joinMember')
-          if($eq user._id member._id)
-            | {{_ 'activity-joined' cardLabel}}.
-          else
-            | {{{_ 'activity-added' memberLink cardLabel}}}.
-        if($eq activityType 'unjoinMember')
-          if($eq user._id member._id)
-            | {{_ 'activity-unjoined' cardLabel}}.
-          else
-            | {{{_ 'activity-removed' cardLabel memberLink}}}.
-        if($eq activityType 'archivedCard')
-          | {{_ 'activity-archived' cardLabel}}.
-
-        if($eq activityType 'addedLabel')
-          | {{{_ 'activity-added-label-card' lastLabel }}}.
+      //- list activity -------------------------------------------------------
+      if($neq mode 'card')
+        if($eq activity.activityType 'createList')
+          | {{{_ 'activity-added' listLabel boardLabel}}}.
 
 
-        if($eq activityType 'removedLabel')
-          | {{{_ 'activity-removed-label-card' lastLabel }}}.
+        if($eq activity.activityType 'importList')
+          | {{{_ 'activity-imported' listLabel boardLabel sourceLink}}}.
 
 
-        if($eq activityType 'removeChecklist')
-          | {{{_ 'activity-checklist-removed' cardLabel}}}.
+        if($eq activity.activityType 'removeList')
+          | {{{_ 'activity-removed' activity.title boardLabel}}}.
 
 
-        if($eq activityType 'checkedItem')
-          | {{{_ 'activity-checked-item-card' checkItem checklist.title }}}.
+        if($eq activity.activityType 'archivedList')
+          | {{_ 'activity-archived' listLabel}}.
 
 
-        if($eq activityType 'uncheckedItem')
-          | {{{_ 'activity-unchecked-item-card' checkItem checklist.title }}}.
+      //- member activity ----------------------------------------------------
+      if($eq activity.activityType 'joinMember')
+        if($eq user._id activity.member._id)
+          | {{{_ 'activity-joined' cardLink}}}.
+        else
+          | {{{_ 'activity-added' memberLink cardLink}}}.
 
 
-        if($eq activityType 'checklistCompleted')
-          | {{{_ 'activity-checklist-completed-card' checklist.title }}}.
+      if($eq activity.activityType 'unjoinMember')
+        if($eq user._id activity.member._id)
+          | {{{_ 'activity-unjoined' cardLink}}}.
+        else
+          | {{{_ 'activity-removed' memberLink cardLink}}}.
 
 
-        if($eq activityType 'checklistUncompleted')
-          | {{{_ 'activity-checklist-uncompleted-card' checklist.title }}}.
+      //- swimlane activity --------------------------------------------------
+      if($neq mode 'card')
+        if($eq activity.activityType 'createSwimlane')
+          | {{{_ 'activity-added' activity.swimlane.title boardLabel}}}.
 
 
-        if($eq activityType 'restoredCard')
-          | {{_ 'activity-sent' cardLabel boardLabel}}.
-        if($eq activityType 'moveCard')
-          | {{_ 'activity-moved' cardLabel oldList.title list.title}}.
+        if($eq activity.activityType 'archivedSwimlane')
+          | {{_ 'activity-archived' activity.swimlane.title}}.
 
 
-        if($eq activityType 'moveCardBoard')
-          | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}.
 
 
-        if($eq activityType 'addAttachment')
-          | {{{_ 'activity-attached' attachmentLink cardLabel}}}.
-          if attachment.isImage
-            img.attachment-image-preview(src=attachment.url)
-        if($eq activityType 'deleteAttachment')
-          | {{{_ 'activity-delete-attach'  cardLabel}}}.
-        if($eq activityType 'removedChecklist')
-          | {{{_ 'activity-checklist-removed' cardLabel}}}.
-        if($eq activityType 'addChecklist')
-          | {{{_ 'activity-checklist-added' cardLabel}}}.
-          .activity-checklist
-            +viewer
-              = checklist.title
-        if($eq activityType 'addChecklistItem')
-          | {{{_ 'activity-checklist-item-added' checklist.title cardLink}}}.
-          .activity-checklist(href="{{ card.absoluteUrl }}")
-            +viewer
-              = checklistItem.title
-
-        if(currentData.timeKey)
-          | {{{_ activityType }}}
+      //- I don't understand this part ----------------------------------------
+      if(currentData.timeKey)
+        | {{{_ activity.activityType }}}
+        = ' '
+        i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }}
+        if (currentData.timeOldValue)
           = ' '
           = ' '
-          i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }}
-          if (currentData.timeOldValue)
-              = ' '
-              | {{{_ "previous_as" }}}
-              = ' '
-              i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }}
-          = ' @'
-        else if(currentData.timeValue)
-          | {{{_ activityType currentData.timeValue}}}
-
-
-        if($eq activityType 'deleteComment')
-          | {{{_ 'activity-deleteComment' currentData.commentId}}}.
-        if($eq activityType 'editComment')
-          | {{{_ 'activity-editComment' currentData.commentId}}}.
-        if($eq activityType 'addComment')
-          +inlinedForm(classNames='js-edit-comment')
-            +editor(autofocus=true)
-              = comment.text
-            .edit-controls
-              button.primary(type="submit") {{_ 'edit'}}
-          else
-            .activity-comment
-              +viewer
-                = comment.text
-            span(title=createdAt).activity-meta {{ moment createdAt }}
-              if ($eq currentUser._id comment.userId)
-                = ' - '
-                a.js-open-inlined-form {{_ "edit"}}
-                = ' - '
-                a.js-delete-comment {{_ "delete"}}
-
-        else
-          span(title=createdAt).activity-meta {{ moment createdAt }}
+            | {{{_ "previous_as" }}}
+            = ' '
+            i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }}
+        = ' @'
+      else if(currentData.timeValue)
+        | {{{_ activity.activityType currentData.timeValue}}}
+
+      span(title=activity.createdAt).activity-meta {{ moment activity.createdAt }}

+ 74 - 41
client/components/activities/activities.js

@@ -41,7 +41,9 @@ BlazeComponent.extendComponent({
       });
       });
     });
     });
   },
   },
+}).register('activities');
 
 
+BlazeComponent.extendComponent({
   loadNextPage() {
   loadNextPage() {
     if (this.loadNextPageLocked === false) {
     if (this.loadNextPageLocked === false) {
       this.page.set(this.page.get() + 1);
       this.page.set(this.page.get() + 1);
@@ -50,41 +52,37 @@ BlazeComponent.extendComponent({
   },
   },
 
 
   checkItem() {
   checkItem() {
-    const checkItemId = this.currentData().checklistItemId;
+    const checkItemId = this.currentData().activity.checklistItemId;
     const checkItem = ChecklistItems.findOne({ _id: checkItemId });
     const checkItem = ChecklistItems.findOne({ _id: checkItemId });
-    return checkItem.title;
+    return checkItem && checkItem.title;
   },
   },
 
 
   boardLabel() {
   boardLabel() {
+    const data = this.currentData();
+    if (data.mode !== 'board') {
+      return createBoardLink(data.activity.board(), data.activity.listName);
+    }
     return TAPi18n.__('this-board');
     return TAPi18n.__('this-board');
   },
   },
 
 
   cardLabel() {
   cardLabel() {
+    const data = this.currentData();
+    if (data.mode !== 'card') {
+      return createCardLink(this.currentData().activity.card());
+    }
     return TAPi18n.__('this-card');
     return TAPi18n.__('this-card');
   },
   },
 
 
   cardLink() {
   cardLink() {
-    const card = this.currentData().card();
-    return (
-      card &&
-      Blaze.toHTML(
-        HTML.A(
-          {
-            href: card.absoluteUrl(),
-            class: 'action-card',
-          },
-          card.title,
-        ),
-      )
-    );
+    return createCardLink(this.currentData().activity.card());
   },
   },
 
 
   lastLabel() {
   lastLabel() {
-    const lastLabelId = this.currentData().labelId;
+    const lastLabelId = this.currentData().activity.labelId;
     if (!lastLabelId) return null;
     if (!lastLabelId) return null;
-    const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
-      lastLabelId,
-    );
+    const lastLabel = Boards.findOne(
+      this.currentData().activity.boardId,
+    ).getLabelById(lastLabelId);
     if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) {
     if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) {
       return lastLabel.color;
       return lastLabel.color;
     } else {
     } else {
@@ -94,7 +92,7 @@ BlazeComponent.extendComponent({
 
 
   lastCustomField() {
   lastCustomField() {
     const lastCustomField = CustomFields.findOne(
     const lastCustomField = CustomFields.findOne(
-      this.currentData().customFieldId,
+      this.currentData().activity.customFieldId,
     );
     );
     if (!lastCustomField) return null;
     if (!lastCustomField) return null;
     return lastCustomField.name;
     return lastCustomField.name;
@@ -102,10 +100,10 @@ BlazeComponent.extendComponent({
 
 
   lastCustomFieldValue() {
   lastCustomFieldValue() {
     const lastCustomField = CustomFields.findOne(
     const lastCustomField = CustomFields.findOne(
-      this.currentData().customFieldId,
+      this.currentData().activity.customFieldId,
     );
     );
     if (!lastCustomField) return null;
     if (!lastCustomField) return null;
-    const value = this.currentData().value;
+    const value = this.currentData().activity.value;
     if (
     if (
       lastCustomField.settings.dropdownItems &&
       lastCustomField.settings.dropdownItems &&
       lastCustomField.settings.dropdownItems.length > 0
       lastCustomField.settings.dropdownItems.length > 0
@@ -122,11 +120,13 @@ BlazeComponent.extendComponent({
   },
   },
 
 
   listLabel() {
   listLabel() {
-    return this.currentData().list().title;
+    const activity = this.currentData().activity;
+    const list = activity.list();
+    return (list && list.title) || activity.title;
   },
   },
 
 
   sourceLink() {
   sourceLink() {
-    const source = this.currentData().source;
+    const source = this.currentData().activity.source;
     if (source) {
     if (source) {
       if (source.url) {
       if (source.url) {
         return Blaze.toHTML(
         return Blaze.toHTML(
@@ -146,31 +146,32 @@ BlazeComponent.extendComponent({
 
 
   memberLink() {
   memberLink() {
     return Blaze.toHTMLWithData(Template.memberName, {
     return Blaze.toHTMLWithData(Template.memberName, {
-      user: this.currentData().member(),
+      user: this.currentData().activity.member(),
     });
     });
   },
   },
 
 
   attachmentLink() {
   attachmentLink() {
-    const attachment = this.currentData().attachment();
+    const attachment = this.currentData().activity.attachment();
     const link = attachment.link('original', '/');
     const link = attachment.link('original', '/');
     // trying to display url before file is stored generates js errors
     // trying to display url before file is stored generates js errors
     return (
     return (
-      attachment &&
-      link &&
-      Blaze.toHTML(
-        HTML.A(
-          {
-            href: link,
-            target: '_blank',
-          },
-          attachment.get('name'),
-        ),
-      )
+      (attachment &&
+        link &&
+        Blaze.toHTML(
+          HTML.A(
+            {
+              href: link,
+              target: '_blank',
+            },
+            attachment.name(),
+          ),
+        )) ||
+      this.currentData().activity.attachmentName
     );
     );
   },
   },
 
 
   customField() {
   customField() {
-    const customField = this.currentData().customField();
+    const customField = this.currentData().activity.customField();
     if (!customField) return null;
     if (!customField) return null;
     return customField.name;
     return customField.name;
   },
   },
@@ -180,7 +181,7 @@ BlazeComponent.extendComponent({
       {
       {
         // XXX We should use Popup.afterConfirmation here
         // XXX We should use Popup.afterConfirmation here
         'click .js-delete-comment'() {
         'click .js-delete-comment'() {
-          const commentId = this.currentData().commentId;
+          const commentId = this.currentData().activity.commentId;
           CardComments.remove(commentId);
           CardComments.remove(commentId);
         },
         },
         'submit .js-edit-comment'(evt) {
         'submit .js-edit-comment'(evt) {
@@ -188,7 +189,7 @@ BlazeComponent.extendComponent({
           const commentText = this.currentComponent()
           const commentText = this.currentComponent()
             .getValue()
             .getValue()
             .trim();
             .trim();
-          const commentId = Template.parentData().commentId;
+          const commentId = Template.parentData().activity.commentId;
           if (commentText) {
           if (commentText) {
             CardComments.update(commentId, {
             CardComments.update(commentId, {
               $set: {
               $set: {
@@ -200,4 +201,36 @@ BlazeComponent.extendComponent({
       },
       },
     ];
     ];
   },
   },
-}).register('activities');
+}).register('activity');
+
+function createCardLink(card) {
+  return (
+    card &&
+    Blaze.toHTML(
+      HTML.A(
+        {
+          href: card.absoluteUrl(),
+          class: 'action-card',
+        },
+        card.title,
+      ),
+    )
+  );
+}
+
+function createBoardLink(board, list) {
+  let text = board.title;
+  if (list) text += `: ${list}`;
+  return (
+    board &&
+    Blaze.toHTML(
+      HTML.A(
+        {
+          href: board.absoluteUrl(),
+          class: 'action-board',
+        },
+        text,
+      ),
+    )
+  );
+}

+ 1 - 1
client/components/boards/boardArchive.js

@@ -7,7 +7,7 @@ BlazeComponent.extendComponent({
     return Boards.find(
     return Boards.find(
       { archived: true },
       { archived: true },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
   },
   },

+ 3 - 7
client/components/boards/boardBody.js

@@ -1,7 +1,7 @@
 import { Cookies } from 'meteor/ostrio:cookies';
 import { Cookies } from 'meteor/ostrio:cookies';
 const cookies = new Cookies();
 const cookies = new Cookies();
 const subManager = new SubsManager();
 const subManager = new SubsManager();
-const { calculateIndex, enableClickOnTouch } = Utils;
+const { calculateIndex } = Utils;
 const swimlaneWhileSortingHeight = 150;
 const swimlaneWhileSortingHeight = 150;
 
 
 BlazeComponent.extendComponent({
 BlazeComponent.extendComponent({
@@ -191,9 +191,6 @@ BlazeComponent.extendComponent({
       },
       },
     });
     });
 
 
-    // ugly touch event hotfix
-    enableClickOnTouch('.js-swimlane:not(.placeholder)');
-
     this.autorun(() => {
     this.autorun(() => {
       let showDesktopDragHandles = false;
       let showDesktopDragHandles = false;
       currentUser = Meteor.user();
       currentUser = Meteor.user();
@@ -205,7 +202,7 @@ BlazeComponent.extendComponent({
       } else {
       } else {
         showDesktopDragHandles = false;
         showDesktopDragHandles = false;
       }
       }
-      if (!Utils.isMiniScreen() && showDesktopDragHandles) {
+      if (Utils.isMiniScreen() || showDesktopDragHandles) {
         $swimlanesDom.sortable({
         $swimlanesDom.sortable({
           handle: '.js-swimlane-header-handle',
           handle: '.js-swimlane-header-handle',
         });
         });
@@ -215,9 +212,8 @@ BlazeComponent.extendComponent({
         });
         });
       }
       }
 
 
-      // Disable drag-dropping if the current user is not a board member or is miniscreen
+      // Disable drag-dropping if the current user is not a board member
       $swimlanesDom.sortable('option', 'disabled', !userIsMember());
       $swimlanesDom.sortable('option', 'disabled', !userIsMember());
-      $swimlanesDom.sortable('option', 'disabled', Utils.isMiniScreen());
     });
     });
 
 
     function userIsMember() {
     function userIsMember() {

+ 0 - 14
client/components/boards/boardHeader.jade

@@ -193,20 +193,6 @@ template(name="boardChangeViewPopup")
           | {{_ 'board-view-cal'}}
           | {{_ 'board-view-cal'}}
           if $eq Utils.boardView "board-view-cal"
           if $eq Utils.boardView "board-view-cal"
             i.fa.fa-check
             i.fa.fa-check
-    if currentUser.isAdmin
-      hr
-      li
-        with "board-view-rules"
-          a.js-open-rules-view(title="{{_ 'rules'}}")
-            i.fa.fa-magic
-            | {{_ 'rules'}}
-    else if currentUser.isBoardAdmin
-      hr
-      li
-        with "board-view-rules"
-          a.js-open-rules-view(title="{{_ 'rules'}}")
-            i.fa.fa-magic
-            | {{_ 'rules'}}
 
 
 template(name="createBoard")
 template(name="createBoard")
   form
   form

+ 0 - 20
client/components/boards/boardHeader.js

@@ -33,22 +33,6 @@ Template.boardMenuPopup.events({
   'click .js-card-settings': Popup.open('boardCardSettings'),
   'click .js-card-settings': Popup.open('boardCardSettings'),
 });
 });
 
 
-Template.boardMenuPopup.helpers({
-  exportUrl() {
-    const params = {
-      boardId: Session.get('currentBoard'),
-    };
-    const queryParams = {
-      authToken: Accounts._storedLoginToken(),
-    };
-    return FlowRouter.path('/api/boards/:boardId/export', params, queryParams);
-  },
-  exportFilename() {
-    const boardId = Session.get('currentBoard');
-    return `wekan-export-board-${boardId}.json`;
-  },
-});
-
 Template.boardChangeTitlePopup.events({
 Template.boardChangeTitlePopup.events({
   submit(event, templateInstance) {
   submit(event, templateInstance) {
     const newTitle = templateInstance
     const newTitle = templateInstance
@@ -191,10 +175,6 @@ Template.boardChangeViewPopup.events({
     Utils.setBoardView('board-view-cal');
     Utils.setBoardView('board-view-cal');
     Popup.close();
     Popup.close();
   },
   },
-  'click .js-open-rules-view'() {
-    Modal.openWide('rulesMain');
-    Popup.close();
-  },
 });
 });
 
 
 const CreateBoard = BlazeComponent.extendComponent({
 const CreateBoard = BlazeComponent.extendComponent({

+ 4 - 4
client/components/boards/boardsList.jade

@@ -1,10 +1,10 @@
 template(name="boardList")
 template(name="boardList")
   .wrapper
   .wrapper
-    ul.board-list.clearfix
+    ul.board-list.clearfix.js-boards
       li.js-add-board
       li.js-add-board
         a.board-list-item.label {{_ 'add-board'}}
         a.board-list-item.label {{_ 'add-board'}}
       each boards
       each boards
-        li(class="{{#if isStarred}}starred{{/if}}" class=colorClass)
+        li(class="{{#if isStarred}}starred{{/if}}" class=colorClass).js-board
           if isInvited
           if isInvited
             .board-list-item
             .board-list-item
               span.details
               span.details
@@ -39,7 +39,7 @@ template(name="boardList")
                     i.fa.js-archive-board(
                     i.fa.js-archive-board(
                         class="fa-archive"
                         class="fa-archive"
                         title="{{_ 'archive-board'}}")
                         title="{{_ 'archive-board'}}")
-                  else if currentUser.isBoardAdmin
+                  else if isAdministrable
                     i.fa.js-clone-board(
                     i.fa.js-clone-board(
                         class="fa-clone"
                         class="fa-clone"
                         title="{{_ 'duplicate-board'}}")
                         title="{{_ 'duplicate-board'}}")
@@ -55,7 +55,7 @@ template(name="boardList")
                         title="{{_ 'archive-board'}}")
                         title="{{_ 'archive-board'}}")
 
 
 template(name="boardListHeaderBar")
 template(name="boardListHeaderBar")
-  h1 {{_ 'my-boards'}}
+  h1 {{_ title }}
   .board-header-btns.right
   .board-header-btns.right
     a.board-header-btn.js-open-archived-board
     a.board-header-btn.js-open-archived-board
       i.fa.fa-archive
       i.fa.fa-archive

+ 72 - 8
client/components/boards/boardsList.js

@@ -1,4 +1,5 @@
 const subManager = new SubsManager();
 const subManager = new SubsManager();
+const { calculateIndex, enableClickOnTouch } = Utils;
 
 
 Template.boardListHeaderBar.events({
 Template.boardListHeaderBar.events({
   'click .js-open-archived-board'() {
   'click .js-open-archived-board'() {
@@ -7,6 +8,9 @@ Template.boardListHeaderBar.events({
 });
 });
 
 
 Template.boardListHeaderBar.helpers({
 Template.boardListHeaderBar.helpers({
+  title() {
+    return FlowRouter.getRouteName() === 'home' ? 'my-boards' : 'public';
+  },
   templatesBoardId() {
   templatesBoardId() {
     return Meteor.user() && Meteor.user().getTemplatesBoardId();
     return Meteor.user() && Meteor.user().getTemplatesBoardId();
   },
   },
@@ -20,20 +24,80 @@ BlazeComponent.extendComponent({
     Meteor.subscribe('setting');
     Meteor.subscribe('setting');
   },
   },
 
 
-  boards() {
-    return Boards.find(
-      {
-        archived: false,
-        'members.userId': Meteor.userId(),
-        type: 'board',
+  onRendered() {
+    const self = this;
+    function userIsAllowedToMove() {
+      return Meteor.user();
+    }
+
+    const itemsSelector = '.js-board:not(.placeholder)';
+
+    const $boards = this.$('.js-boards');
+    $boards.sortable({
+      connectWith: '.js-boards',
+      tolerance: 'pointer',
+      appendTo: '.board-list',
+      helper: 'clone',
+      distance: 7,
+      items: itemsSelector,
+      placeholder: 'board-wrapper placeholder',
+      start(evt, ui) {
+        ui.helper.css('z-index', 1000);
+        ui.placeholder.height(ui.helper.height());
+        EscapeActions.executeUpTo('popup-close');
+      },
+      stop(evt, ui) {
+        // To attribute the new index number, we need to get the DOM element
+        // of the previous and the following card -- if any.
+        const prevBoardDom = ui.item.prev('.js-board').get(0);
+        const nextBoardBom = ui.item.next('.js-board').get(0);
+        const sortIndex = calculateIndex(prevBoardDom, nextBoardBom, 1);
+
+        const boardDomElement = ui.item.get(0);
+        const board = Blaze.getData(boardDomElement);
+        // Normally the jquery-ui sortable library moves the dragged DOM element
+        // to its new position, which disrupts Blaze reactive updates mechanism
+        // (especially when we move the last card of a list, or when multiple
+        // users move some cards at the same time). To prevent these UX glitches
+        // we ask sortable to gracefully cancel the move, and to put back the
+        // DOM in its initial state. The card move is then handled reactively by
+        // Blaze with the below query.
+        $boards.sortable('cancel');
+
+        board.move(sortIndex.base);
       },
       },
-      { sort: ['title'] },
-    );
+    });
+
+    // ugly touch event hotfix
+    enableClickOnTouch(itemsSelector);
+
+    // Disable drag-dropping if the current user is not a board member or is comment only
+    this.autorun(() => {
+      $boards.sortable('option', 'disabled', !userIsAllowedToMove());
+    });
+  },
+
+  boards() {
+    let query = {
+      archived: false,
+      type: 'board',
+    };
+    if (FlowRouter.getRouteName() === 'home')
+      query['members.userId'] = Meteor.userId();
+    else query.permission = 'public';
+
+    return Boards.find(query, {
+      sort: { sort: 1 /* boards default sorting */ },
+    });
   },
   },
   isStarred() {
   isStarred() {
     const user = Meteor.user();
     const user = Meteor.user();
     return user && user.hasStarred(this.currentData()._id);
     return user && user.hasStarred(this.currentData()._id);
   },
   },
+  isAdministrable() {
+    const user = Meteor.user();
+    return user && user.isBoardAdmin(this.currentData()._id);
+  },
 
 
   hasOvertimeCards() {
   hasOvertimeCards() {
     subManager.subscribe('board', this.currentData()._id, false);
     subManager.subscribe('board', this.currentData()._id, false);

+ 17 - 3
client/components/boards/boardsList.styl

@@ -11,6 +11,19 @@ $spaceBetweenTiles = 16px
     box-sizing: border-box
     box-sizing: border-box
     position: relative
     position: relative
 
 
+    &.placeholder:after
+      content: '';
+      display: block;
+      background: darken(white, 20%)
+      border-radius: 3px;
+      height: 106px;
+      margin: 8px;
+
+    &.ui-sortable-helper
+      cursor: grabbing
+      transform: rotate(4deg)
+      display: block !important
+
     &.starred
     &.starred
       .fa-star,
       .fa-star,
       .fa-star-o
       .fa-star-o
@@ -20,7 +33,7 @@ $spaceBetweenTiles = 16px
     overflow: hidden;
     overflow: hidden;
     background-color: #999
     background-color: #999
     color: #f6f6f6
     color: #f6f6f6
-    height: 90px
+    height: auto
     font-size: 16px
     font-size: 16px
     line-height: 22px
     line-height: 22px
     border-radius: 3px
     border-radius: 3px
@@ -31,6 +44,7 @@ $spaceBetweenTiles = 16px
     margin: ($spaceBetweenTiles/2)
     margin: ($spaceBetweenTiles/2)
     position: relative
     position: relative
     text-decoration: none
     text-decoration: none
+    word-wrap: break-word
 
 
     &.tile
     &.tile
       background-size: auto
       background-size: auto
@@ -55,7 +69,7 @@ $spaceBetweenTiles = 16px
 
 
     .label
     .label
       font-weight: normal
       font-weight: normal
-      line-height:90px
+      line-height: 56px
 
 
     :hover
     :hover
       background-color:#939393
       background-color:#939393
@@ -183,7 +197,7 @@ $spaceBetweenTiles = 16px
     overflow: scroll
     overflow: scroll
 
 
     li
     li
-      width: 50% 
+      width: 50%
 
 
     .board-list-item
     .board-list-item
       overflow: hidden
       overflow: hidden

+ 1 - 1
client/components/cards/attachments.jade

@@ -62,5 +62,5 @@ template(name="attachmentsGalery")
         unless currentUser.isWorker
         unless currentUser.isWorker
           //li.attachment-item.add-attachment
           //li.attachment-item.add-attachment
           a.js-add-attachment
           a.js-add-attachment
-            i.fa.fa-paperclip
+            i.fa.fa-plus
             | {{_ 'add-attachment' }}
             | {{_ 'add-attachment' }}

+ 68 - 5
client/components/cards/cardDetails.jade

@@ -32,7 +32,7 @@ template(name="cardDetails")
           // else
           // else
             {{_ 'top-level-card'}}
             {{_ 'top-level-card'}}
         if isLinkedCard
         if isLinkedCard
-          h3.linked-card-location
+          a.linked-card-location.js-go-to-linked-card
             +viewer
             +viewer
               | {{getBoardTitle}} > {{getTitle}}
               | {{getBoardTitle}} > {{getTitle}}
 
 
@@ -199,10 +199,29 @@ template(name="cardDetails")
             +viewer
             +viewer
               = getAssignedBy
               = getAssignedBy
 
 
+    if getVoteQuestion
+      hr
+      .vote-title
+        h3
+          i.fa.fa-thumbs-up
+          card-details-item-title {{_ 'vote-question'}}
+        .vote-result
+          if votePublic
+            a.card-label.card-label-green.js-show-positive-votes {{ voteCountPositive }}
+            a.card-label.card-label-red.js-show-negative-votes {{ voteCountNegative }}
+          else
+            .card-label.card-label-green {{ voteCountPositive }}
+            .card-label.card-label-red {{ voteCountNegative }}
+      +viewer
+        = getVoteQuestion
+      button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}") {{_ 'vote-for-it'}}
+      button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'vote-against'}}
+
     //- XXX We should use "editable" to avoid repetiting ourselves
     //- XXX We should use "editable" to avoid repetiting ourselves
     if canModifyCard
     if canModifyCard
       unless currentUser.isWorker
       unless currentUser.isWorker
         if currentBoard.allowsDescriptionTitle
         if currentBoard.allowsDescriptionTitle
+          hr
           h3
           h3
             i.fa.fa-align-left
             i.fa.fa-align-left
             card-details-item-title {{_ 'description'}}
             card-details-item-title {{_ 'description'}}
@@ -229,6 +248,7 @@ template(name="cardDetails")
                   a.js-close-inlined-form {{_ 'discard'}}
                   a.js-close-inlined-form {{_ 'discard'}}
     else if getDescription
     else if getDescription
       if currentBoard.allowsDescriptionTitle
       if currentBoard.allowsDescriptionTitle
+        hr
         h3.card-details-item-title {{_ 'description'}}
         h3.card-details-item-title {{_ 'description'}}
       if currentBoard.allowsDescriptionText
       if currentBoard.allowsDescriptionText
         +viewer
         +viewer
@@ -237,15 +257,16 @@ template(name="cardDetails")
     .card-checklist-attachmentGalerys
     .card-checklist-attachmentGalerys
       .card-checklist-attachmentGalery.card-checklists
       .card-checklist-attachmentGalery.card-checklists
         if currentBoard.allowsChecklists
         if currentBoard.allowsChecklists
+          hr
           +checklists(cardId = _id)
           +checklists(cardId = _id)
         if currentBoard.allowsSubtasks
         if currentBoard.allowsSubtasks
           hr
           hr
           +subtasks(cardId = _id)
           +subtasks(cardId = _id)
       if currentBoard.allowsAttachments
       if currentBoard.allowsAttachments
-        //- hr
-        //- h3
-        //- i.fa.fa-paperclip
-        //- | {{_ 'attachments'}}
+        hr
+        h3
+          i.fa.fa-paperclip
+          | {{_ 'attachments'}}
         .card-checklist-attachmentGalery.card-attachmentGalery
         .card-checklist-attachmentGalery.card-attachmentGalery
           +attachmentsGalery
           +attachmentsGalery
 
 
@@ -312,6 +333,16 @@ template(name="cardDetailsActionsPopup")
         //li: a.js-members {{_ 'card-edit-members'}}
         //li: a.js-members {{_ 'card-edit-members'}}
         //li: a.js-labels {{_ 'card-edit-labels'}}
         //li: a.js-labels {{_ 'card-edit-labels'}}
         //li: a.js-attachments {{_ 'card-edit-attachments'}}
         //li: a.js-attachments {{_ 'card-edit-attachments'}}
+        if getVoteQuestion
+          li
+            a.js-cancel-voting
+              i.fa.fa-thumbs-up
+              | {{_ 'card-cancel-voting'}}
+        else
+          li
+            a.js-start-voting
+              i.fa.fa-thumbs-up
+              | {{_ 'card-start-voting'}}
         li
         li
           a.js-custom-fields
           a.js-custom-fields
             i.fa.fa-list-alt
             i.fa.fa-list-alt
@@ -535,3 +566,35 @@ template(name="cardDeletePopup")
   unless archived
   unless archived
    p {{_ "card-delete-suggest-archive"}}
    p {{_ "card-delete-suggest-archive"}}
   button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
   button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
+
+template(name="cardStartVotingPopup")
+  form.edit-vote-question
+    .fields
+      label(for="vote") {{_ 'vote-question'}}
+      input.js-vote-field#vote(type="text" name="vote" value="{{card.getVoteQuestion}}" autofocus)
+      label(for="vote-public") {{_ 'vote-public'}}
+        a.js-toggle-vote-public
+          .materialCheckBox#vote-public(name="vote-public")
+
+    button.primary.confirm.js-submit {{_ 'save'}}
+    //- button.js-remove-color.negate.wide.right {{_ 'delete'}}
+
+template(name="positiveVoteMembersPopup")
+  ul.pop-over-list.js-card-member-list
+    each m in voteMemberPositive
+      li.item
+        a.name
+          +userAvatar(userId=m._id)
+          span.full-name
+            = m.profile.fullname
+            | (<span class="username">{{ m.username }}</span>)
+
+template(name="negativeVoteMembersPopup")
+  ul.pop-over-list.js-card-member-list
+    each m in voteMemberNegative
+      li.item
+        a.name
+          +userAvatar(userId=m._id)
+          span.full-name
+            = m.profile.fullname
+            | (<span class="username">{{ m.username }}</span>)

+ 90 - 19
client/components/cards/cardDetails.js

@@ -1,5 +1,5 @@
 const subManager = new SubsManager();
 const subManager = new SubsManager();
-const { calculateIndexData, enableClickOnTouch } = Utils;
+const { calculateIndexData } = Utils;
 
 
 let cardColors;
 let cardColors;
 Meteor.startup(() => {
 Meteor.startup(() => {
@@ -38,6 +38,37 @@ BlazeComponent.extendComponent({
     Meteor.subscribe('unsaved-edits');
     Meteor.subscribe('unsaved-edits');
   },
   },
 
 
+  voteState() {
+    const card = this.currentData();
+    const userId = Meteor.userId();
+    let state;
+    if (card.vote) {
+      if (card.vote.positive) {
+        state = _.contains(card.vote.positive, userId);
+        if (state === true) return true;
+      }
+      if (card.vote.negative) {
+        state = _.contains(card.vote.negative, userId);
+        if (state === true) return false;
+      }
+    }
+    return null;
+  },
+  votePublic() {
+    const card = this.currentData();
+    if (card.vote) return card.vote.public;
+    return null;
+  },
+  voteCountPositive() {
+    const card = this.currentData();
+    if (card.vote && card.vote.positive) return card.vote.positive.length;
+    return null;
+  },
+  voteCountNegative() {
+    const card = this.currentData();
+    if (card.vote && card.vote.negative) return card.vote.negative.length;
+    return null;
+  },
   isWatching() {
   isWatching() {
     const card = this.currentData();
     const card = this.currentData();
     return card.findWatcher(Meteor.userId());
     return card.findWatcher(Meteor.userId());
@@ -200,9 +231,6 @@ BlazeComponent.extendComponent({
       },
       },
     });
     });
 
 
-    // ugly touch event hotfix
-    enableClickOnTouch('.card-checklist-items .js-checklist');
-
     const $subtasksDom = this.$('.card-subtasks-items');
     const $subtasksDom = this.$('.card-subtasks-items');
 
 
     $subtasksDom.sortable({
     $subtasksDom.sortable({
@@ -238,26 +266,21 @@ BlazeComponent.extendComponent({
       },
       },
     });
     });
 
 
-    // ugly touch event hotfix
-    enableClickOnTouch('.card-subtasks-items .js-subtasks');
-
     function userIsMember() {
     function userIsMember() {
       return Meteor.user() && Meteor.user().isBoardMember();
       return Meteor.user() && Meteor.user().isBoardMember();
     }
     }
 
 
     // Disable sorting if the current user is not a board member
     // Disable sorting if the current user is not a board member
     this.autorun(() => {
     this.autorun(() => {
-      if ($checklistsDom.data('sortable')) {
-        $checklistsDom.sortable('option', 'disabled', !userIsMember());
-      }
-      if ($subtasksDom.data('sortable')) {
-        $subtasksDom.sortable('option', 'disabled', !userIsMember());
-      }
-      if ($checklistsDom.data('sortable')) {
-        $checklistsDom.sortable('option', 'disabled', Utils.isMiniScreen());
+      const disabled = !userIsMember() || Utils.isMiniScreen();
+      if (
+        $checklistsDom.data('uiSortable') ||
+        $checklistsDom.data('sortable')
+      ) {
+        $checklistsDom.sortable('option', 'disabled', disabled);
       }
       }
-      if ($subtasksDom.data('sortable')) {
-        $subtasksDom.sortable('option', 'disabled', Utils.isMiniScreen());
+      if ($subtasksDom.data('uiSortable') || $subtasksDom.data('sortable')) {
+        $subtasksDom.sortable('option', 'disabled', disabled);
       }
       }
     });
     });
   },
   },
@@ -347,6 +370,9 @@ BlazeComponent.extendComponent({
             this.data().setRequestedBy('');
             this.data().setRequestedBy('');
           }
           }
         },
         },
+        'click .js-go-to-linked-card'() {
+          Utils.goCardId(this.data().linkedId);
+        },
         'click .js-member': Popup.open('cardMember'),
         'click .js-member': Popup.open('cardMember'),
         'click .js-add-members': Popup.open('cardMembers'),
         'click .js-add-members': Popup.open('cardMembers'),
         'click .js-assignee': Popup.open('cardAssignee'),
         'click .js-assignee': Popup.open('cardAssignee'),
@@ -356,6 +382,8 @@ BlazeComponent.extendComponent({
         'click .js-start-date': Popup.open('editCardStartDate'),
         'click .js-start-date': Popup.open('editCardStartDate'),
         'click .js-due-date': Popup.open('editCardDueDate'),
         'click .js-due-date': Popup.open('editCardDueDate'),
         'click .js-end-date': Popup.open('editCardEndDate'),
         'click .js-end-date': Popup.open('editCardEndDate'),
+        'click .js-show-positive-votes': Popup.open('positiveVoteMembers'),
+        'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
         'mouseenter .js-card-details'() {
         'mouseenter .js-card-details'() {
           const parentComponent = this.parentComponent().parentComponent();
           const parentComponent = this.parentComponent().parentComponent();
           //on mobile view parent is Board, not BoardBody.
           //on mobile view parent is Board, not BoardBody.
@@ -379,6 +407,18 @@ BlazeComponent.extendComponent({
         'click #toggleButton'() {
         'click #toggleButton'() {
           Meteor.call('toggleSystemMessages');
           Meteor.call('toggleSystemMessages');
         },
         },
+        'click .js-vote'(e) {
+          const forIt = $(e.target).hasClass('js-vote-positive');
+          let newState = null;
+          if (
+            this.voteState() === null ||
+            (this.voteState() === false && forIt) ||
+            (this.voteState() === true && !forIt)
+          ) {
+            newState = forIt;
+          }
+          this.data().setVote(Meteor.userId(), newState);
+        },
       },
       },
     ];
     ];
   },
   },
@@ -560,6 +600,7 @@ Template.cardDetailsActionsPopup.events({
   'click .js-assignees': Popup.open('cardAssignees'),
   'click .js-assignees': Popup.open('cardAssignees'),
   'click .js-labels': Popup.open('cardLabels'),
   'click .js-labels': Popup.open('cardLabels'),
   'click .js-attachments': Popup.open('cardAttachments'),
   'click .js-attachments': Popup.open('cardAttachments'),
+  'click .js-start-voting': Popup.open('cardStartVoting'),
   'click .js-custom-fields': Popup.open('cardCustomFields'),
   'click .js-custom-fields': Popup.open('cardCustomFields'),
   'click .js-received-date': Popup.open('editCardReceivedDate'),
   'click .js-received-date': Popup.open('editCardReceivedDate'),
   'click .js-start-date': Popup.open('editCardStartDate'),
   'click .js-start-date': Popup.open('editCardStartDate'),
@@ -570,6 +611,11 @@ Template.cardDetailsActionsPopup.events({
   'click .js-copy-card': Popup.open('copyCard'),
   'click .js-copy-card': Popup.open('copyCard'),
   'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
   'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
   'click .js-set-card-color': Popup.open('setCardColor'),
   'click .js-set-card-color': Popup.open('setCardColor'),
+  'click .js-cancel-voting'(event) {
+    event.preventDefault();
+    this.unsetVote();
+    Popup.close();
+  },
   'click .js-move-card-to-top'(event) {
   'click .js-move-card-to-top'(event) {
     event.preventDefault();
     event.preventDefault();
     const minOrder = _.min(
     const minOrder = _.min(
@@ -672,7 +718,7 @@ BlazeComponent.extendComponent({
         _id: { $ne: Meteor.user().getTemplatesBoardId() },
         _id: { $ne: Meteor.user().getTemplatesBoardId() },
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
     return boards;
     return boards;
@@ -848,7 +894,7 @@ BlazeComponent.extendComponent({
         },
         },
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
     return boards;
     return boards;
@@ -945,6 +991,31 @@ BlazeComponent.extendComponent({
   },
   },
 }).register('cardMorePopup');
 }).register('cardMorePopup');
 
 
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.currentCard = this.currentData();
+    this.voteQuestion = new ReactiveVar(this.currentCard.voteQuestion);
+  },
+
+  events() {
+    return [
+      {
+        'submit .edit-vote-question'(evt) {
+          evt.preventDefault();
+          const voteQuestion = evt.target.vote.value;
+          const publicVote = $('#vote-public').hasClass('is-checked');
+          this.currentCard.setVoteQuestion(voteQuestion, publicVote);
+          Popup.close();
+        },
+        'click a.js-toggle-vote-public'(event) {
+          event.preventDefault();
+          $('#vote-public').toggleClass('is-checked');
+        },
+      },
+    ];
+  },
+}).register('cardStartVotingPopup');
+
 // Close the card details pane by pressing escape
 // Close the card details pane by pressing escape
 EscapeActions.register(
 EscapeActions.register(
   'detailsPane',
   'detailsPane',

+ 14 - 3
client/components/cards/cardDetails.styl

@@ -94,17 +94,18 @@ avatar-radius = 50%
   animation: flexGrowIn 0.1s
   animation: flexGrowIn 0.1s
   box-shadow: 0 0 7px 0 darken(white, 30%)
   box-shadow: 0 0 7px 0 darken(white, 30%)
   transition: flex-basis 0.1s
   transition: flex-basis 0.1s
+  box-sizing: border-box
 
 
   .mCustomScrollBox
   .mCustomScrollBox
     padding-left: 0
     padding-left: 0
 
 
   .ps-scrollbar-y-rail
   .ps-scrollbar-y-rail
     pointer-event: all
     pointer-event: all
-    position: absolute;
+    position: absolute
 
 
   .card-details-canvas
   .card-details-canvas
     width: 470px
     width: 470px
-    padding-left: 20px;
+    padding-left: 20px
 
 
   .card-details-header
   .card-details-header
     margin: 0 -20px 5px
     margin: 0 -20px 5px
@@ -241,7 +242,7 @@ input[type="submit"].attachment-add-link-submit
 
 
     .card-details-canvas
     .card-details-canvas
       width: 100%
       width: 100%
-      padding-left: 0px;
+      padding-left: 0px
 
 
     .card-details-header
     .card-details-header
       .close-card-details
       .close-card-details
@@ -330,3 +331,13 @@ card-details-color(background, color...)
 
 
 .card-details-indigo
 .card-details-indigo
   card-details-color(#4b0082, #ffffff) //White text for better visibility
   card-details-color(#4b0082, #ffffff) //White text for better visibility
+
+.voted
+  opacity: .7
+.vote-title
+  display: flex
+  justify-content: space-between
+.vote-result
+  display: flex
+.js-show-positive-votes
+  cursor: pointer

+ 2 - 1
client/components/cards/checklists.jade

@@ -88,7 +88,8 @@ template(name="checklistItems")
 template(name='checklistItemDetail')
 template(name='checklistItemDetail')
   .js-checklist-item.checklist-item
   .js-checklist-item.checklist-item
     if canModifyCard
     if canModifyCard
-      .check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
+      .check-box-container
+        .check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
       .item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
       .item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
         +viewer
         +viewer
           = item.title
           = item.title

+ 20 - 11
client/components/cards/checklists.js

@@ -1,4 +1,4 @@
-const { calculateIndexData, enableClickOnTouch } = Utils;
+const { calculateIndexData, capitalize } = Utils;
 
 
 function initSorting(items) {
 function initSorting(items) {
   items.sortable({
   items.sortable({
@@ -36,9 +36,6 @@ function initSorting(items) {
       checklistItem.move(checklistId, sortIndex.base);
       checklistItem.move(checklistId, sortIndex.base);
     },
     },
   });
   });
-
-  // ugly touch event hotfix
-  enableClickOnTouch('.js-checklist-item:not(.placeholder)');
 }
 }
 
 
 BlazeComponent.extendComponent({
 BlazeComponent.extendComponent({
@@ -54,14 +51,15 @@ BlazeComponent.extendComponent({
       return Meteor.user() && Meteor.user().isBoardMember();
       return Meteor.user() && Meteor.user().isBoardMember();
     }
     }
 
 
-    // Disable sorting if the current user is not a board member
+    // Disable sorting if the current user is not a board member or is a miniscreen
     self.autorun(() => {
     self.autorun(() => {
       const $itemsDom = $(self.itemsDom);
       const $itemsDom = $(self.itemsDom);
-      if ($itemsDom.data('sortable')) {
-        $(self.itemsDom).sortable('option', 'disabled', !userIsMember());
-      }
-      if ($itemsDom.data('sortable')) {
-        $(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen());
+      if ($itemsDom.data('uiSortable') || $itemsDom.data('sortable')) {
+        $(self.itemsDom).sortable(
+          'option',
+          'disabled',
+          !userIsMember() || Utils.isMiniScreen(),
+        );
       }
       }
     });
     });
   },
   },
@@ -177,6 +175,16 @@ BlazeComponent.extendComponent({
     }
     }
   },
   },
 
 
+  focusChecklistItem(event) {
+    // If a new checklist is created, pre-fill the title and select it.
+    const checklist = this.currentData().checklist;
+    if (!checklist) {
+      const textarea = event.target;
+      textarea.value = capitalize(TAPi18n.__('r-checklist'));
+      textarea.select();
+    }
+  },
+
   events() {
   events() {
     const events = {
     const events = {
       'click .toggle-delete-checklist-dialog'(event) {
       'click .toggle-delete-checklist-dialog'(event) {
@@ -196,6 +204,7 @@ BlazeComponent.extendComponent({
         'submit .js-edit-checklist-item': this.editChecklistItem,
         'submit .js-edit-checklist-item': this.editChecklistItem,
         'click .js-delete-checklist-item': this.deleteItem,
         'click .js-delete-checklist-item': this.deleteItem,
         'click .confirm-checklist-delete': this.deleteChecklist,
         'click .confirm-checklist-delete': this.deleteChecklist,
+        'focus .js-add-checklist-item': this.focusChecklistItem,
         keydown: this.pressKey,
         keydown: this.pressKey,
       },
       },
     ];
     ];
@@ -250,7 +259,7 @@ BlazeComponent.extendComponent({
   events() {
   events() {
     return [
     return [
       {
       {
-        'click .js-checklist-item .check-box': this.toggleItem,
+        'click .js-checklist-item .check-box-container': this.toggleItem,
       },
       },
     ];
     ];
   },
   },

+ 4 - 1
client/components/cards/checklists.styl

@@ -113,6 +113,9 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
   &:hover
   &:hover
     background-color: darken(white, 8%)
     background-color: darken(white, 8%)
 
 
+  .check-box-container
+    padding-right: 1px;
+
   .check-box
   .check-box
     margin: 0.1em 0 0 0;
     margin: 0.1em 0 0 0;
     &.is-checked
     &.is-checked
@@ -121,7 +124,7 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
 
 
   .item-title
   .item-title
     flex: 1
     flex: 1
-    padding-left: 10px;
+    margin-left: 10px;
     &.is-checked
     &.is-checked
       color: #8c8c8c
       color: #8c8c8c
       font-style: italic
       font-style: italic

+ 2 - 0
client/components/cards/labels.styl

@@ -158,6 +158,8 @@
 
 
 .edit-labels-pop-over
 .edit-labels-pop-over
   margin-bottom: 8px
   margin-bottom: 8px
+  .card-label .viewer p
+    margin: 0
 
 
 .edit-labels-pop-over .shortcut
 .edit-labels-pop-over .shortcut
   display: inline-block
   display: inline-block

+ 6 - 2
client/components/cards/minicard.jade

@@ -4,8 +4,8 @@ template(name="minicard")
     class="{{#if isLinkedBoard}}linked-board{{/if}}"
     class="{{#if isLinkedBoard}}linked-board{{/if}}"
     class="minicard-{{colorClass}}")
     class="minicard-{{colorClass}}")
     if isMiniScreen
     if isMiniScreen
-      //.handle
-      //  .fa.fa-arrows
+      .handle
+        .fa.fa-arrows
     unless isMiniScreen
     unless isMiniScreen
       if showDesktopDragHandles
       if showDesktopDragHandles
         .handle
         .handle
@@ -100,6 +100,10 @@ template(name="minicard")
       if getDescription
       if getDescription
         .badge.badge-state-image-only(title=getDescription)
         .badge.badge-state-image-only(title=getDescription)
           span.badge-icon.fa.fa-align-left
           span.badge-icon.fa.fa-align-left
+      if getVoteQuestion
+        .badge.badge-state-image-only(title=getVoteQuestion)
+          span.badge-icon.fa.fa-thumbs-up
+          span.badge-icon.fa.fa-thumbs-down
       if attachments.count
       if attachments.count
         .badge
         .badge
           span.badge-icon.fa.fa-paperclip
           span.badge-icon.fa.fa-paperclip

+ 1 - 1
client/components/cards/minicard.styl

@@ -79,7 +79,7 @@
     border-radius: top 2px
     border-radius: top 2px
 
 
   .minicard-labels
   .minicard-labels
-    float: right
+    float: none
     display: flex
     display: flex
     flex-wrap: wrap
     flex-wrap: wrap
 
 

+ 16 - 1
client/components/cards/subtasks.js

@@ -20,7 +20,22 @@ BlazeComponent.extendComponent({
     const crtBoard = Boards.findOne(card.boardId);
     const crtBoard = Boards.findOne(card.boardId);
     const targetBoard = crtBoard.getDefaultSubtasksBoard();
     const targetBoard = crtBoard.getDefaultSubtasksBoard();
     const listId = targetBoard.getDefaultSubtasksListId();
     const listId = targetBoard.getDefaultSubtasksListId();
-    const swimlaneId = targetBoard.getDefaultSwimline()._id;
+
+    //Get the full swimlane data for the parent task.
+    const parentSwimlane = Swimlanes.findOne({
+      boardId: crtBoard._id,
+      _id: card.swimlaneId,
+    });
+    //find the swimlane of the same name in the target board.
+    const targetSwimlane = Swimlanes.findOne({
+      boardId: targetBoard._id,
+      title: parentSwimlane.title,
+    });
+    //If no swimlane with a matching title exists in the target board, fall back to the default swimlane.
+    const swimlaneId =
+      targetSwimlane === undefined
+        ? targetBoard.getDefaultSwimline()._id
+        : targetSwimlane._id;
 
 
     if (title) {
     if (title) {
       const _id = Cards.insert({
       const _id = Cards.insert({

+ 0 - 3
client/components/import/import.jade

@@ -15,9 +15,6 @@ template(name="importTextarea")
     p: label(for='import-textarea') {{_ instruction}} {{_ 'import-board-instruction-about-errors'}}
     p: label(for='import-textarea') {{_ instruction}} {{_ 'import-board-instruction-about-errors'}}
     textarea.js-import-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus)
     textarea.js-import-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus)
       | {{jsonText}}
       | {{jsonText}}
-    if isSandstorm
-      h1.warning {{_ 'import-sandstorm-backup-warning'}}
-      p.warning {{_ 'import-sandstorm-warning'}}
     input.primary.wide(type="submit" value="{{_ 'import'}}")
     input.primary.wide(type="submit" value="{{_ 'import'}}")
 
 
 template(name="importMapMembers")
 template(name="importMapMembers")

+ 4 - 18
client/components/lists/list.js

@@ -1,6 +1,6 @@
 import { Cookies } from 'meteor/ostrio:cookies';
 import { Cookies } from 'meteor/ostrio:cookies';
 const cookies = new Cookies();
 const cookies = new Cookies();
-const { calculateIndex, enableClickOnTouch } = Utils;
+const { calculateIndex } = Utils;
 
 
 BlazeComponent.extendComponent({
 BlazeComponent.extendComponent({
   // Proxy
   // Proxy
@@ -114,9 +114,6 @@ BlazeComponent.extendComponent({
       },
       },
     });
     });
 
 
-    // ugly touch event hotfix
-    enableClickOnTouch(itemsSelector);
-
     this.autorun(() => {
     this.autorun(() => {
       let showDesktopDragHandles = false;
       let showDesktopDragHandles = false;
       currentUser = Meteor.user();
       currentUser = Meteor.user();
@@ -129,7 +126,7 @@ BlazeComponent.extendComponent({
         showDesktopDragHandles = false;
         showDesktopDragHandles = false;
       }
       }
 
 
-      if (!Utils.isMiniScreen() && showDesktopDragHandles) {
+      if (Utils.isMiniScreen() || showDesktopDragHandles) {
         $cards.sortable({
         $cards.sortable({
           handle: '.handle',
           handle: '.handle',
         });
         });
@@ -139,27 +136,16 @@ BlazeComponent.extendComponent({
         });
         });
       }
       }
 
 
-      if ($cards.data('sortable')) {
+      if ($cards.data('uiSortable') || $cards.data('sortable')) {
         $cards.sortable(
         $cards.sortable(
           'option',
           'option',
           'disabled',
           'disabled',
-          // Disable drag-dropping when user is not member/is miniscreen
+          // Disable drag-dropping when user is not member
           !userIsMember(),
           !userIsMember(),
           // Not disable drag-dropping while in multi-selection mode
           // Not disable drag-dropping while in multi-selection mode
           // MultiSelection.isActive() || !userIsMember(),
           // MultiSelection.isActive() || !userIsMember(),
         );
         );
       }
       }
-
-      if ($cards.data('sortable')) {
-        $cards.sortable(
-          'option',
-          'disabled',
-          // Disable drag-dropping when user is not member/is miniscreen
-          Utils.isMiniScreen(),
-          // Not disable drag-dropping while in multi-selection mode
-          // MultiSelection.isActive() || !userIsMember(),
-        );
-      }
     });
     });
 
 
     // We want to re-run this function any time a card is added.
     // We want to re-run this function any time a card is added.

+ 3 - 3
client/components/lists/list.styl

@@ -43,9 +43,6 @@
       background: white
       background: white
       margin: -3px 0 8px
       margin: -3px 0 8px
 
 
-.list-header-card-count
-  height: 35px
-
 .list-header-add
 .list-header-add
   flex: 0 0 auto
   flex: 0 0 auto
   padding: 20px 12px 4px
   padding: 20px 12px 4px
@@ -60,6 +57,9 @@
   background-color: #e4e4e4;
   background-color: #e4e4e4;
   border-bottom: 6px solid #e4e4e4;
   border-bottom: 6px solid #e4e4e4;
 
 
+  &.list-header-card-count
+    min-height: 35px
+    height: auto
 
 
   &.ui-sortable-handle
   &.ui-sortable-handle
     cursor: grab
     cursor: grab

+ 19 - 3
client/components/lists/listBody.js

@@ -411,7 +411,7 @@ BlazeComponent.extendComponent({
         type: 'board',
         type: 'board',
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
     return boards;
     return boards;
@@ -597,7 +597,7 @@ BlazeComponent.extendComponent({
         type: 'board',
         type: 'board',
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
     return boards;
     return boards;
@@ -743,9 +743,25 @@ BlazeComponent.extendComponent({
   },
   },
 
 
   updateList() {
   updateList() {
+    // Use fallback when requestIdleCallback is not available on iOS and Safari
+    // https://www.afasterweb.com/2017/11/20/utilizing-idle-moments/
+    checkIdleTime =
+      window.requestIdleCallback ||
+      function(handler) {
+        const startTime = Date.now();
+        return setTimeout(function() {
+          handler({
+            didTimeout: false,
+            timeRemaining() {
+              return Math.max(0, 50.0 - (Date.now() - startTime));
+            },
+          });
+        }, 1);
+      };
+
     if (this.spinnerInView()) {
     if (this.spinnerInView()) {
       this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
       this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
-      window.requestIdleCallback(() => this.updateList());
+      checkIdleTime(() => this.updateList());
     }
     }
   },
   },
 
 

+ 1 - 2
client/components/lists/listHeader.jade

@@ -30,10 +30,9 @@ template(name="listHeader")
               if canSeeAddCard
               if canSeeAddCard
                 a.js-add-card.fa.fa-plus.list-header-plus-icon
                 a.js-add-card.fa.fa-plus.list-header-plus-icon
               a.fa.fa-navicon.js-open-list-menu
               a.fa.fa-navicon.js-open-list-menu
-          //a.list-header-handle.handle.fa.fa-arrows.js-list-handle
         else
         else
           a.list-header-menu-icon.fa.fa-angle-right.js-select-list
           a.list-header-menu-icon.fa.fa-angle-right.js-select-list
-          //a.list-header-handle.handle.fa.fa-arrows.js-list-handle
+          a.list-header-handle.handle.fa.fa-arrows.js-list-handle
       else if currentUser.isBoardMember
       else if currentUser.isBoardMember
         if isWatching
         if isWatching
           i.list-header-watch-icon.fa.fa-eye
           i.list-header-watch-icon.fa.fa-eye

+ 64 - 128
client/components/main/editor.js

@@ -1,87 +1,3 @@
-import _sanitizeXss from 'xss';
-const ASIS = 'asis';
-const sanitizeXss = (input, options) => {
-  const defaultAllowedIframeSrc = /^(https:){0,1}\/\/.*?(youtube|vimeo|dailymotion|youku)/i;
-  const allowedIframeSrcRegex = (function() {
-    let reg = defaultAllowedIframeSrc;
-    const SAFE_IFRAME_SRC_PATTERN =
-      Meteor.settings.public.SAFE_IFRAME_SRC_PATTERN;
-    try {
-      if (SAFE_IFRAME_SRC_PATTERN !== undefined) {
-        reg = new RegExp(SAFE_IFRAME_SRC_PATTERN, 'i');
-      }
-    } catch (e) {
-      /*eslint no-console: ["error", { allow: ["warn", "error"] }] */
-
-      console.error('Wrong pattern specified', SAFE_IFRAM_SRC_PATTERN, e);
-    }
-    return reg;
-  })();
-  const targetWindow = '_blank';
-  const getHtmlDOM = html => {
-    const i = document.createElement('i');
-    i.innerHTML = html;
-    return i.firstChild;
-  };
-  options = {
-    onTag(tag, html, options) {
-      const htmlDOM = getHtmlDOM(html);
-      const getAttr = attr => {
-        return htmlDOM && attr && htmlDOM.getAttribute(attr);
-      };
-      if (tag === 'iframe') {
-        const clipCls = 'note-vide-clip';
-        if (!options.isClosing) {
-          const iframeCls = getAttr('class');
-          let safe = iframeCls.indexOf(clipCls) > -1;
-          const src = getAttr('src');
-          if (allowedIframeSrcRegex.exec(src)) {
-            safe = true;
-          }
-          if (safe)
-            return `<iframe src='${src}' class="${clipCls}" width=100% height=auto allowfullscreen></iframe>`;
-        } else {
-          // remove </iframe> tag
-          return '';
-        }
-      } else if (tag === 'a') {
-        if (!options.isClosing) {
-          if (getAttr(ASIS) === 'true') {
-            // if has a ASIS attribute, don't do anything, it's a member id
-            return html;
-          } else {
-            const href = getAttr('href');
-            if (href.match(/^((http(s){0,1}:){0,1}\/\/|\/)/)) {
-              // a valid url
-              return `<a href=${href} target=${targetWindow}>`;
-            }
-          }
-        }
-      } else if (tag === 'img') {
-        if (!options.isClosing) {
-          const src = getAttr('src');
-          if (src) {
-            return `<a href='${src}' class='swipebox'><img src='${src}' class="attachment-image-preview mCS_img_loaded"></a>`;
-          }
-        }
-      }
-      return undefined;
-    },
-    onTagAttr(tag, name, value) {
-      if (tag === 'img' && name === 'src') {
-        if (value && value.substr(0, 5) === 'data:') {
-          // allow image with dataURI src
-          return `${name}='${value}'`;
-        }
-      } else if (tag === 'a' && name === 'target') {
-        return `${name}='${targetWindow}'`; // always change a href target to a new window
-      }
-      return undefined;
-    },
-    ...options,
-  };
-  return _sanitizeXss(input, options);
-};
 Template.editor.onRendered(() => {
 Template.editor.onRendered(() => {
   const textareaSelector = 'textarea';
   const textareaSelector = 'textarea';
   const mentions = [
   const mentions = [
@@ -94,13 +10,7 @@ Template.editor.onRendered(() => {
           currentBoard
           currentBoard
             .activeMembers()
             .activeMembers()
             .map(member => {
             .map(member => {
-              const user = Users.findOne(member.userId);
-              if (user._id === Meteor.userId()) {
-                return null;
-              }
-              const value = user.username;
-              const username =
-                value && value.match(/\s+/) ? `"${value}"` : value;
+              const username = Users.findOne(member.userId).username;
               return username.includes(term) ? username : null;
               return username.includes(term) ? username : null;
             })
             })
             .filter(Boolean),
             .filter(Boolean),
@@ -126,10 +36,9 @@ Template.editor.onRendered(() => {
       ? [
       ? [
           ['view', ['fullscreen']],
           ['view', ['fullscreen']],
           ['table', ['table']],
           ['table', ['table']],
-          ['font', ['bold']],
-          ['color', ['color']],
-          ['insert', ['video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
+          ['font', ['bold', 'underline']],
           //['fontsize', ['fontsize']],
           //['fontsize', ['fontsize']],
+          ['color', ['color']],
         ]
         ]
       : [
       : [
           ['style', ['style']],
           ['style', ['style']],
@@ -139,11 +48,47 @@ Template.editor.onRendered(() => {
           ['color', ['color']],
           ['color', ['color']],
           ['para', ['ul', 'ol', 'paragraph']],
           ['para', ['ul', 'ol', 'paragraph']],
           ['table', ['table']],
           ['table', ['table']],
-          ['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
+          //['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
           //['insert', ['link', 'picture']], // modal popup has issue somehow :(
           //['insert', ['link', 'picture']], // modal popup has issue somehow :(
           ['view', ['fullscreen', 'help']],
           ['view', ['fullscreen', 'help']],
         ];
         ];
-    const cleanPastedHTML = sanitizeXss;
+    const cleanPastedHTML = function(input) {
+      const badTags = [
+        'style',
+        'script',
+        'applet',
+        'embed',
+        'noframes',
+        'noscript',
+        'meta',
+        'link',
+        'button',
+        'form',
+      ].join('|');
+      const badPatterns = new RegExp(
+        `(?:${[
+          `<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
+          `<(${badTags})[^>]*?\\/>`,
+        ].join('|')})`,
+        'gi',
+      );
+      let output = input;
+      // remove bad Tags
+      output = output.replace(badPatterns, '');
+      // remove attributes ' style="..."'
+      const badAttributes = new RegExp(
+        `(?:${[
+          'on\\S+=([\'"]?).*?\\1',
+          'href=([\'"]?)javascript:.*?\\2',
+          'style=([\'"]?).*?\\3',
+          'target=\\S+',
+        ].join('|')})`,
+        'gi',
+      );
+      output = output.replace(badAttributes, '');
+      output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
+      return output;
+    };
     const editor = '.editor';
     const editor = '.editor';
     const selectors = [
     const selectors = [
       `.js-new-comment-form ${editor}`,
       `.js-new-comment-form ${editor}`,
@@ -163,37 +108,14 @@ Template.editor.onRendered(() => {
         }
         }
         return undefined;
         return undefined;
       };
       };
-      let popupShown = false;
       inputs.each(function(idx, input) {
       inputs.each(function(idx, input) {
         mSummernotes[idx] = $(input).summernote({
         mSummernotes[idx] = $(input).summernote({
           placeholder,
           placeholder,
           callbacks: {
           callbacks: {
-            onKeydown(e) {
-              if (popupShown) {
-                e.preventDefault();
-              }
-            },
-            onKeyup(e) {
-              if (popupShown) {
-                e.preventDefault();
-              }
-            },
             onInit(object) {
             onInit(object) {
               const originalInput = this;
               const originalInput = this;
-              const setAutocomplete = function(jEditor) {
-                if (jEditor !== undefined) {
-                  jEditor.escapeableTextComplete(mentions).on({
-                    'textComplete:show'() {
-                      popupShown = true;
-                    },
-                    'textComplete:hide'() {
-                      popupShown = false;
-                    },
-                  });
-                }
-              };
               $(originalInput).on('submitted', function() {
               $(originalInput).on('submitted', function() {
-                // resetCommentInput has been called
+                // when comment is submitted, the original textarea will be set to '', so shall we
                 if (!this.value) {
                 if (!this.value) {
                   const sn = getSummernote(this);
                   const sn = getSummernote(this);
                   sn && sn.summernote('code', '');
                   sn && sn.summernote('code', '');
@@ -201,7 +123,9 @@ Template.editor.onRendered(() => {
               });
               });
               const jEditor = object && object.editable;
               const jEditor = object && object.editable;
               const toolbar = object && object.toolbar;
               const toolbar = object && object.toolbar;
-              setAutocomplete(jEditor);
+              if (jEditor !== undefined) {
+                jEditor.escapeableTextComplete(mentions);
+              }
               if (toolbar !== undefined) {
               if (toolbar !== undefined) {
                 const fBtn = toolbar.find('.btn-fullscreen');
                 const fBtn = toolbar.find('.btn-fullscreen');
                 fBtn.on('click', function() {
                 fBtn.on('click', function() {
@@ -211,6 +135,7 @@ Template.editor.onRendered(() => {
                 });
                 });
               }
               }
             },
             },
+
             onImageUpload(files) {
             onImageUpload(files) {
               const $summernote = getSummernote(this);
               const $summernote = getSummernote(this);
               if (files && files.length > 0) {
               if (files && files.length > 0) {
@@ -287,6 +212,12 @@ Template.editor.onRendered(() => {
               const thisNote = this;
               const thisNote = this;
               const updatePastedText = function(object) {
               const updatePastedText = function(object) {
                 const someNote = getSummernote(object);
                 const someNote = getSummernote(object);
+                // Fix Pasting text into a card is adding a line before and after
+                // (and multiplies by pasting more) by changing paste "p" to "br".
+                // Fixes https://github.com/wekan/wekan/2890 .
+                // == Fix Start ==
+                someNote.execCommand('defaultParagraphSeparator', false, 'br');
+                // == Fix End ==
                 const original = someNote.summernote('code');
                 const original = someNote.summernote('code');
                 const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
                 const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
                 someNote.summernote('code', ''); //clear original
                 someNote.summernote('code', ''); //clear original
@@ -329,6 +260,8 @@ Template.editor.onRendered(() => {
   }
   }
 });
 });
 
 
+import sanitizeXss from 'xss';
+
 // XXX I believe we should compute a HTML rendered field on the server that
 // XXX I believe we should compute a HTML rendered field on the server that
 // would handle markdown and user mentions. We can simply have two
 // would handle markdown and user mentions. We can simply have two
 // fields, one source, and one compiled version (in HTML) and send only the
 // fields, one source, and one compiled version (in HTML) and send only the
@@ -350,7 +283,7 @@ Blaze.Template.registerHelper(
       }
       }
       return member;
       return member;
     });
     });
-    const mentionRegex = /\B@(?:(?:"([\w.\s]*)")|([\w.]+))/gi; // including space in username
+    const mentionRegex = /\B@([\w.]*)/gi;
 
 
     let currentMention;
     let currentMention;
     while ((currentMention = mentionRegex.exec(content)) !== null) {
     while ((currentMention = mentionRegex.exec(content)) !== null) {
@@ -366,7 +299,12 @@ Blaze.Template.registerHelper(
       if (knowedUser.userId === Meteor.userId()) {
       if (knowedUser.userId === Meteor.userId()) {
         linkClass += ' me';
         linkClass += ' me';
       }
       }
-      const link = HTML.A(
+      // This @user mention link generation did open same Wekan
+      // window in new tab, so now A is changed to U so it's
+      // underlined and there is no link popup. This way also
+      // text can be selected more easily.
+      //const link = HTML.A(
+      const link = HTML.U(
         {
         {
           class: linkClass,
           class: linkClass,
           // XXX Hack. Since we stringify this render function result below with
           // XXX Hack. Since we stringify this render function result below with
@@ -374,16 +312,17 @@ Blaze.Template.registerHelper(
           // `userId` to the popup as usual, and we need to store it in the DOM
           // `userId` to the popup as usual, and we need to store it in the DOM
           // using a data attribute.
           // using a data attribute.
           'data-userId': knowedUser.userId,
           'data-userId': knowedUser.userId,
-          [ASIS]: 'true',
         },
         },
         linkValue,
         linkValue,
       );
       );
 
 
       content = content.replace(fullMention, Blaze.toHTML(link));
       content = content.replace(fullMention, Blaze.toHTML(link));
     }
     }
+
     return HTML.Raw(sanitizeXss(content));
     return HTML.Raw(sanitizeXss(content));
   }),
   }),
 );
 );
+
 Template.viewer.events({
 Template.viewer.events({
   // Viewer sometimes have click-able wrapper around them (for instance to edit
   // Viewer sometimes have click-able wrapper around them (for instance to edit
   // the corresponding text). Clicking a link shouldn't fire these actions, stop
   // the corresponding text). Clicking a link shouldn't fire these actions, stop
@@ -395,10 +334,7 @@ Template.viewer.events({
       Popup.open('member').call({ userId }, event, templateInstance);
       Popup.open('member').call({ userId }, event, templateInstance);
     } else {
     } else {
       const href = event.currentTarget.href;
       const href = event.currentTarget.href;
-      const child = event.currentTarget.firstElementChild;
-      if (child && child.tagName === 'IMG') {
-        prevent = false;
-      } else if (href) {
+      if (href) {
         window.open(href, '_blank');
         window.open(href, '_blank');
       }
       }
     }
     }

+ 7 - 0
client/components/main/header.jade

@@ -24,6 +24,11 @@ template(name="header")
             a(href="{{pathFor 'home'}}")
             a(href="{{pathFor 'home'}}")
               span.fa.fa-home
               span.fa.fa-home
               | {{_ 'all-boards'}}
               | {{_ 'all-boards'}}
+          li.separator -
+          li
+            a(href="{{pathFor 'public'}}")
+              span.fa.fa-globe
+              | {{_ 'public'}}
           each currentUser.starredBoards
           each currentUser.starredBoards
             li.separator -
             li.separator -
             li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
             li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
@@ -35,6 +40,8 @@ template(name="header")
       a#header-new-board-icon.js-create-board
       a#header-new-board-icon.js-create-board
         i.fa.fa-plus(title="Create a new board")
         i.fa.fa-plus(title="Create a new board")
 
 
+      +notifications
+
       +headerUserBar
       +headerUserBar
 
 
   #header(class=currentBoard.colorClass)
   #header(class=currentBoard.colorClass)

+ 3 - 3
client/components/main/header.styl

@@ -99,7 +99,7 @@
   height: 28px
   height: 28px
   font-size: 12px
   font-size: 12px
   display: flex
   display: flex
-  z-index: 17
+  z-index: 21
 
 
   #header-user-bar,
   #header-user-bar,
   #header-new-board-icon,
   #header-new-board-icon,
@@ -127,7 +127,7 @@
       &.current
       &.current
         color: darken(white, 5%)
         color: darken(white, 5%)
 
 
-      &:first-child .fa-home
+      &:first-child .fa-home,&:nth-child(3) .fa-globe
         margin-right: 5px
         margin-right: 5px
 
 
       a.js-create-board
       a.js-create-board
@@ -175,7 +175,7 @@
       .board-header-btn
       .board-header-btn
         height: 32px
         height: 32px
         line-height: @height
         line-height: @height
-        font-size: 16px
+        font-size: 15px
 
 
         i.fa
         i.fa
           line-height: 32px
           line-height: 32px

+ 10 - 4
client/components/main/layouts.jade

@@ -6,10 +6,16 @@ head
     where the application is deployed with a path prefix, but it seems to be
     where the application is deployed with a path prefix, but it seems to be
     difficult to do that cleanly with Blaze -- at least without adding extra
     difficult to do that cleanly with Blaze -- at least without adding extra
     packages.
     packages.
-  link(rel="shortcut icon" href="/wekan-favicon.png")
-  link(rel="apple-touch-icon" href="/wekan-favicon.png")
-  link(rel="mask-icon" href="/wekan-logo-150.svg")
-  link(rel="manifest" href="/wekan-manifest.json")
+  link(rel="shortcut icon" type="image/x-icon" href="/favicon.ico")
+  link(rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png")
+  link(rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png")
+  link(rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png")
+  link(rel="manifest" href="/site.webmanifest")
+  link(rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5")
+  meta(name="apple-mobile-web-app-title" content="Wekan")
+  meta(name="application-name" content="Wekan")
+  meta(name="msapplication-TileColor" content="#00aba9")
+  meta(name="theme-color" content="#ffffff")
 
 
 template(name="userFormsLayout")
 template(name="userFormsLayout")
   section.auth-layout
   section.auth-layout

+ 7 - 0
client/components/main/layouts.js

@@ -31,6 +31,11 @@ Template.userFormsLayout.onCreated(function() {
       return this.stop();
       return this.stop();
     },
     },
   });
   });
+  Meteor.call('isPasswordLoginDisabled', (_, result) => {
+    if (result) {
+      $('.at-pwd-form').hide();
+    }
+  });
 });
 });
 
 
 Template.userFormsLayout.onRendered(() => {
 Template.userFormsLayout.onRendered(() => {
@@ -73,6 +78,8 @@ Template.userFormsLayout.helpers({
         name = 'Igbo';
         name = 'Igbo';
       } else if (lang.name === 'oc') {
       } else if (lang.name === 'oc') {
         name = 'Occitan';
         name = 'Occitan';
+      } else if (lang.name === '繁体中文(台湾)') {
+        name = '繁體中文(台灣)';
       }
       }
       return { tag, name };
       return { tag, name };
     }).sort(function(a, b) {
     }).sort(function(a, b) {

+ 5 - 0
client/components/main/popup.styl

@@ -135,6 +135,10 @@ $popupWidth = 300px
   margin-bottom: 8px
   margin-bottom: 8px
 
 
 .pop-over-list
 .pop-over-list
+  li
+    display: block
+    clear: both
+
   li > a
   li > a
     clear: both
     clear: both
     cursor: pointer
     cursor: pointer
@@ -316,6 +320,7 @@ $popupWidth = 300px
         input[type="file"]
         input[type="file"]
           margin: 4px 0 12px
           margin: 4px 0 12px
           width: 100%
           width: 100%
+          box-sizing: border-box
 
 
     .pop-over-list
     .pop-over-list
       li > a
       li > a

+ 10 - 0
client/components/notifications/notification.jade

@@ -0,0 +1,10 @@
+template(name='notification')
+  li.notification(class="{{#if read}}read{{/if}}")
+    .read-status
+      .materialCheckBox(class="{{#if read}}is-checked{{/if}}")
+      +notificationIcon(activityData)
+    .details
+      +activity(activity=activityData mode='none')
+    if read
+      .remove
+        a.fa.fa-trash

+ 28 - 0
client/components/notifications/notification.js

@@ -0,0 +1,28 @@
+Template.notification.events({
+  'click .read-status .materialCheckBox'() {
+    const update = {};
+    update[`profile.notifications.${this.index}.read`] = this.read
+      ? null
+      : Date.now();
+    Users.update(Meteor.userId(), { $set: update });
+  },
+  'click .remove a'() {
+    Meteor.user().removeNotification(this.activityData._id);
+  },
+});
+
+Template.notification.helpers({
+  mode: 'board',
+  isOfActivityType(activityId, type) {
+    const activity = Activities.findOne(activityId);
+    return activity && activity.activityType === type;
+  },
+  activityType(activityId) {
+    const activity = Activities.findOne(activityId);
+    return activity ? activity.activityType : '';
+  },
+  activityUser(activityId) {
+    const activity = Activities.findOne(activityId);
+    return activity && activity.userId;
+  },
+});

+ 57 - 0
client/components/notifications/notification.styl

@@ -0,0 +1,57 @@
+#notifications-drawer
+  &.show-read .notification.read
+    display: flex
+
+  .notification
+    display: flex
+    float: none
+    padding: 12px 8px 8px
+    color: black
+    border-bottom: 1px solid #dbdbdb
+
+    &.read
+      display: none
+
+    .read-status
+      width: 30px
+
+      input
+        width: 24px
+        height: 24px
+
+      .activity-type
+        margin: 16px 0 0
+        width: 17px
+        height: 17px
+        font-size: 17px
+        display: block
+        color: #bbb
+
+    .details
+      width: calc(100% - 30px)
+
+      .activity
+        display: flex
+
+        .activity-desc
+          width: 100%;
+
+        .activity-comment
+          display: block
+          width: 100%
+          border-radius: 3px
+          background: #fff
+          text-decoration: none
+          box-shadow: 0 1px 2px rgba(0,0,0,0.2)
+          margin-top: 5px
+          padding: 5px
+
+        .activity-meta
+          display: block
+          font-size: 0.8em
+          color: #999
+          font-style: italic
+
+    .remove
+      a:hover
+        color #eb4646 !important

+ 53 - 0
client/components/notifications/notificationIcon.jade

@@ -0,0 +1,53 @@
+template(name='notificationIcon')
+  if($in activityType 'deleteAttachment' 'addAttachment')
+    i.fa.fa-paperclip.activity-type(title="attachment")
+  else if($in activityType 'createBoard' 'importBoard')
+    i.fa.fa-chalkboard.activity-type(title="board")
+
+  else if($in activityType 'createCard' 'importCard' 'moveCard')
+    +cardNotificationIcon
+  else if($in activityType 'moveCardBoard' 'archivedCard' 'restoredCard')
+    +cardNotificationIcon
+    //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it
+    //- DRY and consistant
+
+  else if($in activityType 'addChecklist' 'removedChecklist' 'completeChecklist')
+    +checklistNotificationIcon
+  else if($in activityType 'uncompleteChecklist')
+    +checklistNotificationIcon
+    //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it
+    //- DRY and consistant
+
+  else if($in activityType 'checkedItem' 'uncheckedItem' 'addChecklistItem' 'removedChecklistItem')
+    i.fa.fa-check-square.activity-type(title="checklist item")
+  else if($in activityType 'addComment')
+    i.fa.fa-comment-o.activity-type(title="comment")
+  else if($in activityType 'createCustomField' 'setCustomField' 'unsetCustomField')
+    i.fa.fa-code.activity-type(title="custom field")
+  else if($in activityType 'addedLabel' 'removedLabel')
+    i.fa.fa-tag.activity-type(title="label")
+
+  else if($in activityType 'createList' 'removeList' 'archivedList')
+    +listNotificationIcon
+  else if($in activityType  'importList')
+    +listNotificationIcon
+    //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it
+    //- DRY and consistant
+
+    //- elswhere in the app we use fa-trello to indicate lists...
+    //- i personally like fa-columns a bit better
+  else if($in activityType 'unjoinMember' 'addBoardMember' 'joinMember' 'removeBoardMember')
+    i.fa.fa-user.activity-type(title="member")
+  else if($in activityType 'createSwimlane' 'archivedSwimlane')
+    i.fa.fa-th-large.activity-type(title="swimlane")
+  else
+    i.fa.fa-bug.activity-type(title="can't find icon for #{activityType}")
+
+template(name='cardNotificationIcon')
+  i.fa.fa-clone.activity-type(title="card")
+
+template(name='checklistNotificationIcon')
+  i.fa.fa-list.activity-type(title="checklist")
+
+template(name='listNotificationIcon')
+  i.fa.fa-columns.activity-type(title="list")

+ 5 - 0
client/components/notifications/notifications.jade

@@ -0,0 +1,5 @@
+template(name='notifications')
+  #notifications.board-header-btns.right
+    a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}")
+    if $.Session.get 'showNotificationsDrawer'
+      +notificationsDrawer(unreadNotifications=unreadNotifications)

+ 32 - 0
client/components/notifications/notifications.js

@@ -0,0 +1,32 @@
+// this hides the notifications drawer if anyone clicks off of the panel
+Template.body.events({
+  click(event) {
+    if (
+      !$(event.target).is('#notifications *') &&
+      Session.get('showNotificationsDrawer')
+    ) {
+      toggleNotificationsDrawer();
+    }
+  },
+});
+
+Template.notifications.helpers({
+  unreadNotifications() {
+    const notifications = Users.findOne(Meteor.userId()).notifications();
+    const unreadNotifications = _.filter(notifications, v => !v.read);
+    return unreadNotifications.length;
+  },
+});
+
+Template.notifications.events({
+  'click .notifications-drawer-toggle'() {
+    toggleNotificationsDrawer();
+  },
+});
+
+export function toggleNotificationsDrawer() {
+  Session.set(
+    'showNotificationsDrawer',
+    !Session.get('showNotificationsDrawer'),
+  );
+}

+ 17 - 0
client/components/notifications/notifications.styl

@@ -0,0 +1,17 @@
+#notifications
+  position: relative
+
+  .notifications-drawer-toggle
+    display: block
+    line-height: 28px
+    color: #f2f2f2
+    margin: 0 10px
+    width: 28px
+    height: 28px
+    text-align: center
+    border: 0
+    padding: 0
+
+    &.alert
+      background-color: #eb4646;
+

+ 20 - 0
client/components/notifications/notificationsDrawer.jade

@@ -0,0 +1,20 @@
+template(name='notificationsDrawer')
+  section#notifications-drawer(class="{{#if $.Session.get 'showReadNotifications'}}show-read{{/if}}")
+    .header
+      if $.Session.get 'showReadNotifications'
+        a.toggle-read {{_ 'filter-by-unread'}}
+      else
+        a.toggle-read {{_ 'view-all'}}
+      h5 {{_ 'notifications'}}
+        if($gt unreadNotifications 0)
+          |(#{unreadNotifications})
+      a.fa.fa-times-thin.close
+    ul.notifications
+      each transformedProfile.notifications
+        +notification(activityData=activity index=dbIndex read=read)
+    if($gt unreadNotifications 0)
+      a.all-read {{_ 'mark-all-as-read'}}
+    if ($and ($.Session.get 'showReadNotifications') ($gt readNotifications 0))
+      a.remove-read
+        i.fa.fa-trash
+        | {{_ 'remove-all-read'}}

+ 53 - 0
client/components/notifications/notificationsDrawer.js

@@ -0,0 +1,53 @@
+import { toggleNotificationsDrawer } from './notifications.js';
+
+Template.notificationsDrawer.onCreated(function() {
+  Meteor.subscribe('notificationActivities');
+  Meteor.subscribe('notificationCards');
+  Meteor.subscribe('notificationUsers');
+  Meteor.subscribe('notificationsAttachments');
+  Meteor.subscribe('notificationChecklistItems');
+  Meteor.subscribe('notificationChecklists');
+  Meteor.subscribe('notificationComments');
+  Meteor.subscribe('notificationLists');
+  Meteor.subscribe('notificationSwimlanes');
+});
+
+Template.notificationsDrawer.helpers({
+  transformedProfile() {
+    return Users.findOne(Meteor.userId());
+  },
+  readNotifications() {
+    const readNotifications = _.filter(
+      Meteor.user().profile.notifications,
+      v => !!v.read,
+    );
+    return readNotifications.length;
+  },
+});
+
+Template.notificationsDrawer.events({
+  'click .all-read'() {
+    const notifications = Meteor.user().profile.notifications;
+    for (const index in notifications) {
+      if (notifications.hasOwnProperty(index) && !notifications[index].read) {
+        const update = {};
+        update[`profile.notifications.${index}.read`] = Date.now();
+        Users.update(Meteor.userId(), { $set: update });
+      }
+    }
+  },
+  'click .close'() {
+    toggleNotificationsDrawer();
+  },
+  'click .toggle-read'() {
+    Session.set('showReadNotifications', !Session.get('showReadNotifications'));
+  },
+  'click .remove-read'() {
+    const user = Meteor.user();
+    for (const notification of user.profile.notifications) {
+      if (notification.read) {
+        user.removeNotification(notification.activity);
+      }
+    }
+  },
+});

+ 69 - 0
client/components/notifications/notificationsDrawer.styl

@@ -0,0 +1,69 @@
+belize = #2980b9
+
+section#notifications-drawer
+  position: fixed
+  top: 28px
+  right: 0
+  width: 400px
+  background-color: #fafafa
+  box-shadow: 0 1px 2px rgba(0,0,0,0.15)
+  border-radius: 2px
+  max-height: calc(100vh - 28px - 36px)
+  color: black
+  padding-top 36px
+
+  a:hover
+    color: belize !important
+
+  .header
+    position: fixed
+    top 28px
+    right 0
+    width calc(400px - 32px)
+    padding: 8px 16px
+    background: #ededed
+    border-bottom: 1px solid #dbdbdb
+    z-index 2
+
+    .toggle-read
+      position absolute
+      left 16px
+      top calc(50% - 8px)
+      color belize
+
+    h5
+      text-align: center
+      margin: 0
+
+    .close
+      position: absolute
+      top: calc(50% - 12px)
+      right: 12px
+      font-size: 24px
+      height: 24px
+      line-height: 24px
+      opacity 1
+
+  .all-read,
+  .remove-read
+    color belize
+    background-color: #fafafa
+    margin 8px 16px 12px
+    display inline-block
+
+  .remove-read
+    float right
+
+    &:hover
+      color #eb4646 !important
+
+      i.fa
+        color inherit
+
+
+  ul.notifications
+    display: block
+    padding: 0px 16px
+    margin: 0
+    height: calc(100vh - 102px)
+    overflow-y: scroll

+ 1 - 1
client/components/rules/actions/boardActions.js

@@ -11,7 +11,7 @@ BlazeComponent.extendComponent({
         },
         },
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
     return boards;
     return boards;

+ 53 - 1
client/components/settings/peopleBody.jade

@@ -40,9 +40,15 @@ template(name="peopleGeneral")
         th {{_ 'active'}}
         th {{_ 'active'}}
         th {{_ 'authentication-method'}}
         th {{_ 'authentication-method'}}
         th
         th
+          +newUserRow
       each user in peopleList
       each user in peopleList
         +peopleRow(userId=user._id)
         +peopleRow(userId=user._id)
 
 
+template(name="newUserRow")
+  a.new-user
+    i.fa.fa-edit
+    | {{_ 'new'}}
+
 template(name="peopleRow")
 template(name="peopleRow")
   tr
   tr
     if userData.loginDisabled
     if userData.loginDisabled
@@ -104,7 +110,7 @@ template(name="editUserPopup")
     label.hide.userId(type="text" value=user._id)
     label.hide.userId(type="text" value=user._id)
     label
     label
       | {{_ 'fullname'}}
       | {{_ 'fullname'}}
-      input.js-profile-fullname(type="text" value=user.profile.fullname autofocus)
+      input.js-profile-fullname(type="text" value=user.profile.fullname)
     label
     label
       | {{_ 'username'}}
       | {{_ 'username'}}
       span.error.hide.username-taken
       span.error.hide.username-taken
@@ -148,3 +154,49 @@ template(name="editUserPopup")
     //  div
     //  div
     //  input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
     //  input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
 
 
+template(name="newUserPopup")
+  form
+    //label.hide.userId(type="text" value=user._id)
+    label
+      | {{_ 'fullname'}}
+      input.js-profile-fullname(type="text" value="")
+    label
+      | {{_ 'username'}}
+      span.error.hide.username-taken
+        | {{_ 'error-username-taken'}}
+      //if isLdap
+      //  input.js-profile-username(type="text" value=user.username readonly)
+      //else
+      input.js-profile-username(type="text" value="")
+    label
+      | {{_ 'email'}}
+      span.error.hide.email-taken
+        | {{_ 'error-email-taken'}}
+      //if isLdap
+      //  input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly)
+      //else
+      input.js-profile-email(type="email" value="")
+    label
+      | {{_ 'admin'}}
+      select.select-role.js-profile-isadmin
+        option(value="false" selected="selected") {{_ 'no'}}
+        option(value="true") {{_ 'yes'}}
+    label
+      | {{_ 'active'}}
+      select.select-active.js-profile-isactive
+        option(value="false" selected="selected") {{_ 'yes'}}
+        option(value="true") {{_ 'no'}}
+    label
+      | {{_ 'authentication-type'}}
+      select.select-authenticationMethod.js-authenticationMethod
+        each authentications
+          if isSelected value
+            option(value="{{value}}" selected) {{_ value}}
+          else
+            option(value="{{value}}") {{_ value}}
+    hr
+    label
+      | {{_ 'password'}}
+      input.js-profile-password(type="password")
+    div.buttonsContainer
+      input.primary.wide(type="submit" value="{{_ 'save'}}")

+ 95 - 0
client/components/settings/peopleBody.js

@@ -39,6 +39,9 @@ BlazeComponent.extendComponent({
             this.filterPeople();
             this.filterPeople();
           }
           }
         },
         },
+        'click #newUserButton'() {
+          Popup.open('newUser');
+        },
       },
       },
     ];
     ];
   },
   },
@@ -141,6 +144,47 @@ Template.editUserPopup.helpers({
   },
   },
 });
 });
 
 
+Template.newUserPopup.onCreated(function() {
+  this.authenticationMethods = new ReactiveVar([]);
+  this.errorMessage = new ReactiveVar('');
+
+  Meteor.call('getAuthenticationsEnabled', (_, result) => {
+    if (result) {
+      // TODO : add a management of different languages
+      // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
+      this.authenticationMethods.set([
+        { value: 'password' },
+        // Gets only the authentication methods availables
+        ...Object.entries(result)
+          .filter(e => e[1])
+          .map(e => ({ value: e[0] })),
+      ]);
+    }
+  });
+});
+
+Template.newUserPopup.helpers({
+  //user() {
+  //  return Users.findOne(this.userId);
+  //},
+  authentications() {
+    return Template.instance().authenticationMethods.get();
+  },
+  //isSelected(match) {
+  //  const userId = Template.instance().data.userId;
+  //  const selected = Users.findOne(userId).authenticationMethod;
+  //  return selected === match;
+  //},
+  //isLdap() {
+  //  const userId = Template.instance().data.userId;
+  //  const selected = Users.findOne(userId).authenticationMethod;
+  //  return selected === 'ldap';
+  //},
+  errorMessage() {
+    return Template.instance().errorMessage.get();
+  },
+});
+
 BlazeComponent.extendComponent({
 BlazeComponent.extendComponent({
   onCreated() {},
   onCreated() {},
   user() {
   user() {
@@ -155,6 +199,16 @@ BlazeComponent.extendComponent({
   },
   },
 }).register('peopleRow');
 }).register('peopleRow');
 
 
+BlazeComponent.extendComponent({
+  events() {
+    return [
+      {
+        'click a.new-user': Popup.open('newUser'),
+      },
+    ];
+  },
+}).register('newUserRow');
+
 Template.editUserPopup.events({
 Template.editUserPopup.events({
   submit(event, templateInstance) {
   submit(event, templateInstance) {
     event.preventDefault();
     event.preventDefault();
@@ -248,3 +302,44 @@ Template.editUserPopup.events({
     Popup.close();
     Popup.close();
   }),
   }),
 });
 });
+
+Template.newUserPopup.events({
+  submit(event, templateInstance) {
+    event.preventDefault();
+    const fullname = templateInstance.find('.js-profile-fullname').value.trim();
+    const username = templateInstance.find('.js-profile-username').value.trim();
+    const password = templateInstance.find('.js-profile-password').value;
+    const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
+    const isActive = templateInstance.find('.js-profile-isactive').value.trim();
+    const email = templateInstance.find('.js-profile-email').value.trim();
+
+    Meteor.call(
+      'setCreateUser',
+      fullname,
+      username,
+      password,
+      isAdmin,
+      isActive,
+      email.toLowerCase(),
+      function(error) {
+        const usernameMessageElement = templateInstance.$('.username-taken');
+        const emailMessageElement = templateInstance.$('.email-taken');
+        if (error) {
+          const errorElement = error.error;
+          if (errorElement === 'username-already-taken') {
+            usernameMessageElement.show();
+            emailMessageElement.hide();
+          } else if (errorElement === 'email-already-taken') {
+            usernameMessageElement.hide();
+            emailMessageElement.show();
+          }
+        } else {
+          usernameMessageElement.hide();
+          emailMessageElement.hide();
+          Popup.close();
+        }
+      },
+    );
+    Popup.close();
+  },
+});

+ 1 - 1
client/components/settings/settingBody.js

@@ -48,7 +48,7 @@ BlazeComponent.extendComponent({
         'members.isAdmin': true,
         'members.isAdmin': true,
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
   },
   },

+ 21 - 12
client/components/sidebar/sidebar.jade

@@ -245,7 +245,7 @@ template(name="outgoingWebhooksPopup")
         b &nbsp;
         b &nbsp;
         .materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
         .materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
       input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title)
       input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title)
-      input.js-outgoing-webhooks-url(type="text" name="url" value=url autofocus)
+      input.js-outgoing-webhooks-url(type="text" name="url" value=url)
       input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token")
       input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token")
       select.js-outgoing-webhooks-type(name="type")
       select.js-outgoing-webhooks-type(name="type")
           each _type in types
           each _type in types
@@ -257,7 +257,7 @@ template(name="outgoingWebhooksPopup")
       input(type="hidden" value=_id name="id")
       input(type="hidden" value=_id name="id")
       input.primary.wide(type="submit" value="{{_ 'save'}}")
       input.primary.wide(type="submit" value="{{_ 'save'}}")
   form.integration-form
   form.integration-form
-    input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" autofocus)
+    input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title")
     input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url")
     input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url")
     input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token")
     input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token")
     select.js-outgoing-webhooks-type(name="type")
     select.js-outgoing-webhooks-type(name="type")
@@ -267,7 +267,14 @@ template(name="outgoingWebhooksPopup")
 
 
 template(name="boardMenuPopup")
 template(name="boardMenuPopup")
   ul.pop-over-list
   ul.pop-over-list
-    li: a.js-custom-fields {{_ 'custom-fields'}}
+    li
+      a.js-open-rules-view(title="{{_ 'rules'}}")
+        i.fa.fa-magic
+        | {{_ 'rules'}}
+    li
+      a.js-custom-fields
+        i.fa.fa-list-alt
+        | {{_ 'custom-fields'}}
     li
     li
       a.js-open-archives
       a.js-open-archives
         i.fa.fa-archive
         i.fa.fa-archive
@@ -291,10 +298,11 @@ template(name="boardMenuPopup")
     if currentUser.isBoardAdmin
     if currentUser.isBoardAdmin
       hr
       hr
       ul.pop-over-list
       ul.pop-over-list
-        li
-          a(href="{{exportUrl}}", download="{{exportFilename}}")
-            i.fa.fa-share-alt
-            | {{_ 'export-board'}}
+        if withApi
+          li
+            a(href="{{exportUrl}}", download="{{exportFilename}}")
+              i.fa.fa-share-alt
+              | {{_ 'export-board'}}
         li
         li
           a.js-outgoing-webhooks
           a.js-outgoing-webhooks
             i.fa.fa-globe
             i.fa.fa-globe
@@ -319,11 +327,12 @@ template(name="boardMenuPopup")
   if isSandstorm
   if isSandstorm
     hr
     hr
     ul.pop-over-list
     ul.pop-over-list
-      li
-        a(href="{{exportUrl}}", download="{{exportFilename}}")
-          i.fa.fa-share-alt
-          i.fa.fa-sign-out
-          | {{_ 'export-board'}}
+      if withApi
+        li
+          a(href="{{exportUrl}}", download="{{exportFilename}}")
+            i.fa.fa-share-alt
+            i.fa.fa-sign-out
+            | {{_ 'export-board'}}
       li
       li
         a.js-import-board
         a.js-import-board
           i.fa.fa-share-alt
           i.fa.fa-share-alt

+ 16 - 2
client/components/sidebar/sidebar.js

@@ -182,6 +182,10 @@ Template.memberPopup.helpers({
 
 
 Template.boardMenuPopup.events({
 Template.boardMenuPopup.events({
   'click .js-rename-board': Popup.open('boardChangeTitle'),
   'click .js-rename-board': Popup.open('boardChangeTitle'),
+  'click .js-open-rules-view'() {
+    Modal.openWide('rulesMain');
+    Popup.close();
+  },
   'click .js-custom-fields'() {
   'click .js-custom-fields'() {
     Sidebar.setView('customFields');
     Sidebar.setView('customFields');
     Popup.close();
     Popup.close();
@@ -211,7 +215,17 @@ Template.boardMenuPopup.events({
   'click .js-card-settings': Popup.open('boardCardSettings'),
   'click .js-card-settings': Popup.open('boardCardSettings'),
 });
 });
 
 
+Template.boardMenuPopup.onCreated(function() {
+  this.apiEnabled = new ReactiveVar(false);
+  Meteor.call('_isApiEnabled', (e, result) => {
+    this.apiEnabled.set(result);
+  });
+});
+
 Template.boardMenuPopup.helpers({
 Template.boardMenuPopup.helpers({
+  withApi() {
+    return Template.instance().apiEnabled.get();
+  },
   exportUrl() {
   exportUrl() {
     const params = {
     const params = {
       boardId: Session.get('currentBoard'),
       boardId: Session.get('currentBoard'),
@@ -495,7 +509,7 @@ BlazeComponent.extendComponent({
         'members.userId': Meteor.userId(),
         'members.userId': Meteor.userId(),
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
   },
   },
@@ -673,7 +687,7 @@ BlazeComponent.extendComponent({
         'members.userId': Meteor.userId(),
         'members.userId': Meteor.userId(),
       },
       },
       {
       {
-        sort: ['title'],
+        sort: { sort: 1 /* boards default sorting */ },
       },
       },
     );
     );
   },
   },

+ 18 - 0
client/components/sidebar/sidebarFilters.jade

@@ -45,6 +45,24 @@ template(name="filterSidebar")
             if Filter.members.isSelected _id
             if Filter.members.isSelected _id
               i.fa.fa-check
               i.fa.fa-check
   hr
   hr
+  ul.sidebar-list
+    li(class="{{#if Filter.assignees.isSelected undefined}}active{{/if}}")
+      a.name.js-toggle-assignee-filter
+        span.sidebar-list-item-description
+          | {{_ 'filter-no-assignee'}}
+        if Filter.assignees.isSelected undefined
+          i.fa.fa-check
+    each currentBoard.activeMembers
+      with getUser userId
+        li(class="{{#if Filter.assignees.isSelected _id}}active{{/if}}")
+          a.name.js-toggle-assignee-filter
+            +userAvatar(userId=this._id)
+            span.sidebar-list-item-description
+              = profile.fullname
+              | (<span class="username">{{ username }}</span>)
+            if Filter.assignees.isSelected _id
+              i.fa.fa-check
+  hr
   ul.sidebar-list
   ul.sidebar-list
     li(class="{{#if Filter.customFields.isSelected undefined}}active{{/if}}")
     li(class="{{#if Filter.customFields.isSelected undefined}}active{{/if}}")
           a.name.js-toggle-custom-fields-filter
           a.name.js-toggle-custom-fields-filter

+ 5 - 0
client/components/sidebar/sidebarFilters.js

@@ -18,6 +18,11 @@ BlazeComponent.extendComponent({
           Filter.members.toggle(this.currentData()._id);
           Filter.members.toggle(this.currentData()._id);
           Filter.resetExceptions();
           Filter.resetExceptions();
         },
         },
+        'click .js-toggle-assignee-filter'(evt) {
+          evt.preventDefault();
+          Filter.assignees.toggle(this.currentData()._id);
+          Filter.resetExceptions();
+        },
         'click .js-toggle-archive-filter'(evt) {
         'click .js-toggle-archive-filter'(evt) {
           evt.preventDefault();
           evt.preventDefault();
           Filter.archive.toggle(this.currentData()._id);
           Filter.archive.toggle(this.currentData()._id);

+ 6 - 32
client/components/swimlanes/swimlanes.js

@@ -1,6 +1,6 @@
 import { Cookies } from 'meteor/ostrio:cookies';
 import { Cookies } from 'meteor/ostrio:cookies';
 const cookies = new Cookies();
 const cookies = new Cookies();
-const { calculateIndex, enableClickOnTouch } = Utils;
+const { calculateIndex } = Utils;
 
 
 function currentListIsInThisSwimlane(swimlaneId) {
 function currentListIsInThisSwimlane(swimlaneId) {
   const currentList = Lists.findOne(Session.get('currentList'));
   const currentList = Lists.findOne(Session.get('currentList'));
@@ -87,9 +87,6 @@ function initSortable(boardComponent, $listsDom) {
     },
     },
   });
   });
 
 
-  // ugly touch event hotfix
-  enableClickOnTouch('.js-list:not(.js-list-composer)');
-
   function userIsMember() {
   function userIsMember() {
     return (
     return (
       Meteor.user() &&
       Meteor.user() &&
@@ -111,7 +108,7 @@ function initSortable(boardComponent, $listsDom) {
       showDesktopDragHandles = false;
       showDesktopDragHandles = false;
     }
     }
 
 
-    if (!Utils.isMiniScreen() && showDesktopDragHandles) {
+    if (Utils.isMiniScreen() || showDesktopDragHandles) {
       $listsDom.sortable({
       $listsDom.sortable({
         handle: '.js-list-handle',
         handle: '.js-list-handle',
       });
       });
@@ -122,34 +119,12 @@ function initSortable(boardComponent, $listsDom) {
     }
     }
 
 
     const $listDom = $listsDom;
     const $listDom = $listsDom;
-    if ($listDom.data('sortable')) {
-      $listsDom.sortable(
-        'option',
-        'disabled',
-        // Disable drag-dropping when user is not member/is worker/is miniscreen
-        !userIsMember(),
-        // Not disable drag-dropping while in multi-selection mode
-        // MultiSelection.isActive() || !userIsMember(),
-      );
-    }
-
-    if ($listDom.data('sortable')) {
-      $listsDom.sortable(
-        'option',
-        'disabled',
-        // Disable drag-dropping when user is not member/is worker/is miniscreen
-        Meteor.user().isWorker(),
-        // Not disable drag-dropping while in multi-selection mode
-        // MultiSelection.isActive() || !userIsMember(),
-      );
-    }
-
-    if ($listDom.data('sortable')) {
+    if ($listDom.data('uiSortable') || $listDom.data('sortable')) {
       $listsDom.sortable(
       $listsDom.sortable(
         'option',
         'option',
         'disabled',
         'disabled',
-        // Disable drag-dropping when user is not member/is worker/is miniscreen
-        Utils.isMiniScreen(),
+        // Disable drag-dropping when user is not member/is worker
+        !userIsMember() || Meteor.user().isWorker(),
         // Not disable drag-dropping while in multi-selection mode
         // Not disable drag-dropping while in multi-selection mode
         // MultiSelection.isActive() || !userIsMember(),
         // MultiSelection.isActive() || !userIsMember(),
       );
       );
@@ -210,8 +185,7 @@ BlazeComponent.extendComponent({
           }
           }
 
 
           const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
           const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
-            Utils.isMiniScreen() ||
-              (!Utils.isMiniScreen() && showDesktopDragHandles)
+            Utils.isMiniScreen() || showDesktopDragHandles
               ? ['.js-list-handle', '.js-swimlane-header-handle']
               ? ['.js-list-handle', '.js-swimlane-header-handle']
               : ['.js-list-header'],
               : ['.js-list-header'],
           );
           );

+ 17 - 8
client/components/users/userHeader.jade

@@ -98,12 +98,12 @@ template(name="changeLanguagePopup")
 
 
 template(name="changeSettingsPopup")
 template(name="changeSettingsPopup")
   ul.pop-over-list
   ul.pop-over-list
-    li
-      a.js-toggle-system-messages
-        i.fa.fa-comments-o
-        | {{_ 'hide-system-messages'}}
-        if hiddenSystemMessages
-          i.fa.fa-check
+    //li
+    //  a.js-toggle-system-messages
+    //    i.fa.fa-comments-o
+    //    | {{_ 'hide-system-messages'}}
+    //    if hiddenSystemMessages
+    //      i.fa.fa-check
     li
     li
       a.js-toggle-desktop-drag-handles
       a.js-toggle-desktop-drag-handles
         i.fa.fa-arrows
         i.fa.fa-arrows
@@ -112,11 +112,20 @@ template(name="changeSettingsPopup")
           i.fa.fa-check
           i.fa.fa-check
     unless currentUser.isWorker
     unless currentUser.isWorker
       li
       li
-        label.bold
+        label.bold.clear
           i.fa.fa-sort-numeric-asc
           i.fa.fa-sort-numeric-asc
           | {{_ 'show-cards-minimum-count'}}
           | {{_ 'show-cards-minimum-count'}}
         input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
         input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
-        input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
+        label.bold.clear
+          i.fa.fa-calendar
+          | {{_ 'start-day-of-week'}}
+        select#start-day-of-week.inline-input.left
+          each day in weekDays startDayOfWeek
+            if day.isSelected
+              option(selected="true", value="#{day.value}") #{day.name}
+            else
+              option(value="#{day.value}") #{day.name}
+        input.js-apply-user-settings.left(type="submit" value="{{_ 'apply'}}")
 
 
 template(name="userDeletePopup")
 template(name="userDeletePopup")
   unless currentUser.isWorker
   unless currentUser.isWorker

+ 37 - 3
client/components/users/userHeader.js

@@ -166,6 +166,8 @@ Template.changeLanguagePopup.helpers({
         name = 'Igbo';
         name = 'Igbo';
       } else if (lang.name === 'oc') {
       } else if (lang.name === 'oc') {
         name = 'Occitan';
         name = 'Occitan';
+      } else if (lang.name === '繁体中文(台湾)') {
+        name = '繁體中文(台灣)';
       }
       }
       return { tag, name };
       return { tag, name };
     }).sort(function(a, b) {
     }).sort(function(a, b) {
@@ -222,6 +224,27 @@ Template.changeSettingsPopup.helpers({
       return cookies.get('limitToShowCardsCount');
       return cookies.get('limitToShowCardsCount');
     }
     }
   },
   },
+  weekDays(startDay) {
+    return [
+      TAPi18n.__('sunday'),
+      TAPi18n.__('monday'),
+      TAPi18n.__('tuesday'),
+      TAPi18n.__('wednesday'),
+      TAPi18n.__('thursday'),
+      TAPi18n.__('friday'),
+      TAPi18n.__('saturday'),
+    ].map(function(day, index) {
+      return { name: day, value: index, isSelected: index === startDay };
+    });
+  },
+  startDayOfWeek() {
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return currentUser.getStartDayOfWeek();
+    } else {
+      return cookies.get('startDayOfWeek');
+    }
+  },
 });
 });
 
 
 Template.changeSettingsPopup.events({
 Template.changeSettingsPopup.events({
@@ -245,20 +268,31 @@ Template.changeSettingsPopup.events({
       cookies.set('hasHiddenSystemMessages', 'true');
       cookies.set('hasHiddenSystemMessages', 'true');
     }
     }
   },
   },
-  'click .js-apply-show-cards-at'(event, templateInstance) {
+  'click .js-apply-user-settings'(event, templateInstance) {
     event.preventDefault();
     event.preventDefault();
     const minLimit = parseInt(
     const minLimit = parseInt(
       templateInstance.$('#show-cards-count-at').val(),
       templateInstance.$('#show-cards-count-at').val(),
       10,
       10,
     );
     );
+    const startDay = parseInt(
+      templateInstance.$('#start-day-of-week').val(),
+      10,
+    );
+    const currentUser = Meteor.user();
     if (!isNaN(minLimit)) {
     if (!isNaN(minLimit)) {
-      currentUser = Meteor.user();
       if (currentUser) {
       if (currentUser) {
         Meteor.call('changeLimitToShowCardsCount', minLimit);
         Meteor.call('changeLimitToShowCardsCount', minLimit);
       } else {
       } else {
         cookies.set('limitToShowCardsCount', minLimit);
         cookies.set('limitToShowCardsCount', minLimit);
       }
       }
-      Popup.back();
     }
     }
+    if (!isNaN(startDay)) {
+      if (currentUser) {
+        Meteor.call('changeStartDayOfWeek', startDay);
+      } else {
+        cookies.set('startDayOfWeek', startDay);
+      }
+    }
+    Popup.back();
   },
   },
 });
 });

+ 10 - 0
client/lib/datepicker.js

@@ -10,12 +10,22 @@ DatePicker = BlazeComponent.extendComponent({
     this.defaultTime = defaultTime;
     this.defaultTime = defaultTime;
   },
   },
 
 
+  startDayOfWeek() {
+    const currentUser = Meteor.user();
+    if (currentUser) {
+      return currentUser.getStartDayOfWeek();
+    } else {
+      return 1;
+    }
+  },
+
   onRendered() {
   onRendered() {
     const $picker = this.$('.js-datepicker')
     const $picker = this.$('.js-datepicker')
       .datepicker({
       .datepicker({
         todayHighlight: true,
         todayHighlight: true,
         todayBtn: 'linked',
         todayBtn: 'linked',
         language: TAPi18n.getLanguage(),
         language: TAPi18n.getLanguage(),
+        weekStart: this.startDayOfWeek(),
       })
       })
       .on(
       .on(
         'changeDate',
         'changeDate',

+ 9 - 1
client/lib/filter.js

@@ -459,13 +459,21 @@ Filter = {
   // before changing the schema.
   // before changing the schema.
   labelIds: new SetFilter(),
   labelIds: new SetFilter(),
   members: new SetFilter(),
   members: new SetFilter(),
+  assignees: new SetFilter(),
   archive: new SetFilter(),
   archive: new SetFilter(),
   hideEmpty: new SetFilter(),
   hideEmpty: new SetFilter(),
   customFields: new SetFilter('_id'),
   customFields: new SetFilter('_id'),
   advanced: new AdvancedFilter(),
   advanced: new AdvancedFilter(),
   lists: new AdvancedFilter(), // we need the ability to filter list by name as well
   lists: new AdvancedFilter(), // we need the ability to filter list by name as well
 
 
-  _fields: ['labelIds', 'members', 'archive', 'hideEmpty', 'customFields'],
+  _fields: [
+    'labelIds',
+    'members',
+    'assignees',
+    'archive',
+    'hideEmpty',
+    'customFields',
+  ],
 
 
   // We don't filter cards that have been added after the last filter change. To
   // We don't filter cards that have been added after the last filter change. To
   // implement this we keep the id of these cards in this `_exceptions` fields
   // implement this we keep the id of these cards in this `_exceptions` fields

+ 21 - 11
client/lib/keyboard.js

@@ -1,6 +1,16 @@
 // XXX There is no reason to define these shortcuts globally, they should be
 // XXX There is no reason to define these shortcuts globally, they should be
 // attached to a template (most of them will go in the `board` template).
 // attached to a template (most of them will go in the `board` template).
 
 
+function getHoveredCardId() {
+  const card = $('.js-minicard:hover').get(0);
+  if (!card) return null;
+  return Blaze.getData(card)._id;
+}
+
+function getSelectedCardId() {
+  return Session.get('selectedCard') || getHoveredCardId();
+}
+
 Mousetrap.bind('?', () => {
 Mousetrap.bind('?', () => {
   FlowRouter.go('shortcuts');
   FlowRouter.go('shortcuts');
 });
 });
@@ -50,9 +60,9 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
   }
   }
 });
 });
 
 
-// XXX This shortcut should also work when hovering over a card in board view
 Mousetrap.bind('space', evt => {
 Mousetrap.bind('space', evt => {
-  if (!Session.get('currentCard')) {
+  const cardId = getSelectedCardId();
+  if (!cardId) {
     return;
     return;
   }
   }
 
 
@@ -62,7 +72,7 @@ Mousetrap.bind('space', evt => {
   }
   }
 
 
   if (Meteor.user().isBoardMember()) {
   if (Meteor.user().isBoardMember()) {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Cards.findOne(cardId);
     card.toggleMember(currentUserId);
     card.toggleMember(currentUserId);
     // We should prevent scrolling in card when spacebar is clicked
     // We should prevent scrolling in card when spacebar is clicked
     // This should do it according to Mousetrap docs, but it doesn't
     // This should do it according to Mousetrap docs, but it doesn't
@@ -70,9 +80,9 @@ Mousetrap.bind('space', evt => {
   }
   }
 });
 });
 
 
-// XXX This shortcut should also work when hovering over a card in board view
 Mousetrap.bind('c', evt => {
 Mousetrap.bind('c', evt => {
-  if (!Session.get('currentCard')) {
+  const cardId = getSelectedCardId();
+  if (!cardId) {
     return;
     return;
   }
   }
 
 
@@ -86,7 +96,7 @@ Mousetrap.bind('c', evt => {
     !Meteor.user().isCommentOnly() &&
     !Meteor.user().isCommentOnly() &&
     !Meteor.user().isWorker()
     !Meteor.user().isWorker()
   ) {
   ) {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Cards.findOne(cardId);
     card.archive();
     card.archive();
     // We should prevent scrolling in card when spacebar is clicked
     // We should prevent scrolling in card when spacebar is clicked
     // This should do it according to Mousetrap docs, but it doesn't
     // This should do it according to Mousetrap docs, but it doesn't
@@ -97,19 +107,19 @@ Mousetrap.bind('c', evt => {
 Template.keyboardShortcuts.helpers({
 Template.keyboardShortcuts.helpers({
   mapping: [
   mapping: [
     {
     {
-      keys: ['W'],
+      keys: ['w'],
       action: 'shortcut-toggle-sidebar',
       action: 'shortcut-toggle-sidebar',
     },
     },
     {
     {
-      keys: ['Q'],
+      keys: ['q'],
       action: 'shortcut-filter-my-cards',
       action: 'shortcut-filter-my-cards',
     },
     },
     {
     {
-      keys: ['F'],
+      keys: ['f'],
       action: 'shortcut-toggle-filterbar',
       action: 'shortcut-toggle-filterbar',
     },
     },
     {
     {
-      keys: ['X'],
+      keys: ['x'],
       action: 'shortcut-clear-filters',
       action: 'shortcut-clear-filters',
     },
     },
     {
     {
@@ -129,7 +139,7 @@ Template.keyboardShortcuts.helpers({
       action: 'shortcut-assign-self',
       action: 'shortcut-assign-self',
     },
     },
     {
     {
-      keys: ['C'],
+      keys: ['c'],
       action: 'archive-card',
       action: 'archive-card',
     },
     },
   ],
   ],

+ 21 - 0
config/router.js

@@ -26,6 +26,27 @@ FlowRouter.route('/', {
   },
   },
 });
 });
 
 
+FlowRouter.route('/public', {
+  name: 'public',
+  triggersEnter: [AccountsTemplates.ensureSignedIn],
+  action() {
+    Session.set('currentBoard', null);
+    Session.set('currentList', null);
+    Session.set('currentCard', null);
+
+    Filter.reset();
+    EscapeActions.executeAll();
+
+    Utils.manageCustomUI();
+    Utils.manageMatomo();
+
+    BlazeLayout.render('defaultLayout', {
+      headerBar: 'boardListHeaderBar',
+      content: 'boardList',
+    });
+  },
+});
+
 FlowRouter.route('/b/:id/:slug', {
 FlowRouter.route('/b/:id/:slug', {
   name: 'board',
   name: 'board',
   action(params) {
   action(params) {

+ 20 - 3
docker-compose.yml

@@ -38,7 +38,7 @@ version: '2'
 #      sudo service docker start
 #      sudo service docker start
 # ----------------------------------------------------------------------------------
 # ----------------------------------------------------------------------------------
 # ==== USAGE OF THIS docker-compose.yml ====
 # ==== USAGE OF THIS docker-compose.yml ====
-# 1) For seeing does Wekan work, try this and check with your webbroser:
+# 1) For seeing does Wekan work, try this and check with your web browser:
 #      docker-compose up
 #      docker-compose up
 # 2) Stop Wekan and start Wekan in background:
 # 2) Stop Wekan and start Wekan in background:
 #     docker-compose stop
 #     docker-compose stop
@@ -93,7 +93,7 @@ services:
     #-------------------------------------------------------------------------------------
     #-------------------------------------------------------------------------------------
     # ==== MONGODB AND METEOR VERSION ====
     # ==== MONGODB AND METEOR VERSION ====
     # a) For Wekan Meteor 1.8.x version at master branch, use mongo 4.x
     # a) For Wekan Meteor 1.8.x version at master branch, use mongo 4.x
-    image: mongo:4.2.2
+    image: mongo:latest
     # b) For Wekan Meteor 1.6.x version at devel branch.
     # b) For Wekan Meteor 1.6.x version at devel branch.
     # Only for Snap and Sandstorm while they are not upgraded yet to Meteor 1.8.x
     # Only for Snap and Sandstorm while they are not upgraded yet to Meteor 1.8.x
     #image: mongo:3.2.21
     #image: mongo:3.2.21
@@ -240,6 +240,11 @@ services:
       # https://github.com/wekan/wekan/pull/2560
       # https://github.com/wekan/wekan/pull/2560
       - RICHER_CARD_COMMENT_EDITOR=false
       - RICHER_CARD_COMMENT_EDITOR=false
       #---------------------------------------------------------------
       #---------------------------------------------------------------
+      # ==== MOUSE SCROLL ====
+      # https://github.com/wekan/wekan/issues/2949
+      - SCROLLINERTIA=0
+      - SCROLLAMOUNT=auto
+      #---------------------------------------------------------------
       # ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
       # ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
       # https://github.com/wekan/wekan/issues/2518
       # https://github.com/wekan/wekan/issues/2518
       - CARD_OPENED_WEBHOOK_ENABLED=false
       - CARD_OPENED_WEBHOOK_ENABLED=false
@@ -249,6 +254,11 @@ services:
       #-MAX_IMAGE_PIXEL=1024
       #-MAX_IMAGE_PIXEL=1024
       #-IMAGE_COMPRESS_RATIO=80
       #-IMAGE_COMPRESS_RATIO=80
       #---------------------------------------------------------------
       #---------------------------------------------------------------
+      # ==== NOTIFICATION TRAY AFTER READ DAYS BEFORE REMOVE =====
+      # Number of days after a notification is read before we remove it.
+      # Default: 2
+      #- NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE=2
+      #---------------------------------------------------------------
       # ==== BIGEVENTS DUE ETC NOTIFICATIONS =====
       # ==== BIGEVENTS DUE ETC NOTIFICATIONS =====
       # https://github.com/wekan/wekan/pull/2541
       # https://github.com/wekan/wekan/pull/2541
       # Introduced a system env var BIGEVENTS_PATTERN default as "NONE",
       # Introduced a system env var BIGEVENTS_PATTERN default as "NONE",
@@ -504,18 +514,22 @@ services:
       # The limit number of entries (0=unlimited)
       # The limit number of entries (0=unlimited)
       #- LDAP_SEARCH_SIZE_LIMIT=0
       #- LDAP_SEARCH_SIZE_LIMIT=0
       #
       #
-      # Enable group filtering
+      # Enable group filtering. Note the authenticated ldap user must be able to query all relevant group data with own login data from ldap.
       #- LDAP_GROUP_FILTER_ENABLE=false
       #- LDAP_GROUP_FILTER_ENABLE=false
       #
       #
       # The object class for filtering. Example: group
       # The object class for filtering. Example: group
       #- LDAP_GROUP_FILTER_OBJECTCLASS=
       #- LDAP_GROUP_FILTER_OBJECTCLASS=
       #
       #
+      # The attribute of a group identifying it. Example: cn
       #- LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=
       #- LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=
       #
       #
+      # The attribute inside a group object listing its members. Example: member
       #- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
       #- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
       #
       #
+      # The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn ist saved as value into the attribute.
       #- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
       #- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
       #
       #
+      # The group name (id) that matches all users.
       #- LDAP_GROUP_FILTER_GROUP_NAME=
       #- LDAP_GROUP_FILTER_GROUP_NAME=
       #
       #
       # LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier). Example: guid
       # LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier). Example: guid
@@ -584,6 +598,9 @@ services:
       # example : LOGOUT_ON_MINUTES=55
       # example : LOGOUT_ON_MINUTES=55
       #- LOGOUT_ON_MINUTES=
       #- LOGOUT_ON_MINUTES=
       #-------------------------------------------------------------------
       #-------------------------------------------------------------------
+      # Hide password login form 
+      # - PASSWORD_LOGIN_ENABLED=true
+      #-------------------------------------------------------------------
     depends_on:
     depends_on:
       - wekandb
       - wekandb
 
 

+ 7 - 0
helm/wekan/README.md

@@ -56,3 +56,10 @@ mongodb-replicaset:
 This section controls the scale of the MongoDB redundant Replica Set.
 This section controls the scale of the MongoDB redundant Replica Set.
 
 
 **replicas:** This is the number of MongoDB instances to include in the set. You can set this to 1 for a single server - this will still allow you to scale-up later with a helm upgrade.
 **replicas:** This is the number of MongoDB instances to include in the set. You can set this to 1 for a single server - this will still allow you to scale-up later with a helm upgrade.
+
+### Install OCP route
+If you use this chart to deploy Wekan on an OCP cluster, you can create route instead of ingress with following command:
+
+``` bash
+$ helm template --set route.enabled=true,ingress.enabled=false values.yaml . | oc apply -f-
+```

+ 6 - 0
helm/wekan/templates/deployment.yaml

@@ -37,6 +37,12 @@ spec:
               value: {{ .Values.root_url | default "https://wekan.local" | quote }}
               value: {{ .Values.root_url | default "https://wekan.local" | quote }}
             - name: MONGO_URL
             - name: MONGO_URL
               value: "{{ template "mongodb-replicaset.url" . }}"
               value: "{{ template "mongodb-replicaset.url" . }}"
+          {{- range $key := .Values.env }}
+          {{- if .value }}
+            - name: {{ .name }}
+              value: {{ .value | quote }}
+          {{- end }}
+          {{- end }}
           livenessProbe:
           livenessProbe:
             httpGet:
             httpGet:
               path: /
               path: /

+ 23 - 0
helm/wekan/templates/route.yaml

@@ -0,0 +1,23 @@
+{{- if .Values.route.enabled -}}
+{{- $fullName := include "wekan.fullname" . -}}
+apiVersion: route.openshift.io/v1
+kind: Route
+metadata:
+  annotations:
+    haproxy.router.openshift.io/timeout: 4m
+    openshift.io/host.generated: "true"
+  labels:
+    app: {{ template "wekan.name" . }}
+    service: {{ template "wekan.name" . }}
+  name: {{ template "wekan.name" . }}
+spec:
+  port:
+    targetPort: http
+  tls:
+    termination: edge
+  to:
+    kind: Service
+    name: {{ template "wekan.name" . }}
+    weight: 100
+  wildcardPolicy: None
+  {{- end }}

+ 4 - 0
helm/wekan/templates/serviceaccount.yaml

@@ -2,6 +2,10 @@
 apiVersion: v1
 apiVersion: v1
 kind: ServiceAccount
 kind: ServiceAccount
 metadata:
 metadata:
+{{- if .Values.serviceAccounts.annotations }}
+  annotations:
+{{ .Values.serviceAccounts.annotations | indent 4}}
+{{- end }}
   labels:
   labels:
     app: {{ template "wekan.name" . }}
     app: {{ template "wekan.name" . }}
     chart: {{ template "wekan.chart" . }}
     chart: {{ template "wekan.chart" . }}

+ 8 - 2
helm/wekan/values.yaml

@@ -8,6 +8,7 @@
 serviceAccounts:
 serviceAccounts:
   create: true
   create: true
   name: ""
   name: ""
+  annotations: ""
 
 
 ## Wekan image configuration
 ## Wekan image configuration
 ##
 ##
@@ -29,7 +30,9 @@ credentials:
 
 
 ## Specify additional environmental variables for the Deployment
 ## Specify additional environmental variables for the Deployment
 ##
 ##
-env: {}
+env:
+  - name: ""
+    value: ""
 
 
 service:
 service:
   type: NodePort
   type: NodePort
@@ -59,7 +62,10 @@ ingress:
   #    hosts:
   #    hosts:
   #      - wekan-example.local
   #      - wekan-example.local
 
 
-resources: 
+route:
+  enabled: false
+
+resources:
   requests:
   requests:
     memory: 128Mi
     memory: 128Mi
     cpu: 300m
     cpu: 300m

+ 58 - 30
i18n/ar.i18n.json

@@ -64,7 +64,7 @@
   "activity-unchecked-item": "ازالة تحقق %s من قائمة التحقق %s من %s",
   "activity-unchecked-item": "ازالة تحقق %s من قائمة التحقق %s من %s",
   "activity-checklist-added": "أضاف قائمة تحقق إلى %s",
   "activity-checklist-added": "أضاف قائمة تحقق إلى %s",
   "activity-checklist-removed": "ازالة قائمة التحقق من %s",
   "activity-checklist-removed": "ازالة قائمة التحقق من %s",
-  "activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "activity-checklist-completed": "completed checklist %s of %s",
   "activity-checklist-uncompleted": "لم يتم انجاز قائمة التحقق %s من %s",
   "activity-checklist-uncompleted": "لم يتم انجاز قائمة التحقق %s من %s",
   "activity-checklist-item-added": "تم اضافة عنصر قائمة التحقق الى '%s' في %s",
   "activity-checklist-item-added": "تم اضافة عنصر قائمة التحقق الى '%s' في %s",
   "activity-checklist-item-removed": "تم ازالة عنصر قائمة التحقق الى '%s' في %s",
   "activity-checklist-item-removed": "تم ازالة عنصر قائمة التحقق الى '%s' في %s",
@@ -74,12 +74,12 @@
   "activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
   "activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
   "activity-checklist-uncompleted-card": "uncompleted the checklist %s",
   "activity-checklist-uncompleted-card": "uncompleted the checklist %s",
   "activity-editComment": "edited comment %s",
   "activity-editComment": "edited comment %s",
-  "activity-deleteComment": "deleted comment %s",
+  "activity-deleteComment": "تعليق محذوف %s",
   "add-attachment": "إضافة مرفق",
   "add-attachment": "إضافة مرفق",
   "add-board": "إضافة لوحة",
   "add-board": "إضافة لوحة",
   "add-card": "إضافة بطاقة",
   "add-card": "إضافة بطاقة",
   "add-swimlane": "Add Swimlane",
   "add-swimlane": "Add Swimlane",
-  "add-subtask": "Add Subtask",
+  "add-subtask": "إضافة مهمة فرعية",
   "add-checklist": "إضافة قائمة تدقيق",
   "add-checklist": "إضافة قائمة تدقيق",
   "add-checklist-item": "إضافة عنصر إلى قائمة التحقق",
   "add-checklist-item": "إضافة عنصر إلى قائمة التحقق",
   "add-cover": "إضافة غلاف",
   "add-cover": "إضافة غلاف",
@@ -111,8 +111,8 @@
   "restore-board": "استعادة اللوحة",
   "restore-board": "استعادة اللوحة",
   "no-archived-boards": "لا توجد لوحات في الأرشيف.",
   "no-archived-boards": "لا توجد لوحات في الأرشيف.",
   "archives": "أرشيف",
   "archives": "أرشيف",
-  "template": "Template",
-  "templates": "Templates",
+  "template": "نموذج",
+  "templates": "نماذج",
   "assign-member": "تعيين عضو",
   "assign-member": "تعيين عضو",
   "attached": "أُرفق)",
   "attached": "أُرفق)",
   "attachment": "مرفق",
   "attachment": "مرفق",
@@ -152,6 +152,8 @@
   "card-spent": "امضى وقتا",
   "card-spent": "امضى وقتا",
   "card-edit-attachments": "تعديل المرفقات",
   "card-edit-attachments": "تعديل المرفقات",
   "card-edit-custom-fields": "تعديل الحقل المعدل",
   "card-edit-custom-fields": "تعديل الحقل المعدل",
+  "card-start-voting": "ابدأ التصويت",
+  "card-cancel-voting": "حذف التصويت وجميع الأصوات",
   "card-edit-labels": "تعديل العلامات",
   "card-edit-labels": "تعديل العلامات",
   "card-edit-members": "تعديل الأعضاء",
   "card-edit-members": "تعديل الأعضاء",
   "card-labels-title": "تعديل علامات البطاقة.",
   "card-labels-title": "تعديل علامات البطاقة.",
@@ -161,6 +163,14 @@
   "cardAttachmentsPopup-title": "إرفاق من",
   "cardAttachmentsPopup-title": "إرفاق من",
   "cardCustomField-datePopup-title": "تغير التاريخ",
   "cardCustomField-datePopup-title": "تغير التاريخ",
   "cardCustomFieldsPopup-title": "تعديل الحقل المعدل",
   "cardCustomFieldsPopup-title": "تعديل الحقل المعدل",
+  "cardStartVotingPopup-title": "ابدأ تصويت",
+  "positiveVoteMembersPopup-title": "Proponents",
+  "negativeVoteMembersPopup-title": "Opponents",
+  "allowNonBoardMembers": "Allow anonymous vote on public board",
+  "vote-question": "Voting question",
+  "vote-public": "Show who voted what",
+  "vote-for-it": "مع",
+  "vote-against": "ضد",
   "cardDeletePopup-title": "حذف البطاقة ?",
   "cardDeletePopup-title": "حذف البطاقة ?",
   "cardDetailsActionsPopup-title": "إجراءات على البطاقة",
   "cardDetailsActionsPopup-title": "إجراءات على البطاقة",
   "cardLabelsPopup-title": "علامات",
   "cardLabelsPopup-title": "علامات",
@@ -183,7 +193,7 @@
   "changePasswordPopup-title": "تغيير كلمة المرور",
   "changePasswordPopup-title": "تغيير كلمة المرور",
   "changePermissionsPopup-title": "تعديل الصلاحيات",
   "changePermissionsPopup-title": "تعديل الصلاحيات",
   "changeSettingsPopup-title": "تغيير الاعدادات",
   "changeSettingsPopup-title": "تغيير الاعدادات",
-  "subtasks": "Subtasks",
+  "subtasks": "المهمات الفرعية",
   "checklists": "قوائم التّدقيق",
   "checklists": "قوائم التّدقيق",
   "click-to-star": "اضغط لإضافة اللوحة للمفضلة.",
   "click-to-star": "اضغط لإضافة اللوحة للمفضلة.",
   "click-to-unstar": "اضغط لحذف اللوحة من المفضلة.",
   "click-to-unstar": "اضغط لحذف اللوحة من المفضلة.",
@@ -194,9 +204,9 @@
   "color-black": "black",
   "color-black": "black",
   "color-blue": "blue",
   "color-blue": "blue",
   "color-crimson": "crimson",
   "color-crimson": "crimson",
-  "color-darkgreen": "darkgreen",
-  "color-gold": "gold",
-  "color-gray": "gray",
+  "color-darkgreen": "اخضر غامق",
+  "color-gold": "ذهبي",
+  "color-gray": "رمادي",
   "color-green": "green",
   "color-green": "green",
   "color-indigo": "indigo",
   "color-indigo": "indigo",
   "color-lime": "lime",
   "color-lime": "lime",
@@ -211,17 +221,17 @@
   "color-purple": "purple",
   "color-purple": "purple",
   "color-red": "red",
   "color-red": "red",
   "color-saddlebrown": "saddlebrown",
   "color-saddlebrown": "saddlebrown",
-  "color-silver": "silver",
+  "color-silver": "فضي",
   "color-sky": "sky",
   "color-sky": "sky",
   "color-slateblue": "slateblue",
   "color-slateblue": "slateblue",
-  "color-white": "white",
+  "color-white": "أبيض",
   "color-yellow": "yellow",
   "color-yellow": "yellow",
   "unset-color": "Unset",
   "unset-color": "Unset",
   "comment": "تعليق",
   "comment": "تعليق",
   "comment-placeholder": "أكتب تعليق",
   "comment-placeholder": "أكتب تعليق",
   "comment-only": "التعليق فقط",
   "comment-only": "التعليق فقط",
   "comment-only-desc": "يمكن التعليق على بطاقات فقط.",
   "comment-only-desc": "يمكن التعليق على بطاقات فقط.",
-  "no-comments": "No comments",
+  "no-comments": "لا يوجد تعليقات",
   "no-comments-desc": "Can not see comments and activities.",
   "no-comments-desc": "Can not see comments and activities.",
   "worker": "Worker",
   "worker": "Worker",
   "worker-desc": "Can only move cards, assign itself to card and comment.",
   "worker-desc": "Can only move cards, assign itself to card and comment.",
@@ -239,8 +249,8 @@
   "createBoardPopup-title": "إنشاء لوحة",
   "createBoardPopup-title": "إنشاء لوحة",
   "chooseBoardSourcePopup-title": "استيراد لوحة",
   "chooseBoardSourcePopup-title": "استيراد لوحة",
   "createLabelPopup-title": "إنشاء علامة",
   "createLabelPopup-title": "إنشاء علامة",
-  "createCustomField": "Create Field",
-  "createCustomFieldPopup-title": "Create Field",
+  "createCustomField": "انشاء حقل",
+  "createCustomFieldPopup-title": "انشاء حقل",
   "current": "الحالي",
   "current": "الحالي",
   "custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
   "custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
   "custom-field-checkbox": "Checkbox",
   "custom-field-checkbox": "Checkbox",
@@ -250,8 +260,8 @@
   "custom-field-dropdown-options": "List Options",
   "custom-field-dropdown-options": "List Options",
   "custom-field-dropdown-options-placeholder": "Press enter to add more options",
   "custom-field-dropdown-options-placeholder": "Press enter to add more options",
   "custom-field-dropdown-unknown": "(unknown)",
   "custom-field-dropdown-unknown": "(unknown)",
-  "custom-field-number": "Number",
-  "custom-field-text": "Text",
+  "custom-field-number": "رقم",
+  "custom-field-text": "نص",
   "custom-fields": "Custom Fields",
   "custom-fields": "Custom Fields",
   "date": "تاريخ",
   "date": "تاريخ",
   "decline": "Decline",
   "decline": "Decline",
@@ -319,6 +329,7 @@
   "filter-clear": "مسح التصفية",
   "filter-clear": "مسح التصفية",
   "filter-no-label": "لا يوجد ملصق",
   "filter-no-label": "لا يوجد ملصق",
   "filter-no-member": "ليس هناك أي عضو",
   "filter-no-member": "ليس هناك أي عضو",
+  "filter-no-assignee": "No assignee",
   "filter-no-custom-fields": "No Custom Fields",
   "filter-no-custom-fields": "No Custom Fields",
   "filter-show-archive": "Show archived lists",
   "filter-show-archive": "Show archived lists",
   "filter-hide-empty": "Hide empty lists",
   "filter-hide-empty": "Hide empty lists",
@@ -333,13 +344,11 @@
   "headerBarCreateBoardPopup-title": "إنشاء لوحة",
   "headerBarCreateBoardPopup-title": "إنشاء لوحة",
   "home": "الرئيسية",
   "home": "الرئيسية",
   "import": "Import",
   "import": "Import",
-  "link": "Link",
+  "link": "رابط",
   "import-board": "استيراد لوحة",
   "import-board": "استيراد لوحة",
   "import-board-c": "استيراد لوحة",
   "import-board-c": "استيراد لوحة",
   "import-board-title-trello": "Import board from Trello",
   "import-board-title-trello": "Import board from Trello",
   "import-board-title-wekan": "Import board from previous export",
   "import-board-title-wekan": "Import board from previous export",
-  "import-sandstorm-backup-warning": "Do not delete data you import from original exported board or Trello before checking does this grain close and open again, or do you get Board not found error, that means data loss.",
-  "import-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
   "from-trello": "من تريلو",
   "from-trello": "من تريلو",
   "from-wekan": "From previous export",
   "from-wekan": "From previous export",
   "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
   "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
@@ -440,7 +449,7 @@
   "save": "حفظ",
   "save": "حفظ",
   "search": "بحث",
   "search": "بحث",
   "rules": "Rules",
   "rules": "Rules",
-  "search-cards": "Search from card/list titles and descriptions on this board",
+  "search-cards": "Search from card/list titles, descriptions and custom fields on this board",
   "search-example": "Text to search for?",
   "search-example": "Text to search for?",
   "select-color": "اختيار اللون",
   "select-color": "اختيار اللون",
   "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
   "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
@@ -466,15 +475,15 @@
   "this-board": "هذه اللوحة",
   "this-board": "هذه اللوحة",
   "this-card": "هذه البطاقة",
   "this-card": "هذه البطاقة",
   "spent-time-hours": "Spent time (hours)",
   "spent-time-hours": "Spent time (hours)",
-  "overtime-hours": "Overtime (hours)",
-  "overtime": "Overtime",
+  "overtime-hours": "وقت اضافي (ساعات)",
+  "overtime": "وقت اضافي",
   "has-overtime-cards": "Has overtime cards",
   "has-overtime-cards": "Has overtime cards",
   "has-spenttime-cards": "Has spent time cards",
   "has-spenttime-cards": "Has spent time cards",
   "time": "الوقت",
   "time": "الوقت",
   "title": "عنوان",
   "title": "عنوان",
   "tracking": "تتبع",
   "tracking": "تتبع",
   "tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",
   "tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",
-  "type": "Type",
+  "type": "النوع",
   "unassign-member": "إلغاء تعيين العضو",
   "unassign-member": "إلغاء تعيين العضو",
   "unsaved-description": "لديك وصف غير محفوظ",
   "unsaved-description": "لديك وصف غير محفوظ",
   "unwatch": "غير مُشاهد",
   "unwatch": "غير مُشاهد",
@@ -548,7 +557,7 @@
   "OS_Totalmem": "الذاكرة الكلية لنظام التشغيل",
   "OS_Totalmem": "الذاكرة الكلية لنظام التشغيل",
   "OS_Type": "نوع نظام التشغيل",
   "OS_Type": "نوع نظام التشغيل",
   "OS_Uptime": "مدة تشغيل نظام التشغيل",
   "OS_Uptime": "مدة تشغيل نظام التشغيل",
-  "days": "days",
+  "days": "أيام",
   "hours": "الساعات",
   "hours": "الساعات",
   "minutes": "الدقائق",
   "minutes": "الدقائق",
   "seconds": "الثواني",
   "seconds": "الثواني",
@@ -562,17 +571,17 @@
   "accounts-allowUserNameChange": "Allow Username Change",
   "accounts-allowUserNameChange": "Allow Username Change",
   "createdAt": "Created at",
   "createdAt": "Created at",
   "verified": "Verified",
   "verified": "Verified",
-  "active": "Active",
+  "active": "نشط",
   "card-received": "Received",
   "card-received": "Received",
   "card-received-on": "Received on",
   "card-received-on": "Received on",
   "card-end": "End",
   "card-end": "End",
   "card-end-on": "Ends on",
   "card-end-on": "Ends on",
   "editCardReceivedDatePopup-title": "Change received date",
   "editCardReceivedDatePopup-title": "Change received date",
   "editCardEndDatePopup-title": "Change end date",
   "editCardEndDatePopup-title": "Change end date",
-  "setCardColorPopup-title": "Set color",
-  "setCardActionsColorPopup-title": "Choose a color",
-  "setSwimlaneColorPopup-title": "Choose a color",
-  "setListColorPopup-title": "Choose a color",
+  "setCardColorPopup-title": "حدد اللون",
+  "setCardActionsColorPopup-title": "اختر لوناً",
+  "setSwimlaneColorPopup-title": "اختر لوناً",
+  "setListColorPopup-title": "اختر لوناً",
   "assigned-by": "Assigned By",
   "assigned-by": "Assigned By",
   "requested-by": "Requested By",
   "requested-by": "Requested By",
   "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
   "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
@@ -665,6 +674,7 @@
   "r-of-checklist": "of checklist",
   "r-of-checklist": "of checklist",
   "r-send-email": "Send an email",
   "r-send-email": "Send an email",
   "r-to": "to",
   "r-to": "to",
+  "r-of": "of",
   "r-subject": "subject",
   "r-subject": "subject",
   "r-rule-details": "Rule details",
   "r-rule-details": "Rule details",
   "r-d-move-to-top-gen": "Move card to top of its list",
   "r-d-move-to-top-gen": "Move card to top of its list",
@@ -756,5 +766,23 @@
   "assignee": "Assignee",
   "assignee": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "addmore-detail": "Add a more detailed description",
   "addmore-detail": "Add a more detailed description",
-  "show-on-card": "Show on Card"
+  "show-on-card": "Show on Card",
+  "new": "New",
+  "editUserPopup-title": "Edit User",
+  "newUserPopup-title": "New User",
+  "notifications": "Notifications",
+  "view-all": "View All",
+  "filter-by-unread": "Filter by Unread",
+  "mark-all-as-read": "Mark all as read",
+  "remove-all-read": "Remove all read",
+  "allow-rename": "Allow Rename",
+  "allowRenamePopup-title": "Allow Rename",
+  "start-day-of-week": "Set day of the week start",
+  "monday": "Monday",
+  "tuesday": "Tuesday",
+  "wednesday": "Wednesday",
+  "thursday": "Thursday",
+  "friday": "Friday",
+  "saturday": "Saturday",
+  "sunday": "Sunday"
 }
 }

+ 33 - 5
i18n/bg.i18n.json

@@ -64,7 +64,7 @@
   "activity-unchecked-item": "размаркира %s от списък със задачи %s на %s",
   "activity-unchecked-item": "размаркира %s от списък със задачи %s на %s",
   "activity-checklist-added": "добави списък със задачи към %s",
   "activity-checklist-added": "добави списък със задачи към %s",
   "activity-checklist-removed": "премахна списък със задачи от %s",
   "activity-checklist-removed": "премахна списък със задачи от %s",
-  "activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "activity-checklist-completed": "completed checklist %s of %s",
   "activity-checklist-uncompleted": "\"отзавърши\" чеклистта %s в %s",
   "activity-checklist-uncompleted": "\"отзавърши\" чеклистта %s в %s",
   "activity-checklist-item-added": "добави точка към '%s' в/във %s",
   "activity-checklist-item-added": "добави точка към '%s' в/във %s",
   "activity-checklist-item-removed": "премахна точка от '%s' в %s",
   "activity-checklist-item-removed": "премахна точка от '%s' в %s",
@@ -152,6 +152,8 @@
   "card-spent": "Изработено време",
   "card-spent": "Изработено време",
   "card-edit-attachments": "Промени прикачените файлове",
   "card-edit-attachments": "Промени прикачените файлове",
   "card-edit-custom-fields": "Промени собствените полета",
   "card-edit-custom-fields": "Промени собствените полета",
+  "card-start-voting": "Start voting",
+  "card-cancel-voting": "Delete voting and all votes",
   "card-edit-labels": "Промени етикетите",
   "card-edit-labels": "Промени етикетите",
   "card-edit-members": "Промени членовете",
   "card-edit-members": "Промени членовете",
   "card-labels-title": "Промени етикетите за картата.",
   "card-labels-title": "Промени етикетите за картата.",
@@ -161,6 +163,14 @@
   "cardAttachmentsPopup-title": "Прикачи от",
   "cardAttachmentsPopup-title": "Прикачи от",
   "cardCustomField-datePopup-title": "Промени датата",
   "cardCustomField-datePopup-title": "Промени датата",
   "cardCustomFieldsPopup-title": "Промени собствените полета",
   "cardCustomFieldsPopup-title": "Промени собствените полета",
+  "cardStartVotingPopup-title": "Start a vote",
+  "positiveVoteMembersPopup-title": "Proponents",
+  "negativeVoteMembersPopup-title": "Opponents",
+  "allowNonBoardMembers": "Allow anonymous vote on public board",
+  "vote-question": "Voting question",
+  "vote-public": "Show who voted what",
+  "vote-for-it": "for it",
+  "vote-against": "against",
   "cardDeletePopup-title": "Желаете да изтриете картата?",
   "cardDeletePopup-title": "Желаете да изтриете картата?",
   "cardDetailsActionsPopup-title": "Опции",
   "cardDetailsActionsPopup-title": "Опции",
   "cardLabelsPopup-title": "Етикети",
   "cardLabelsPopup-title": "Етикети",
@@ -319,6 +329,7 @@
   "filter-clear": "Премахване на филтрите",
   "filter-clear": "Премахване на филтрите",
   "filter-no-label": "без етикет",
   "filter-no-label": "без етикет",
   "filter-no-member": "без член",
   "filter-no-member": "без член",
+  "filter-no-assignee": "No assignee",
   "filter-no-custom-fields": "Няма Собствени полета",
   "filter-no-custom-fields": "Няма Собствени полета",
   "filter-show-archive": "Show archived lists",
   "filter-show-archive": "Show archived lists",
   "filter-hide-empty": "Hide empty lists",
   "filter-hide-empty": "Hide empty lists",
@@ -338,8 +349,6 @@
   "import-board-c": "Импортирай Табло",
   "import-board-c": "Импортирай Табло",
   "import-board-title-trello": "Импорт на табло от Trello",
   "import-board-title-trello": "Импорт на табло от Trello",
   "import-board-title-wekan": "Import board from previous export",
   "import-board-title-wekan": "Import board from previous export",
-  "import-sandstorm-backup-warning": "Do not delete data you import from original exported board or Trello before checking does this grain close and open again, or do you get Board not found error, that means data loss.",
-  "import-sandstorm-warning": "Импортирането ще изтрие всичката налична информация в таблото и ще я замени с нова.",
   "from-trello": "От Trello",
   "from-trello": "От Trello",
   "from-wekan": "From previous export",
   "from-wekan": "From previous export",
   "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
   "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
@@ -440,7 +449,7 @@
   "save": "Запази",
   "save": "Запази",
   "search": "Търсене",
   "search": "Търсене",
   "rules": "Правила",
   "rules": "Правила",
-  "search-cards": "Search from card/list titles and descriptions on this board",
+  "search-cards": "Search from card/list titles, descriptions and custom fields on this board",
   "search-example": "Text to search for?",
   "search-example": "Text to search for?",
   "select-color": "Избери цвят",
   "select-color": "Избери цвят",
   "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
   "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
@@ -665,6 +674,7 @@
   "r-of-checklist": "of checklist",
   "r-of-checklist": "of checklist",
   "r-send-email": "Send an email",
   "r-send-email": "Send an email",
   "r-to": "to",
   "r-to": "to",
+  "r-of": "of",
   "r-subject": "subject",
   "r-subject": "subject",
   "r-rule-details": "Детайли за правилото",
   "r-rule-details": "Детайли за правилото",
   "r-d-move-to-top-gen": "Move card to top of its list",
   "r-d-move-to-top-gen": "Move card to top of its list",
@@ -756,5 +766,23 @@
   "assignee": "Assignee",
   "assignee": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "addmore-detail": "Add a more detailed description",
   "addmore-detail": "Add a more detailed description",
-  "show-on-card": "Show on Card"
+  "show-on-card": "Show on Card",
+  "new": "New",
+  "editUserPopup-title": "Edit User",
+  "newUserPopup-title": "New User",
+  "notifications": "Notifications",
+  "view-all": "View All",
+  "filter-by-unread": "Filter by Unread",
+  "mark-all-as-read": "Mark all as read",
+  "remove-all-read": "Remove all read",
+  "allow-rename": "Allow Rename",
+  "allowRenamePopup-title": "Allow Rename",
+  "start-day-of-week": "Set day of the week start",
+  "monday": "Monday",
+  "tuesday": "Tuesday",
+  "wednesday": "Wednesday",
+  "thursday": "Thursday",
+  "friday": "Friday",
+  "saturday": "Saturday",
+  "sunday": "Sunday"
 }
 }

+ 33 - 5
i18n/br.i18n.json

@@ -64,7 +64,7 @@
   "activity-unchecked-item": "unchecked %s in checklist %s of %s",
   "activity-unchecked-item": "unchecked %s in checklist %s of %s",
   "activity-checklist-added": "added checklist to %s",
   "activity-checklist-added": "added checklist to %s",
   "activity-checklist-removed": "removed a checklist from %s",
   "activity-checklist-removed": "removed a checklist from %s",
-  "activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "activity-checklist-completed": "completed checklist %s of %s",
   "activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
   "activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
   "activity-checklist-item-added": "added checklist item to '%s' in %s",
   "activity-checklist-item-added": "added checklist item to '%s' in %s",
   "activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
   "activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
@@ -152,6 +152,8 @@
   "card-spent": "Spent Time",
   "card-spent": "Spent Time",
   "card-edit-attachments": "Edit attachments",
   "card-edit-attachments": "Edit attachments",
   "card-edit-custom-fields": "Edit custom fields",
   "card-edit-custom-fields": "Edit custom fields",
+  "card-start-voting": "Start voting",
+  "card-cancel-voting": "Delete voting and all votes",
   "card-edit-labels": "Edit labels",
   "card-edit-labels": "Edit labels",
   "card-edit-members": "Edit members",
   "card-edit-members": "Edit members",
   "card-labels-title": "Change the labels for the card.",
   "card-labels-title": "Change the labels for the card.",
@@ -161,6 +163,14 @@
   "cardAttachmentsPopup-title": "Attach From",
   "cardAttachmentsPopup-title": "Attach From",
   "cardCustomField-datePopup-title": "Change date",
   "cardCustomField-datePopup-title": "Change date",
   "cardCustomFieldsPopup-title": "Edit custom fields",
   "cardCustomFieldsPopup-title": "Edit custom fields",
+  "cardStartVotingPopup-title": "Start a vote",
+  "positiveVoteMembersPopup-title": "Proponents",
+  "negativeVoteMembersPopup-title": "Opponents",
+  "allowNonBoardMembers": "Allow anonymous vote on public board",
+  "vote-question": "Voting question",
+  "vote-public": "Show who voted what",
+  "vote-for-it": "for it",
+  "vote-against": "against",
   "cardDeletePopup-title": "Diverkañ ar gartenn ?",
   "cardDeletePopup-title": "Diverkañ ar gartenn ?",
   "cardDetailsActionsPopup-title": "Card Actions",
   "cardDetailsActionsPopup-title": "Card Actions",
   "cardLabelsPopup-title": "Labels",
   "cardLabelsPopup-title": "Labels",
@@ -319,6 +329,7 @@
   "filter-clear": "Clear filter",
   "filter-clear": "Clear filter",
   "filter-no-label": "No label",
   "filter-no-label": "No label",
   "filter-no-member": "No member",
   "filter-no-member": "No member",
+  "filter-no-assignee": "No assignee",
   "filter-no-custom-fields": "No Custom Fields",
   "filter-no-custom-fields": "No Custom Fields",
   "filter-show-archive": "Show archived lists",
   "filter-show-archive": "Show archived lists",
   "filter-hide-empty": "Hide empty lists",
   "filter-hide-empty": "Hide empty lists",
@@ -338,8 +349,6 @@
   "import-board-c": "Import board",
   "import-board-c": "Import board",
   "import-board-title-trello": "Import board from Trello",
   "import-board-title-trello": "Import board from Trello",
   "import-board-title-wekan": "Import board from previous export",
   "import-board-title-wekan": "Import board from previous export",
-  "import-sandstorm-backup-warning": "Do not delete data you import from original exported board or Trello before checking does this grain close and open again, or do you get Board not found error, that means data loss.",
-  "import-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
   "from-trello": "From Trello",
   "from-trello": "From Trello",
   "from-wekan": "From previous export",
   "from-wekan": "From previous export",
   "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
   "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text",
@@ -440,7 +449,7 @@
   "save": "Save",
   "save": "Save",
   "search": "Search",
   "search": "Search",
   "rules": "Rules",
   "rules": "Rules",
-  "search-cards": "Search from card/list titles and descriptions on this board",
+  "search-cards": "Search from card/list titles, descriptions and custom fields on this board",
   "search-example": "Text to search for?",
   "search-example": "Text to search for?",
   "select-color": "Select Color",
   "select-color": "Select Color",
   "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
   "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
@@ -665,6 +674,7 @@
   "r-of-checklist": "of checklist",
   "r-of-checklist": "of checklist",
   "r-send-email": "Send an email",
   "r-send-email": "Send an email",
   "r-to": "to",
   "r-to": "to",
+  "r-of": "of",
   "r-subject": "subject",
   "r-subject": "subject",
   "r-rule-details": "Rule details",
   "r-rule-details": "Rule details",
   "r-d-move-to-top-gen": "Move card to top of its list",
   "r-d-move-to-top-gen": "Move card to top of its list",
@@ -756,5 +766,23 @@
   "assignee": "Assignee",
   "assignee": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "addmore-detail": "Add a more detailed description",
   "addmore-detail": "Add a more detailed description",
-  "show-on-card": "Show on Card"
+  "show-on-card": "Show on Card",
+  "new": "New",
+  "editUserPopup-title": "Edit User",
+  "newUserPopup-title": "New User",
+  "notifications": "Notifications",
+  "view-all": "View All",
+  "filter-by-unread": "Filter by Unread",
+  "mark-all-as-read": "Mark all as read",
+  "remove-all-read": "Remove all read",
+  "allow-rename": "Allow Rename",
+  "allowRenamePopup-title": "Allow Rename",
+  "start-day-of-week": "Set day of the week start",
+  "monday": "Monday",
+  "tuesday": "Tuesday",
+  "wednesday": "Wednesday",
+  "thursday": "Thursday",
+  "friday": "Friday",
+  "saturday": "Saturday",
+  "sunday": "Sunday"
 }
 }

+ 46 - 18
i18n/ca.i18n.json

@@ -64,7 +64,7 @@
   "activity-unchecked-item": "unchecked %s in checklist %s of %s",
   "activity-unchecked-item": "unchecked %s in checklist %s of %s",
   "activity-checklist-added": "Checklist afegida a %s",
   "activity-checklist-added": "Checklist afegida a %s",
   "activity-checklist-removed": "removed a checklist from %s",
   "activity-checklist-removed": "removed a checklist from %s",
-  "activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "activity-checklist-completed": "completed checklist %s of %s",
   "activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
   "activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
   "activity-checklist-item-added": "afegida entrada de checklist de '%s' a %s",
   "activity-checklist-item-added": "afegida entrada de checklist de '%s' a %s",
   "activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
   "activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
@@ -111,8 +111,8 @@
   "restore-board": "Restaura Tauler",
   "restore-board": "Restaura Tauler",
   "no-archived-boards": "No hi han Taulers al Arxiu.",
   "no-archived-boards": "No hi han Taulers al Arxiu.",
   "archives": "Desa",
   "archives": "Desa",
-  "template": "Template",
-  "templates": "Templates",
+  "template": "Plantilla",
+  "templates": "Plantilles",
   "assign-member": "Assignar membre",
   "assign-member": "Assignar membre",
   "attached": "adjuntat",
   "attached": "adjuntat",
   "attachment": "Adjunt",
   "attachment": "Adjunt",
@@ -131,13 +131,13 @@
   "boardChangeTitlePopup-title": "Canvia el nom tauler",
   "boardChangeTitlePopup-title": "Canvia el nom tauler",
   "boardChangeVisibilityPopup-title": "Canvia visibilitat",
   "boardChangeVisibilityPopup-title": "Canvia visibilitat",
   "boardChangeWatchPopup-title": "Canvia seguiment",
   "boardChangeWatchPopup-title": "Canvia seguiment",
-  "boardMenuPopup-title": "Board Settings",
+  "boardMenuPopup-title": "Configuració del tauler",
   "boardChangeViewPopup-title": "Visió del tauler",
   "boardChangeViewPopup-title": "Visió del tauler",
   "boards": "Taulers",
   "boards": "Taulers",
   "board-view": "Visió del tauler",
   "board-view": "Visió del tauler",
   "board-view-cal": "Calendari",
   "board-view-cal": "Calendari",
   "board-view-swimlanes": "Carrils de Natació",
   "board-view-swimlanes": "Carrils de Natació",
-  "board-view-collapse": "Collapse",
+  "board-view-collapse": "Contraure",
   "board-view-lists": "Llistes",
   "board-view-lists": "Llistes",
   "bucket-example": "Igual que “Bucket List”,  per exemple",
   "bucket-example": "Igual que “Bucket List”,  per exemple",
   "cancel": "Cancel·la",
   "cancel": "Cancel·la",
@@ -152,6 +152,8 @@
   "card-spent": "Temps Dedicat",
   "card-spent": "Temps Dedicat",
   "card-edit-attachments": "Edita arxius adjunts",
   "card-edit-attachments": "Edita arxius adjunts",
   "card-edit-custom-fields": "Editar camps personalitzats",
   "card-edit-custom-fields": "Editar camps personalitzats",
+  "card-start-voting": "Start voting",
+  "card-cancel-voting": "Delete voting and all votes",
   "card-edit-labels": "Edita etiquetes",
   "card-edit-labels": "Edita etiquetes",
   "card-edit-members": "Edita membres",
   "card-edit-members": "Edita membres",
   "card-labels-title": "Canvia les etiquetes de la fitxa",
   "card-labels-title": "Canvia les etiquetes de la fitxa",
@@ -161,6 +163,14 @@
   "cardAttachmentsPopup-title": "Adjunta des de",
   "cardAttachmentsPopup-title": "Adjunta des de",
   "cardCustomField-datePopup-title": "Canviar data",
   "cardCustomField-datePopup-title": "Canviar data",
   "cardCustomFieldsPopup-title": "Editar camps personalitzats",
   "cardCustomFieldsPopup-title": "Editar camps personalitzats",
+  "cardStartVotingPopup-title": "Start a vote",
+  "positiveVoteMembersPopup-title": "Proponents",
+  "negativeVoteMembersPopup-title": "Opponents",
+  "allowNonBoardMembers": "Allow anonymous vote on public board",
+  "vote-question": "Voting question",
+  "vote-public": "Show who voted what",
+  "vote-for-it": "for it",
+  "vote-against": "against",
   "cardDeletePopup-title": "Esborrar fitxa?",
   "cardDeletePopup-title": "Esborrar fitxa?",
   "cardDetailsActionsPopup-title": "Accions de fitxes",
   "cardDetailsActionsPopup-title": "Accions de fitxes",
   "cardLabelsPopup-title": "Etiquetes",
   "cardLabelsPopup-title": "Etiquetes",
@@ -319,6 +329,7 @@
   "filter-clear": "Elimina filtre",
   "filter-clear": "Elimina filtre",
   "filter-no-label": "Sense etiqueta",
   "filter-no-label": "Sense etiqueta",
   "filter-no-member": "Sense membres",
   "filter-no-member": "Sense membres",
+  "filter-no-assignee": "No assignee",
   "filter-no-custom-fields": "No Custom Fields",
   "filter-no-custom-fields": "No Custom Fields",
   "filter-show-archive": "Show archived lists",
   "filter-show-archive": "Show archived lists",
   "filter-hide-empty": "Hide empty lists",
   "filter-hide-empty": "Hide empty lists",
@@ -338,8 +349,6 @@
   "import-board-c": "Importa tauler",
   "import-board-c": "Importa tauler",
   "import-board-title-trello": "Importa tauler des de Trello",
   "import-board-title-trello": "Importa tauler des de Trello",
   "import-board-title-wekan": "Import board from previous export",
   "import-board-title-wekan": "Import board from previous export",
-  "import-sandstorm-backup-warning": "Do not delete data you import from original exported board or Trello before checking does this grain close and open again, or do you get Board not found error, that means data loss.",
-  "import-sandstorm-warning": "Estau segur que voleu esborrar aquesta checklist?",
   "from-trello": "Des de Trello",
   "from-trello": "Des de Trello",
   "from-wekan": "From previous export",
   "from-wekan": "From previous export",
   "import-board-instruction-trello": "En el teu tauler Trello, ves a 'Menú', 'Més'.' Imprimir i Exportar', 'Exportar JSON', i copia el text resultant.",
   "import-board-instruction-trello": "En el teu tauler Trello, ves a 'Menú', 'Més'.' Imprimir i Exportar', 'Exportar JSON', i copia el text resultant.",
@@ -440,7 +449,7 @@
   "save": "Desa",
   "save": "Desa",
   "search": "Cerca",
   "search": "Cerca",
   "rules": "Regles",
   "rules": "Regles",
-  "search-cards": "Search from card/list titles and descriptions on this board",
+  "search-cards": "Search from card/list titles, descriptions and custom fields on this board",
   "search-example": "Text que cercar?",
   "search-example": "Text que cercar?",
   "select-color": "Selecciona color",
   "select-color": "Selecciona color",
   "set-wip-limit-value": "Limita el màxim nombre de tasques en aquesta llista",
   "set-wip-limit-value": "Limita el màxim nombre de tasques en aquesta llista",
@@ -583,9 +592,9 @@
   "default": "Default",
   "default": "Default",
   "queue": "Queue",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
   "subtask-settings": "Subtasks Settings",
-  "card-settings": "Card Settings",
+  "card-settings": "Configuració de targeta",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "boardCardSettingsPopup-title": "Card Settings",
+  "boardCardSettingsPopup-title": "Configuració de targeta",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -626,7 +635,7 @@
   "r-moved-from": "Moved from",
   "r-moved-from": "Moved from",
   "r-archived": "Moved to Archive",
   "r-archived": "Moved to Archive",
   "r-unarchived": "Restored from Archive",
   "r-unarchived": "Restored from Archive",
-  "r-a-card": "a card",
+  "r-a-card": "una targeta",
   "r-when-a-label-is": "When a label is",
   "r-when-a-label-is": "When a label is",
   "r-when-the-label": "When the label",
   "r-when-the-label": "When the label",
   "r-list-name": "list name",
   "r-list-name": "list name",
@@ -648,7 +657,7 @@
   "r-its-list": "its list",
   "r-its-list": "its list",
   "r-archive": "Moure al arxiu",
   "r-archive": "Moure al arxiu",
   "r-unarchive": "Restore from Archive",
   "r-unarchive": "Restore from Archive",
-  "r-card": "card",
+  "r-card": "targeta",
   "r-add": "Afegeix",
   "r-add": "Afegeix",
   "r-remove": "Remove",
   "r-remove": "Remove",
   "r-label": "label",
   "r-label": "label",
@@ -665,6 +674,7 @@
   "r-of-checklist": "of checklist",
   "r-of-checklist": "of checklist",
   "r-send-email": "Send an email",
   "r-send-email": "Send an email",
   "r-to": "to",
   "r-to": "to",
+  "r-of": "of",
   "r-subject": "subject",
   "r-subject": "subject",
   "r-rule-details": "Rule details",
   "r-rule-details": "Rule details",
   "r-d-move-to-top-gen": "Move card to top of its list",
   "r-d-move-to-top-gen": "Move card to top of its list",
@@ -679,7 +689,7 @@
   "r-d-unarchive": "Restore card from Archive",
   "r-d-unarchive": "Restore card from Archive",
   "r-d-add-label": "Add label",
   "r-d-add-label": "Add label",
   "r-d-remove-label": "Remove label",
   "r-d-remove-label": "Remove label",
-  "r-create-card": "Create new card",
+  "r-create-card": "Crea una targeta nova",
   "r-in-list": "in list",
   "r-in-list": "in list",
   "r-in-swimlane": "in swimlane",
   "r-in-swimlane": "in swimlane",
   "r-d-add-member": "Add member",
   "r-d-add-member": "Add member",
@@ -700,7 +710,7 @@
   "r-swimlane-name": "swimlane name",
   "r-swimlane-name": "swimlane name",
   "r-board-note": "Note: leave a field empty to match every possible value.",
   "r-board-note": "Note: leave a field empty to match every possible value.",
   "r-checklist-note": "Note: checklist's items have to be written as comma separated values.",
   "r-checklist-note": "Note: checklist's items have to be written as comma separated values.",
-  "r-when-a-card-is-moved": "When a card is moved to another list",
+  "r-when-a-card-is-moved": "Quan una targeta es mou a una altra llista",
   "r-set": "Set",
   "r-set": "Set",
   "r-update": "Update",
   "r-update": "Update",
   "r-datefield": "date field",
   "r-datefield": "date field",
@@ -753,8 +763,26 @@
   "accounts-allowUserDelete": "Allow users to self delete their account",
   "accounts-allowUserDelete": "Allow users to self delete their account",
   "hide-minicard-label-text": "Hide minicard label text",
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "show-desktop-drag-handles": "Show desktop drag handles",
-  "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee",
-  "addmore-detail": "Add a more detailed description",
-  "show-on-card": "Show on Card"
+  "assignee": "Assignat",
+  "cardAssigneesPopup-title": "Assignat",
+  "addmore-detail": "Afegiu una descripció més detallada",
+  "show-on-card": "Show on Card",
+  "new": "New",
+  "editUserPopup-title": "Edit User",
+  "newUserPopup-title": "New User",
+  "notifications": "Notifications",
+  "view-all": "View All",
+  "filter-by-unread": "Filter by Unread",
+  "mark-all-as-read": "Mark all as read",
+  "remove-all-read": "Remove all read",
+  "allow-rename": "Allow Rename",
+  "allowRenamePopup-title": "Allow Rename",
+  "start-day-of-week": "Set day of the week start",
+  "monday": "Monday",
+  "tuesday": "Tuesday",
+  "wednesday": "Wednesday",
+  "thursday": "Thursday",
+  "friday": "Friday",
+  "saturday": "Saturday",
+  "sunday": "Sunday"
 }
 }

+ 41 - 13
i18n/cs.i18n.json

@@ -22,8 +22,8 @@
   "act-createBoard": "přidal(a) tablo __board__",
   "act-createBoard": "přidal(a) tablo __board__",
   "act-createSwimlane": "created swimlane __swimlane__ to board __board__",
   "act-createSwimlane": "created swimlane __swimlane__ to board __board__",
   "act-createCard": "přidal(a) kartu __card__ do sloupce __list__ ve swimlane __swimlane__ na tablu __board__",
   "act-createCard": "přidal(a) kartu __card__ do sloupce __list__ ve swimlane __swimlane__ na tablu __board__",
-  "act-createCustomField": "created custom field __customField__ at board __board__",
-  "act-deleteCustomField": "deleted custom field __customField__ at board __board__",
+  "act-createCustomField": "přidal(a) pole __customField__ na tablo __board__",
+  "act-deleteCustomField": "odebral(a) pole __customField__ na tablu __board__",
   "act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
   "act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
   "act-createList": "přidal(a) sloupec __list__ do tabla __board__",
   "act-createList": "přidal(a) sloupec __list__ do tabla __board__",
   "act-addBoardMember": "přidal(a) člena __member__ do tabla __board__",
   "act-addBoardMember": "přidal(a) člena __member__ do tabla __board__",
@@ -64,7 +64,7 @@
   "activity-unchecked-item": "nedokončen %s v seznamu %s z %s",
   "activity-unchecked-item": "nedokončen %s v seznamu %s z %s",
   "activity-checklist-added": "přidán checklist do %s",
   "activity-checklist-added": "přidán checklist do %s",
   "activity-checklist-removed": "odstraněn checklist z %s",
   "activity-checklist-removed": "odstraněn checklist z %s",
-  "activity-checklist-completed": "dokončil(a) zaškrtávací seznam __checklist__ na kartě __card__ ve sloupci __list__ ve swimlane __swimlane__ na tablu __board__",
+  "activity-checklist-completed": "completed checklist %s of %s",
   "activity-checklist-uncompleted": "nedokončen seznam %s z %s",
   "activity-checklist-uncompleted": "nedokončen seznam %s z %s",
   "activity-checklist-item-added": "přidána položka checklist do '%s' v %s",
   "activity-checklist-item-added": "přidána položka checklist do '%s' v %s",
   "activity-checklist-item-removed": "odstraněna položka seznamu do '%s' v %s",
   "activity-checklist-item-removed": "odstraněna položka seznamu do '%s' v %s",
@@ -137,7 +137,7 @@
   "board-view": "Náhled tabla",
   "board-view": "Náhled tabla",
   "board-view-cal": "Kalendář",
   "board-view-cal": "Kalendář",
   "board-view-swimlanes": "Swimlanes",
   "board-view-swimlanes": "Swimlanes",
-  "board-view-collapse": "Collapse",
+  "board-view-collapse": "Sbalit",
   "board-view-lists": "Sloupce",
   "board-view-lists": "Sloupce",
   "bucket-example": "Například \"O čem sním\"",
   "bucket-example": "Například \"O čem sním\"",
   "cancel": "Zrušit",
   "cancel": "Zrušit",
@@ -152,6 +152,8 @@
   "card-spent": "Strávený čas",
   "card-spent": "Strávený čas",
   "card-edit-attachments": "Upravit přílohy",
   "card-edit-attachments": "Upravit přílohy",
   "card-edit-custom-fields": "Upravit vlastní pole",
   "card-edit-custom-fields": "Upravit vlastní pole",
+  "card-start-voting": "Start voting",
+  "card-cancel-voting": "Delete voting and all votes",
   "card-edit-labels": "Upravit štítky",
   "card-edit-labels": "Upravit štítky",
   "card-edit-members": "Upravit členy",
   "card-edit-members": "Upravit členy",
   "card-labels-title": "Změnit štítky karty.",
   "card-labels-title": "Změnit štítky karty.",
@@ -161,6 +163,14 @@
   "cardAttachmentsPopup-title": "Přiložit formulář",
   "cardAttachmentsPopup-title": "Přiložit formulář",
   "cardCustomField-datePopup-title": "Změnit datum",
   "cardCustomField-datePopup-title": "Změnit datum",
   "cardCustomFieldsPopup-title": "Upravit vlastní pole",
   "cardCustomFieldsPopup-title": "Upravit vlastní pole",
+  "cardStartVotingPopup-title": "Start a vote",
+  "positiveVoteMembersPopup-title": "Proponents",
+  "negativeVoteMembersPopup-title": "Opponents",
+  "allowNonBoardMembers": "Allow anonymous vote on public board",
+  "vote-question": "Voting question",
+  "vote-public": "Show who voted what",
+  "vote-for-it": "for it",
+  "vote-against": "against",
   "cardDeletePopup-title": "Smazat kartu?",
   "cardDeletePopup-title": "Smazat kartu?",
   "cardDetailsActionsPopup-title": "Akce karty",
   "cardDetailsActionsPopup-title": "Akce karty",
   "cardLabelsPopup-title": "Štítky",
   "cardLabelsPopup-title": "Štítky",
@@ -319,9 +329,10 @@
   "filter-clear": "Vyčistit filtr",
   "filter-clear": "Vyčistit filtr",
   "filter-no-label": "Žádný štítek",
   "filter-no-label": "Žádný štítek",
   "filter-no-member": "Žádný člen",
   "filter-no-member": "Žádný člen",
+  "filter-no-assignee": "No assignee",
   "filter-no-custom-fields": "Žádné vlastní pole",
   "filter-no-custom-fields": "Žádné vlastní pole",
-  "filter-show-archive": "Show archived lists",
-  "filter-hide-empty": "Hide empty lists",
+  "filter-show-archive": "Zobrazit archivované listy",
+  "filter-hide-empty": "Skrýt prázdné listy",
   "filter-on": "Filtr je zapnut",
   "filter-on": "Filtr je zapnut",
   "filter-on-desc": "Filtrujete karty tohoto tabla. Pro úpravu filtru klikni sem.",
   "filter-on-desc": "Filtrujete karty tohoto tabla. Pro úpravu filtru klikni sem.",
   "filter-to-selection": "Filtrovat výběr",
   "filter-to-selection": "Filtrovat výběr",
@@ -338,8 +349,6 @@
   "import-board-c": "Importovat tablo",
   "import-board-c": "Importovat tablo",
   "import-board-title-trello": "Import board from Trello",
   "import-board-title-trello": "Import board from Trello",
   "import-board-title-wekan": "Importovat tablo z předchozího exportu",
   "import-board-title-wekan": "Importovat tablo z předchozího exportu",
-  "import-sandstorm-backup-warning": "Nemažte data, která importujete z původního exportovaného tabla nebo Trello předtím, nežli zkontrolujete, jestli lze tuto část zavřít a znovu otevřít nebo jestli se Vám nezobrazuje chyba tabla, což znamená ztrátu dat.",
-  "import-sandstorm-warning": "Importované tablo spaže všechny existující data v tablu a nahradí je importovaným tablem.",
   "from-trello": "Z Trella",
   "from-trello": "Z Trella",
   "from-wekan": "Z předchozího exportu",
   "from-wekan": "Z předchozího exportu",
   "import-board-instruction-trello": "Na svém Trello tablu, otevři 'Menu', pak 'More', 'Print and Export', 'Export JSON', a zkopíruj výsledný text",
   "import-board-instruction-trello": "Na svém Trello tablu, otevři 'Menu', pak 'More', 'Print and Export', 'Export JSON', a zkopíruj výsledný text",
@@ -440,7 +449,7 @@
   "save": "Uložit",
   "save": "Uložit",
   "search": "Hledat",
   "search": "Hledat",
   "rules": "Pravidla",
   "rules": "Pravidla",
-  "search-cards": "Search from card/list titles and descriptions on this board",
+  "search-cards": "Search from card/list titles, descriptions and custom fields on this board",
   "search-example": "Hledaný text",
   "search-example": "Hledaný text",
   "select-color": "Vybrat barvu",
   "select-color": "Vybrat barvu",
   "set-wip-limit-value": "Nastaví limit pro maximální počet úkolů ve sloupci.",
   "set-wip-limit-value": "Nastaví limit pro maximální počet úkolů ve sloupci.",
@@ -665,6 +674,7 @@
   "r-of-checklist": "ze zaškrtávacího seznamu",
   "r-of-checklist": "ze zaškrtávacího seznamu",
   "r-send-email": "Odeslat e-mail",
   "r-send-email": "Odeslat e-mail",
   "r-to": "komu",
   "r-to": "komu",
+  "r-of": "of",
   "r-subject": "předmět",
   "r-subject": "předmět",
   "r-rule-details": "Podrobnosti pravidla",
   "r-rule-details": "Podrobnosti pravidla",
   "r-d-move-to-top-gen": "Přesunout kartu na začátek toho sloupce",
   "r-d-move-to-top-gen": "Přesunout kartu na začátek toho sloupce",
@@ -749,12 +759,30 @@
   "act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
   "act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
   "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
   "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
   "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
   "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
-  "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
-  "accounts-allowUserDelete": "Allow users to self delete their account",
+  "delete-user-confirm-popup": "Jste si jisti, že chcete smazat tento účet? Tuto akci nelze vrátit zpět.",
+  "accounts-allowUserDelete": "Dovolit uživatelům smazat vlastní účet",
   "hide-minicard-label-text": "Hide minicard label text",
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
   "assignee": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
   "cardAssigneesPopup-title": "Assignee",
-  "addmore-detail": "Add a more detailed description",
-  "show-on-card": "Show on Card"
+  "addmore-detail": "Přidat detailnější popis",
+  "show-on-card": "Zobrazit na kartě",
+  "new": "Nový",
+  "editUserPopup-title": "Editovat uživatele",
+  "newUserPopup-title": "Nový uživatel",
+  "notifications": "Upozornění",
+  "view-all": "Zobrazit vše",
+  "filter-by-unread": "Filter by Unread",
+  "mark-all-as-read": "Označit vše jako přečtené",
+  "remove-all-read": "Remove all read",
+  "allow-rename": "Povolit přejmenování",
+  "allowRenamePopup-title": "Povolit přejmenování",
+  "start-day-of-week": "Set day of the week start",
+  "monday": "Monday",
+  "tuesday": "Tuesday",
+  "wednesday": "Wednesday",
+  "thursday": "Thursday",
+  "friday": "Friday",
+  "saturday": "Saturday",
+  "sunday": "Sunday"
 }
 }

+ 761 - 733
i18n/da.i18n.json

@@ -1,760 +1,788 @@
 {
 {
-  "accept": "Accepter",
-  "act-activity-notify": "Activity Notification",
-  "act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addSubtask": "added subtask __subtask__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addedLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removedLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addChecklist": "added checklist __checklist__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addChecklistItem": "added checklist item __checklistItem__ to checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeChecklist": "removed checklist __checklist__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeChecklistItem": "removed checklist item __checklistItem__ from checklist __checkList__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-checkedItem": "checked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-uncheckedItem": "unchecked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-completeChecklist": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-uncompleteChecklist": "uncompleted checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addComment": "commented on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-editComment": "edited comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-deleteComment": "deleted comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-createBoard": "created board __board__",
-  "act-createSwimlane": "created swimlane __swimlane__ to board __board__",
-  "act-createCard": "created card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-createCustomField": "created custom field __customField__ at board __board__",
-  "act-deleteCustomField": "deleted custom field __customField__ at board __board__",
-  "act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-createList": "added list __list__ to board __board__",
-  "act-addBoardMember": "added member __member__ to board __board__",
-  "act-archivedBoard": "Board __board__ moved to Archive",
-  "act-archivedCard": "Card __card__ at list __list__ at swimlane __swimlane__ at board __board__ moved to Archive",
-  "act-archivedList": "List __list__ at swimlane __swimlane__ at board __board__ moved to Archive",
-  "act-archivedSwimlane": "Swimlane __swimlane__ at board __board__ moved to Archive",
-  "act-importBoard": "imported board __board__",
-  "act-importCard": "imported card  __card__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-importList": "imported list __list__ to swimlane __swimlane__ at board __board__",
-  "act-joinMember": "added member __member__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-moveCard": "moved card __card__ at board __board__ from list __oldList__ at swimlane __oldSwimlane__ to list __list__ at swimlane __swimlane__",
-  "act-moveCardToOtherBoard": "moved card __card__ from list __oldList__ at swimlane __oldSwimlane__ at board __oldBoard__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeBoardMember": "removed member __member__ from board __board__",
-  "act-restoredCard": "restored card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-unjoinMember": "removed member __member__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "accept": "Acceptér",
+  "act-activity-notify": "Aktivitetsnotits",
+  "act-addAttachment": "tilføjede vedhæftningen __attachment__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-deleteAttachment": "slettede vedhæftning __attachment__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-addSubtask": "tilføjede delopgaven __subtask__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-addLabel": "Tilføjede etiketten __label__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-addedLabel": "Tilføjede etiketten __label__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-removeLabel": "Fjernede etiketten __label__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på kortet __board__",
+  "act-removedLabel": "Fjernede etiketten __label__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på kortet __board__",
+  "act-addChecklist": "tilføjede tjeklisten __checklist__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-addChecklistItem": "tilføjede elementet i tjekliste __checklistItem__ til tjeklisten __checklist__ i kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-removeChecklist": "fjernede tjeklisten __checklist__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-removeChecklistItem": "fjernede elementet i tjekliste __checklistItem__ fra tjeklisten __checkList__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-checkedItem": "markerede __checklistItem__ fra tjeklisten __checklist__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-uncheckedItem": "afmarkerede __checklistItem__ fra tjeklisten __checklist__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-completeChecklist": "afsluttet tjekliste __checklist__ i kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-uncompleteChecklist": "uafsluttet tjekliste __checklist__ i kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-addComment": "kommenterede på kortet __card__: __comment__ til listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-editComment": "redigerede kommentar på kortet __card__: __comment__ til listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-deleteComment": "slettede kommentar på kortet __card__: __comment__ til listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-createBoard": "oprettede tavlen __board__",
+  "act-createSwimlane": "oprettede svømmebanen __swimlane__ på tavlen __board__",
+  "act-createCard": "oprettede kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-createCustomField": "oprettede brugerdefineret felt __customField__ på tavlen __board__",
+  "act-deleteCustomField": "slettede brugerdefineret felt __customField__ på tavlen __board__",
+  "act-setCustomField": "redigerede brugerdefineret felt __customField__: __customFieldValue__ i kortet __card__ på lsten __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-createList": "tilføjede listen __list__ til tavlen __board__",
+  "act-addBoardMember": "tilføejede medlemmet __member__ til tavlen __board__",
+  "act-archivedBoard": "Tavlen __board__ blev flyttet til Arkiv",
+  "act-archivedCard": "Kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__ er flyttet til arkivet",
+  "act-archivedList": "Listen __list__ i svømmebanen __swimlane__ på tavlen __board__ er flyttet til arkivet",
+  "act-archivedSwimlane": "Svømmebanen __swimlane__ på tavlen __board__ er flyttet til arkivet",
+  "act-importBoard": "importerede tavlen __board__",
+  "act-importCard": "importerede kortet  __card__ til listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-importList": "importerede listen __list__ til svømmebanen __swimlane__ på tavlen __board__",
+  "act-joinMember": "tilføjede medlemmet __member__ til kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-moveCard": "flyttede kortet __card__ på tavlen __board__ fra listen __oldList__ i svømmebanen __oldSwimlane__ til listen __list__ i svømmebanen __swimlane__",
+  "act-moveCardToOtherBoard": "flyttede kortet __card__ fra listen __oldList__ i svømmebanen __oldSwimlane__ på tavlen __oldBoard__ til listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-removeBoardMember": "fjernede medlemmet __member__ fra tavlen __board__",
+  "act-restoredCard": "genskabte kortet __card__ til listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "act-unjoinMember": "fjernede medlemmet __member__ fra kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
   "act-withBoardTitle": "__board__",
   "act-withBoardTitle": "__board__",
   "act-withCardTitle": "[__board__] __card__",
   "act-withCardTitle": "[__board__] __card__",
-  "actions": "Actions",
-  "activities": "Activities",
-  "activity": "Activity",
-  "activity-added": "added %s to %s",
-  "activity-archived": "%s moved to Archive",
-  "activity-attached": "attached %s to %s",
-  "activity-created": "created %s",
-  "activity-customfield-created": "created custom field %s",
-  "activity-excluded": "excluded %s from %s",
-  "activity-imported": "imported %s into %s from %s",
-  "activity-imported-board": "imported %s from %s",
-  "activity-joined": "joined %s",
-  "activity-moved": "moved %s from %s to %s",
-  "activity-on": "on %s",
-  "activity-removed": "removed %s from %s",
-  "activity-sent": "sent %s to %s",
-  "activity-unjoined": "unjoined %s",
-  "activity-subtask-added": "added subtask to %s",
-  "activity-checked-item": "checked %s in checklist %s of %s",
-  "activity-unchecked-item": "unchecked %s in checklist %s of %s",
-  "activity-checklist-added": "added checklist to %s",
-  "activity-checklist-removed": "removed a checklist from %s",
-  "activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "activity-checklist-uncompleted": "uncompleted the checklist %s of %s",
-  "activity-checklist-item-added": "added checklist item to '%s' in %s",
-  "activity-checklist-item-removed": "removed a checklist item from '%s' in %s",
-  "add": "Add",
-  "activity-checked-item-card": "checked %s in checklist %s",
-  "activity-unchecked-item-card": "unchecked %s in checklist %s",
-  "activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "activity-checklist-uncompleted-card": "uncompleted the checklist %s",
-  "activity-editComment": "edited comment %s",
-  "activity-deleteComment": "deleted comment %s",
-  "add-attachment": "Add Attachment",
-  "add-board": "Add Board",
-  "add-card": "Add Card",
-  "add-swimlane": "Add Swimlane",
-  "add-subtask": "Add Subtask",
-  "add-checklist": "Add Checklist",
-  "add-checklist-item": "Add an item to checklist",
-  "add-cover": "Add Cover",
-  "add-label": "Add Label",
-  "add-list": "Add List",
-  "add-members": "Add Members",
-  "added": "Added",
-  "addMemberPopup-title": "Members",
+  "actions": "Handlinger",
+  "activities": "Aktiviteter",
+  "activity": "Aktivitet",
+  "activity-added": "tilføjede %s til %s",
+  "activity-archived": "%s flyttet til Arkiv",
+  "activity-attached": "vedhæftede %s til %s",
+  "activity-created": "oprettede %s",
+  "activity-customfield-created": "oprettede brugerdefineret felt %s",
+  "activity-excluded": "ekskluderet %s fra %s",
+  "activity-imported": "importerede %s ind i %s fra %s",
+  "activity-imported-board": "importerede %s fra %s",
+  "activity-joined": "indgik i  %s",
+  "activity-moved": "flyttede %s fra %s til %s",
+  "activity-on": "per %s",
+  "activity-removed": "fjernede %s fra %s",
+  "activity-sent": "sendte %s til %s",
+  "activity-unjoined": "udgik fra %s",
+  "activity-subtask-added": "tilføjede delopgave til %s",
+  "activity-checked-item": "afkrydsede %s i tjeklisten %s af %s",
+  "activity-unchecked-item": "fjernede kryds %s i tjeklisten %s af %s",
+  "activity-checklist-added": "tilføjede tjeklisten til %s",
+  "activity-checklist-removed": "fjernede en tjekliste fra %s",
+  "activity-checklist-completed": "færdiggjorde tjekliste %s af %s",
+  "activity-checklist-uncompleted": "gjorde tjeklisten ukomplet, %s af %s",
+  "activity-checklist-item-added": "tilføjede element i tjekliste til '%s' i %s",
+  "activity-checklist-item-removed": "fjernede element i tjekliste fra  '%s' i %s",
+  "add": "Tilføj",
+  "activity-checked-item-card": "markerede %s i tjeklisten %s",
+  "activity-unchecked-item-card": "afmarkerede %s i tjeklisten %s",
+  "activity-checklist-completed-card": "udført tjekliste __checklist__ i kortet __card__ på listen __list__ i svømmebanen __swimlane__ på tavlen __board__",
+  "activity-checklist-uncompleted-card": "gjorde tjeklisten ukomplet %s",
+  "activity-editComment": "redigerede kommentar %s",
+  "activity-deleteComment": "slettede kommentar %s",
+  "add-attachment": "Tilføj vedhæftning",
+  "add-board": "Tilføj tavle",
+  "add-card": "Tilføj kort",
+  "add-swimlane": "Tilføj svømmebane",
+  "add-subtask": "Tilføj delopgave",
+  "add-checklist": "Tilføj tjekliste",
+  "add-checklist-item": "Tilføj et element til tjeklisten",
+  "add-cover": "Tilføj omslag",
+  "add-label": "Tilføj etikette",
+  "add-list": "Tilføj liste",
+  "add-members": "Tilføj medlemmer",
+  "added": "Tilføjet",
+  "addMemberPopup-title": "Medlemmer",
   "admin": "Admin",
   "admin": "Admin",
-  "admin-desc": "Can view and edit cards, remove members, and change settings for the board.",
-  "admin-announcement": "Announcement",
-  "admin-announcement-active": "Active System-Wide Announcement",
-  "admin-announcement-title": "Announcement from Administrator",
-  "all-boards": "All boards",
-  "and-n-other-card": "And __count__ other card",
-  "and-n-other-card_plural": "And __count__ other cards",
-  "apply": "Apply",
-  "app-is-offline": "Loading, please wait. Refreshing the page will cause data loss. If loading does not work, please check that server has not stopped.",
-  "archive": "Move to Archive",
-  "archive-all": "Move All to Archive",
-  "archive-board": "Move Board to Archive",
-  "archive-card": "Move Card to Archive",
-  "archive-list": "Move List to Archive",
-  "archive-swimlane": "Move Swimlane to Archive",
-  "archive-selection": "Move selection to Archive",
-  "archiveBoardPopup-title": "Move Board to Archive?",
-  "archived-items": "Archive",
-  "archived-boards": "Boards in Archive",
-  "restore-board": "Restore Board",
-  "no-archived-boards": "No Boards in Archive.",
-  "archives": "Archive",
-  "template": "Template",
-  "templates": "Templates",
-  "assign-member": "Assign member",
-  "attached": "attached",
-  "attachment": "Attachment",
-  "attachment-delete-pop": "Deleting an attachment is permanent. There is no undo.",
-  "attachmentDeletePopup-title": "Delete Attachment?",
-  "attachments": "Attachments",
-  "auto-watch": "Automatically watch boards when they are created",
-  "avatar-too-big": "The avatar is too large (70KB max)",
-  "back": "Back",
-  "board-change-color": "Change color",
-  "board-nb-stars": "%s stars",
-  "board-not-found": "Board not found",
-  "board-private-info": "This board will be <strong>private</strong>.",
-  "board-public-info": "This board will be <strong>public</strong>.",
-  "boardChangeColorPopup-title": "Change Board Background",
-  "boardChangeTitlePopup-title": "Rename Board",
-  "boardChangeVisibilityPopup-title": "Change Visibility",
-  "boardChangeWatchPopup-title": "Change Watch",
-  "boardMenuPopup-title": "Board Settings",
-  "boardChangeViewPopup-title": "Board View",
-  "boards": "Boards",
-  "board-view": "Board View",
-  "board-view-cal": "Calendar",
-  "board-view-swimlanes": "Swimlanes",
-  "board-view-collapse": "Collapse",
-  "board-view-lists": "Lists",
-  "bucket-example": "Like “Bucket List” for example",
-  "cancel": "Cancel",
-  "card-archived": "This card is moved to Archive.",
-  "board-archived": "This board is moved to Archive.",
-  "card-comments-title": "This card has %s comment.",
-  "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
-  "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
-  "card-delete-suggest-archive": "You can move a card to Archive to remove it from the board and preserve the activity.",
-  "card-due": "Due",
-  "card-due-on": "Due on",
-  "card-spent": "Spent Time",
-  "card-edit-attachments": "Edit attachments",
-  "card-edit-custom-fields": "Edit custom fields",
-  "card-edit-labels": "Edit labels",
-  "card-edit-members": "Edit members",
-  "card-labels-title": "Change the labels for the card.",
-  "card-members-title": "Add or remove members of the board from the card.",
+  "admin-desc": "Kan se og redigere kort, fjerne medlemmer og ændre indstillinger for tavlen.",
+  "admin-announcement": "Annoncering",
+  "admin-announcement-active": "Aktivér annoncering på tværs af systemet",
+  "admin-announcement-title": "Annoncering fra administrator",
+  "all-boards": "Alle tavler",
+  "and-n-other-card": "Samt __count__ andre kort",
+  "and-n-other-card_plural": "Samt __count__ andre kort",
+  "apply": "Anvend",
+  "app-is-offline": "Indlæser, vent venligst. Genopfriskes siden er der risiko for tab af data. Fungerer indlæsningen ikke, så tjek venligst om serveren er stoppet. ",
+  "archive": "Flyt til arkiv",
+  "archive-all": "Flyt alle til arkiv",
+  "archive-board": "Flyt tavle til arkiv",
+  "archive-card": "Flyt kort til arkiv",
+  "archive-list": "Flyt liste til arkiv",
+  "archive-swimlane": "Flyt svømmebane til arkiv",
+  "archive-selection": "Flyt valgte til arkiv",
+  "archiveBoardPopup-title": "Flyt tavle til arkiv?",
+  "archived-items": "Arkiv",
+  "archived-boards": "Tavler i arkiv",
+  "restore-board": "Genskab tavle",
+  "no-archived-boards": "Ingen tavler i arkiv",
+  "archives": "Arkiv",
+  "template": "Skabelon",
+  "templates": "Skabeloner",
+  "assign-member": "Tilknyt medlem",
+  "attached": "vedhæftet",
+  "attachment": "Vedhæftning",
+  "attachment-delete-pop": "Slettes en vedhæftning sker det permanent. Det kan ikke omgøres. ",
+  "attachmentDeletePopup-title": "Slet vedhæftning?",
+  "attachments": "Vedhæftninger",
+  "auto-watch": "Følg automatisk tavler når de oprettes ",
+  "avatar-too-big": "Avataren fylder for meget (maks. 70KB)",
+  "back": "Tilbage",
+  "board-change-color": "Skift farve",
+  "board-nb-stars": "%s stjerner",
+  "board-not-found": "Fandt ikke tavle ",
+  "board-private-info": "Denne tavle vil være <strong>privat</strong>.",
+  "board-public-info": "Denne tavle vil være <strong>offentlig</strong>.",
+  "boardChangeColorPopup-title": "Skift tavlens baggrund",
+  "boardChangeTitlePopup-title": "Omdøb tavle",
+  "boardChangeVisibilityPopup-title": "Tilpas synlighed",
+  "boardChangeWatchPopup-title": "Tilpas følgefunktion",
+  "boardMenuPopup-title": "Tavleindstillinger",
+  "boardChangeViewPopup-title": "Tavlevisning",
+  "boards": "Tavler",
+  "board-view": "Tavlevisning",
+  "board-view-cal": "Kalender",
+  "board-view-swimlanes": "Svømmebaner",
+  "board-view-collapse": "Sammenfold",
+  "board-view-lists": "Lister",
+  "bucket-example": "Eksempelvis \"Bucked-liste\"",
+  "cancel": "Annullér",
+  "card-archived": "Dette kort blev flyttet til arkivet.",
+  "board-archived": "Denne tavle blev flyttet til arkivet.",
+  "card-comments-title": "Dette kort har %s kommentar.",
+  "card-delete-notice": "Sletning vil være permanent. Du mister alle handlinger knyttet til dette kort.",
+  "card-delete-pop": "Alle handlinger vil blive fjernet fra aktivitetsfeedet, og du kan ikke genåbne kortet. Det kan ikke omgøres.",
+  "card-delete-suggest-archive": "Du kan flytte et kort til arkivet for at fjerne det fra tavlen, og bevare aktiviteten.",
+  "card-due": "Forfalder",
+  "card-due-on": "Forfaldsdato",
+  "card-spent": "Anvendt tid",
+  "card-edit-attachments": "Redigér vedhæftninger",
+  "card-edit-custom-fields": "Redigér brugerdefinerede felter",
+  "card-start-voting": "Start stemmegivning",
+  "card-cancel-voting": "Slet stemmegivning og alle stemmer",
+  "card-edit-labels": "Redigér etiketter",
+  "card-edit-members": "Redigér medlemmer",
+  "card-labels-title": "Ændr etiketter for kortet.",
+  "card-members-title": "Tilføj eller fjern medlemmer på tavlen fra kortet.",
   "card-start": "Start",
   "card-start": "Start",
-  "card-start-on": "Starts on",
-  "cardAttachmentsPopup-title": "Attach From",
-  "cardCustomField-datePopup-title": "Change date",
-  "cardCustomFieldsPopup-title": "Edit custom fields",
-  "cardDeletePopup-title": "Delete Card?",
-  "cardDetailsActionsPopup-title": "Card Actions",
-  "cardLabelsPopup-title": "Labels",
-  "cardMembersPopup-title": "Members",
-  "cardMorePopup-title": "More",
-  "cardTemplatePopup-title": "Create template",
-  "cards": "Cards",
-  "cards-count": "Cards",
-  "casSignIn": "Sign In with CAS",
-  "cardType-card": "Card",
-  "cardType-linkedCard": "Linked Card",
-  "cardType-linkedBoard": "Linked Board",
-  "change": "Change",
-  "change-avatar": "Change Avatar",
-  "change-password": "Change Password",
-  "change-permissions": "Change permissions",
-  "change-settings": "Change Settings",
-  "changeAvatarPopup-title": "Change Avatar",
-  "changeLanguagePopup-title": "Change Language",
-  "changePasswordPopup-title": "Change Password",
-  "changePermissionsPopup-title": "Change Permissions",
-  "changeSettingsPopup-title": "Change Settings",
-  "subtasks": "Subtasks",
-  "checklists": "Checklists",
-  "click-to-star": "Click to star this board.",
-  "click-to-unstar": "Click to unstar this board.",
-  "clipboard": "Clipboard or drag & drop",
-  "close": "Close",
-  "close-board": "Close Board",
-  "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
-  "color-black": "black",
-  "color-blue": "blue",
-  "color-crimson": "crimson",
-  "color-darkgreen": "darkgreen",
-  "color-gold": "gold",
-  "color-gray": "gray",
-  "color-green": "green",
-  "color-indigo": "indigo",
+  "card-start-on": "Starter per",
+  "cardAttachmentsPopup-title": "Vedhæft fra",
+  "cardCustomField-datePopup-title": "Ændringsdato",
+  "cardCustomFieldsPopup-title": "Redigér brugerdefinerede felter",
+  "cardStartVotingPopup-title": "Start en stemmeafgivning",
+  "positiveVoteMembersPopup-title": "Tilhængere",
+  "negativeVoteMembersPopup-title": "Modstandere",
+  "allowNonBoardMembers": "Tillad anonym stemmeafgivning på offentlige tavler",
+  "vote-question": "Spørgsmål til afstemning",
+  "vote-public": "Vis hvem som stemte hvad",
+  "vote-for-it": "går ind for",
+  "vote-against": "går imod",
+  "cardDeletePopup-title": "Slet kort?",
+  "cardDetailsActionsPopup-title": "Handlinger for kort",
+  "cardLabelsPopup-title": "Etiketter",
+  "cardMembersPopup-title": "Medlemmer",
+  "cardMorePopup-title": "Mere",
+  "cardTemplatePopup-title": "Opret skabelon",
+  "cards": "Kort",
+  "cards-count": "Kort",
+  "casSignIn": "Log ind med CAS",
+  "cardType-card": "Kort",
+  "cardType-linkedCard": "Sammenkædet kort",
+  "cardType-linkedBoard": "Sammenkædet tavle",
+  "change": "Tilpas",
+  "change-avatar": "Tilpas avatar",
+  "change-password": "Skift kodeord",
+  "change-permissions": "Tilpas tilladelser",
+  "change-settings": "Tilpas indstillinger",
+  "changeAvatarPopup-title": "Tilpas avatar",
+  "changeLanguagePopup-title": "Skift sprog",
+  "changePasswordPopup-title": "Skift kodeord",
+  "changePermissionsPopup-title": "Tilpas tilladelser",
+  "changeSettingsPopup-title": "Tilpas indstillinger",
+  "subtasks": "Delopgaver",
+  "checklists": "Tjeklister",
+  "click-to-star": "Klik for at tilføje stjerne til tavlen.",
+  "click-to-unstar": "Klik for at fjerne stjerne fra tavlen.",
+  "clipboard": "Udklipsholder eller træk-og-slip",
+  "close": "Luk",
+  "close-board": "Luk tavle",
+  "close-board-pop": "Du har mulighed for at genskabe tavlen ved at klikke på \"Arkiv\"-knappen fra overskriften hjem.",
+  "color-black": "sort",
+  "color-blue": "blå",
+  "color-crimson": "crimsonrød",
+  "color-darkgreen": "mørkegrøn",
+  "color-gold": "guld",
+  "color-gray": "grå",
+  "color-green": "grøn",
+  "color-indigo": "indigoblå",
   "color-lime": "lime",
   "color-lime": "lime",
   "color-magenta": "magenta",
   "color-magenta": "magenta",
-  "color-mistyrose": "mistyrose",
-  "color-navy": "navy",
+  "color-mistyrose": "lyserød",
+  "color-navy": "navyblå",
   "color-orange": "orange",
   "color-orange": "orange",
-  "color-paleturquoise": "paleturquoise",
-  "color-peachpuff": "peachpuff",
+  "color-paleturquoise": "bleg turkis",
+  "color-peachpuff": "ferskenfarvet",
   "color-pink": "pink",
   "color-pink": "pink",
-  "color-plum": "plum",
-  "color-purple": "purple",
-  "color-red": "red",
-  "color-saddlebrown": "saddlebrown",
-  "color-silver": "silver",
+  "color-plum": "blommefarvet",
+  "color-purple": "lilla",
+  "color-red": "rød",
+  "color-saddlebrown": "saddelbrun",
+  "color-silver": "sølv",
   "color-sky": "sky",
   "color-sky": "sky",
-  "color-slateblue": "slateblue",
-  "color-white": "white",
-  "color-yellow": "yellow",
-  "unset-color": "Unset",
-  "comment": "Comment",
-  "comment-placeholder": "Write Comment",
-  "comment-only": "Comment only",
-  "comment-only-desc": "Can comment on cards only.",
-  "no-comments": "No comments",
-  "no-comments-desc": "Can not see comments and activities.",
-  "worker": "Worker",
-  "worker-desc": "Can only move cards, assign itself to card and comment.",
+  "color-slateblue": "blågrå",
+  "color-white": "hvid",
+  "color-yellow": "gul",
+  "unset-color": "Nulstil",
+  "comment": "Kommentér",
+  "comment-placeholder": "Skriv kommentar",
+  "comment-only": "Kun kommentarer",
+  "comment-only-desc": "Kan kun kommentere på kort.",
+  "no-comments": "Ingen kommentarer",
+  "no-comments-desc": "Kan ikke se kommentarer og aktiviteter.",
+  "worker": "Arbejder",
+  "worker-desc": "Kan kun flytte kort, tildele sig selv til kort og kommentere.",
   "computer": "Computer",
   "computer": "Computer",
-  "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
-  "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
-  "copy-card-link-to-clipboard": "Copy card link to clipboard",
-  "linkCardPopup-title": "Link Card",
-  "searchElementPopup-title": "Search",
-  "copyCardPopup-title": "Copy Card",
-  "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
-  "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
+  "confirm-subtask-delete-dialog": "Er du sikker på du vil slette delopgaven?",
+  "confirm-checklist-delete-dialog": "Er du sikker på du vil slette tjeklisten?",
+  "copy-card-link-to-clipboard": "Kopiér link til kort til udklipsholder ",
+  "linkCardPopup-title": "Sammenkæd kort",
+  "searchElementPopup-title": "Søg",
+  "copyCardPopup-title": "Kopiér kort",
+  "copyChecklistToManyCardsPopup-title": "Kopiér tjeklisteskabelon til flere kort",
+  "copyChecklistToManyCardsPopup-instructions": "Destination for kortenes titler og beskrivelser i dette JSON-format",
   "copyChecklistToManyCardsPopup-format": "[ {\"title\": \"First card title\", \"description\":\"First card description\"}, {\"title\":\"Second card title\",\"description\":\"Second card description\"},{\"title\":\"Last card title\",\"description\":\"Last card description\"} ]",
   "copyChecklistToManyCardsPopup-format": "[ {\"title\": \"First card title\", \"description\":\"First card description\"}, {\"title\":\"Second card title\",\"description\":\"Second card description\"},{\"title\":\"Last card title\",\"description\":\"Last card description\"} ]",
-  "create": "Create",
-  "createBoardPopup-title": "Create Board",
-  "chooseBoardSourcePopup-title": "Import board",
-  "createLabelPopup-title": "Create Label",
-  "createCustomField": "Create Field",
-  "createCustomFieldPopup-title": "Create Field",
-  "current": "current",
-  "custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.",
-  "custom-field-checkbox": "Checkbox",
-  "custom-field-date": "Date",
-  "custom-field-dropdown": "Dropdown List",
-  "custom-field-dropdown-none": "(none)",
-  "custom-field-dropdown-options": "List Options",
-  "custom-field-dropdown-options-placeholder": "Press enter to add more options",
-  "custom-field-dropdown-unknown": "(unknown)",
-  "custom-field-number": "Number",
-  "custom-field-text": "Text",
-  "custom-fields": "Custom Fields",
-  "date": "Date",
-  "decline": "Decline",
-  "default-avatar": "Default avatar",
-  "delete": "Delete",
-  "deleteCustomFieldPopup-title": "Delete Custom Field?",
-  "deleteLabelPopup-title": "Delete Label?",
-  "description": "Description",
-  "disambiguateMultiLabelPopup-title": "Disambiguate Label Action",
-  "disambiguateMultiMemberPopup-title": "Disambiguate Member Action",
-  "discard": "Discard",
-  "done": "Done",
-  "download": "Download",
-  "edit": "Edit",
-  "edit-avatar": "Change Avatar",
-  "edit-profile": "Edit Profile",
-  "edit-wip-limit": "Edit WIP Limit",
-  "soft-wip-limit": "Soft WIP Limit",
-  "editCardStartDatePopup-title": "Change start date",
-  "editCardDueDatePopup-title": "Change due date",
-  "editCustomFieldPopup-title": "Edit Field",
-  "editCardSpentTimePopup-title": "Change spent time",
-  "editLabelPopup-title": "Change Label",
-  "editNotificationPopup-title": "Edit Notification",
-  "editProfilePopup-title": "Edit Profile",
-  "email": "Email",
-  "email-enrollAccount-subject": "An account created for you on __siteName__",
-  "email-enrollAccount-text": "Hello __user__,\n\nTo start using the service, simply click the link below.\n\n__url__\n\nThanks.",
-  "email-fail": "Sending email failed",
-  "email-fail-text": "Error trying to send email",
-  "email-invalid": "Invalid email",
-  "email-invite": "Invite via Email",
-  "email-invite-subject": "__inviter__ sent you an invitation",
-  "email-invite-text": "Dear __user__,\n\n__inviter__ invites you to join board \"__board__\" for collaborations.\n\nPlease follow the link below:\n\n__url__\n\nThanks.",
-  "email-resetPassword-subject": "Reset your password on __siteName__",
-  "email-resetPassword-text": "Hello __user__,\n\nTo reset your password, simply click the link below.\n\n__url__\n\nThanks.",
-  "email-sent": "Email sent",
-  "email-verifyEmail-subject": "Verify your email address on __siteName__",
-  "email-verifyEmail-text": "Hello __user__,\n\nTo verify your account email, simply click the link below.\n\n__url__\n\nThanks.",
-  "enable-wip-limit": "Enable WIP Limit",
-  "error-board-doesNotExist": "This board does not exist",
-  "error-board-notAdmin": "You need to be admin of this board to do that",
-  "error-board-notAMember": "You need to be a member of this board to do that",
-  "error-json-malformed": "Your text is not valid JSON",
-  "error-json-schema": "Your JSON data does not include the proper information in the correct format",
-  "error-list-doesNotExist": "This list does not exist",
-  "error-user-doesNotExist": "This user does not exist",
-  "error-user-notAllowSelf": "You can not invite yourself",
-  "error-user-notCreated": "This user is not created",
-  "error-username-taken": "This username is already taken",
-  "error-email-taken": "Email has already been taken",
-  "export-board": "Export board",
-  "sort": "Sort",
-  "sort-desc": "Click to Sort List",
-  "list-sort-by": "Sort the List By:",
-  "list-label-modifiedAt": "Last Access Time",
-  "list-label-title": "Name of the List",
-  "list-label-sort": "Your Manual Order",
-  "list-label-short-modifiedAt": "(L)",
+  "create": "Opret",
+  "createBoardPopup-title": "Opret tavle",
+  "chooseBoardSourcePopup-title": "Importér tavle",
+  "createLabelPopup-title": "Opret etikette",
+  "createCustomField": "Opret felt",
+  "createCustomFieldPopup-title": "Opret felt",
+  "current": "nuværende",
+  "custom-field-delete-pop": "Du kan ikke fortryde handlingen. Dette vil fjerne dette brugerdefinerede felt fra alle kort og tilintetgøre dens historik.",
+  "custom-field-checkbox": "Afkrydsningsfelt",
+  "custom-field-date": "Dato",
+  "custom-field-dropdown": "Rullegardinliste",
+  "custom-field-dropdown-none": "(ingen)",
+  "custom-field-dropdown-options": "Tilvalg for liste",
+  "custom-field-dropdown-options-placeholder": "Tryk enter for at tilføje flere tilvalg",
+  "custom-field-dropdown-unknown": "(ukendt)",
+  "custom-field-number": "Tal",
+  "custom-field-text": "Tekst",
+  "custom-fields": "Brugerdefinerede felter",
+  "date": "Dato",
+  "decline": "Afslå",
+  "default-avatar": "Standard-avatar",
+  "delete": "Slet",
+  "deleteCustomFieldPopup-title": "Slet brugerdefineret felt?",
+  "deleteLabelPopup-title": "Slet etikette?",
+  "description": "Beskrivelse",
+  "disambiguateMultiLabelPopup-title": "Tydeliggør handling for etikette",
+  "disambiguateMultiMemberPopup-title": "Tydeliggør handling for medlem",
+  "discard": "Forkast",
+  "done": "Færdig",
+  "download": "Hent",
+  "edit": "Redigér",
+  "edit-avatar": "Tilpas avatar",
+  "edit-profile": "Redigér profil",
+  "edit-wip-limit": "Redigér WIP-begrænsning",
+  "soft-wip-limit": "Blød WIP-begrænsning",
+  "editCardStartDatePopup-title": "Skift startdato",
+  "editCardDueDatePopup-title": "Skift forfaldsdato",
+  "editCustomFieldPopup-title": "Redigér felt",
+  "editCardSpentTimePopup-title": "Tilpas forbrugt tid",
+  "editLabelPopup-title": "Skift etikette",
+  "editNotificationPopup-title": "Redigér notifikation",
+  "editProfilePopup-title": "Redigér profil",
+  "email": "E-mail",
+  "email-enrollAccount-subject": "Der er oprettet konto til dig på __siteName__",
+  "email-enrollAccount-text": "Hej __user__,\n\nFor at begynde at benytte tjenesten, så klik linket nedenfor.\n\n__url__\n\nTak.",
+  "email-fail": "Afsendelse af e-mail mislykkedes",
+  "email-fail-text": "Fejl under afsendelse af e-mail",
+  "email-invalid": "Ugyldig e-mail",
+  "email-invite": "Invitér via e-mail",
+  "email-invite-subject": "__inviter__ sendte dig en invitation",
+  "email-invite-text": "Kære __user__,\n\n__inviter__ inviterer dig til deltagelse i tavlen \"__board__\" for samarbejde.\n\nFølg venligst linket nedenfor:\n\n__url__\n\nTak.",
+  "email-resetPassword-subject": "Genskab dit kodeord på __siteName__",
+  "email-resetPassword-text": "Hej __user__,\n\nFor at genskabe dit kodeord, så klik linket nedenfor your password.\n\n__url__\n\nTak.",
+  "email-sent": "E-mail er afsendt",
+  "email-verifyEmail-subject": "Verificér din e-mailadresse på your __siteName__",
+  "email-verifyEmail-text": "Hej __user__,\n\nFor at verificere din e-mail for kontoen, så klik på linket nedenfor.\n\n__url__\n\nTak.",
+  "enable-wip-limit": "Slå WIP-begrænsning til",
+  "error-board-doesNotExist": "Denne tavle eksisterer ikke.",
+  "error-board-notAdmin": "Du skal være administrator for tavlen for at gøre dette",
+  "error-board-notAMember": "Du skal være medlem af denne tavle for at gøre dette",
+  "error-json-malformed": "Din tekst er ikke gyldig JSON",
+  "error-json-schema": "Dine JSON-data indeholder ikke den rette information i det rette format",
+  "error-list-doesNotExist": "Listen findes ikke",
+  "error-user-doesNotExist": "Brugeren findes ikke",
+  "error-user-notAllowSelf": "Du kan ikke invitere dig selv",
+  "error-user-notCreated": "Brugeren er ikke oprettet",
+  "error-username-taken": "Brugernavnet er optaget",
+  "error-email-taken": "E-mailadressen er allerede optaget",
+  "export-board": "Eksportér tavle",
+  "sort": "Sortér",
+  "sort-desc": "Klik for at sortere listen",
+  "list-sort-by": "Sortér listen efter:",
+  "list-label-modifiedAt": "Senest tilgået:",
+  "list-label-title": "Navn på listen",
+  "list-label-sort": "Din manuelle ordre",
+  "list-label-short-modifiedAt": "(S)",
   "list-label-short-title": "(N)",
   "list-label-short-title": "(N)",
   "list-label-short-sort": "(M)",
   "list-label-short-sort": "(M)",
   "filter": "Filter",
   "filter": "Filter",
-  "filter-cards": "Filter Cards or Lists",
-  "list-filter-label": "Filter List by Title",
-  "filter-clear": "Clear filter",
-  "filter-no-label": "No label",
-  "filter-no-member": "No member",
-  "filter-no-custom-fields": "No Custom Fields",
-  "filter-show-archive": "Show archived lists",
-  "filter-hide-empty": "Hide empty lists",
-  "filter-on": "Filter is on",
-  "filter-on-desc": "You are filtering cards on this board. Click here to edit filter.",
-  "filter-to-selection": "Filter to selection",
-  "advanced-filter-label": "Advanced Filter",
-  "advanced-filter-description": "Advanced Filter allows to write a string containing following operators: == != <= >= && || ( ) A space is used as a separator between the Operators. You can filter for all Custom Fields by typing their names and values. For Example: Field1 == Value1. Note: If fields or values contains spaces, you need to encapsulate them into single quotes. For Example: 'Field 1' == 'Value 1'. For single control characters (' \\/) to be skipped, you can use \\. For example: Field1 == I\\'m. Also you can combine multiple conditions. For Example: F1 == V1 || F1 == V2. Normally all operators are interpreted from left to right. You can change the order by placing brackets. For Example: F1 == V1 && ( F2 == V2 || F2 == V3 ). Also you can search text fields using regex: F1 == /Tes.*/i",
-  "fullname": "Full Name",
-  "header-logo-title": "Go back to your boards page.",
-  "hide-system-messages": "Hide system messages",
-  "headerBarCreateBoardPopup-title": "Create Board",
-  "home": "Home",
-  "import": "Import",
+  "filter-cards": "Filtrér kort eller lister",
+  "list-filter-label": "Filtrér lister efter titel",
+  "filter-clear": "Ryd filter",
+  "filter-no-label": "Ingen etikette",
+  "filter-no-member": "Ingen medlemmer",
+  "filter-no-assignee": "Utildelt",
+  "filter-no-custom-fields": "Ingen brugerdefinerede felter",
+  "filter-show-archive": "Vis arkiverede lister",
+  "filter-hide-empty": "Skjul tomme lister",
+  "filter-on": "Filter er slået til",
+  "filter-on-desc": "Du filtrerer kort på denne tavle. Klik her for at redigere filteret.",
+  "filter-to-selection": "Filtrér til valgte",
+  "advanced-filter-label": "Avanceret filter",
+  "advanced-filter-description": "Avanceret filter gør det muligt at skrive en tekststreng indeholdende følgende operatører: == != <= >= && || ( )  Mellemrum anvendes som adskillelsestegn mellem operatørerne. Du kan filtrere alle Brugerdefinerede felter ved at taste deres navne og værdier. Som eksempel: Felt1 == Værdi1. Bemærk: Hvis felter eller værdier indeholder mellemrum, så skal du indkapsle dem i enkeltcitationstegn. Som eksempel: 'Felt 1' == 'Værdi1'. For at springe over enkelte kontroltegn (' \\/), så kan \\ benyttes. Som eksempel: Felt1 == Så\\'n. Du kan også kombinere flere betingelser. Som eksempel: F1 == V1 || F1 == V2. Normalt vil alle operatører blive fortolket fra venstre mod højre. Du kan ændre rækkefølgen ved brug af parenteser. Som eksempel: F1 == V1 && (F2 == V2 || F2 == V3). Du kan også søge i tekstfelter med brug af regulære udtryk: F1 == /Tes.*/i",
+  "fullname": "Fuldt navn",
+  "header-logo-title": "Gå tilbage til siden med dine tavler",
+  "hide-system-messages": "Skjul systembeskeder",
+  "headerBarCreateBoardPopup-title": "Opret tavle",
+  "home": "Hjem",
+  "import": "Importér",
   "link": "Link",
   "link": "Link",
-  "import-board": "import board",
-  "import-board-c": "Import board",
-  "import-board-title-trello": "Import board from Trello",
-  "import-board-title-wekan": "Import board from previous export",
-  "import-sandstorm-backup-warning": "Do not delete data you import from original exported board or Trello before checking does this grain close and open again, or do you get Board not found error, that means data loss.",
-  "import-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
-  "from-trello": "From Trello",
-  "from-wekan": "From previous export",
-  "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.",
-  "import-board-instruction-wekan": "In your board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.",
-  "import-board-instruction-about-errors": "If you get errors when importing board, sometimes importing still works, and board is at All Boards page.",
-  "import-json-placeholder": "Paste your valid JSON data here",
-  "import-map-members": "Map members",
-  "import-members-map": "Your imported board has some members. Please map the members you want to import to your users",
-  "import-show-user-mapping": "Review members mapping",
-  "import-user-select": "Pick your existing user you want to use as this member",
-  "importMapMembersAddPopup-title": "Select member",
+  "import-board": "importér tavle",
+  "import-board-c": "Importér tavle",
+  "import-board-title-trello": "Importér tavle fra Trello",
+  "import-board-title-wekan": "Importér tavler fra tidligere eksport",
+  "from-trello": "Fra Trello",
+  "from-wekan": "Fra forrige eksport",
+  "import-board-instruction-trello": "I din Trello-tavle, gå til 'Menu', dernæst 'More', 'Print and Export', 'Export JSON', og kopiér den tekst som vises.",
+  "import-board-instruction-wekan": "På din tavle, gå til 'Menu', dernæst 'Eksportér tavle', og kopiér teksten i den hentede fil.",
+  "import-board-instruction-about-errors": "Hvis du får fejl når der importeres en tavle, så vil importen undertiden stadig fungere, og tavlen vil være under side Alle tavler.",
+  "import-json-placeholder": "Indsæt dine gyldige JSON-data her",
+  "import-map-members": "Kortlæg medlemmer",
+  "import-members-map": "Dine importerede tavler rummer medlemmer. Kortlæg venligst de medlemmer du ønsker at importere til dine brugere.",
+  "import-show-user-mapping": "Gennemse kortlægning af medlemmer",
+  "import-user-select": "Vælg din eksisterende bruger, som du ønsker at bruge for dette medlem",
+  "importMapMembersAddPopup-title": "Vælg medlem",
   "info": "Version",
   "info": "Version",
-  "initials": "Initials",
-  "invalid-date": "Invalid date",
-  "invalid-time": "Invalid time",
-  "invalid-user": "Invalid user",
-  "joined": "joined",
-  "just-invited": "You are just invited to this board",
-  "keyboard-shortcuts": "Keyboard shortcuts",
-  "label-create": "Create Label",
-  "label-default": "%s label (default)",
-  "label-delete-pop": "There is no undo. This will remove this label from all cards and destroy its history.",
-  "labels": "Labels",
-  "language": "Language",
-  "last-admin-desc": "You can’t change roles because there must be at least one admin.",
-  "leave-board": "Leave Board",
-  "leave-board-pop": "Are you sure you want to leave __boardTitle__? You will be removed from all cards on this board.",
-  "leaveBoardPopup-title": "Leave Board ?",
-  "link-card": "Link to this card",
-  "list-archive-cards": "Move all cards in this list to Archive",
-  "list-archive-cards-pop": "This will remove all the cards in this list from the board. To view cards in Archive and bring them back to the board, click “Menu” > “Archive”.",
-  "list-move-cards": "Move all cards in this list",
-  "list-select-cards": "Select all cards in this list",
-  "set-color-list": "Set Color",
-  "listActionPopup-title": "List Actions",
-  "swimlaneActionPopup-title": "Swimlane Actions",
-  "swimlaneAddPopup-title": "Add a Swimlane below",
-  "listImportCardPopup-title": "Import a Trello card",
-  "listMorePopup-title": "More",
-  "link-list": "Link to this list",
-  "list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
-  "list-delete-suggest-archive": "You can move a list to Archive to remove it from the board and preserve the activity.",
-  "lists": "Lists",
-  "swimlanes": "Swimlanes",
-  "log-out": "Log Out",
-  "log-in": "Log In",
-  "loginPopup-title": "Log In",
-  "memberMenuPopup-title": "Member Settings",
-  "members": "Members",
+  "initials": "Initialer",
+  "invalid-date": "Ugyldig dato",
+  "invalid-time": "Ugyldig tidsangivelse",
+  "invalid-user": "Ugyldig bruger",
+  "joined": "sluttede sig til",
+  "just-invited": "Du er netop blevet inviteret til denne tavle",
+  "keyboard-shortcuts": "Tastaturgenveje",
+  "label-create": "Opret etikette",
+  "label-default": "%s etikette (standard)",
+  "label-delete-pop": "Det er ikke muligt at fortryde. Dette vil fjerne etiketten fra alle kort og ødelægge dets historik.",
+  "labels": "Etiketter",
+  "language": "Sprog",
+  "last-admin-desc": "Du kan ikke ændre roller, da der mindst skal være én administrator.",
+  "leave-board": "Forlad tavle",
+  "leave-board-pop": "Er du sikker på du vil forlade __boardTitle__? Du vil blive fjernet fra alle kort på denne tavle.",
+  "leaveBoardPopup-title": "Forlad tavle?",
+  "link-card": "Link til dette kort",
+  "list-archive-cards": "Flyt alle kort i denne liste til arkivet",
+  "list-archive-cards-pop": "Dette vil fjerne alle kort i denne liste fra tavlen. For at se kort i arkivet og bringe dem tilbage til tavlen, så klik \"Menu\" > \"Arkiv\".",
+  "list-move-cards": "Flyt alle kort i denne liste",
+  "list-select-cards": "Vælg alle kort i denne liste",
+  "set-color-list": "Angiv farve",
+  "listActionPopup-title": "Handlinger for liste",
+  "swimlaneActionPopup-title": "Handlinger for svømmebane",
+  "swimlaneAddPopup-title": "Tilføj en svømmebane nedenfor",
+  "listImportCardPopup-title": "Importér et Trello-kort",
+  "listMorePopup-title": "Mere",
+  "link-list": "Link til denne liste",
+  "list-delete-pop": "Alle handlinger vil blive fjernet fra aktivitetsfeedet og du vil ikke have mulighed for at genskabe listen. Der er ingen måder at fortryde. ",
+  "list-delete-suggest-archive": "Du kan flytte en liste til arkivet for at fjerne det fra tavlen og bevare dets aktivitet.",
+  "lists": "Lister",
+  "swimlanes": "Svømmebaner",
+  "log-out": "Log ud",
+  "log-in": "Log ind",
+  "loginPopup-title": "Log ind",
+  "memberMenuPopup-title": "Medlemsindstillinger",
+  "members": "Medlemmer",
   "menu": "Menu",
   "menu": "Menu",
-  "move-selection": "Move selection",
-  "moveCardPopup-title": "Move Card",
-  "moveCardToBottom-title": "Move to Bottom",
-  "moveCardToTop-title": "Move to Top",
-  "moveSelectionPopup-title": "Move selection",
-  "multi-selection": "Multi-Selection",
-  "multi-selection-on": "Multi-Selection is on",
-  "muted": "Muted",
-  "muted-info": "You will never be notified of any changes in this board",
-  "my-boards": "My Boards",
-  "name": "Name",
-  "no-archived-cards": "No cards in Archive.",
-  "no-archived-lists": "No lists in Archive.",
-  "no-archived-swimlanes": "No swimlanes in Archive.",
-  "no-results": "No results",
+  "move-selection": "Flyt valgte",
+  "moveCardPopup-title": "Flyt kort",
+  "moveCardToBottom-title": "Flyt til bunden",
+  "moveCardToTop-title": "Flyt til toppen",
+  "moveSelectionPopup-title": "Flyt valgte",
+  "multi-selection": "Multivalg",
+  "multi-selection-on": "Multivalg er slået til",
+  "muted": "Slukket",
+  "muted-info": "Du vil aldrig få notifikationer om ændringer i denne tavle",
+  "my-boards": "Mine tavler",
+  "name": "Navn",
+  "no-archived-cards": "Ingen kort i arkivet.",
+  "no-archived-lists": "Ingen lister i arkivet.",
+  "no-archived-swimlanes": "Ingen svømmebaner i arkivet.",
+  "no-results": "Ingen resultater",
   "normal": "Normal",
   "normal": "Normal",
-  "normal-desc": "Can view and edit cards. Can't change settings.",
-  "not-accepted-yet": "Invitation not accepted yet",
-  "notify-participate": "Receive updates to any cards you participate as creater or member",
-  "notify-watch": "Receive updates to any boards, lists, or cards you’re watching",
-  "optional": "optional",
-  "or": "or",
-  "page-maybe-private": "This page may be private. You may be able to view it by <a href='%s'>logging in</a>.",
-  "page-not-found": "Page not found.",
-  "password": "Password",
-  "paste-or-dragdrop": "to paste, or drag & drop image file to it (image only)",
-  "participating": "Participating",
-  "preview": "Preview",
-  "previewAttachedImagePopup-title": "Preview",
-  "previewClipboardImagePopup-title": "Preview",
-  "private": "Private",
-  "private-desc": "This board is private. Only people added to the board can view and edit it.",
-  "profile": "Profile",
-  "public": "Public",
-  "public-desc": "This board is public. It's visible to anyone with the link and will show up in search engines like Google. Only people added to the board can edit.",
-  "quick-access-description": "Star a board to add a shortcut in this bar.",
-  "remove-cover": "Remove Cover",
-  "remove-from-board": "Remove from Board",
-  "remove-label": "Remove Label",
-  "listDeletePopup-title": "Delete List ?",
-  "remove-member": "Remove Member",
-  "remove-member-from-card": "Remove from Card",
-  "remove-member-pop": "Remove __name__ (__username__) from __boardTitle__? The member will be removed from all cards on this board. They will receive a notification.",
-  "removeMemberPopup-title": "Remove Member?",
-  "rename": "Rename",
-  "rename-board": "Rename Board",
-  "restore": "Restore",
-  "save": "Save",
-  "search": "Search",
-  "rules": "Rules",
-  "search-cards": "Search from card/list titles and descriptions on this board",
-  "search-example": "Text to search for?",
-  "select-color": "Select Color",
-  "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
-  "setWipLimitPopup-title": "Set WIP Limit",
-  "shortcut-assign-self": "Assign yourself to current card",
-  "shortcut-autocomplete-emoji": "Autocomplete emoji",
-  "shortcut-autocomplete-members": "Autocomplete members",
-  "shortcut-clear-filters": "Clear all filters",
-  "shortcut-close-dialog": "Close Dialog",
-  "shortcut-filter-my-cards": "Filter my cards",
-  "shortcut-show-shortcuts": "Bring up this shortcuts list",
-  "shortcut-toggle-filterbar": "Toggle Filter Sidebar",
-  "shortcut-toggle-sidebar": "Toggle Board Sidebar",
-  "show-cards-minimum-count": "Show cards count if list contains more than",
-  "sidebar-open": "Open Sidebar",
-  "sidebar-close": "Close Sidebar",
-  "signupPopup-title": "Create an Account",
-  "star-board-title": "Click to star this board. It will show up at top of your boards list.",
-  "starred-boards": "Starred Boards",
-  "starred-boards-description": "Starred boards show up at the top of your boards list.",
-  "subscribe": "Subscribe",
+  "normal-desc": "Du kan se og redigere kort. Indstillinger kan ikke ændres.",
+  "not-accepted-yet": "Invitation er endnu ikke accepteret",
+  "notify-participate": "Modtag opdateringer for alle kort du deltager i, som opretter eller medlem ",
+  "notify-watch": "Modtag opdateringer for alle tavler eller kort som du følger ",
+  "optional": "valgfri",
+  "or": "eller",
+  "page-maybe-private": "Denne side kan være privat. Du kan eventuelt se den ved at <a href='%s'>logge ind</a>.",
+  "page-not-found": "Siden blev ikke fundet.",
+  "password": "Kodeord",
+  "paste-or-dragdrop": "for at indsætte eller træk-og-slip billedfilen til den (kun billede)",
+  "participating": "Deltager",
+  "preview": "Forhåndsvisning",
+  "previewAttachedImagePopup-title": "Forhåndsvisning",
+  "previewClipboardImagePopup-title": "Forhåndsvisning",
+  "private": "Privat",
+  "private-desc": "Denne tavle er privat. Det er kun tilføjede personer som kan se og redigere den. ",
+  "profile": "Profil",
+  "public": "Offentlig",
+  "public-desc": "Denne tavle er offentlig. Den er synlig for alle med linket og vil blive vist i søgemaskiner som Google. Det er kun personer tilføjet til tavlen, der kan redigere. ",
+  "quick-access-description": "Stjernemarkér en tavle for at tilføje genvej i denne bjælke.",
+  "remove-cover": "Fjern omslag",
+  "remove-from-board": "Fjern fra tavle",
+  "remove-label": "Fjern etikette",
+  "listDeletePopup-title": "Slet liste?",
+  "remove-member": "Fjern medlem",
+  "remove-member-from-card": "Fjern fra kort",
+  "remove-member-pop": "Fjern __name__ (__username__) fra __boardTitle__? Medlemmet vil blive fjernetfra alle kort på denne tavle. De vil modtage en notifikation.",
+  "removeMemberPopup-title": "Fjern medlem?",
+  "rename": "Omdøb",
+  "rename-board": "Omdøb tavle",
+  "restore": "Genskab",
+  "save": "Gem",
+  "search": "Søg",
+  "rules": "Regler",
+  "search-cards": "Søg ud fra titler i kort/lister, beskrivelser og brugerdefinerede felter på denne tavle. ",
+  "search-example": "Tekst som der søges efter?",
+  "select-color": "Vælg farve",
+  "set-wip-limit-value": "Angiv en grænse for det maksimale antal opgaver i denne liste",
+  "setWipLimitPopup-title": "Angiv WIP-begrænsning",
+  "shortcut-assign-self": "Tilknyt dig selv til nuværende kort?",
+  "shortcut-autocomplete-emoji": "Auto-fuldfør emoji",
+  "shortcut-autocomplete-members": "Auto-fuldfør medlemmer",
+  "shortcut-clear-filters": "Ryd alle filtre",
+  "shortcut-close-dialog": "Luk dialogboks",
+  "shortcut-filter-my-cards": "Filtrer mine kort",
+  "shortcut-show-shortcuts": "Fremvis denne liste med genveje",
+  "shortcut-toggle-filterbar": "Slå filter-sidebjælke til/fra",
+  "shortcut-toggle-sidebar": "Slå tavle-sidebjælke til/fra",
+  "show-cards-minimum-count": "Vis antal kort når listen indeholder mere end",
+  "sidebar-open": "Åbn sidebjælke",
+  "sidebar-close": "Luk sidebjælke",
+  "signupPopup-title": "Opret en konto",
+  "star-board-title": "Klik for at stjernemarkere denne tavle. Den vil blive vist i toppen af din liste over tavler.",
+  "starred-boards": "Tavler med stjerner",
+  "starred-boards-description": "Tavler med stjerner vises i toppen af din liste over tavler.",
+  "subscribe": "Abonnér",
   "team": "Team",
   "team": "Team",
-  "this-board": "this board",
-  "this-card": "this card",
-  "spent-time-hours": "Spent time (hours)",
-  "overtime-hours": "Overtime (hours)",
-  "overtime": "Overtime",
-  "has-overtime-cards": "Has overtime cards",
-  "has-spenttime-cards": "Has spent time cards",
-  "time": "Time",
-  "title": "Title",
-  "tracking": "Tracking",
-  "tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",
+  "this-board": "denne tavle",
+  "this-card": "dette kort",
+  "spent-time-hours": "Anvendt tid (timer)",
+  "overtime-hours": "Overtid (timer)",
+  "overtime": "Overtid",
+  "has-overtime-cards": "Har kort med overtid",
+  "has-spenttime-cards": "Har kort med anvendt tid",
+  "time": "Tid",
+  "title": "Titel",
+  "tracking": "Sporing",
+  "tracking-info": "Du vil få notifikation om alle ændringer i kort som du har oprettet eller er medlem af.",
   "type": "Type",
   "type": "Type",
-  "unassign-member": "Unassign member",
-  "unsaved-description": "You have an unsaved description.",
-  "unwatch": "Unwatch",
-  "upload": "Upload",
-  "upload-avatar": "Upload an avatar",
-  "uploaded-avatar": "Uploaded an avatar",
-  "username": "Username",
-  "view-it": "View it",
-  "warn-list-archived": "warning: this card is in an list at Archive",
-  "watch": "Watch",
-  "watching": "Watching",
-  "watching-info": "You will be notified of any change in this board",
-  "welcome-board": "Welcome Board",
-  "welcome-swimlane": "Milestone 1",
-  "welcome-list1": "Basics",
-  "welcome-list2": "Advanced",
-  "card-templates-swimlane": "Card Templates",
-  "list-templates-swimlane": "List Templates",
-  "board-templates-swimlane": "Board Templates",
-  "what-to-do": "What do you want to do?",
-  "wipLimitErrorPopup-title": "Invalid WIP Limit",
-  "wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.",
-  "wipLimitErrorPopup-dialog-pt2": "Please move some tasks out of this list, or set a higher WIP limit.",
-  "admin-panel": "Admin Panel",
-  "settings": "Settings",
-  "people": "People",
-  "registration": "Registration",
-  "disable-self-registration": "Disable Self-Registration",
-  "invite": "Invite",
-  "invite-people": "Invite People",
-  "to-boards": "To board(s)",
-  "email-addresses": "Email Addresses",
-  "smtp-host-description": "The address of the SMTP server that handles your emails.",
-  "smtp-port-description": "The port your SMTP server uses for outgoing emails.",
-  "smtp-tls-description": "Enable TLS support for SMTP server",
-  "smtp-host": "SMTP Host",
-  "smtp-port": "SMTP Port",
-  "smtp-username": "Username",
-  "smtp-password": "Password",
-  "smtp-tls": "TLS support",
-  "send-from": "From",
-  "send-smtp-test": "Send a test email to yourself",
-  "invitation-code": "Invitation Code",
-  "email-invite-register-subject": "__inviter__ sent you an invitation",
-  "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to kanban board for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
-  "email-smtp-test-subject": "SMTP Test Email",
-  "email-smtp-test-text": "You have successfully sent an email",
-  "error-invitation-code-not-exist": "Invitation code doesn't exist",
-  "error-notAuthorized": "You are not authorized to view this page.",
-  "webhook-title": "Webhook Name",
-  "webhook-token": "Token (Optional for Authentication)",
-  "outgoing-webhooks": "Outgoing Webhooks",
-  "bidirectional-webhooks": "Two-Way Webhooks",
-  "outgoingWebhooksPopup-title": "Outgoing Webhooks",
-  "boardCardTitlePopup-title": "Card Title Filter",
-  "disable-webhook": "Disable This Webhook",
-  "global-webhook": "Global Webhooks",
-  "new-outgoing-webhook": "New Outgoing Webhook",
-  "no-name": "(Unknown)",
-  "Node_version": "Node version",
-  "Meteor_version": "Meteor version",
-  "MongoDB_version": "MongoDB version",
-  "MongoDB_storage_engine": "MongoDB storage engine",
-  "MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
-  "OS_Arch": "OS Arch",
-  "OS_Cpus": "OS CPU Count",
-  "OS_Freemem": "OS Free Memory",
-  "OS_Loadavg": "OS Load Average",
-  "OS_Platform": "OS Platform",
-  "OS_Release": "OS Release",
-  "OS_Totalmem": "OS Total Memory",
-  "OS_Type": "OS Type",
-  "OS_Uptime": "OS Uptime",
-  "days": "days",
-  "hours": "hours",
-  "minutes": "minutes",
-  "seconds": "seconds",
-  "show-field-on-card": "Show this field on card",
-  "automatically-field-on-card": "Auto create field to all cards",
-  "showLabel-field-on-card": "Show field label on minicard",
-  "yes": "Yes",
-  "no": "No",
-  "accounts": "Accounts",
-  "accounts-allowEmailChange": "Allow Email Change",
-  "accounts-allowUserNameChange": "Allow Username Change",
-  "createdAt": "Created at",
-  "verified": "Verified",
-  "active": "Active",
-  "card-received": "Received",
-  "card-received-on": "Received on",
-  "card-end": "End",
-  "card-end-on": "Ends on",
-  "editCardReceivedDatePopup-title": "Change received date",
-  "editCardEndDatePopup-title": "Change end date",
-  "setCardColorPopup-title": "Set color",
-  "setCardActionsColorPopup-title": "Choose a color",
-  "setSwimlaneColorPopup-title": "Choose a color",
-  "setListColorPopup-title": "Choose a color",
-  "assigned-by": "Assigned By",
-  "requested-by": "Requested By",
-  "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
-  "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
-  "boardDeletePopup-title": "Delete Board?",
-  "delete-board": "Delete Board",
-  "default-subtasks-board": "Subtasks for __board__ board",
-  "default": "Default",
-  "queue": "Queue",
-  "subtask-settings": "Subtasks Settings",
-  "card-settings": "Card Settings",
-  "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "boardCardSettingsPopup-title": "Card Settings",
-  "deposit-subtasks-board": "Deposit subtasks to this board:",
-  "deposit-subtasks-list": "Landing list for subtasks deposited here:",
-  "show-parent-in-minicard": "Show parent in minicard:",
-  "prefix-with-full-path": "Prefix with full path",
-  "prefix-with-parent": "Prefix with parent",
-  "subtext-with-full-path": "Subtext with full path",
-  "subtext-with-parent": "Subtext with parent",
-  "change-card-parent": "Change card's parent",
-  "parent-card": "Parent card",
-  "source-board": "Source board",
-  "no-parent": "Don't show parent",
-  "activity-added-label": "added label '%s' to %s",
-  "activity-removed-label": "removed label '%s' from %s",
-  "activity-delete-attach": "deleted an attachment from %s",
-  "activity-added-label-card": "added label '%s'",
-  "activity-removed-label-card": "removed label '%s'",
-  "activity-delete-attach-card": "deleted an attachment",
-  "activity-set-customfield": "set custom field '%s' to '%s' in %s",
-  "activity-unset-customfield": "unset custom field '%s' in %s",
-  "r-rule": "Rule",
-  "r-add-trigger": "Add trigger",
-  "r-add-action": "Add action",
-  "r-board-rules": "Board rules",
-  "r-add-rule": "Add rule",
-  "r-view-rule": "View rule",
-  "r-delete-rule": "Delete rule",
-  "r-new-rule-name": "New rule title",
-  "r-no-rules": "No rules",
-  "r-when-a-card": "When a card",
-  "r-is": "is",
-  "r-is-moved": "is moved",
-  "r-added-to": "added to",
-  "r-removed-from": "Removed from",
-  "r-the-board": "the board",
-  "r-list": "list",
-  "set-filter": "Set Filter",
-  "r-moved-to": "Moved to",
-  "r-moved-from": "Moved from",
-  "r-archived": "Moved to Archive",
-  "r-unarchived": "Restored from Archive",
-  "r-a-card": "a card",
-  "r-when-a-label-is": "When a label is",
-  "r-when-the-label": "When the label",
-  "r-list-name": "list name",
-  "r-when-a-member": "When a member is",
-  "r-when-the-member": "When the member",
-  "r-name": "name",
-  "r-when-a-attach": "When an attachment",
-  "r-when-a-checklist": "When a checklist is",
-  "r-when-the-checklist": "When the checklist",
-  "r-completed": "Completed",
-  "r-made-incomplete": "Made incomplete",
-  "r-when-a-item": "When a checklist item is",
-  "r-when-the-item": "When the checklist item",
-  "r-checked": "Checked",
-  "r-unchecked": "Unchecked",
-  "r-move-card-to": "Move card to",
-  "r-top-of": "Top of",
-  "r-bottom-of": "Bottom of",
-  "r-its-list": "its list",
-  "r-archive": "Move to Archive",
-  "r-unarchive": "Restore from Archive",
-  "r-card": "card",
-  "r-add": "Add",
-  "r-remove": "Remove",
-  "r-label": "label",
-  "r-member": "member",
-  "r-remove-all": "Remove all members from the card",
-  "r-set-color": "Set color to",
-  "r-checklist": "checklist",
-  "r-check-all": "Check all",
-  "r-uncheck-all": "Uncheck all",
-  "r-items-check": "items of checklist",
-  "r-check": "Check",
-  "r-uncheck": "Uncheck",
-  "r-item": "item",
-  "r-of-checklist": "of checklist",
-  "r-send-email": "Send an email",
-  "r-to": "to",
-  "r-subject": "subject",
-  "r-rule-details": "Rule details",
-  "r-d-move-to-top-gen": "Move card to top of its list",
-  "r-d-move-to-top-spec": "Move card to top of list",
-  "r-d-move-to-bottom-gen": "Move card to bottom of its list",
-  "r-d-move-to-bottom-spec": "Move card to bottom of list",
-  "r-d-send-email": "Send email",
-  "r-d-send-email-to": "to",
-  "r-d-send-email-subject": "subject",
-  "r-d-send-email-message": "message",
-  "r-d-archive": "Move card to Archive",
-  "r-d-unarchive": "Restore card from Archive",
-  "r-d-add-label": "Add label",
-  "r-d-remove-label": "Remove label",
-  "r-create-card": "Create new card",
-  "r-in-list": "in list",
-  "r-in-swimlane": "in swimlane",
-  "r-d-add-member": "Add member",
-  "r-d-remove-member": "Remove member",
-  "r-d-remove-all-member": "Remove all member",
-  "r-d-check-all": "Check all items of a list",
-  "r-d-uncheck-all": "Uncheck all items of a list",
-  "r-d-check-one": "Check item",
-  "r-d-uncheck-one": "Uncheck item",
-  "r-d-check-of-list": "of checklist",
-  "r-d-add-checklist": "Add checklist",
-  "r-d-remove-checklist": "Remove checklist",
-  "r-by": "by",
-  "r-add-checklist": "Add checklist",
-  "r-with-items": "with items",
-  "r-items-list": "item1,item2,item3",
-  "r-add-swimlane": "Add swimlane",
-  "r-swimlane-name": "swimlane name",
-  "r-board-note": "Note: leave a field empty to match every possible value.",
-  "r-checklist-note": "Note: checklist's items have to be written as comma separated values.",
-  "r-when-a-card-is-moved": "When a card is moved to another list",
-  "r-set": "Set",
-  "r-update": "Update",
-  "r-datefield": "date field",
+  "unassign-member": "Fjern medlemstilknytning",
+  "unsaved-description": "Du har en beskrivelse som ikke er gemt",
+  "unwatch": "Ophør med at følge",
+  "upload": "Overfør",
+  "upload-avatar": "Overfør en avatar",
+  "uploaded-avatar": "Overførte en avatar",
+  "username": "Brugernavn",
+  "view-it": "Vis den",
+  "warn-list-archived": "advarsel: dette kort er i en liste i arkivet",
+  "watch": "Følg",
+  "watching": "Følger",
+  "watching-info": "Du vil få notifikation om alle ændringer i denne tavle.",
+  "welcome-board": "Velkomsttavle",
+  "welcome-swimlane": "Milepæl 1",
+  "welcome-list1": "Grundlæggende",
+  "welcome-list2": "Avanceret",
+  "card-templates-swimlane": "Kortskabeloner",
+  "list-templates-swimlane": "Listeskabeloner",
+  "board-templates-swimlane": "Tavleskabeloner",
+  "what-to-do": "Hvad ønsker du at foretage dig?",
+  "wipLimitErrorPopup-title": "Ugyldig WIP-begrænsning",
+  "wipLimitErrorPopup-dialog-pt1": "Antallet af opgaver i denne liste er højere end WIP-begræsningen du har angivet.",
+  "wipLimitErrorPopup-dialog-pt2": "Flyt venligst nogle af opgaverne ud af listen, eller angiv en højere WIP-begrænsning.",
+  "admin-panel": "Admin-panel",
+  "settings": "Indstillinger",
+  "people": "Personer",
+  "registration": "Tilmelding",
+  "disable-self-registration": "Slå selv-tilmelding fra",
+  "invite": "Invitér",
+  "invite-people": "Invitér personer",
+  "to-boards": "Til tavle(r)",
+  "email-addresses": "E-mailadresser",
+  "smtp-host-description": "Adressen på SMTP-serveren som håndterer din e-mail.",
+  "smtp-port-description": "Den port som din SMTP-server benytter til udgående e-mail. ",
+  "smtp-tls-description": "Slå TLS-understøttelse til for SMTP-serveren",
+  "smtp-host": "SMTP-vært",
+  "smtp-port": "SMTP-port",
+  "smtp-username": "Brugernavn",
+  "smtp-password": "Kodeord",
+  "smtp-tls": "TLS-understøttelse",
+  "send-from": "Fra",
+  "send-smtp-test": "Send en e-mailtest til dig selv",
+  "invitation-code": "Invitationskode",
+  "email-invite-register-subject": "__inviter__ sendte dig en invitation",
+  "email-invite-register-text": "Kære __user__,\n\n__inviter__ inviterer dig til at samarbejde på kanbantavlen.\n\nFølg venligst linket nedenfor:\n__url__\n\nDin invitationskode er: __icode__\n\nTak.",
+  "email-smtp-test-subject": "SMTP for e-mailtest",
+  "email-smtp-test-text": "Afsendelse af e-mail blev udført ",
+  "error-invitation-code-not-exist": "Invitationskoden findes ikke",
+  "error-notAuthorized": "Du har ikke tilladelse til at se denne side.",
+  "webhook-title": "Navn på webhook",
+  "webhook-token": "Token (valgfri til godkendelse)",
+  "outgoing-webhooks": "Udgående webhooks",
+  "bidirectional-webhooks": "To-vejs webhooks",
+  "outgoingWebhooksPopup-title": "Udgående webhooks",
+  "boardCardTitlePopup-title": "Filter for korttitel",
+  "disable-webhook": "Slå denne webhook fra",
+  "global-webhook": "Globale webhooks",
+  "new-outgoing-webhook": "Ny udgående webhook",
+  "no-name": "(Ukendt)",
+  "Node_version": "Node-version",
+  "Meteor_version": "Meteor-version",
+  "MongoDB_version": "MongoDB-version",
+  "MongoDB_storage_engine": "Lagringsmotor for MongoDB",
+  "MongoDB_Oplog_enabled": "MongoDB Oplog er slået til",
+  "OS_Arch": "OS - Arch",
+  "OS_Cpus": "CPU-antal for OS",
+  "OS_Freemem": "Fri hukommelse for OS",
+  "OS_Loadavg": "Gns.belastning for OS",
+  "OS_Platform": "OS-platform",
+  "OS_Release": "OS-udgivelse",
+  "OS_Totalmem": "Samlet hukommelse for OS",
+  "OS_Type": "OS-type",
+  "OS_Uptime": "OS-oppetid",
+  "days": "dage",
+  "hours": "timer",
+  "minutes": "minutter",
+  "seconds": "sekunder",
+  "show-field-on-card": "Vis dette felt på kortet",
+  "automatically-field-on-card": "Auto-opret felter på alle kort",
+  "showLabel-field-on-card": "Vis feltetikette på minikort",
+  "yes": "Ja",
+  "no": "Nej",
+  "accounts": "Konti",
+  "accounts-allowEmailChange": "Tillad ændring af e-mail",
+  "accounts-allowUserNameChange": "Tillad ændring af brugernavn",
+  "createdAt": "Oprettet per",
+  "verified": "Verificeret",
+  "active": "Aktiv",
+  "card-received": "Modtaget",
+  "card-received-on": "Modtaget per",
+  "card-end": "Slut",
+  "card-end-on": "Slutter per",
+  "editCardReceivedDatePopup-title": "Tilpas modtagelsesdato",
+  "editCardEndDatePopup-title": "Tilpas slutdato",
+  "setCardColorPopup-title": "Angiv farve",
+  "setCardActionsColorPopup-title": "Vælg en farve",
+  "setSwimlaneColorPopup-title": "Vælg en farve",
+  "setListColorPopup-title": "Vælg en farve",
+  "assigned-by": "Tildelt af",
+  "requested-by": "Anmodet af",
+  "board-delete-notice": "Sletning er permanent. Du vil miste alle lister, kort og handlinger knyttet til denne tavle.",
+  "delete-board-confirm-popup": "Alle lister, kort, etiketter og aktiviteter vil blive slettet og du får ikke mulighed for at genskabe tavlens indhold. Dette kan ikke fortrydes.",
+  "boardDeletePopup-title": "Slet tavle?",
+  "delete-board": "Slet tavle",
+  "default-subtasks-board": "Delopgaver for tavlen __board__",
+  "default": "Standard",
+  "queue": "Kø",
+  "subtask-settings": "Indstillinger for delopgaver",
+  "card-settings": "Indstillinger for kort",
+  "boardSubtaskSettingsPopup-title": "Indstillinger for delopgaver i tavle",
+  "boardCardSettingsPopup-title": "Indstillinger for kort",
+  "deposit-subtasks-board": "Indsæt delopgaver på denne tavle: ",
+  "deposit-subtasks-list": "Liste som der landes på, når delopgaver indsættes her:",
+  "show-parent-in-minicard": "Vis overordnede i minikort:",
+  "prefix-with-full-path": "Præfiks med fuld sti",
+  "prefix-with-parent": "Præfiks med overordnede",
+  "subtext-with-full-path": "Undertekst med fuld sti",
+  "subtext-with-parent": "Undertekst med overordnede",
+  "change-card-parent": "Skift kortets overordnede",
+  "parent-card": "Overordnede kort",
+  "source-board": "Kilde for tavle",
+  "no-parent": "Vis ikke overordnede",
+  "activity-added-label": "tilføjede etiketten '%s' til %s",
+  "activity-removed-label": "tilføjede etiketten '%s' fra %s",
+  "activity-delete-attach": "slettede en vedhæftning fra %s",
+  "activity-added-label-card": "tilføjede etiketten '%s'",
+  "activity-removed-label-card": "fjernede etiketten '%s'",
+  "activity-delete-attach-card": "slettede en vedhæftning",
+  "activity-set-customfield": "angiv brugerdefineret felt '%s' til '%s' i %s",
+  "activity-unset-customfield": "fjernede brugerdefineret felt '%s' i %s",
+  "r-rule": "Regel",
+  "r-add-trigger": "Tilføj trigger-udløser",
+  "r-add-action": "Tilføj handling",
+  "r-board-rules": "Regler for tavle",
+  "r-add-rule": "Tilføj regel",
+  "r-view-rule": "Vis regel",
+  "r-delete-rule": "Slet regel",
+  "r-new-rule-name": "Ny titel for regel",
+  "r-no-rules": "Ingen regler",
+  "r-when-a-card": "Når et kort",
+  "r-is": "er",
+  "r-is-moved": "flyttes",
+  "r-added-to": "tilføjes til",
+  "r-removed-from": "Fjernes fra",
+  "r-the-board": "tavlen",
+  "r-list": "liste",
+  "set-filter": "Angiv filter",
+  "r-moved-to": "Flyttet til",
+  "r-moved-from": "Flyttet fra",
+  "r-archived": "Flyttet fra arkiv",
+  "r-unarchived": "Genskabt fra arkiv",
+  "r-a-card": "et kort",
+  "r-when-a-label-is": "Når en etikette er",
+  "r-when-the-label": "Når etiketten",
+  "r-list-name": "listenavn",
+  "r-when-a-member": "Når et medlem er",
+  "r-when-the-member": "Når medlemmet",
+  "r-name": "navn",
+  "r-when-a-attach": "Når en vedhæftning",
+  "r-when-a-checklist": "Når en tjekliste er",
+  "r-when-the-checklist": "Når tjeklisten",
+  "r-completed": "Fuldført",
+  "r-made-incomplete": "Gjort ukomplet",
+  "r-when-a-item": "Når et element i tjeklisten er",
+  "r-when-the-item": "Når elementet i tjeklisten",
+  "r-checked": "Markeret",
+  "r-unchecked": "Umarkeret",
+  "r-move-card-to": "Flyt kort til",
+  "r-top-of": "Toppen af",
+  "r-bottom-of": "Bunden af",
+  "r-its-list": "dens liste",
+  "r-archive": "Flyt til arkiv",
+  "r-unarchive": "Genskab fra arkiv",
+  "r-card": "kort",
+  "r-add": "Tilføj",
+  "r-remove": "Fjern",
+  "r-label": "etikette",
+  "r-member": "medlem",
+  "r-remove-all": "Fjern alle medlemmer fra kortet",
+  "r-set-color": "Angiv farven til",
+  "r-checklist": "tjekliste",
+  "r-check-all": "Markér alle",
+  "r-uncheck-all": "Fravælg alle",
+  "r-items-check": "elementer fra tjekliste",
+  "r-check": "Markér",
+  "r-uncheck": "Fravælg",
+  "r-item": "element",
+  "r-of-checklist": "fra tjekliste",
+  "r-send-email": "Send en e-mail",
+  "r-to": "til",
+  "r-of": "af",
+  "r-subject": "emne",
+  "r-rule-details": "Detaljer for regel",
+  "r-d-move-to-top-gen": "Flyt kortet til toppen af dens liste",
+  "r-d-move-to-top-spec": "Flyt kortet til toppen af listen",
+  "r-d-move-to-bottom-gen": "Flyt kortet til bunden af dens liste",
+  "r-d-move-to-bottom-spec": "Flyt kortet til bunden af listen",
+  "r-d-send-email": "Send e-mail",
+  "r-d-send-email-to": "til",
+  "r-d-send-email-subject": "emne",
+  "r-d-send-email-message": "besked",
+  "r-d-archive": "Flyt kortet til arkiv",
+  "r-d-unarchive": "Genskab kort fra arkiv",
+  "r-d-add-label": "Tilføj etikette",
+  "r-d-remove-label": "Fjern etikette",
+  "r-create-card": "Opret nyt kort",
+  "r-in-list": "i liste",
+  "r-in-swimlane": "i svømmebane",
+  "r-d-add-member": "Tilføj medlem",
+  "r-d-remove-member": "Fjern medlem",
+  "r-d-remove-all-member": "Fjern alle medlemmer",
+  "r-d-check-all": "Markér alle elementer fra en liste",
+  "r-d-uncheck-all": "Fravælg alle elementer fra en liste",
+  "r-d-check-one": "Markér element",
+  "r-d-uncheck-one": "Fravælg element",
+  "r-d-check-of-list": "fra tjekliste",
+  "r-d-add-checklist": "Tilføj tjekliste",
+  "r-d-remove-checklist": "Fjern tjekliste",
+  "r-by": "af",
+  "r-add-checklist": "Tilføj tjekliste",
+  "r-with-items": "med elementer",
+  "r-items-list": "element1,element2,element3",
+  "r-add-swimlane": "Tilføj svømmebane",
+  "r-swimlane-name": "navn på svømmebane",
+  "r-board-note": "Bemærk: lad et felt stå tomt for at matche alle værdier.",
+  "r-checklist-note": "Bemærk: tjeklistens elementer skal skrives som en kommaadskilte værdier.",
+  "r-when-a-card-is-moved": "Når et kort flyttes til en anden liste.",
+  "r-set": "Angiv",
+  "r-update": "Opdatér",
+  "r-datefield": "datofelt",
   "r-df-start-at": "start",
   "r-df-start-at": "start",
-  "r-df-due-at": "due",
-  "r-df-end-at": "end",
-  "r-df-received-at": "received",
-  "r-to-current-datetime": "to current date/time",
-  "r-remove-value-from": "Remove value from",
+  "r-df-due-at": "forfalder",
+  "r-df-end-at": "slutter",
+  "r-df-received-at": "modtaget",
+  "r-to-current-datetime": "til nuværende dato/tidspunkt",
+  "r-remove-value-from": "Fjern værdi fra",
   "ldap": "LDAP",
   "ldap": "LDAP",
   "oauth2": "OAuth2",
   "oauth2": "OAuth2",
   "cas": "CAS",
   "cas": "CAS",
-  "authentication-method": "Authentication method",
-  "authentication-type": "Authentication type",
-  "custom-product-name": "Custom Product Name",
+  "authentication-method": "Godkendelsesmetode",
+  "authentication-type": "Godkendelsestype",
+  "custom-product-name": "Tilpasset produktnavn",
   "layout": "Layout",
   "layout": "Layout",
-  "hide-logo": "Hide Logo",
-  "add-custom-html-after-body-start": "Add Custom HTML after <body> start",
-  "add-custom-html-before-body-end": "Add Custom HTML before </body> end",
-  "error-undefined": "Something went wrong",
-  "error-ldap-login": "An error occurred while trying to login",
-  "display-authentication-method": "Display Authentication Method",
-  "default-authentication-method": "Default Authentication Method",
-  "duplicate-board": "Duplicate Board",
-  "people-number": "The number of people is:",
-  "swimlaneDeletePopup-title": "Delete Swimlane ?",
-  "swimlane-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the swimlane. There is no undo.",
-  "restore-all": "Restore all",
-  "delete-all": "Delete all",
-  "loading": "Loading, please wait.",
-  "previous_as": "last time was",
-  "act-a-dueAt": "modified due time to \nWhen: __timeValue__\nWhere: __card__\n previous due was __timeOldValue__",
-  "act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)",
-  "act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)",
-  "act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)",
-  "a-dueAt": "modified due time to be",
-  "a-endAt": "modified ending time to be",
-  "a-startAt": "modified starting time to be",
-  "a-receivedAt": "modified received time to be",
-  "almostdue": "current due time %s is approaching",
-  "pastdue": "current due time %s is past",
-  "duenow": "current due time %s is today",
-  "act-newDue": "__list__/__card__ has 1st due reminder [__board__]",
-  "act-withDue": "__list__/__card__ due reminders [__board__]",
-  "act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching",
-  "act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
-  "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
-  "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
-  "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
-  "accounts-allowUserDelete": "Allow users to self delete their account",
-  "hide-minicard-label-text": "Hide minicard label text",
-  "show-desktop-drag-handles": "Show desktop drag handles",
-  "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee",
-  "addmore-detail": "Add a more detailed description",
-  "show-on-card": "Show on Card"
+  "hide-logo": "Skjul logo",
+  "add-custom-html-after-body-start": "Tilføj tilpasset HTML efter <body> start",
+  "add-custom-html-before-body-end": "Tilføj tilpasset HTML før </body> slutning",
+  "error-undefined": "Noget gik galt",
+  "error-ldap-login": "Fejl under forsøg på login",
+  "display-authentication-method": "Vis godkendelsesmetode",
+  "default-authentication-method": "Standard for godkendelsesmetode",
+  "duplicate-board": "Duplikér tavle",
+  "people-number": "Antallet af personer er:",
+  "swimlaneDeletePopup-title": "Slet svømmebane?",
+  "swimlane-delete-pop": "Alle handlinger vil blive fjernet fra aktivitetsfeedet, og du vil ikke kunne genskabe svømmebanen. Dette kan ikke fortrydes.",
+  "restore-all": "Genskab alle",
+  "delete-all": "Slet alle",
+  "loading": "Indlæser, vent venligst",
+  "previous_as": "seneste tidspunkt var",
+  "act-a-dueAt": "ændrede forfaldstidspunkt til \nHvornår: __timeValue__\nHvor: __card__\n forrige forfaldstidspunkt var __timeOldValue__",
+  "act-a-endAt": "ændrede sluttidspunkt til __timeValue__ fra (__timeOldValue__)",
+  "act-a-startAt": "ændrede starttidspunkt til __timeValue__ fra (__timeOldValue__)",
+  "act-a-receivedAt": "ændrede modtagelsestidspunkt til __timeValue__ fra (__timeOldValue__)",
+  "a-dueAt": "ændrede forfaldstidspunkt til at være",
+  "a-endAt": "ændrede sluttidspunkt til at være",
+  "a-startAt": "ændrede starttidspunkt til at være",
+  "a-receivedAt": "ændrede modtagelsestidspunkt til at være",
+  "almostdue": "aktuelt forfaldstidspunkt %s nærmer sig",
+  "pastdue": "aktuelt forfaldstidspunkt %s er passeret",
+  "duenow": "aktuelt forfaldstidspunkt %s er i dag",
+  "act-newDue": "__list__/__card__ har 1. påmindelse om forfald [__board__]",
+  "act-withDue": "__list__/__card__ påmindelse om forfald [__board__]",
+  "act-almostdue": "påmindede om at aktuelt forfald (__timeValue__) for __card__ nærmer sig",
+  "act-pastdue": "påmindede om at aktuelt forfald (__timeValue__) of __card__ er passeret",
+  "act-duenow": "påmindede om at aktuelt forfald (__timeValue__) of __card__ er nu",
+  "act-atUserComment": "Du blev nævnt i [__board__] __list__/__card__",
+  "delete-user-confirm-popup": "Er du sikker på du vil slette denne konto? Det er ikke muligt at fortryde.",
+  "accounts-allowUserDelete": "Tillad brugere at slette deres egen konto",
+  "hide-minicard-label-text": "Skjul etiketteteksten for minikort",
+  "show-desktop-drag-handles": "Vis trække-håndtag for skrivebord",
+  "assignee": "Tildelt til",
+  "cardAssigneesPopup-title": "Tildelt til",
+  "addmore-detail": "Tilføj en mere detaljeret beskrivelse",
+  "show-on-card": "Vis på kort",
+  "new": "Ny",
+  "editUserPopup-title": "Redigér bruger",
+  "newUserPopup-title": "Ny bruger",
+  "notifications": "Notifikationer",
+  "view-all": "Vis alle",
+  "filter-by-unread": "Filtrér efter ulæst",
+  "mark-all-as-read": "Markér alle som læst",
+  "remove-all-read": "Fjern alle læste",
+  "allow-rename": "Tillad omdøb",
+  "allowRenamePopup-title": "Tillad omdøb",
+  "start-day-of-week": "Angiv dag for ugestart",
+  "monday": "Mandag",
+  "tuesday": "Tirsdag",
+  "wednesday": "Onsdag",
+  "thursday": "Torsdag",
+  "friday": "Fredag",
+  "saturday": "Lørdag",
+  "sunday": "Søndag"
 }
 }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.