Kaynağa Gözat

Merge branch 'master' of github.com:jcbrand/converse.js

JC Brand 8 yıl önce
ebeveyn
işleme
a717481b2c
100 değiştirilmiş dosya ile 94358 ekleme ve 84767 silme
  1. 9 0
      .babelrc
  2. 5 2
      .eslintrc.json
  3. 10 6
      .gitignore
  4. 4 2
      3rdparty/lodash.fp.js
  5. 45 1
      CHANGES.md
  6. 12 0
      COPYRIGHT
  7. 48 30
      Makefile
  8. 0 13
      bower.json
  9. 3 0
      builds/README.rst
  10. 238 154
      css/converse.css
  11. 226 161
      css/inverse.css
  12. 1 1
      css/theme.css
  13. 10 11
      demo/without_bundled_dependencies.html
  14. 26281 25118
      dist/converse-mobile.js
  15. 1125 1739
      dist/converse-no-dependencies.js
  16. 26285 25122
      dist/converse.js
  17. 26285 25122
      dist/inverse.js
  18. 0 0
      dist/locales.js
  19. 1 1
      docs/source/_templates/layout.html
  20. 2 2
      docs/source/conf.py
  21. 27 6
      docs/source/configuration.rst
  22. 23 22
      docs/source/developer_api.rst
  23. 154 8
      docs/source/events.rst
  24. 3 1
      docs/source/plugin_development.rst
  25. 2 32
      index.html
  26. 2 2
      inverse.html
  27. 14 82
      locale/af/LC_MESSAGES/converse.json
  28. 212 266
      locale/af/LC_MESSAGES/converse.po
  29. 14 46
      locale/ca/LC_MESSAGES/converse.json
  30. 210 273
      locale/ca/LC_MESSAGES/converse.po
  31. 208 264
      locale/converse.pot
  32. 14 82
      locale/de/LC_MESSAGES/converse.json
  33. 212 267
      locale/de/LC_MESSAGES/converse.po
  34. 12 48
      locale/es/LC_MESSAGES/converse.json
  35. 208 270
      locale/es/LC_MESSAGES/converse.po
  36. 12 76
      locale/fr/LC_MESSAGES/converse.json
  37. 212 268
      locale/fr/LC_MESSAGES/converse.po
  38. 13 49
      locale/he/LC_MESSAGES/converse.json
  39. 209 271
      locale/he/LC_MESSAGES/converse.po
  40. 14 50
      locale/hu/LC_MESSAGES/converse.json
  41. 210 272
      locale/hu/LC_MESSAGES/converse.po
  42. 12 44
      locale/id/LC_MESSAGES/converse.json
  43. 208 271
      locale/id/LC_MESSAGES/converse.po
  44. 16 80
      locale/it/LC_MESSAGES/converse.json
  45. 212 267
      locale/it/LC_MESSAGES/converse.po
  46. 12 44
      locale/ja/LC_MESSAGES/converse.json
  47. 208 271
      locale/ja/LC_MESSAGES/converse.po
  48. 13 49
      locale/nb/LC_MESSAGES/converse.json
  49. 209 271
      locale/nb/LC_MESSAGES/converse.po
  50. 12 44
      locale/nl/LC_MESSAGES/converse.json
  51. 208 271
      locale/nl/LC_MESSAGES/converse.po
  52. 14 50
      locale/pl/LC_MESSAGES/converse.json
  53. 187 245
      locale/pl/LC_MESSAGES/converse.po
  54. 12 44
      locale/pt_BR/LC_MESSAGES/converse.json
  55. 208 271
      locale/pt_BR/LC_MESSAGES/converse.po
  56. 16 44
      locale/ru/LC_MESSAGES/converse.json
  57. 210 274
      locale/ru/LC_MESSAGES/converse.po
  58. 13 49
      locale/uk/LC_MESSAGES/converse.json
  59. 209 271
      locale/uk/LC_MESSAGES/converse.po
  60. 12 44
      locale/zh/LC_MESSAGES/converse.json
  61. 208 271
      locale/zh/LC_MESSAGES/converse.po
  62. 6976 0
      package-lock.json
  63. 8 3
      package.json
  64. 103 78
      sass/_chatbox.scss
  65. 17 11
      sass/_chatrooms.scss
  66. 17 11
      sass/_controlbox.scss
  67. 11 3
      sass/_core.scss
  68. 45 0
      sass/converse/_chatbox.scss
  69. 9 1
      sass/converse/_variables.scss
  70. 15 3
      sass/inverse/_chatbox.scss
  71. 4 3
      sass/inverse/_chatrooms.scss
  72. 6 1
      sass/inverse/_variables.scss
  73. 56 23
      spec/bookmarks.js
  74. 334 169
      spec/chatbox.js
  75. 524 435
      spec/chatroom.js
  76. 238 52
      spec/controlbox.js
  77. 39 29
      spec/converse.js
  78. 166 2
      spec/disco.js
  79. 7 2
      spec/headline.js
  80. 34 29
      spec/mam.js
  81. 41 20
      spec/minchats.js
  82. 90 68
      spec/notification.js
  83. 14 5
      spec/otr.js
  84. 12 2
      spec/ping.js
  85. 6 1
      spec/presence.js
  86. 50 15
      spec/protocol.js
  87. 2 3
      spec/register.js
  88. 71 57
      spec/roomslist.js
  89. 2 2
      spec/transcripts.js
  90. 3 3
      spec/utils.js
  91. 2 3
      spec/xmppstatus.js
  92. 9 0
      src/build-esnext.js
  93. 9 0
      src/build-inverse-esnext.js
  94. 22 0
      src/build-inverse.js
  95. 26 2
      src/build-no-dependencies.js
  96. 23 1
      src/build.js
  97. 21 8
      src/config.js
  98. 116 97
      src/converse-bookmarks.js
  99. 318 176
      src/converse-chatview.js
  100. 138 134
      src/converse-controlbox.js

+ 9 - 0
.babelrc

@@ -0,0 +1,9 @@
+{
+  "presets": [
+		["env", {
+			"targets": {
+				"browsers": ["last 2 versions", "safari >= 10", "IE 11"]
+			}
+		}]
+  ]
+}

+ 5 - 2
.eslintrc.json

@@ -1,4 +1,7 @@
 {
+    "parserOptions": {
+        "ecmaVersion": 6
+    },
     "env": {
         "browser": true,
         "jasmine": true
@@ -15,7 +18,7 @@
         "lodash/prefer-lodash-method": [2, {
             "ignoreMethods": [
                 "find", "endsWith", "startsWith", "filter", "reduce",
-                "map", "replace", "toLower", "split", "trim"
+                "map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase"
             ]
         }],
         "lodash/prefer-startswith": "off",
@@ -56,7 +59,7 @@
         "dot-notation": [
             "error",
             {
-                "allowKeywords": false
+                "allowKeywords": true
             }
         ],
         "eol-last": "error",

+ 10 - 6
.gitignore

@@ -9,16 +9,24 @@
 .svn/
 .project
 .pydevproject
+.idea
+.su?
+builds/*
+
 analytics.js
 inverse-analytics.js
-.idea
+
+# python/buildout
 eggs
 .Python
+build
+parts
+*.pyc
+*.egg-info
 
 dev-jc.html
 inverse-dev.html
 inverse-dev-jc.html
-
 converse-logs/*.html
 
 # Ruby/Sass/Bundler
@@ -30,7 +38,6 @@ bourbon
 Backbone.Overview
 tags
 stamp-npm
-stamp-bower
 stamp-bundler
 
 # Sphinx
@@ -50,8 +57,5 @@ develop-eggs
 .DS_Store
 
 # Builds
-dist/*
-css/*.map
-css/*.min.css
 .sv?
 /vendor/

+ 4 - 2
3rdparty/lodash.fp.js

@@ -69,7 +69,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	}
 
 	if (typeof _ == 'function' && typeof _.runInContext == 'function') {
-	  _ = browserConvert(_.runInContext());
+      // XXX: Customization in order to be able to run both _ and fp in the
+      // non-AMD usecase.
+	  fp = browserConvert(_.runInContext());
 	}
 	module.exports = browserConvert;
 
@@ -1037,4 +1039,4 @@ return /******/ (function(modules) { // webpackBootstrap
 /***/ }
 /******/ ])
 });
-;
+;

+ 45 - 1
CHANGES.md

@@ -1,6 +1,50 @@
 # Changelog
 
-## 3.1.1 ((2017-07-12))
+## 3.2.0 (2017-08-09)
+
+### New Plugins
+- New plugin `converse-disco` which replaces the original support for
+  [XEP-0030](https://xmpp.org/extensions/xep-0030.html) and which has been
+  refactored to allow features for multiple entities to be stored.
+
+### New features and improvements
+- Add support for Emojis (either native, or via <a href="https://www.emojione.com/">Emojione</a>).
+- Add JID validation to the contact add form, the occupant invite form and the login form.
+- #896 Consistently use `XMPP username` in user-facing text (instead of JID, Jabber ID etc.).
+
+### New configuration settings
+* The `visible_toolbar_buttons.emoticons` configuration option is now changed to `visible_toolbar_buttons.emoji`.
+* [use_emojione](https://conversejs.org/docs/html/configurations.html#use-emojione)
+  is used to determine whether Emojione should be used to render emojis,
+  otherwise rendering falls back to native browser or OS support.
+* [emojione_image_path](https://conversejs.org/docs/html/configurations.html#emojione-image-path)
+  is used to specify from where Emojione will load images for rendering emojis.
+
+### New events
+* ['discoInitialized'](https://conversejs.org/docs/html/development.html#discoInitialized)
+* ['afterMessagesFetched'](https://conversejs.org/docs/html/development.html#afterMessagesFetched)
+
+### Code changes
+- Removed jQuery from `converse-core`, `converse-vcard` and `converse-roomslist`.
+- Remove `jquery.easing` from the full build. Was only being used by the
+  [conversejs.org](https://conversejs.org) website, which has been updated to not rely on it.
+- All promises are now native (or polyfilled) ES2015 Promises instead of jQuery's Deferred.
+- #866 Add babel in order to support ES2015 syntax
+
+#### Bugfixes:
+- The domain was queried for MAM:2 support, instead of the JID.
+- Roster filter is not shown when all groups are collapsed.
+- When filtering, contacts in closed groups appear.
+- Room name wasn't being updated after changing it in the configuration form.
+- Server disco features were "forgotten" after logging out and then logging in again.
+- Don't show duplicate sent groupchat messages in Slack chat rooms.
+- Bookmark icon shown in the open rooms list when `allow_bookmarks` is to `false`.
+- It wasn't possible to add or remove bookmarks via the "Open Rooms" list.
+- #879 Text in links are converted to smileys leading to non-clickable links.
+- #899: Only touch `stamp-npm` if `npm install` was successful
+- #902 `make build` dependends on non-existing files
+
+## 3.1.1 (2017-07-12)
 
 - Use a patched version of [awesomplete](https://github.com/LeaVerou/awesomplete)
   which doesn't render suggestions as HTML (possible XSS attack vector). [jcbrand]

+ 12 - 0
COPYRIGHT

@@ -0,0 +1,12 @@
+/** Converse.js
+ *
+ *  An XMPP chat client that runs in the browser.
+ *
+ *  Version: 3.2.0-rc
+ *
+ *  Copyright: JC Brand 2012-2017
+ *  Except for 3rd party dependencies.
+ *  Please refer to the unminified version of this file for details.
+ *
+ *  You can download it at: https://github.com/jcbrand/converse.js/releases
+ */

+ 48 - 30
Makefile

@@ -1,4 +1,5 @@
 # You can set these variables from the command line.
+UGLIFYJS		?= node_modules/.bin/uglifyjs
 BABEL			?= node_modules/.bin/babel
 BOURBON_TEMPLATES = ./node_modules/bourbon/app/assets/stylesheets/ 
 BUILDDIR		= ./docs
@@ -60,7 +61,7 @@ serve_bg: dev
 ########################################################################
 ## Translation machinery
 
-GETTEXT = xgettext --keyword=__ --keyword=___ --from-code=UTF-8 --output=locale/converse.pot src/*.js --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=3.1.0 -c
+GETTEXT = xgettext --keyword=__ --keyword=___ --from-code=UTF-8 --output=locale/converse.pot src/*.js --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=3.2.0-rc -c
 
 .PHONY: pot
 pot:
@@ -79,6 +80,7 @@ po2json:
 
 .PHONY: release
 release:
+	$(SED) -ri s/Version:\ [0-9]\+\.[0-9]\+\.[0-9]\+/Version:\ $(VERSION)/ COPYRIGHT
 	$(SED) -ri s/Version:\ [0-9]\+\.[0-9]\+\.[0-9]\+/Version:\ $(VERSION)/ src/start.frag
 	$(SED) -ri s/Project-Id-Version:\ Converse\.js\ [0-9]\+\.[0-9]\+\.[0-9]\+/Project-Id-Version:\ Converse.js\ $(VERSION)/ locale/converse.pot
 	$(SED) -ri s/\"version\":\ \"[0-9]\+\.[0-9]\+\.[0-9]\+\"/\"version\":\ \"$(VERSION)\"/ bower.json
@@ -98,8 +100,7 @@ release:
 ## Install dependencies
 
 stamp-npm: package.json
-	npm install
-	touch stamp-npm
+	npm install && touch stamp-npm
 
 stamp-bundler: Gemfile
 	mkdir -p .bundle
@@ -109,7 +110,7 @@ stamp-bundler: Gemfile
 
 .PHONY: clean
 clean:
-	-rm -f stamp-npm stamp-bundler
+	-rm -f stamp-npm stamp-bundler package-lock.json
 	-rm -rf node_modules .bundle
 
 .PHONY: dev
@@ -147,49 +148,66 @@ css/mobile.min.css:: stamp-npm sass/*
 
 .PHONY: watch
 watch: stamp-bundler
-	$(SASS) --watch -I ./node_modules/bourbon/app/assets/stylesheets/ sass/converse.scss:css/converse.css sass/_muc_embedded.scss:css/converse-muc-embedded.css
+	$(SASS) --watch -I ./node_modules/bourbon/app/assets/stylesheets/ sass/converse/converse.scss:css/converse.css sass/_muc_embedded.scss:css/converse-muc-embedded.css sass/inverse/inverse.scss:css/inverse.css
 
 .PHONY: watchjs
 watchjs: stamp-npm
-	$(BABEL) --source-maps --watch=./src --out-dir=./build
+	$(BABEL) --source-maps --watch=./src --out-dir=./builds
+
+.PHONY: transpile
+transpile: stamp-npm
+	$(BABEL) --source-maps --out-dir=./builds ./src
 
 BUILDS = dist/converse.js \
 		 dist/converse.min.js \
-         dist/inverse.js \
+		 dist/converse-esnext.js \
+		 dist/converse-esnext.min.js \
+		 dist/inverse.js \
 		 dist/inverse.min.js \
-         dist/converse-mobile.js \
-         dist/converse-mobile.min.js \
-         dist/converse-muc-embedded.js \
-         dist/converse-muc-embedded.min.js \
-         dist/converse-no-jquery.js \
+		 dist/converse-mobile.js \
+		 dist/converse-mobile.min.js \
+		 dist/converse-muc-embedded.js \
+		 dist/converse-muc-embedded.min.js \
+		 dist/converse-no-jquery.js \
  		 dist/converse-no-jquery.min.js \
 		 dist/converse-no-dependencies.min.js \
 		 dist/converse-no-dependencies.js
 
-dist/converse.min.js: src locale node_modules *.js
-	$(RJS) -o src/build.js include=converse out=dist/converse.min.js
-dist/converse.js: src locale node_modules *.js
+dist/converse.js: transpile src locale node_modules *.js
 	$(RJS) -o src/build.js include=converse out=dist/converse.js optimize=none 
-dist/inverse.js: src locale node_modules *.js
+dist/converse.min.js: src locale node_modules *.js
+	$(UGLIFYJS) --verbose dist/converse.js -o dist/converse.min.js
+	cat COPYRIGHT > tmpfile && cat dist/converse.min.js >> tmpfile && mv tmpfile dist/converse.min.js
+dist/converse-esnext.js: src locale node_modules *.js transpile
+	$(RJS) -o src/build-esnext.js include=converse out=dist/converse-esnext.js optimize=none 
+dist/converse-esnext.min.js: src locale node_modules *.js transpile
+	$(UGLIFYJS) --verbose dist/converse-esnext.js -o dist/converse-esnext.min.js
+	cat COPYRIGHT > tmpfile && cat dist/converse-esnext.min.js >> tmpfile && mv tmpfile dist/converse-esnext.min.js
+dist/inverse.js: transpile src locale node_modules *.js
 	$(RJS) -o src/build-inverse.js include=inverse out=dist/inverse.js optimize=none 
 dist/inverse.min.js: src locale node_modules *.js
-	$(RJS) -o src/build-inverse.js include=inverse out=dist/inverse.min.js
-dist/converse-no-jquery.min.js: src locale node_modules *.js
-	$(RJS) -o src/build.js include=converse wrap.endFile=end-no-jquery.frag exclude=jquery exclude=jquery.noconflict out=dist/converse-no-jquery.min.js
-dist/converse-no-jquery.js: src locale node_modules *.js
+	$(UGLIFYJS) --verbose dist/inverse.js -o dist/inverse.min.js
+	cat COPYRIGHT > tmpfile && cat dist/inverse.min.js >> tmpfile && mv tmpfile dist/inverse.min.js
+dist/converse-no-jquery.js: transpile src locale node_modules *.js
 	$(RJS) -o src/build.js include=converse wrap.endFile=end-no-jquery.frag exclude=jquery exclude=jquery.noconflict out=dist/converse-no-jquery.js optimize=none 
-dist/converse-no-dependencies.min.js: src locale node_modules *.js
-	$(RJS) -o src/build-no-dependencies.js
-dist/converse-no-dependencies.js: src locale node_modules *.js
+dist/converse-no-jquery.min.js: src locale node_modules *.js transpile
+	$(UGLIFYJS) --verbose dist/converse-no-jquery.js -o dist/converse-no-jquery.min.js
+	cat COPYRIGHT > tmpfile && cat dist/converse-no-jquery.min.js >> tmpfile && mv tmpfile dist/converse-no-jquery.min.js
+dist/converse-no-dependencies.js: transpile src locale node_modules *.js
 	$(RJS) -o src/build-no-dependencies.js optimize=none out=dist/converse-no-dependencies.js
-dist/converse-mobile.min.js: src locale node_modules *.js
-	$(RJS) -o src/build.js paths.converse=src/converse-mobile include=converse out=dist/converse-mobile.min.js
-dist/converse-mobile.js: src locale node_modules *.js
+dist/converse-no-dependencies.min.js: src locale node_modules *.js
+	$(UGLIFYJS) --verbose dist/converse-no-dependencies.js -o dist/converse-no-dependencies.min.js
+	cat COPYRIGHT > tmpfile && cat dist/converse-no-dependencies.min.js >> tmpfile && mv tmpfile dist/converse-no-dependencies.min.js
+dist/converse-mobile.js: transpile src locale node_modules *.js
 	$(RJS) -o src/build.js paths.converse=src/converse-mobile include=converse out=dist/converse-mobile.js optimize=none 
-dist/converse-muc-embedded.min.js: src locale node_modules *.js
-	$(RJS) -o src/build.js paths.converse=src/converse-embedded include=converse out=dist/converse-muc-embedded.min.js
-dist/converse-muc-embedded.js: src locale node_modules *.js
+dist/converse-mobile.min.js: src locale node_modules *.js
+	$(UGLIFYJS) --verbose dist/converse-mobile.js -o dist/converse-mobile.min.js
+	cat COPYRIGHT > tmpfile && cat dist/converse-mobile.min.js >> tmpfile && mv tmpfile dist/converse-mobile.min.js
+dist/converse-muc-embedded.js: transpile src locale node_modules *.js
 	$(RJS) -o src/build.js paths.converse=src/converse-embedded include=converse out=dist/converse-muc-embedded.js optimize=none 
+dist/converse-muc-embedded.min.js: src locale node_modules *.js
+	$(UGLIFYJS) --verbose dist/converse-muc-embedded.js -o dist/converse-muc-embedded.min.js
+	cat COPYRIGHT > tmpfile && cat dist/converse-muc-embedded.min.js >> tmpfile && mv tmpfile dist/converse-muc-embedded.min.js
 
 .PHONY: jsmin
 jsmin: $(BUILDS)
@@ -198,7 +216,7 @@ jsmin: $(BUILDS)
 dist:: build
 
 .PHONY: build
-build:: dev css
+build:: dev css transpile
 	$(GRUNT) json
 	make jsmin
 

+ 0 - 13
bower.json

@@ -1,13 +0,0 @@
-{
-  "name": "converse.js",
-  "description": "Web-based XMPP/Jabber chat client written in javascript",
-  "version": "3.1.1",
-  "license": "MPL-2.0",
-  "devDependencies": {},
-  "dependencies": {},
-  "exportsOverride": {},
-  "ignore": [
-    "docs",
-    "mockup"
-  ]
-}

+ 3 - 0
builds/README.rst

@@ -0,0 +1,3 @@
+This directory exists as a location for intermediate files generated by the
+Babel compiler, before they're bundled into distribution bundles in the
+`./dist/` directory.

+ 238 - 154
css/converse.css

@@ -1146,7 +1146,7 @@
 #converse-embedded-chat,
 #conversejs {
   bottom: 0;
-  color: #818479;
+  color: #777;
   direction: ltr;
   display: block;
   font-family: "Helvetica", "Arial", sans-serif;
@@ -1201,9 +1201,6 @@
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none; }
-  #converse-embedded-chat .emoticon,
-  #conversejs .emoticon {
-    font-size: 14px; }
 @keyframes fadein {
   0% {
     opacity: 0; }
@@ -1234,6 +1231,10 @@
   #conversejs .hidden {
     opacity: 0;
     display: none; }
+  #converse-embedded-chat .collapsed,
+  #conversejs .collapsed {
+    height: 0;
+    overflow: hidden; }
   #converse-embedded-chat .locked,
   #conversejs .locked {
     padding-right: 22px; }
@@ -1260,6 +1261,9 @@
     -ms-transform: rotate(359deg);
     -o-transform: rotate(359deg);
     transform: rotate(359deg); } }
+  #converse-embedded-chat .emojione,
+  #conversejs .emojione {
+    height: 20px; }
   #converse-embedded-chat .spinner,
   #conversejs .spinner {
     -webkit-animation: spin 2s infinite, linear;
@@ -1332,6 +1336,9 @@
   #converse-embedded-chat .activated,
   #conversejs .activated {
     display: block !important; }
+  #converse-embedded-chat .pure-form-message,
+  #conversejs .pure-form-message {
+    padding: 0.5em 0; }
   #converse-embedded-chat .pure-button,
   #conversejs .pure-button {
     border-radius: 4px; }
@@ -1346,14 +1353,14 @@
   #converse-embedded-chat .button-cancel,
   #conversejs .button-cancel {
     color: white;
-    background-color: #818479; }
+    background-color: #777; }
   #converse-embedded-chat form.pure-form.converse-form,
   #conversejs form.pure-form.converse-form {
     background: white;
     padding: 1em; }
     #converse-embedded-chat form.pure-form.converse-form legend,
     #conversejs form.pure-form.converse-form legend {
-      color: #818479; }
+      color: #777; }
     #converse-embedded-chat form.pure-form.converse-form label,
     #conversejs form.pure-form.converse-form label {
       margin-top: 1em;
@@ -1380,7 +1387,7 @@
     #converse-embedded-chat form.pure-form.converse-form input.error,
     #conversejs form.pure-form.converse-form input.error {
       border: 1px solid #A53214;
-      color: #818479; }
+      color: #777; }
     #converse-embedded-chat form.pure-form.converse-form .form-help,
     #conversejs form.pure-form.converse-form .form-help {
       color: gray;
@@ -1388,7 +1395,7 @@
       padding-top: 0.5em; }
       #converse-embedded-chat form.pure-form.converse-form .form-help:hover,
       #conversejs form.pure-form.converse-form .form-help:hover {
-        color: #818479; }
+        color: #777; }
   #converse-embedded-chat form.pure-form.converse-centered-form,
   #conversejs form.pure-form.converse-centered-form {
     text-align: center;
@@ -1518,10 +1525,11 @@
   #converse-embedded-chat .chatbox .chat-title,
   #conversejs .chatbox .chat-title {
     color: white;
-    line-height: 15px;
     display: block;
+    line-height: 15px;
+    overflow: hidden;
     text-overflow: ellipsis;
-    overflow: hidden; }
+    white-space: nowrap; }
     #converse-embedded-chat .chatbox .chat-title a,
     #conversejs .chatbox .chat-title a {
       color: white;
@@ -1547,7 +1555,7 @@
         border-bottom-right-radius: 0; } }
     #converse-embedded-chat .chatbox .chat-body p,
     #conversejs .chatbox .chat-body p {
-      color: #818479;
+      color: #777;
       font-size: 14px;
       margin: 0;
       padding: 5px; }
@@ -1578,24 +1586,27 @@
       font-style: italic; }
     #converse-embedded-chat .chatbox .chat-body .chat-message,
     #conversejs .chatbox .chat-body .chat-message {
-      margin: 0.3em; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-author,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-author {
+      overflow: auto; }
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-author,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-author {
         font-weight: bold;
         white-space: nowrap;
         float: left;
         text-overflow: ellipsis;
         overflow: hidden; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-them,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-them {
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-them,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-them {
         color: #E77051; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-me,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-me {
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-me,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-me {
         color: #578EA9; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-content,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-content {
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-content,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-content {
         max-width: 100%;
         word-wrap: break-word; }
+        #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-content .emojione,
+        #conversejs .chatbox .chat-body .chat-message .chat-msg-content .emojione {
+          margin-bottom: -6px; }
     #converse-embedded-chat .chatbox .chat-body .delayed .chat-msg-them,
     #conversejs .chatbox .chat-body .delayed .chat-msg-them {
       color: #FB5D50; }
@@ -1619,7 +1630,7 @@
     position: relative;
     padding: 0.5em;
     font-size: 13px;
-    color: #818479;
+    color: #777;
     overflow-y: auto;
     border: 0;
     background-color: #ffffff;
@@ -1691,12 +1702,9 @@
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar a,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar a {
         font-size: 14px;
-        color: #818479;
+        color: #777;
         text-decoration: none;
         text-shadow: none; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toolbar-picker-panel a,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toolbar-picker-panel a {
-        color: #578EA9; }
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .chat-toolbar-text,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .chat-toolbar-text {
         font-size: 12px;
@@ -1705,11 +1713,11 @@
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted {
-        color: #818479; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-picker-panel a,
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-picker-panel a,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-picker-panel a,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-picker-panel a {
+        color: #777; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-menu a,
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-menu a,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-menu a,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-menu a {
           color: #578EA9; }
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unverified a,
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unverified,
@@ -1730,67 +1738,89 @@
         float: right; }
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li {
+        cursor: pointer;
         display: inline-block;
         list-style: none;
-        padding: 0 3px 0 3px;
-        cursor: pointer;
-        margin-top: 1px; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li:hover,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li:hover {
-        cursor: pointer; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar ul,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul {
-        background: #fff;
-        bottom: 100%;
-        box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
-        display: none;
-        font-size: 12px;
-        margin: 0;
-        position: absolute;
-        right: 0; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar ul li,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li {
-          cursor: pointer;
-          list-style: none;
-          position: relative; }
-          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar ul li a:hover,
-          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li a:hover {
-            color: #8f2831; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li {
-        margin-left: 0; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley {
-        color: #818479;
-        padding-left: 5px; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul {
-          left: 0; }
-          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li,
-          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li {
-            font-size: 14px;
-            padding: 5px;
-            z-index: 98; }
-          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li:hover,
-          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li:hover {
+        margin-top: 1px;
+        padding: 0 3px 0 3px; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li:hover,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li:hover {
+          cursor: pointer; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu {
+          background-color: #fff;
+          bottom: 100%;
+          box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
+          font-size: 12px;
+          margin: 0;
+          position: absolute;
+          right: 0; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu a,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu a {
+            color: #578EA9; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-picker,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-picker {
+            height: 100px;
+            overflow: scroll;
+            padding: 0.5em; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar {
+            /* offset-x | offset-y | blur-radius | spread-radius | color */
+            box-shadow: 0 -1px 2px 0 rgba(0, 0, 0, 0.4); }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar {
+            overflow: hidden;
+            left: 0; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar .picked,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar .picked {
+              background-color: #DCF9F6; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li {
+              height: 30px;
+              padding: 4px;
+              z-index: 98; }
+              #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li.emoji a,
+              #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li.emoji a {
+                font-size: 20px; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li {
+            margin-left: 0;
+            cursor: pointer;
+            list-style: none;
+            position: relative; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji {
+              padding: 0.3em; }
+              #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji:hover,
+              #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji:hover {
+                background-color: #DCF9F6; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li a:hover,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li a:hover {
+              color: #8f2831; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-toolbar-menu,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-toolbar-menu {
+          color: #777; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley {
+          padding-left: 5px; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover,
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover {
             background-color: #DCF9F6; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li {
-        padding: 7px;
-        background-color: white;
-        display: block;
-        z-index: 99; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li a,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li a {
-          -moz-transition: background-color 0.2s ease-in-out;
-          -webkit-transition: background-color 0.2s ease-in-out;
-          transition: background-color 0.2s ease-in-out;
-          display: block;
-          padding: 1px;
-          text-decoration: none; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li:hover,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li:hover {
-        background-color: #DCF9F6; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul {
+          z-index: 99; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li {
+            display: block;
+            padding: 7px; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li:hover,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li:hover {
+              background-color: #DCF9F6; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li a,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li a {
+              display: block; }
   #converse-embedded-chat .chatbox .dragresize,
   #conversejs .chatbox .dragresize {
     background: transparent;
@@ -1832,6 +1862,31 @@
     #conversejs .chat-head {
       border-top-left-radius: 0;
       border-top-right-radius: 0; } }
+#converse-embedded-chat .chatbox .chat-body .chat-message,
+#conversejs .chatbox .chat-body .chat-message {
+  margin: 0.3em;
+  line-height: 20px; }
+  #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-author,
+  #conversejs .chatbox .chat-body .chat-message .chat-msg-author {
+    line-height: 20px; }
+  #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-content,
+  #conversejs .chatbox .chat-body .chat-message .chat-msg-content {
+    line-height: 20px; }
+    #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-content .emojione,
+    #conversejs .chatbox .chat-body .chat-message .chat-msg-content .emojione {
+      margin-bottom: -5px; }
+#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar,
+#conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar {
+  width: 100%; }
+  #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar .emoji-category,
+  #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar .emoji-category {
+    float: left; }
+  #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li,
+  #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li {
+    padding: 2px; }
+#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley ul li,
+#conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley ul li {
+  padding: 2px; }
 
 #conversejs #controlbox {
   margin-right: 1em; }
@@ -1911,7 +1966,7 @@
       color: gray;
       font-size: 85%; }
       #conversejs #controlbox #converse-register .instructions:hover {
-        color: #818479; }
+        color: #777; }
   #conversejs #controlbox #converse-register, #conversejs #controlbox #converse-login {
     margin-top: 2em; }
     #conversejs #controlbox #converse-register .login-anon, #conversejs #controlbox #converse-login .login-anon {
@@ -1936,7 +1991,7 @@
     margin: 0; }
     #conversejs #controlbox #chatrooms .rooms-list-container .rooms-toggle {
       display: block;
-      color: #818479;
+      color: #777;
       margin-top: 1em; }
       #conversejs #controlbox #chatrooms .rooms-list-container .rooms-toggle:hover {
         color: #585B51; }
@@ -1945,88 +2000,111 @@
       text-align: left; }
       #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dt {
         border: none;
-        color: #818479;
+        color: #777;
         font-weight: normal;
         padding: 0;
         padding-bottom: 0.5em;
         text-shadow: 0 1px 0 #FAFAFA; }
-      #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom {
+      #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom,
+      #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom {
         border: none;
         clear: both;
-        color: #818479;
+        color: #777;
         display: block;
         overflow: hidden;
         padding: 0.3em 0;
         text-shadow: 0 1px 0 #FAFAFA;
         word-wrap: break-word; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a:hover {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a:hover,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a:hover {
           color: #206485; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom.unread-msgs .open-room {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom.unread-msgs .available-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom.unread-msgs .open-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom.unread-msgs .available-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom.unread-msgs .open-room {
           max-width: 55%;
           width: auto;
           font-weight: bold; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a.room-info:before {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a.room-info:before,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a.room-info:before {
           font-size: 15px; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a.open-room {
-          float: left;
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a.open-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a.open-room {
           width: 68%;
+          float: left;
           overflow: hidden;
           text-overflow: ellipsis;
           white-space: nowrap;
           padding-right: 0.5em; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .remove-bookmark {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a.available-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a.available-room {
+          width: 85%; }
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .add-bookmark,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .remove-bookmark,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .add-bookmark,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .remove-bookmark {
           color: #A8ABA1; }
-          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .remove-bookmark.button-on {
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .add-bookmark.button-on,
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .remove-bookmark.button-on,
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .add-bookmark.button-on,
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .remove-bookmark.button-on {
             color: #578EA9; }
-            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .remove-bookmark.button-on:hover {
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .add-bookmark.button-on:hover,
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .remove-bookmark.button-on:hover,
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .add-bookmark.button-on:hover,
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .remove-bookmark.button-on:hover {
               color: #206485; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .room-info {
           font-size: 12px;
           font-style: normal;
           font-weight: normal; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom li.room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom li.room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom li.room-info {
           display: block;
           margin-left: 5px; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom p.room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom p.room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom p.room-info {
           line-height: 16px;
           margin: 0;
           display: block;
           white-space: normal; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom div.room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom div.room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom div.room-info {
           padding: 0.3em 0;
           clear: left;
           width: 100%; }
-  #conversejs #controlbox .dropdown {
-    /* Custom addition for CSP */ }
-    #conversejs #controlbox .dropdown a {
-      width: 143px;
-      display: inline-block; }
-    #conversejs #controlbox .dropdown li {
-      list-style: none;
-      padding-left: 0; }
-    #conversejs #controlbox .dropdown dd ul {
-      padding: 0;
-      list-style: none;
+  #conversejs #controlbox .dropdown a {
+    width: 143px;
+    display: inline-block; }
+  #conversejs #controlbox .dropdown li {
+    list-style: none;
+    padding-left: 0; }
+  #conversejs #controlbox .dropdown dd ul {
+    padding: 0;
+    list-style: none;
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    z-index: 21;
+    background-color: #FCFDFD; }
+    #conversejs #controlbox .dropdown dd ul li:hover {
+      background-color: #DCF9F6; }
+  #conversejs #controlbox .dropdown dd.search-xmpp {
+    height: 0; }
+    #conversejs #controlbox .dropdown dd.search-xmpp .contact-form-container {
       position: absolute;
-      left: 0;
-      top: 0;
-      border: 1px solid #B1BFC4;
-      width: 100%;
-      z-index: 21;
+      z-index: 22; }
+      #conversejs #controlbox .dropdown dd.search-xmpp .contact-form-container form {
+        box-shadow: 1px 4px 10px 1px rgba(0, 0, 0, 0.4);
+        background-color: white; }
+    #conversejs #controlbox .dropdown dd.search-xmpp li:hover {
       background-color: #FCFDFD; }
-      #conversejs #controlbox .dropdown dd ul li:hover {
-        background-color: #DCF9F6; }
-    #conversejs #controlbox .dropdown dd.search-xmpp {
-      display: none;
-      width: 100%; }
-    #conversejs #controlbox .dropdown dd.search-xmpp ul {
-      box-shadow: 1px 4px 10px 1px rgba(0, 0, 0, 0.4); }
-      #conversejs #controlbox .dropdown dd.search-xmpp ul li:hover {
-        background-color: #FCFDFD; }
-    #conversejs #controlbox .dropdown dt a span {
-      cursor: pointer;
-      display: block;
-      padding: 4px 7px 0 5px; }
+  #conversejs #controlbox .dropdown dt a span {
+    cursor: pointer;
+    display: block;
+    padding: 4px 7px 0 5px; }
   #conversejs #controlbox #select-xmpp-status {
     display: none;
     float: right;
@@ -2049,7 +2127,7 @@
         border-top-left-radius: 5px;
         border-top-right-radius: 5px;
         box-shadow: inset 2px -2px 20px rgba(0, 0, 0, 0.3);
-        color: #818479;
+        color: #777;
         display: block;
         font-size: 12px;
         height: 54px;
@@ -2067,7 +2145,7 @@
           border-top-right-radius: 5px;
           float: right; }
         #conversejs #controlbox #controlbox-tabs li a:hover {
-          color: #818479; }
+          color: #777; }
           #conversejs #controlbox #controlbox-tabs li a:hover .msgs-indicator {
             opacity: 1; }
         #conversejs #controlbox #controlbox-tabs li a.current, #conversejs #controlbox #controlbox-tabs li a.current:hover {
@@ -2075,7 +2153,7 @@
           border-bottom: 0;
           height: 55px;
           cursor: default;
-          color: #818479; }
+          color: #777; }
   #conversejs #controlbox .fancy-dropdown {
     border: 1px solid #B1BFC4;
     height: 25px;
@@ -2143,7 +2221,6 @@
   #conversejs #controlbox #users {
     overflow-y: hidden; }
   #conversejs #controlbox .add-xmpp-contact {
-    background: none;
     padding: 1em 0.5em; }
     #conversejs #controlbox .add-xmpp-contact input {
       margin: 0 0 1rem;
@@ -2257,13 +2334,13 @@
     display: none; }
     #conversejs #converse-roster .roster-contacts dt.roster-group {
       border: none;
-      color: #818479;
+      color: #777;
       display: none;
       font-weight: normal;
       margin: 1em 0 0.5em 0;
       text-shadow: 0 1px 0 #FAFAFA; }
       #conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle {
-        color: #818479;
+        color: #777;
         display: block;
         width: 100%; }
         #conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle:hover {
@@ -2271,7 +2348,7 @@
     #conversejs #converse-roster .roster-contacts dd {
       border: none;
       clear: both;
-      color: #818479;
+      color: #777;
       display: block;
       height: 24px;
       overflow-y: hidden;
@@ -2367,17 +2444,24 @@
   #conversejs .chat-head-chatroom .chatbox-btn.button-on {
     background-color: white;
     color: #E77051; }
-  #converse-embedded-chat .chat-head-chatroom .chatroom-description,
-  #conversejs .chat-head-chatroom .chatroom-description {
-    color: white;
-    font-size: 80%;
-    font-style: italic;
-    height: 1.3em;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    margin: 0;
-    margin-top: 0.3em; }
+  #converse-embedded-chat .chat-head-chatroom .chat-title,
+  #conversejs .chat-head-chatroom .chat-title {
+    color: #FF977C; }
+    #converse-embedded-chat .chat-head-chatroom .chat-title .chatroom-name,
+    #conversejs .chat-head-chatroom .chat-title .chatroom-name {
+      color: white; }
+    #converse-embedded-chat .chat-head-chatroom .chat-title .chatroom-jid,
+    #conversejs .chat-head-chatroom .chat-title .chatroom-jid {
+      font-size: 12px; }
+    #converse-embedded-chat .chat-head-chatroom .chat-title .chatroom-description,
+    #conversejs .chat-head-chatroom .chat-title .chatroom-description {
+      color: white;
+      font-size: 80%;
+      font-style: italic;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      margin: 0.3em 0; }
 #converse-embedded-chat .chatroom,
 #conversejs .chatroom {
   width: 300px; }
@@ -2454,7 +2538,7 @@
         float: right;
         vertical-align: top;
         background-color: white;
-        border-left: 1px solid #818479;
+        border-left: 1px solid #777;
         border-bottom-right-radius: 4px;
         width: 30%;
         height: 100%;
@@ -2537,7 +2621,7 @@
         border-bottom-left-radius: 4px;
         border-bottom-right-radius: 4px;
         border: 0;
-        color: #818479;
+        color: #777;
         font-size: 14px;
         height: 289px;
         width: 100%;

+ 226 - 161
css/inverse.css

@@ -1146,7 +1146,7 @@
 #converse-embedded-chat,
 #conversejs {
   bottom: 0;
-  color: #818479;
+  color: #777;
   direction: ltr;
   display: block;
   font-family: "Helvetica", "Arial", sans-serif;
@@ -1201,9 +1201,6 @@
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none; }
-  #converse-embedded-chat .emoticon,
-  #conversejs .emoticon {
-    font-size: 16px; }
 @keyframes fadein {
   0% {
     opacity: 0; }
@@ -1234,6 +1231,10 @@
   #conversejs .hidden {
     opacity: 0;
     display: none; }
+  #converse-embedded-chat .collapsed,
+  #conversejs .collapsed {
+    height: 0;
+    overflow: hidden; }
   #converse-embedded-chat .locked,
   #conversejs .locked {
     padding-right: 22px; }
@@ -1260,6 +1261,9 @@
     -ms-transform: rotate(359deg);
     -o-transform: rotate(359deg);
     transform: rotate(359deg); } }
+  #converse-embedded-chat .emojione,
+  #conversejs .emojione {
+    height: 22px; }
   #converse-embedded-chat .spinner,
   #conversejs .spinner {
     -webkit-animation: spin 2s infinite, linear;
@@ -1332,6 +1336,9 @@
   #converse-embedded-chat .activated,
   #conversejs .activated {
     display: block !important; }
+  #converse-embedded-chat .pure-form-message,
+  #conversejs .pure-form-message {
+    padding: 0.5em 0; }
   #converse-embedded-chat .pure-button,
   #conversejs .pure-button {
     border-radius: 7px; }
@@ -1346,14 +1353,14 @@
   #converse-embedded-chat .button-cancel,
   #conversejs .button-cancel {
     color: white;
-    background-color: #818479; }
+    background-color: #777; }
   #converse-embedded-chat form.pure-form.converse-form,
   #conversejs form.pure-form.converse-form {
     background: white;
     padding: 1em; }
     #converse-embedded-chat form.pure-form.converse-form legend,
     #conversejs form.pure-form.converse-form legend {
-      color: #818479; }
+      color: #777; }
     #converse-embedded-chat form.pure-form.converse-form label,
     #conversejs form.pure-form.converse-form label {
       margin-top: 1em;
@@ -1380,7 +1387,7 @@
     #converse-embedded-chat form.pure-form.converse-form input.error,
     #conversejs form.pure-form.converse-form input.error {
       border: 1px solid #A53214;
-      color: #818479; }
+      color: #777; }
     #converse-embedded-chat form.pure-form.converse-form .form-help,
     #conversejs form.pure-form.converse-form .form-help {
       color: gray;
@@ -1388,7 +1395,7 @@
       padding-top: 0.5em; }
       #converse-embedded-chat form.pure-form.converse-form .form-help:hover,
       #conversejs form.pure-form.converse-form .form-help:hover {
-        color: #818479; }
+        color: #777; }
   #converse-embedded-chat form.pure-form.converse-centered-form,
   #conversejs form.pure-form.converse-centered-form {
     text-align: center;
@@ -1439,7 +1446,7 @@ body {
   #conversejs form.pure-form.converse-form {
     margin: 1em; }
     #conversejs form.pure-form.converse-form legend {
-      color: #818479; }
+      color: #777; }
     #conversejs form.pure-form.converse-form label {
       margin-top: 1em; }
     #conversejs form.pure-form.converse-form input[type=text],
@@ -1564,10 +1571,11 @@ body {
   #converse-embedded-chat .chatbox .chat-title,
   #conversejs .chatbox .chat-title {
     color: white;
-    line-height: 15px;
     display: block;
+    line-height: 15px;
+    overflow: hidden;
     text-overflow: ellipsis;
-    overflow: hidden; }
+    white-space: nowrap; }
     #converse-embedded-chat .chatbox .chat-title a,
     #conversejs .chatbox .chat-title a {
       color: white;
@@ -1593,7 +1601,7 @@ body {
         border-bottom-right-radius: 0; } }
     #converse-embedded-chat .chatbox .chat-body p,
     #conversejs .chatbox .chat-body p {
-      color: #818479;
+      color: #777;
       font-size: 16px;
       margin: 0;
       padding: 5px; }
@@ -1624,24 +1632,27 @@ body {
       font-style: italic; }
     #converse-embedded-chat .chatbox .chat-body .chat-message,
     #conversejs .chatbox .chat-body .chat-message {
-      margin: 0.3em; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-author,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-author {
+      overflow: auto; }
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-author,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-author {
         font-weight: bold;
         white-space: nowrap;
         float: left;
         text-overflow: ellipsis;
         overflow: hidden; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-them,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-them {
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-them,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-them {
         color: #E77051; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-me,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-me {
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-me,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-me {
         color: #578EA9; }
-      #converse-embedded-chat .chatbox .chat-body .chat-message span.chat-msg-content,
-      #conversejs .chatbox .chat-body .chat-message span.chat-msg-content {
+      #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-content,
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-content {
         max-width: 100%;
         word-wrap: break-word; }
+        #converse-embedded-chat .chatbox .chat-body .chat-message .chat-msg-content .emojione,
+        #conversejs .chatbox .chat-body .chat-message .chat-msg-content .emojione {
+          margin-bottom: -6px; }
     #converse-embedded-chat .chatbox .chat-body .delayed .chat-msg-them,
     #conversejs .chatbox .chat-body .delayed .chat-msg-them {
       color: #FB5D50; }
@@ -1665,7 +1676,7 @@ body {
     position: relative;
     padding: 0.5em;
     font-size: 13px;
-    color: #818479;
+    color: #777;
     overflow-y: auto;
     border: 0;
     background-color: #ffffff;
@@ -1737,12 +1748,9 @@ body {
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar a,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar a {
         font-size: 16px;
-        color: #818479;
+        color: #777;
         text-decoration: none;
         text-shadow: none; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toolbar-picker-panel a,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toolbar-picker-panel a {
-        color: #578EA9; }
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .chat-toolbar-text,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .chat-toolbar-text {
         font-size: 12px;
@@ -1751,11 +1759,11 @@ body {
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted {
-        color: #818479; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-picker-panel a,
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-picker-panel a,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-picker-panel a,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-picker-panel a {
+        color: #777; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-menu a,
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-menu a,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a .toolbar-menu a,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted .toolbar-menu a {
           color: #578EA9; }
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unverified a,
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .unverified,
@@ -1776,67 +1784,89 @@ body {
         float: right; }
       #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li,
       #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li {
+        cursor: pointer;
         display: inline-block;
         list-style: none;
-        padding: 0 3px 0 3px;
-        cursor: pointer;
-        margin-top: 1px; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li:hover,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li:hover {
-        cursor: pointer; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar ul,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul {
-        background: #fff;
-        bottom: 100%;
-        box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
-        display: none;
-        font-size: 12px;
-        margin: 0;
-        position: absolute;
-        right: 0; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar ul li,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li {
-          cursor: pointer;
-          list-style: none;
-          position: relative; }
-          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar ul li a:hover,
-          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li a:hover {
-            color: #8f2831; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li {
-        margin-left: 0; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley {
-        color: #818479;
-        padding-left: 5px; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul {
-          left: 0; }
-          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li,
-          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li {
-            font-size: 16px;
-            padding: 5px;
-            z-index: 98; }
-          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li:hover,
-          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li:hover {
+        margin-top: 1px;
+        padding: 0 3px 0 3px; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li:hover,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li:hover {
+          cursor: pointer; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu {
+          background-color: #fff;
+          bottom: 100%;
+          box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
+          font-size: 12px;
+          margin: 0;
+          position: absolute;
+          right: 0; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu a,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu a {
+            color: #578EA9; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-picker,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-picker {
+            height: 150px;
+            overflow: scroll;
+            padding: 0.5em; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar {
+            /* offset-x | offset-y | blur-radius | spread-radius | color */
+            box-shadow: 0 -1px 2px 0 rgba(0, 0, 0, 0.4); }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar {
+            overflow: hidden;
+            left: 0; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar .picked,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar .picked {
+              background-color: #DCF9F6; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li {
+              height: 32px;
+              padding: 4px;
+              z-index: 98; }
+              #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li.emoji a,
+              #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul.emoji-toolbar li.emoji a {
+                font-size: 26px; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li {
+            margin-left: 0;
+            cursor: pointer;
+            list-style: none;
+            position: relative; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji {
+              padding: 0.3em; }
+              #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji:hover,
+              #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji:hover {
+                background-color: #DCF9F6; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li a:hover,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu ul li a:hover {
+              color: #8f2831; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-toolbar-menu,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-toolbar-menu {
+          color: #777; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley {
+          padding-left: 5px; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover,
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover {
             background-color: #DCF9F6; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li {
-        padding: 7px;
-        background-color: white;
-        display: block;
-        z-index: 99; }
-        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li a,
-        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li a {
-          -moz-transition: background-color 0.2s ease-in-out;
-          -webkit-transition: background-color 0.2s ease-in-out;
-          transition: background-color 0.2s ease-in-out;
-          display: block;
-          padding: 1px;
-          text-decoration: none; }
-      #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li:hover,
-      #conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li:hover {
-        background-color: #DCF9F6; }
+        #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul,
+        #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul {
+          z-index: 99; }
+          #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li,
+          #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li {
+            display: block;
+            padding: 7px; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li:hover,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li:hover {
+              background-color: #DCF9F6; }
+            #converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li a,
+            #conversejs .chatbox form.sendXMPPMessage .chat-toolbar li.toggle-otr ul li a {
+              display: block; }
   #converse-embedded-chat .chatbox .dragresize,
   #conversejs .chatbox .dragresize {
     background: transparent;
@@ -1900,9 +1930,13 @@ body {
     border-top-left-radius: 7px;
     border-top-right-radius: 7px; }
     #conversejs .chatbox .chat-body .chat-message {
+      line-height: 22px;
       font-size: 14px;
-      line-height: 20px;
       margin: 0.5em 0; }
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-author {
+        line-height: 22px; }
+      #conversejs .chatbox .chat-body .chat-message .chat-msg-content {
+        line-height: 22px; }
   #conversejs .chatbox .chat-content {
     padding: 0 1em 1em 1em;
     border-top-left-radius: 7px;
@@ -1914,8 +1948,11 @@ body {
     width: 100%; }
   #conversejs .chatbox form.sendXMPPMessage .toggle-smiley {
     padding-left: 0.5em; }
-    #conversejs .chatbox form.sendXMPPMessage .toggle-smiley ul li {
-      padding: 0.5em; }
+    #conversejs .chatbox form.sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category-picker {
+      margin-right: 5em; }
+    #conversejs .chatbox form.sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category {
+      padding-left: 10px;
+      padding-right: 10px; }
 
 #conversejs #controlbox {
   margin-right: 1em; }
@@ -1995,7 +2032,7 @@ body {
       color: gray;
       font-size: 85%; }
       #conversejs #controlbox #converse-register .instructions:hover {
-        color: #818479; }
+        color: #777; }
   #conversejs #controlbox #converse-register, #conversejs #controlbox #converse-login {
     margin-top: 2em; }
     #conversejs #controlbox #converse-register .login-anon, #conversejs #controlbox #converse-login .login-anon {
@@ -2020,7 +2057,7 @@ body {
     margin: 0; }
     #conversejs #controlbox #chatrooms .rooms-list-container .rooms-toggle {
       display: block;
-      color: #818479;
+      color: #777;
       margin-top: 1em; }
       #conversejs #controlbox #chatrooms .rooms-list-container .rooms-toggle:hover {
         color: #585B51; }
@@ -2029,88 +2066,111 @@ body {
       text-align: left; }
       #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dt {
         border: none;
-        color: #818479;
+        color: #777;
         font-weight: normal;
         padding: 0;
         padding-bottom: 0.5em;
         text-shadow: 0 1px 0 #FAFAFA; }
-      #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom {
+      #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom,
+      #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom {
         border: none;
         clear: both;
-        color: #818479;
+        color: #777;
         display: block;
         overflow: hidden;
         padding: 0.3em 0;
         text-shadow: 0 1px 0 #FAFAFA;
         word-wrap: break-word; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a:hover {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a:hover,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a:hover {
           color: #206485; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom.unread-msgs .open-room {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom.unread-msgs .available-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom.unread-msgs .open-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom.unread-msgs .available-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom.unread-msgs .open-room {
           max-width: 55%;
           width: auto;
           font-weight: bold; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a.room-info:before {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a.room-info:before,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a.room-info:before {
           font-size: 15px; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a.open-room {
-          float: left;
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a.open-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a.open-room {
           width: 68%;
+          float: left;
           overflow: hidden;
           text-overflow: ellipsis;
           white-space: nowrap;
           padding-right: 0.5em; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .remove-bookmark {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom a.available-room,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom a.available-room {
+          width: 85%; }
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .add-bookmark,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .remove-bookmark,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .add-bookmark,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .remove-bookmark {
           color: #A8ABA1; }
-          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .remove-bookmark.button-on {
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .add-bookmark.button-on,
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .remove-bookmark.button-on,
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .add-bookmark.button-on,
+          #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .remove-bookmark.button-on {
             color: #578EA9; }
-            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .remove-bookmark.button-on:hover {
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .add-bookmark.button-on:hover,
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .remove-bookmark.button-on:hover,
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .add-bookmark.button-on:hover,
+            #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .remove-bookmark.button-on:hover {
               color: #206485; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom .room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom .room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom .room-info {
           font-size: 14px;
           font-style: normal;
           font-weight: normal; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom li.room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom li.room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom li.room-info {
           display: block;
           margin-left: 5px; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom p.room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom p.room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom p.room-info {
           line-height: 22px;
           margin: 0;
           display: block;
           white-space: normal; }
-        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom div.room-info {
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .available-chatroom div.room-info,
+        #conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list .open-chatroom div.room-info {
           padding: 0.3em 0;
           clear: left;
           width: 100%; }
-  #conversejs #controlbox .dropdown {
-    /* Custom addition for CSP */ }
-    #conversejs #controlbox .dropdown a {
-      width: 143px;
-      display: inline-block; }
-    #conversejs #controlbox .dropdown li {
-      list-style: none;
-      padding-left: 0; }
-    #conversejs #controlbox .dropdown dd ul {
-      padding: 0;
-      list-style: none;
+  #conversejs #controlbox .dropdown a {
+    width: 143px;
+    display: inline-block; }
+  #conversejs #controlbox .dropdown li {
+    list-style: none;
+    padding-left: 0; }
+  #conversejs #controlbox .dropdown dd ul {
+    padding: 0;
+    list-style: none;
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    z-index: 21;
+    background-color: #FCFDFD; }
+    #conversejs #controlbox .dropdown dd ul li:hover {
+      background-color: #DCF9F6; }
+  #conversejs #controlbox .dropdown dd.search-xmpp {
+    height: 0; }
+    #conversejs #controlbox .dropdown dd.search-xmpp .contact-form-container {
       position: absolute;
-      left: 0;
-      top: 0;
-      border: 1px solid #B1BFC4;
-      width: 100%;
-      z-index: 21;
+      z-index: 22; }
+      #conversejs #controlbox .dropdown dd.search-xmpp .contact-form-container form {
+        box-shadow: 1px 4px 10px 1px rgba(0, 0, 0, 0.4);
+        background-color: white; }
+    #conversejs #controlbox .dropdown dd.search-xmpp li:hover {
       background-color: #FCFDFD; }
-      #conversejs #controlbox .dropdown dd ul li:hover {
-        background-color: #DCF9F6; }
-    #conversejs #controlbox .dropdown dd.search-xmpp {
-      display: none;
-      width: 100%; }
-    #conversejs #controlbox .dropdown dd.search-xmpp ul {
-      box-shadow: 1px 4px 10px 1px rgba(0, 0, 0, 0.4); }
-      #conversejs #controlbox .dropdown dd.search-xmpp ul li:hover {
-        background-color: #FCFDFD; }
-    #conversejs #controlbox .dropdown dt a span {
-      cursor: pointer;
-      display: block;
-      padding: 4px 7px 0 5px; }
+  #conversejs #controlbox .dropdown dt a span {
+    cursor: pointer;
+    display: block;
+    padding: 4px 7px 0 5px; }
   #conversejs #controlbox #select-xmpp-status {
     display: none;
     float: right;
@@ -2133,7 +2193,7 @@ body {
         border-top-left-radius: 5px;
         border-top-right-radius: 5px;
         box-shadow: inset 2px -2px 20px rgba(0, 0, 0, 0.3);
-        color: #818479;
+        color: #777;
         display: block;
         font-size: 14px;
         height: 61px;
@@ -2151,7 +2211,7 @@ body {
           border-top-right-radius: 5px;
           float: right; }
         #conversejs #controlbox #controlbox-tabs li a:hover {
-          color: #818479; }
+          color: #777; }
           #conversejs #controlbox #controlbox-tabs li a:hover .msgs-indicator {
             opacity: 1; }
         #conversejs #controlbox #controlbox-tabs li a.current, #conversejs #controlbox #controlbox-tabs li a.current:hover {
@@ -2159,7 +2219,7 @@ body {
           border-bottom: 0;
           height: 62px;
           cursor: default;
-          color: #818479; }
+          color: #777; }
   #conversejs #controlbox .fancy-dropdown {
     border: 1px solid #B1BFC4;
     height: 30px;
@@ -2227,7 +2287,6 @@ body {
   #conversejs #controlbox #users {
     overflow-y: hidden; }
   #conversejs #controlbox .add-xmpp-contact {
-    background: none;
     padding: 1em 0.5em; }
     #conversejs #controlbox .add-xmpp-contact input {
       margin: 0 0 1rem;
@@ -2379,13 +2438,13 @@ body {
     display: none; }
     #conversejs #converse-roster .roster-contacts dt.roster-group {
       border: none;
-      color: #818479;
+      color: #777;
       display: none;
       font-weight: normal;
       margin: 1em 0 0.5em 0;
       text-shadow: 0 1px 0 #FAFAFA; }
       #conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle {
-        color: #818479;
+        color: #777;
         display: block;
         width: 100%; }
         #conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle:hover {
@@ -2393,7 +2452,7 @@ body {
     #conversejs #converse-roster .roster-contacts dd {
       border: none;
       clear: both;
-      color: #818479;
+      color: #777;
       display: block;
       height: 24px;
       overflow-y: hidden;
@@ -2492,17 +2551,24 @@ body {
   #conversejs .chat-head-chatroom .chatbox-btn.button-on {
     background-color: white;
     color: #E77051; }
-  #converse-embedded-chat .chat-head-chatroom .chatroom-description,
-  #conversejs .chat-head-chatroom .chatroom-description {
-    color: white;
-    font-size: 80%;
-    font-style: italic;
-    height: 1.3em;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    margin: 0;
-    margin-top: 0.3em; }
+  #converse-embedded-chat .chat-head-chatroom .chat-title,
+  #conversejs .chat-head-chatroom .chat-title {
+    color: #FF977C; }
+    #converse-embedded-chat .chat-head-chatroom .chat-title .chatroom-name,
+    #conversejs .chat-head-chatroom .chat-title .chatroom-name {
+      color: white; }
+    #converse-embedded-chat .chat-head-chatroom .chat-title .chatroom-jid,
+    #conversejs .chat-head-chatroom .chat-title .chatroom-jid {
+      font-size: 14px; }
+    #converse-embedded-chat .chat-head-chatroom .chat-title .chatroom-description,
+    #conversejs .chat-head-chatroom .chat-title .chatroom-description {
+      color: white;
+      font-size: 80%;
+      font-style: italic;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      margin: 0.3em 0; }
 #converse-embedded-chat .chatroom,
 #conversejs .chatroom {
   width: 300px; }
@@ -2579,7 +2645,7 @@ body {
         float: right;
         vertical-align: top;
         background-color: white;
-        border-left: 1px solid #818479;
+        border-left: 1px solid #777;
         border-bottom-right-radius: 7px;
         width: 30%;
         height: 100%;
@@ -2662,7 +2728,7 @@ body {
         border-bottom-left-radius: 7px;
         border-bottom-right-radius: 7px;
         border: 0;
-        color: #818479;
+        color: #777;
         font-size: 16px;
         height: 289px;
         width: 100%;
@@ -2707,9 +2773,8 @@ body {
   font-size: 20px; }
   #conversejs .chat-head-chatroom .close-chatbox-button:before {
     content: "\e601"; }
-  #conversejs .chat-head-chatroom .chatroom-description {
-    font-size: 66%;
-    margin-top: 3px; }
+  #conversejs .chat-head-chatroom .chat-title .chatroom-description {
+    font-size: 65%; }
 #conversejs .chatroom {
   width: -webkit-calc(100% - 250px);
   width: calc(100% - 250px); }

+ 1 - 1
css/theme.css

@@ -344,5 +344,5 @@ ul.features {
 .sponsors {
     clear: both;
     font-size: 1.1em;
-    padding: 6em 0 7em 0;
+    padding: 2em 0 7em 0;
 }

+ 10 - 11
demo/without_bundled_dependencies.html

@@ -52,9 +52,10 @@
     <script type="text/javascript" src="../dist/locales.js"></script>
     <!-- END I18N -->
 
-    <script type="text/javascript" src="../node_modules/awesomplete/awesomplete.js"></script>
+    <script type="text/javascript" src="../node_modules/awesomplete-avoid-xss/awesomplete.js"></script>
     <script type="text/javascript" src="../node_modules/moment/min/moment-with-locales.js"></script>
 
+    <script type="text/javascript" src="../3rdparty/lodash.fp.js"></script>
 	<script src="../dist/converse-no-dependencies.js"></script>
 </head>
 <body id="page-top" data-spy="scroll" data-target=".navbar-custom">
@@ -80,16 +81,14 @@
 </body>
 
 <script>
-    require(['converse'], function (converse) {
-        converse.initialize({
-            bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
-            i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
-            prebind: false,
-            show_controlbox_by_default: true,
-            debug: true,
-            roster_groups: true,
-            keepalive: true
-        });
+    converse.initialize({
+        bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
+        i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
+        prebind: false,
+        show_controlbox_by_default: true,
+        debug: true,
+        roster_groups: true,
+        keepalive: true
     });
 </script>
 </html>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 26281 - 25118
dist/converse-mobile.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1125 - 1739
dist/converse-no-dependencies.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 26285 - 25122
dist/converse.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 26285 - 25122
dist/inverse.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/locales.js


+ 1 - 1
docs/source/_templates/layout.html

@@ -2,7 +2,7 @@
 {% extends "!layout.html" %}
 
 {# Custom CSS overrides #}
-{% set bootswatch_css_custom = ['_static/style.css', "../../css/converse.min.css"] %}
+{% set css_files = css_files + ['_static/style.css', "../../css/converse.min.css"] %}
 {% set script_files = script_files + ["../../dist/converse.min.js", "../../analytics.js"] %}
 
 {# Add some extra stuff before and use existing with 'super()' call. #}

+ 2 - 2
docs/source/conf.py

@@ -48,9 +48,9 @@ copyright = u'2014, JC Brand'
 # built documents.
 #
 # The short X.Y version.
-version = '3.1.1'
+version = '3.2.0-rc'
 # The full version, including alpha/beta/rc tags.
-release = '3.1.1'
+release = '3.2.0-rc'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.

+ 27 - 6
docs/source/configuration.rst

@@ -19,7 +19,7 @@ on your website.
 You'll most likely want to call the *initialize* method in your HTML page. For
 an example of how this is done, please see the bottom of the *./index.html* page.
 
-Please refer to the `Configuration variables`_ section below for info on
+Please refer to the `Configuration settings`_ section below for info on
 all the available configuration settings.
 
 After you have configured *Converse.js*, you'll have to regenerate the minified
@@ -516,6 +516,16 @@ domain_placeholder
 
 The placeholder text shown in the domain input on the registration form.
 
+
+emojione_image_path
+-------------------
+
+* Default: ``'https://cdn.jsdelivr.net/emojione/assets/' + emojioneVersion + '/png/'``
+
+When `use_emojione`_ is set to ``true``, then this is the URL from where PNG image files for
+displaying emojis will be fetched.
+
+
 expose_rid_and_sid
 ------------------
 
@@ -993,6 +1003,17 @@ Notification will be shown in the following cases:
 
 Requires the `src/converse-notification.js` plugin.
 
+use_emojione
+------------
+* Default: ``true``
+
+Determines whether `Emojione <https://www.emojione.com/>`_ should be used to
+render emojis. If set to ``false``, then rendering support will fall back to
+the operating system or browser (which might not support emoji).
+
+See also `emojione_image_path`_.
+
+
 show_only_online_users
 ----------------------
 
@@ -1088,7 +1109,7 @@ loaded), then an error will be raised.
 
 Otherwise a message will simply be logged and the override instruction ignored.
 
-The Converse.js plugins architecture can have an ``optional_dependencies``
+The Converse.js plugins architecture can have an :ref:`optional_dependencies`
 plugin attribute. This enables you to specify an array of optional, or
 "soft", dependencies. Converse.js (more specifically,
 `pluggable.js <https://jcbrand.github.io/pluggable.js/>`_) will try to first
@@ -1154,7 +1175,7 @@ visible_toolbar_buttons
     {
         call: false,
         clear: true,
-        emoticons: true,
+        emoji: true,
         toggle_occupants: true
     }
 
@@ -1173,9 +1194,9 @@ Allows you to show or hide buttons on the chat boxes' toolbars.
         });
 * *clear*:
     Provides a button for clearing messages from a chat box.
-* *emoticons*:
-    Enables rendering of emoticons and provides a toolbar button for choosing them.
-* toggle_occupants:
+* *emoji*:
+    Enables rendering of emoji and provides a toolbar button for choosing them.
+* *toggle_occupants*:
     Shows a button for toggling (i.e. showing/hiding) the list of occupants in a chat room.
 
 .. _`websocket-url`:

+ 23 - 22
docs/source/developer_api.rst

@@ -188,16 +188,15 @@ two important ways:
 
 Converse.js has the following promises:
 
-* cachedRoster
-* chatBoxesFetched
-* connected
-* pluginsInitialized
-* roster
-* rosterContactsFetched
-* rosterGroupsFetched
-* rosterInitialized
-* statusInitialized
-* roomsPanelRendered (only via the `converse-muc` plugin)
+* :ref:`cachedRoster`
+* :ref:`chatBoxesFetched`
+* :ref:`pluginsInitialized`
+* :ref:`roster`
+* :ref:`rosterContactsFetched`
+* :ref:`rosterGroupsFetched`
+* :ref:`rosterInitialized`
+* :ref:`statusInitialized`
+* :ref:`roomsPanelRendered` (only via the `converse-muc` plugin)
 
 Below is an example from `converse-muc.js <https://github.com/jcbrand/converse.js/blob/master/src/converse-muc.js>`_
 where the `rosterContactsFetched` promise is waited on. The method
@@ -903,22 +902,24 @@ The **promises** grouping
 -------------------------
 
 Converse.js and its plugins emit various events which you can listen to via the 
-:refs:`listen-grouping`.
+:ref:`listen-grouping`.
 
-These events can also be turned into promises, and by default some already
-are.
+Some of these events are also available as `ES2015 Promises <http://es6-features.org/#PromiseUsage>`_,
+although not all of them could logically act as promises, since some events
+might be fired multpile times whereas promises are to be resolved (or
+rejected) only once.
 
 The core events, which are also promises are:
 
-* cachedRoster
-* chatBoxesFetched
-* connected
-* pluginsInitialized
-* roster
-* rosterContactsFetched
-* rosterGroupsFetched
-* rosterInitialized
-* statusInitialized
+* :ref:`cachedRoster`
+* :ref:`chatBoxesFetched`
+* :ref:`pluginsInitialized`
+* :ref:`roster`
+* :ref:`rosterContactsFetched`
+* :ref:`rosterGroupsFetched`
+* :ref:`rosterInitialized`
+* :ref:`statusInitialized`
+* :ref:`roomsPanelRendered` (only via the `converse-muc` plugin)
 
 The various plugins might also provide promises, and they do this by using the
 ``promises.add`` api method.

+ 154 - 8
docs/source/events.rst

@@ -4,18 +4,41 @@
 
 .. _`events-API`:
 
-Events emitted by converse.js
-=============================
+Events and promises
+===================
 
 .. contents:: Table of Contents
    :depth: 2
    :local:
 
+Converse.js and its plugins emit various events which you can listen to via the
+:ref:`listen-grouping`.
 
-.. note:: see also :ref:`listen-grouping` above.
+Some of these events are also available as `ES2015 Promises <http://es6-features.org/#PromiseUsage>`_,
+although not all of them could logically act as promises, since some events
+might be fired multpile times whereas promises are to be resolved (or
+rejected) only once.
 
-Event Types
------------
+The core events, which are also promises are:
+
+* `cachedRoster`_
+* `chatBoxesFetched`_
+* `pluginsInitialized`_
+* `roster`_
+* `rosterContactsFetched`_
+* `rosterGroupsFetched`_
+* `rosterInitialized`_
+* `statusInitialized`_
+* `roomsPanelRendered`_ (only via the `converse-muc` plugin)
+
+For more info on how to use (or add promises), you can read the
+:ref:`promises-grouping` in the API documentation.
+
+Below we will now list all events and also specify whether they are available
+as promises.
+
+List of Events (and promises)
+-----------------------------
 
 Hooking into events that Converse.js emits is a great way to extend or
 customize its functionality.
@@ -27,6 +50,22 @@ Refer to the :ref:`whitelisted_plugins` setting.
 
 Here follows the different events that are emitted:
 
+afterMessagesFetched
+~~~~~~~~~~~~~~~~~~~~
+
+Emitted whenever a chat box has fetched its messages from ``sessionStorage`` and
+**NOT** from the server.
+
+This event is listened to by the ``converse-mam`` plugin to know when it can
+fetch archived messages from the server.
+
+The event handler is passed the ``Backbone.View`` instance of the relevant chat
+box.
+
+``_converse.on('afterMessagesFetched', function (chatboxview) { ... });``
+
+.. _`cachedRoster`:
+
 cachedRoster
 ~~~~~~~~~~~~
 
@@ -34,6 +73,14 @@ The contacts roster has been retrieved from the local cache (`sessionStorage`).
 
 ``_converse.on('cachedRoster', function (items) { ... });``
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('cachedRoster').then(function () {
+        // Your code here...
+    });
+
 See also the `roster`_ event further down.
 
 callButtonClicked
@@ -43,6 +90,26 @@ When a call button (i.e. with class .toggle-call) on a chat box has been clicked
 
 ``_converse.on('callButtonClicked', function (connection, model) { ... });``
 
+.. _`chatBoxesFetched`:
+
+chatBoxesFetched
+~~~~~~~~~~~~~~~~
+
+Any open chat boxes (from this current session) has been retrieved from the local cache (`sessionStorage`).
+
+You should wait for this event or promise before attempting to do things
+related to open chat boxes.
+
+``_converse.on('chatBoxesFetched', function (items) { ... });``
+
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('chatBoxesFetched').then(function () {
+        // Your code here...
+    });
+
 chatBoxInitialized
 ~~~~~~~~~~~~~~~~~~
 
@@ -121,6 +188,15 @@ When a chat buddy's custom status message has changed.
 
 ``_converse.on('contactStatusMessageChanged', function (data) { ... });``
 
+discoInitialized
+~~~~~~~~~~~~~~~~
+
+Emitted once the ``converse-disco`` plugin has been initialized and the
+``_converse.disco_entities`` collection will be available and populated with at
+least the service discovery features of the user's own server.
+
+``_converse.on('discoInitialized', function () { ... });``
+
 disconnected
 ~~~~~~~~~~~~
 
@@ -151,7 +227,7 @@ Once a message has been added to a chat box. The passed in data object contains
 a `chatbox` attribute, referring to the chat box receiving the message, as well
 as a `message` attribute which refers to the Message model.
 
-.. code-block:: javascript 
+.. code-block:: javascript
 
     _converse.on('messageAdded', function (data) {
         // The message is at `data.message`
@@ -172,10 +248,12 @@ When keepalive=true but there aren't any stored prebind tokens.
 
 ``_converse.on('noResumeableSession', function () { ... });``
 
+.. _`pluginsInitialized`:
+
 pluginsInitialized
 ~~~~~~~~~~~~~~~~~~
 
-Once all plugins have been initialized. This is a useful event if you want to
+Emitted once all plugins have been initialized. This is a useful event if you want to
 register event handlers but would like your own handlers to be overridable by
 plugins. In that case, you need to first wait until all plugins have been
 initialized, so that their overrides are active. One example where this is used
@@ -183,6 +261,14 @@ is in `converse-notifications.js <https://github.com/jcbrand/converse.js/blob/ma
 
 ``_converse.on('pluginsInitialized', function () { ... });``
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('pluginsInitialized').then(function () {
+        // Your code here...
+    });
+
 reconnecting
 ~~~~~~~~~~~~
 
@@ -212,6 +298,8 @@ After the user has sent out a direct invitation, to a roster contact, asking the
 
 ``_converse.on('roomInvite', function (data) { ... });``
 
+.. _`roomsPanelRendered`:
+
 roomsPanelRendered
 ~~~~~~~~~~~~~~~~~~
 
@@ -221,6 +309,16 @@ render themselves in that panel.
 
 ``_converse.on('roomsPanelRendered', function (data) { ... });``
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('roomsPanelRendered').then(function () {
+        // Your code here...
+    });
+
+.. _`roster`:
+
 roster
 ~~~~~~
 
@@ -228,15 +326,35 @@ When the roster has been received from the XMPP server.
 
 ``_converse.on('roster', function (items) { ... });``
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('roster').then(function () {
+        // Your code here...
+    });
+
 See also the `cachedRoster` event further up, which gets called instead of
 `roster` if its already in `sessionStorage`.
 
+.. _`rosterContactsFetched`:
+
 rosterContactsFetched
 ~~~~~~~~~~~~~~~~~~~~~
 
 Triggered once roster contacts have been fetched. Used by the
 `converse-rosterview.js` plugin to know when it can start to show the roster.
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('rosterContactsFetched').then(function () {
+        // Your code here...
+    });
+
+.. _`rosterGroupsFetched`:
+
 rosterGroupsFetched
 ~~~~~~~~~~~~~~~~~~~
 
@@ -244,6 +362,16 @@ Triggered once roster groups have been fetched. Used by the
 `converse-rosterview.js` plugin to know when it can start alphabetically
 position roster groups.
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('rosterGroupsFetched').then(function () {
+        // Your code here...
+    });
+
+.. _`rosterInitialized`:
+
 rosterInitialized
 ~~~~~~~~~~~~~~~~~
 
@@ -252,6 +380,14 @@ but not yet populated with data.
 
 This event is useful when you want to create views for these collections.
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('rosterInitialized').then(function () {
+        // Your code here...
+    });
+
 rosterPush
 ~~~~~~~~~~
 
@@ -266,13 +402,23 @@ Similar to `rosterInitialized`, but instead pertaining to reconnection. This
 event indicates that the Backbone collections representing the roster and its
 groups are now again available after converse.js has reconnected.
 
+.. _`statusInitialized`:
+
 statusInitialized
 ~~~~~~~~~~~~~~~~~
 
-When own chat status has been initialized.
+When the user's own chat status has been initialized.
 
 ``_converse.on('statusInitialized', function (status) { ... });``
 
+Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
+
+.. code-block:: javascript
+
+    _converse.api.waitUntil('statusInitialized').then(function () {
+        // Your code here...
+    });
+
 statusChanged
 ~~~~~~~~~~~~~
 

+ 3 - 1
docs/source/plugin_development.rst

@@ -167,6 +167,8 @@ A better approach is to listen to the events emitted by Converse.js, and to add
 your code in event handlers. This is however not always possible, in which case
 the overrides are a powerful tool.
 
+.. _`optional_dependencies`:
+
 Optional plugin dependencies
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -189,7 +191,7 @@ In this case, you can't specify the plugin as a dependency in the ``define``
 statement at the top of the plugin, since it might not always be available,
 which would cause ``require.js`` to throw an error.
 
-To resolve this problem we thave the ``optional_dependencies`` Array attribute.
+To resolve this problem we have the ``optional_dependencies`` Array attribute.
 With this you can specify those dependencies which need to be loaded before
 your plugin, if they exist. If they don't exist, they won't be ignored.
 

+ 2 - 32
index.html

@@ -15,7 +15,8 @@
     <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/3.1.0/css/converse.min.css" />
     <script type="text/javascript" src="analytics.js"></script>
     <noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
-    <![if gte IE 9]>
+	<script src="src/website.js"></script>
+    <![if gte IE 11]>
 	<script src="https://cdn.conversejs.org/3.1.0/dist/converse.min.js"></script>
     <![endif]>
 </head>
@@ -223,37 +224,6 @@
 </body>
 
 <script>
-    (function () {
-        /* XXX: This function initializes jquery.easing for the https://conversejs.org
-        * website. This code is only useful in the context of the converse.js
-        * website and converse.js itself is NOT dependent on it.
-        */
-        var $ = converse.env.jQuery;
-        $.extend( $.easing, {
-            easeInOutExpo: function (x, t, b, c, d) {
-                if (t==0) return b;
-                if (t==d) return b+c;
-                if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
-                return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
-            },
-        });
-
-        $(window).scroll(function() {
-            if ($(".navbar").offset().top > 50) {
-                $(".navbar-fixed-top").addClass("top-nav-collapse");
-            } else {
-                $(".navbar-fixed-top").removeClass("top-nav-collapse");
-            }
-        });
-        //jQuery for page scrolling feature - requires jQuery Easing plugin
-        $('.page-scroll a').bind('click', function(event) {
-            var $anchor = $(this);
-            $('html, body').stop().animate({
-                scrollTop: $($anchor.attr('href')).offset().top
-            }, 700, 'easeInOutExpo');
-            event.preventDefault();
-        });
-    })();
     converse.initialize({
         // Please use this connection manager only for testing purposes
         bosh_service_url: 'https://conversejs.org/http-bind/',

+ 2 - 2
inverse.html

@@ -8,7 +8,7 @@
     <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/3.1.0/css/inverse.min.css" />
     <script type="text/javascript" src="inverse-analytics.js"></script>
     <noscript><p><img src="//stats.opkode.com/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
-    <script src="https://cdn.conversejs.org/3.1.0/dist/inverse.min.js"></script>
+    <script src="dist/inverse.min.js"></script>
 </head>
 <body>
     <div class="content">
@@ -20,7 +20,7 @@
     converse.initialize({
         authentication: 'login',
         auto_away: 300,
-        blacklisted_plugins: ['converse-minimize', 'converse-dragresize', 'converse-otr'],
+        blacklisted_plugins: ['converse-minimize', 'converse-dragresize'],
         whitelisted_plugins: ['converse-inverse', 'converse-singleton'],
         auto_reconnect: true,
         bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes

+ 14 - 82
locale/af/LC_MESSAGES/converse.json

@@ -31,6 +31,10 @@
             null,
             "Kanseleer"
          ],
+         "Are you sure you want to remove the bookmark \"%1$s\"?": [
+            null,
+            "Is u seker u wil die boekmerk \"%1$s\" verwyder?"
+         ],
          "Sorry, something went wrong while trying to save your bookmark.": [
             null,
             "Jammer, 'n fout het voorgekom tydens storing van u boekmerk."
@@ -39,10 +43,6 @@
             null,
             "Klik om die boekmerklys te skakel"
          ],
-         "Are you sure you want to remove the bookmark \"%1$s\"?": [
-            null,
-            "Is u seker u wil die boekmerk \"%1$s\" verwyder?"
-         ],
          "Remove this bookmark": [
             null,
             "Verwyder hierdie boekmerk"
@@ -255,54 +255,6 @@
             null,
             "Geen gebruikers gevind"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Klik om as kletskontak by te voeg"
-         ],
-         "Toggle chat": [
-            null,
-            "Klets"
-         ],
-         "Reconnecting": [
-            null,
-            "Herkonnekteer"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            "Die konneksie is onderbreek, probeer tans tans om te herkonnekteer."
-         ],
-         "Connection error": [
-            null,
-            "Fout tydens verbinding"
-         ],
-         "An error occurred while connecting to the chat server.": [
-            null,
-            "A fout het voorgekom tydens verbinding met die kletsbediener."
-         ],
-         "Connecting": [
-            null,
-            "Verbind tans"
-         ],
-         "Authenticating": [
-            null,
-            "Besig om te bekragtig"
-         ],
-         "Authentication Failed": [
-            null,
-            "Bekragtiging het gefaal"
-         ],
-         "Connection failed": [
-            null,
-            "Verbinding het gefaal"
-         ],
-         "An error occurred while connecting to the chat server: ": [
-            null,
-            "A fout het voorgekom tydens verbinding met die kletsbediener: "
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Jammer, 'n ander fout het voorgekom tydens byvoeging. "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Hierdie klient laat nie beskikbaarheidsinskrywings toe nie"
@@ -431,9 +383,9 @@
             null,
             "Verskuil die lys van deelnemers"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Fout: die \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -535,9 +487,9 @@
             null,
             "Die gegewe rede is: \"%1$s\"."
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "Die gegewe rede is: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -587,10 +539,6 @@
             null,
             "Onderwerp deur %1$s bygewerk na: %2$s"
          ],
-         "Click to mention ": [
-            null,
-            "Klik om te noem "
-         ],
          "This user is a moderator.": [
             null,
             "Hierdie gebruiker is 'n moderator."
@@ -683,10 +631,6 @@
             null,
             "Hierdie kletskamer word gemodereer"
          ],
-         "All other room occupants can see your Jabber ID": [
-            null,
-            "Alle ander deelnemers can u Jabber ID sien"
-         ],
          "Anyone can join this room": [
             null,
             "Enige iemand kan hierdie kletskamer binnekom"
@@ -703,10 +647,6 @@
             null,
             "Hierdie kletskamer is publiek opspoorbaar"
          ],
-         "Only moderators can see your Jabber ID": [
-            null,
-            "Slegs moderators kan u Jabber ID sien"
-         ],
          "This room will disappear once the last person leaves": [
             null,
             "Hierdie kletskamer sal verdwyn sodra die laaste persoon dit verlaat"
@@ -727,6 +667,10 @@
             null,
             "U mag na keuse 'n boodskap insluit, om bv. die rede vir die uitnodiging te staaf."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Kamer naam"
@@ -751,17 +695,13 @@
             null,
             "Geen kletskamers op %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Kletskamers op %1$s"
-         ],
          "Description:": [
             null,
             "Beskrywing:"
          ],
-         "Server:": [
+         "Room Address (JID):": [
             null,
-            "Bediener:"
+            ""
          ],
          "Occupants:": [
             null,
@@ -987,10 +927,6 @@
             null,
             "Suksesvol geregistreer"
          ],
-         "Return": [
-            null,
-            "Terug"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "Die verskaffer het u registrasieversoek verwerp. Kontrolleer asb. jou gegewe waardes vir korrektheid."
@@ -1079,10 +1015,6 @@
             null,
             "Is u seker u wil hierdie gespreksmaat verwyder?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "Jammer, 'n fout het voorgekom tydens die verwydering van "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Is u seker dat u hierdie persoon se versoek wil afkeur?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 212 - 266
locale/af/LC_MESSAGES/converse.po


+ 14 - 46
locale/ca/LC_MESSAGES/converse.json

@@ -235,34 +235,6 @@
             null,
             "No s'ha trobat cap usuari"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Feu clic per afegir com a contacte del xat"
-         ],
-         "Toggle chat": [
-            null,
-            "Canvia de xat"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "S'està establint la connexió"
-         ],
-         "Authenticating": [
-            null,
-            "S'està efectuant l'autenticació"
-         ],
-         "Authentication Failed": [
-            null,
-            "Error d'autenticació"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "S'ha produït un error en intentar afegir "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Aquest client no admet les subscripcions de presència"
@@ -343,9 +315,9 @@
             null,
             "Amaga la llista d'ocupants"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Error: el \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -427,9 +399,9 @@
             null,
             "Envia"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "El motiu indicat és: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -495,7 +467,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -503,7 +475,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -519,6 +491,10 @@
             null,
             "Teniu l'opció d'incloure un missatge per explicar el motiu de la invitació."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nom de la sala"
@@ -543,14 +519,14 @@
             null,
             "No hi ha cap sala a %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Sales a %1$s"
-         ],
          "Description:": [
             null,
             "Descripció:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Ocupants:"
@@ -771,10 +747,6 @@
             null,
             "Registre correcte"
          ],
-         "Return": [
-            null,
-            "Torna"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "El proveïdor ha rebutjat l'intent de registre. Comproveu que els valors que heu introduït siguin correctes."
@@ -863,10 +835,6 @@
             null,
             "Segur que voleu eliminar aquest contacte?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "S'ha produït un error en intentar eliminar "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Segur que voleu rebutjar aquesta sol·licitud de contacte?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 210 - 273
locale/ca/LC_MESSAGES/converse.po


Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 264
locale/converse.pot


+ 14 - 82
locale/de/LC_MESSAGES/converse.json

@@ -31,6 +31,10 @@
             null,
             "Abbrechen"
          ],
+         "Are you sure you want to remove the bookmark \"%1$s\"?": [
+            null,
+            "Wollen Sie dieses Lesezeichen wirklich entfernen \"%1$s\"?"
+         ],
          "Sorry, something went wrong while trying to save your bookmark.": [
             null,
             "Etwas ging beim Versuch des Abspeicherns des Lesezeichens schief."
@@ -39,10 +43,6 @@
             null,
             "Zum Aus-/Einklappen klicken"
          ],
-         "Are you sure you want to remove the bookmark \"%1$s\"?": [
-            null,
-            "Wollen Sie dieses Lesezeichen wirklich entfernen \"%1$s\"?"
-         ],
          "Remove this bookmark": [
             null,
             "Dieses Lesezeichen entfernen"
@@ -255,54 +255,6 @@
             null,
             "Keine Benutzer gefunden"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Hier klicken um als Kontakt hinzuzufügen"
-         ],
-         "Toggle chat": [
-            null,
-            "Chat ein-/ausblenden"
-         ],
-         "Reconnecting": [
-            null,
-            "Verbindung wiederherstellen"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            "Die Verbindung wurde unterbrochen. Versuche neu zu verbinden."
-         ],
-         "Connection error": [
-            null,
-            "Verbindungsfehler"
-         ],
-         "An error occurred while connecting to the chat server.": [
-            null,
-            "Beim Speichern des Formulars ist ein Fehler aufgetreten."
-         ],
-         "Connecting": [
-            null,
-            "Verbindungsaufbau"
-         ],
-         "Authenticating": [
-            null,
-            "Authentifizierung"
-         ],
-         "Authentication Failed": [
-            null,
-            "Authentifizierung gescheitert"
-         ],
-         "Connection failed": [
-            null,
-            "Verbindung fehlgeschlagen"
-         ],
-         "An error occurred while connecting to the chat server: ": [
-            null,
-            "Es trat ein Fehler während des Verbindungsvorgangs mit dem Chat-Server auf: "
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Entschuldige, es kam zu einem Fehler bei der Hinzufügung von "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Dieser Kontakt erlaubt die Abonnieren des Status nicht"
@@ -431,9 +383,9 @@
             null,
             "Teilnehmerliste ausblenden"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Fehler: Das \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -535,9 +487,9 @@
             null,
             "Die angegebene Begründung lautet: \"%1$s\"."
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "Die angegebene Begründung lautet: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -587,10 +539,6 @@
             null,
             "%1$s hat das Thema zu \"%2$s\" geändert"
          ],
-         "Click to mention ": [
-            null,
-            "Drücke um zu erwähnen "
-         ],
          "This user is a moderator.": [
             null,
             "Dieser Benutzer ist ein Moderator."
@@ -683,10 +631,6 @@
             null,
             "Dieser Raum ist moderiert"
          ],
-         "All other room occupants can see your Jabber ID": [
-            null,
-            "Jeder in dem Raum kann deine Jabber ID sehen"
-         ],
          "Anyone can join this room": [
             null,
             "Jeder kann diesen Raum betreten"
@@ -703,10 +647,6 @@
             null,
             "Dieser Raum ist per Suche auffindbar"
          ],
-         "Only moderators can see your Jabber ID": [
-            null,
-            "Nur Moderatoren können deine Jabber ID sehen"
-         ],
          "This room will disappear once the last person leaves": [
             null,
             "Dieser Raum verschwindet sobald diesen die letzte Person verlassen hat"
@@ -727,6 +667,10 @@
             null,
             "Du kannst optional eine Nachricht mit den Gründen der Einladung mitsenden."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Raumname"
@@ -751,17 +695,13 @@
             null,
             "Keine Räume auf %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Räume auf %1$s"
-         ],
          "Description:": [
             null,
             "Beschreibung:"
          ],
-         "Server:": [
+         "Room Address (JID):": [
             null,
-            "Server:"
+            ""
          ],
          "Occupants:": [
             null,
@@ -987,10 +927,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            "Zurück"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -1079,10 +1015,6 @@
             null,
             "Wollen Sie diesen Kontakt wirklich entfernen?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Wollen Sie diese Kontaktanfrage wirklich ablehnen?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 212 - 267
locale/de/LC_MESSAGES/converse.po


+ 12 - 48
locale/es/LC_MESSAGES/converse.json

@@ -203,38 +203,6 @@
             null,
             "Sin usuarios encontrados"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Haga click para agregar como contacto de chat"
-         ],
-         "Toggle chat": [
-            null,
-            "Chat"
-         ],
-         "Reconnecting": [
-            null,
-            "Reconectando"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Conectando"
-         ],
-         "Authenticating": [
-            null,
-            "Autenticando"
-         ],
-         "Authentication Failed": [
-            null,
-            "La autenticación falló"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -311,7 +279,7 @@
             null,
             ""
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -387,7 +355,7 @@
             null,
             ""
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
             ""
          ],
@@ -459,7 +427,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -467,7 +435,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -483,6 +451,10 @@
             null,
             ""
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nombre de sala"
@@ -503,14 +475,14 @@
             null,
             "Sin salas en %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Salas en %1$s"
-         ],
          "Description:": [
             null,
             "Descripción"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Ocupantes:"
@@ -695,10 +667,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            ""
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -786,10 +754,6 @@
          "Are you sure you want to remove this contact?": [
             null,
             "¿Esta seguro de querer eliminar este contacto?"
-         ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
          ]
       }
    }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 270
locale/es/LC_MESSAGES/converse.po


+ 12 - 76
locale/fr/LC_MESSAGES/converse.json

@@ -251,54 +251,6 @@
             null,
             "Aucun utilisateur trouvé"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Cliquer pour ajouter aux contacts"
-         ],
-         "Toggle chat": [
-            null,
-            "Ouvrir IM"
-         ],
-         "Reconnecting": [
-            null,
-            "Reconnexion"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            "La connexion a été perdue, tentative de reconnexion en cours."
-         ],
-         "Connection error": [
-            null,
-            "Erreur de connexion"
-         ],
-         "An error occurred while connecting to the chat server.": [
-            null,
-            "Une erreur est survenue lors de la connexion au serveur de discussion."
-         ],
-         "Connecting": [
-            null,
-            "Connexion"
-         ],
-         "Authenticating": [
-            null,
-            "Authentification"
-         ],
-         "Authentication Failed": [
-            null,
-            "L’authentification a échoué"
-         ],
-         "Connection failed": [
-            null,
-            "La connexion a échoué"
-         ],
-         "An error occurred while connecting to the chat server: ": [
-            null,
-            "Une erreur est survenue lors de la connexion au serveur de discussion : "
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Désolé, il y a eu une erreur lors de la tentative d’ajout "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Ce client ne permet pas les mises à jour de disponibilité"
@@ -427,9 +379,9 @@
             null,
             "Cacher la liste des participants"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Erreur : \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -531,9 +483,9 @@
             null,
             "La raison indiquée est : « %1$s »."
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "La raison indiquée est : \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -555,10 +507,6 @@
             null,
             "Le sujet « %2$s » a été défini par %1$s"
          ],
-         "Click to mention ": [
-            null,
-            "Cliquer pour citer "
-         ],
          "This user is a moderator.": [
             null,
             "Cet utilisateur est un modérateur."
@@ -647,10 +595,6 @@
             null,
             "Ce salon est modéré"
          ],
-         "All other room occupants can see your Jabber ID": [
-            null,
-            "Tous les autres occupants de ce salon peuvent voir votre ID Jabber"
-         ],
          "Anyone can join this room": [
             null,
             "N’importe qui peut rejoindre ce salon"
@@ -659,10 +603,6 @@
             null,
             "Ce salon nécessite un mot de passe pour y accéder"
          ],
-         "Only moderators can see your Jabber ID": [
-            null,
-            "Seuls les modérateurs peuvent voir votre identifiant Jabber"
-         ],
          "This room will disappear once the last person leaves": [
             null,
             "Ce salon disparaîtra au départ de la dernière personne"
@@ -683,6 +623,10 @@
             null,
             "Vous pouvez facultativement ajouter un message, expliquant la raison de cette invitation."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nom du salon"
@@ -707,14 +651,14 @@
             null,
             "Aucun salon dans %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Salons dans %1$s"
-         ],
          "Description:": [
             null,
             "Description :"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Participants :"
@@ -939,10 +883,6 @@
             null,
             "Enregistré avec succès"
          ],
-         "Return": [
-            null,
-            "Retourner"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "Le fournisseur a rejeté votre demande d’enregistrement."
@@ -1031,10 +971,6 @@
             null,
             "Voulez-vous vraiment supprimer ce contact ?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "Désolé, il y a eu une erreur lors de la tentative de retrait "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Voulez-vous vraiment refuser cette demande de contact ?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 212 - 268
locale/fr/LC_MESSAGES/converse.po


+ 13 - 49
locale/he/LC_MESSAGES/converse.json

@@ -231,38 +231,6 @@
             null,
             "לא נמצאו משתמשים"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "לחץ כדי להוסיף בתור איש קשר שיחה"
-         ],
-         "Toggle chat": [
-            null,
-            "הפעל שיח"
-         ],
-         "Reconnecting": [
-            null,
-            "כעת מתחבר"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "כעת מתחבר"
-         ],
-         "Authenticating": [
-            null,
-            "כעת מאמת"
-         ],
-         "Authentication Failed": [
-            null,
-            "אימות נכשל"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "מצטערים, היתה שגיאה במהלך ניסיון הוספת "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "לקוח זה לא מתיר הרשמות נוכחות"
@@ -339,7 +307,7 @@
             null,
             "הודעה"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -419,9 +387,9 @@
             null,
             "שלח"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "הסיבה שניתנה היא: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -491,7 +459,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -499,7 +467,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -515,6 +483,10 @@
             null,
             "באפשרותך להכליל הודעה, אשר  מסבירה את הסיבה להזמנה."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "שם חדר"
@@ -539,14 +511,14 @@
             null,
             "אין חדרים על %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "חדרים על %1$s"
-         ],
          "Description:": [
             null,
             "תיאור:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "נוכחים:"
@@ -767,10 +739,6 @@
             null,
             "נרשם בהצלחה"
          ],
-         "Return": [
-            null,
-            "חזור"
-         ],
          "Retry": [
             null,
             ""
@@ -855,10 +823,6 @@
             null,
             "האם אתה בטוח כי ברצונך להסיר את איש קשר זה?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "מצטערים, היתה שגיאה במהלך ניסיון להסיר את "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "האם אתה בטוח כי ברצונך לסרב את בקשת איש קשר זה?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 209 - 271
locale/he/LC_MESSAGES/converse.po


+ 14 - 50
locale/hu/LC_MESSAGES/converse.json

@@ -234,38 +234,6 @@
             null,
             "Nincs felhasználó"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Felvétel a csevegőpartnerek közé"
-         ],
-         "Toggle chat": [
-            null,
-            "Csevegőablak"
-         ],
-         "Reconnecting": [
-            null,
-            "Kapcsolódás"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Kapcsolódás"
-         ],
-         "Authenticating": [
-            null,
-            "Azonosítás"
-         ],
-         "Authentication Failed": [
-            null,
-            "Azonosítási hiba"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Sajnáljuk, hiba történt a hozzáadás során"
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Ez a kliens nem engedélyezi a jelenlét követését"
@@ -346,9 +314,9 @@
             null,
             "A résztvevők listájának elrejtése"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Hiba: a \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -430,9 +398,9 @@
             null,
             "Küldés"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "Az indok: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -502,7 +470,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -510,7 +478,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -526,6 +494,10 @@
             null,
             "Megadhat egy üzenet a meghívás okaként."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Szoba neve"
@@ -550,14 +522,14 @@
             null,
             "Nincs csevegőszoba a(z) %1$s szerveren"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Csevegőszobák a(z) %1$s szerveren:"
-         ],
          "Description:": [
             null,
             "Leírás:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Jelenlevők:"
@@ -778,10 +750,6 @@
             null,
             "Sikeres regisztráció"
          ],
-         "Return": [
-            null,
-            "Visza"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "A szolgáltató visszautasította a regisztrációs kérelmet. Kérem ellenőrízze a bevitt adatok pontosságát."
@@ -870,10 +838,6 @@
             null,
             "Valóban törölni szeretné a csevegőpartnerét?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "Sajnáljuk, hiba történt a törlés során"
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Valóban elutasítja ezt a partnerkérelmet?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 210 - 272
locale/hu/LC_MESSAGES/converse.po


+ 12 - 44
locale/id/LC_MESSAGES/converse.json

@@ -202,34 +202,6 @@
             null,
             "Pengguna tak ditemukan"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Klik untuk menambahkan sebagai teman"
-         ],
-         "Toggle chat": [
-            null,
-            ""
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Menyambung"
-         ],
-         "Authenticating": [
-            null,
-            "Melakukan otentikasi"
-         ],
-         "Authentication Failed": [
-            null,
-            "Otentikasi gagal"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -302,7 +274,7 @@
             null,
             ""
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -374,7 +346,7 @@
             null,
             ""
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
             ""
          ],
@@ -442,7 +414,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -450,7 +422,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -466,6 +438,10 @@
             null,
             ""
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nama ruangan"
@@ -486,14 +462,14 @@
             null,
             "Tak ada ruangan di %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Ruangan di %1$s"
-         ],
          "Description:": [
             null,
             "Keterangan:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Penghuni:"
@@ -682,10 +658,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            ""
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -769,10 +741,6 @@
          "Name": [
             null,
             ""
-         ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
          ]
       }
    }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 271
locale/id/LC_MESSAGES/converse.po


+ 16 - 80
locale/it/LC_MESSAGES/converse.json

@@ -31,6 +31,10 @@
             null,
             "Annulla"
          ],
+         "Are you sure you want to remove the bookmark \"%1$s\"?": [
+            null,
+            "Sei sicuro di voler rimuovere il segnalibro \"%1$s\"?"
+         ],
          "Sorry, something went wrong while trying to save your bookmark.": [
             null,
             "Si è verificato un errore nel salvataggio del bookmark."
@@ -39,10 +43,6 @@
             null,
             "Clicca per aprire/chiudere la lista bookmarks"
          ],
-         "Are you sure you want to remove the bookmark \"%1$s\"?": [
-            null,
-            "Sei sicuro di voler rimuovere il segnalibro \"%1$s\"?"
-         ],
          "Remove this bookmark": [
             null,
             "Rimuovi questo bookmark"
@@ -255,54 +255,6 @@
             null,
             "Nessun utente trovato"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Clicca per aggiungere il contatto alla chat"
-         ],
-         "Toggle chat": [
-            null,
-            "Attiva/disattiva chat"
-         ],
-         "Reconnecting": [
-            null,
-            "Riconnessione"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            "La connessione è caduta, attendi la riconnessione."
-         ],
-         "Connection error": [
-            null,
-            "Errore di connessione"
-         ],
-         "An error occurred while connecting to the chat server.": [
-            null,
-            "Si è verificato un errore durante la connessione al server."
-         ],
-         "Connecting": [
-            null,
-            "Connessione in corso"
-         ],
-         "Authenticating": [
-            null,
-            "Autenticazione in corso"
-         ],
-         "Authentication Failed": [
-            null,
-            "Autenticazione fallita"
-         ],
-         "Connection failed": [
-            null,
-            "Errore di connessione"
-         ],
-         "An error occurred while connecting to the chat server: ": [
-            null,
-            "Si è verificato un errore durante la connessione al server. "
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Si è verificato un errore durante il tentativo di aggiunta "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Questo client non consente sottoscrizioni di presenza"
@@ -431,9 +383,9 @@
             null,
             "Nascondi la lista degli occupanti"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Errore: il \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -527,9 +479,9 @@
             null,
             "Invia"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "La ragione data è: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -551,10 +503,6 @@
             null,
             "Topic impostato da %1$s a: %2$s"
          ],
-         "Click to mention ": [
-            null,
-            "Clicca per citare "
-         ],
          "This user is a moderator.": [
             null,
             "Questo utente è un moderatore."
@@ -643,10 +591,6 @@
             null,
             "Questa stanza è moderata"
          ],
-         "All other room occupants can see your Jabber ID": [
-            null,
-            "Tutti gli occupanti della stanza possono vedere il tuo Jabber ID"
-         ],
          "Anyone can join this room": [
             null,
             "Chiunque può collegarsi a questa stanza"
@@ -659,10 +603,6 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
-            null,
-            "Solo il moderatore può vedere il tuo Jabber ID"
-         ],
          "This room will disappear once the last person leaves": [
             null,
             ""
@@ -683,6 +623,10 @@
             null,
             "Puoi includere un messaggio per spiegare le ragioni dell'invito."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nome stanza"
@@ -707,14 +651,14 @@
             null,
             "Nessuna stanza su %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Stanze su %1$s"
-         ],
          "Description:": [
             null,
             "Descrizione:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Utenti presenti:"
@@ -939,10 +883,6 @@
             null,
             "Registrazione riuscita"
          ],
-         "Return": [
-            null,
-            "Ritorna"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "Il provider ha respinto il tentativo di registrazione. Controlla i dati inseriti."
@@ -1031,10 +971,6 @@
             null,
             "Sei sicuro di voler rimuovere questo contatto?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "Si è verificato un errore durante il tentativo di rimozione "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Sei sicuro dirifiutare questa richiesta di contatto?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 212 - 267
locale/it/LC_MESSAGES/converse.po


+ 12 - 44
locale/ja/LC_MESSAGES/converse.json

@@ -203,34 +203,6 @@
             null,
             "ユーザーが見つかりません"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "クリックしてチャットの相手先として追加"
-         ],
-         "Toggle chat": [
-            null,
-            ""
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "接続中です"
-         ],
-         "Authenticating": [
-            null,
-            "認証中"
-         ],
-         "Authentication Failed": [
-            null,
-            "認証に失敗"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -303,7 +275,7 @@
             null,
             ""
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -375,7 +347,7 @@
             null,
             ""
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
             ""
          ],
@@ -443,7 +415,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -451,7 +423,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -467,6 +439,10 @@
             null,
             ""
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "談話室の名前"
@@ -487,14 +463,14 @@
             null,
             "%1$s に談話室はありません"
          ],
-         "Rooms on %1$s": [
-            null,
-            "%1$s の談話室一覧"
-         ],
          "Description:": [
             null,
             "説明: "
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "入室者:"
@@ -683,10 +659,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            ""
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -770,10 +742,6 @@
          "Name": [
             null,
             ""
-         ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
          ]
       }
    }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 271
locale/ja/LC_MESSAGES/converse.po


+ 13 - 49
locale/nb/LC_MESSAGES/converse.json

@@ -215,38 +215,6 @@
             null,
             "Ingen brukere funnet"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Klikk for å legge til som meldingskontakt"
-         ],
-         "Toggle chat": [
-            null,
-            "Endre chatten"
-         ],
-         "Reconnecting": [
-            null,
-            "Kobler til igjen"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Kobler til"
-         ],
-         "Authenticating": [
-            null,
-            "Godkjenner"
-         ],
-         "Authentication Failed": [
-            null,
-            "Godkjenning mislyktes"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -323,7 +291,7 @@
             null,
             "Melding"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -399,9 +367,9 @@
             null,
             "Send"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "Årsaken som er oppgitt er: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -471,7 +439,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -479,7 +447,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -495,6 +463,10 @@
             null,
             "Du kan eventuelt inkludere en melding og forklare årsaken til invitasjonen."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Romnavn"
@@ -515,14 +487,14 @@
             null,
             "Ingen rom på %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Rom på %1$s"
-         ],
          "Description:": [
             null,
             "Beskrivelse:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Brukere her:"
@@ -743,10 +715,6 @@
             null,
             "Registrering var vellykket"
          ],
-         "Return": [
-            null,
-            "Tilbake"
-         ],
          "Retry": [
             null,
             ""
@@ -831,10 +799,6 @@
             null,
             "Er du sikker på at du vil fjerne denne kontakten?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Er du sikker på at du vil avslå denne kontaktforespørselen?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 209 - 271
locale/nb/LC_MESSAGES/converse.po


+ 12 - 44
locale/nl/LC_MESSAGES/converse.json

@@ -195,34 +195,6 @@
             null,
             "Geen gebruikers gevonden"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Klik om contact toe te voegen"
-         ],
-         "Toggle chat": [
-            null,
-            ""
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Verbinden"
-         ],
-         "Authenticating": [
-            null,
-            "Authenticeren"
-         ],
-         "Authentication Failed": [
-            null,
-            "Authenticeren mislukt"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -307,7 +279,7 @@
             null,
             ""
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -379,7 +351,7 @@
             null,
             ""
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
             ""
          ],
@@ -447,7 +419,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -455,7 +427,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -471,6 +443,10 @@
             null,
             ""
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Room naam"
@@ -491,14 +467,14 @@
             null,
             "Geen room op %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Room op %1$s"
-         ],
          "Description:": [
             null,
             "Beschrijving"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Deelnemers:"
@@ -695,10 +671,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            ""
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -782,10 +754,6 @@
          "Name": [
             null,
             ""
-         ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
          ]
       }
    }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 271
locale/nl/LC_MESSAGES/converse.po


+ 14 - 50
locale/pl/LC_MESSAGES/converse.json

@@ -247,38 +247,6 @@
             null,
             "Nie znaleziono użytkowników"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Kliknij aby dodać jako kontakt"
-         ],
-         "Toggle chat": [
-            null,
-            "Przełącz rozmowę"
-         ],
-         "Reconnecting": [
-            null,
-            "Przywracam połączenie"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Łączę się"
-         ],
-         "Authenticating": [
-            null,
-            "Autoryzuję"
-         ],
-         "Authentication Failed": [
-            null,
-            "Autoryzacja nie powiodła się"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Wystąpił błąd w czasie próby dodania "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Klient nie umożliwia subskrybcji obecności"
@@ -363,9 +331,9 @@
             null,
             "Ukryj listę rozmówców"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Błąd: \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -455,9 +423,9 @@
             null,
             "Wyślij"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "Podana przyczyna to: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -539,7 +507,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -547,7 +515,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -563,6 +531,10 @@
             null,
             "Masz opcjonalną możliwość dołączenia wiadomości, która wyjaśni przyczynę zaproszenia."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nazwa pokoju"
@@ -587,14 +559,14 @@
             null,
             "Brak jest pokojów na %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Pokoje na %1$s"
-         ],
          "Description:": [
             null,
             "Opis:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Uczestnicy:"
@@ -819,10 +791,6 @@
             null,
             "Szczęśliwie zarejestrowany"
          ],
-         "Return": [
-            null,
-            "Powrót"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "Dostawca odrzucił twoją próbę rejestracji. Sprawdź proszę poprawność danych które zostały wprowadzone."
@@ -911,10 +879,6 @@
             null,
             "Czy potwierdzasz zamiar usnunięcia tego kontaktu?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "Wystąpił błąd w trakcie próby usunięcia "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Czy potwierdzasz odrzucenie chęci nawiązania kontaktu?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 187 - 245
locale/pl/LC_MESSAGES/converse.po


+ 12 - 44
locale/pt_BR/LC_MESSAGES/converse.json

@@ -195,34 +195,6 @@
             null,
             "Não foram encontrados usuários"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Clique para adicionar como um contato do chat"
-         ],
-         "Toggle chat": [
-            null,
-            "Alternar bate-papo"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Conectando"
-         ],
-         "Authenticating": [
-            null,
-            "Autenticando"
-         ],
-         "Authentication Failed": [
-            null,
-            "Falha de autenticação"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -295,7 +267,7 @@
             null,
             ""
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -367,7 +339,7 @@
             null,
             ""
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
             ""
          ],
@@ -435,7 +407,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -443,7 +415,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -459,6 +431,10 @@
             null,
             ""
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Nome da sala"
@@ -479,14 +455,14 @@
             null,
             "Sem salas em %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Salas em %1$s"
-         ],
          "Description:": [
             null,
             "Descrição:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Ocupantes:"
@@ -671,10 +647,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            ""
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -758,10 +730,6 @@
          "Name": [
             null,
             ""
-         ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
          ]
       }
    }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 271
locale/pt_BR/LC_MESSAGES/converse.po


+ 16 - 44
locale/ru/LC_MESSAGES/converse.json

@@ -230,34 +230,6 @@
             null,
             "Пользователи не найдены"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Кликните, чтобы добавить контакт"
-         ],
-         "Toggle chat": [
-            null,
-            "Включить чат"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Соединение"
-         ],
-         "Authenticating": [
-            null,
-            "Авторизация"
-         ],
-         "Authentication Failed": [
-            null,
-            "Не удалось авторизоваться"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            "Возникла ошибка при добавлении "
-         ],
          "This client does not allow presence subscriptions": [
             null,
             "Программа не поддерживает уведомления о статусе"
@@ -338,9 +310,9 @@
             null,
             "Спрятать список участников"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
-            "Ошибка:  \""
+            ""
          ],
          "Are you sure you want to clear the messages from this room?": [
             null,
@@ -418,6 +390,10 @@
             null,
             "Отправить"
          ],
+         "${notification.reason}": [
+            null,
+            ""
+         ],
          " has left the room. \"": [
             null,
             ""
@@ -486,7 +462,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -494,7 +470,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -510,6 +486,10 @@
             null,
             "Вы можете дополнительно вставить сообщение, объясняющее причину приглашения."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Имя чата"
@@ -534,14 +514,14 @@
             null,
             "Нет чатов %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Чаты %1$s:"
-         ],
          "Description:": [
             null,
             "Описание:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Участники:"
@@ -746,10 +726,6 @@
             null,
             "Зарегистрирован успешно"
          ],
-         "Return": [
-            null,
-            "Назад"
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             "Провайдер отклонил вашу попытку зарегистрироваться. Пожалуйста, проверьте, правильно ли введены значения."
@@ -838,10 +814,6 @@
             null,
             "Вы уверены, что хотите удалить этот контакт?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            "Возникла ошибка при удалении "
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Вы уверены, что хотите отклонить запрос от этого контакта?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 210 - 274
locale/ru/LC_MESSAGES/converse.po


+ 13 - 49
locale/uk/LC_MESSAGES/converse.json

@@ -227,38 +227,6 @@
             null,
             "Жодного користувача не знайдено"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "Клацніть, щоб додати як чат-контакт"
-         ],
-         "Toggle chat": [
-            null,
-            "Включити чат"
-         ],
-         "Reconnecting": [
-            null,
-            "Перепід'єднуюсь"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "Під'єднуюсь"
-         ],
-         "Authenticating": [
-            null,
-            "Автентикуюсь"
-         ],
-         "Authentication Failed": [
-            null,
-            "Автентикація невдала"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -335,7 +303,7 @@
             null,
             "Повідомлення"
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -415,9 +383,9 @@
             null,
             "Надіслати"
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
-            "Причиною вказано: \""
+            ""
          ],
          " has left the room. \"": [
             null,
@@ -487,7 +455,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -495,7 +463,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -511,6 +479,10 @@
             null,
             "Ви можете опціонально додати повідомлення, щоб пояснити причину запрошення."
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "Назва кімнати"
@@ -535,14 +507,14 @@
             null,
             "Жодної кімнати на %1$s"
          ],
-         "Rooms on %1$s": [
-            null,
-            "Кімнати на %1$s"
-         ],
          "Description:": [
             null,
             "Опис:"
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "Присутні:"
@@ -763,10 +735,6 @@
             null,
             "Успішно зареєстровано"
          ],
-         "Return": [
-            null,
-            "Вернутися"
-         ],
          "Retry": [
             null,
             ""
@@ -851,10 +819,6 @@
             null,
             "Ви впевнені, що хочете видалити цей контакт?"
          ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
-         ],
          "Are you sure you want to decline this contact request?": [
             null,
             "Ви впевнені, що хочете відхилити цей запит контакту?"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 209 - 271
locale/uk/LC_MESSAGES/converse.po


+ 12 - 44
locale/zh/LC_MESSAGES/converse.json

@@ -202,34 +202,6 @@
             null,
             "未找到用户"
          ],
-         "Click to add as a chat contact": [
-            null,
-            "点击添加为好友"
-         ],
-         "Toggle chat": [
-            null,
-            "折叠聊天窗口"
-         ],
-         "The connection has dropped, attempting to reconnect.": [
-            null,
-            ""
-         ],
-         "Connecting": [
-            null,
-            "连接中"
-         ],
-         "Authenticating": [
-            null,
-            "验证中"
-         ],
-         "Authentication Failed": [
-            null,
-            "验证失败"
-         ],
-         "Sorry, there was an error while trying to add ": [
-            null,
-            ""
-         ],
          "This client does not allow presence subscriptions": [
             null,
             ""
@@ -302,7 +274,7 @@
             null,
             ""
          ],
-         "Error: the \"": [
+         "${command}": [
             null,
             ""
          ],
@@ -374,7 +346,7 @@
             null,
             ""
          ],
-         "The reason given is: \"": [
+         "${notification.reason}": [
             null,
             ""
          ],
@@ -442,7 +414,7 @@
             null,
             ""
          ],
-         "All other room occupants can see your Jabber ID": [
+         "All other room occupants can see your XMPP username": [
             null,
             ""
          ],
@@ -450,7 +422,7 @@
             null,
             ""
          ],
-         "Only moderators can see your Jabber ID": [
+         "Only moderators can see your XMPP username": [
             null,
             ""
          ],
@@ -466,6 +438,10 @@
             null,
             ""
          ],
+         "Please enter a valid XMPP username": [
+            null,
+            ""
+         ],
          "Room name": [
             null,
             "聊天室名称"
@@ -486,14 +462,14 @@
             null,
             "%1$s 上没有聊天室"
          ],
-         "Rooms on %1$s": [
-            null,
-            "%1$s 上的聊天室"
-         ],
          "Description:": [
             null,
             "描述: "
          ],
+         "Room Address (JID):": [
+            null,
+            ""
+         ],
          "Occupants:": [
             null,
             "成员:"
@@ -678,10 +654,6 @@
             null,
             ""
          ],
-         "Return": [
-            null,
-            ""
-         ],
          "The provider rejected your registration attempt. Please check the values you entered for correctness.": [
             null,
             ""
@@ -765,10 +737,6 @@
          "Name": [
             null,
             ""
-         ],
-         "Sorry, there was an error while trying to remove ": [
-            null,
-            ""
          ]
       }
    }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 271
locale/zh/LC_MESSAGES/converse.po


+ 6976 - 0
package-lock.json

@@ -0,0 +1,6976 @@
+{
+  "name": "converse.js",
+  "version": "3.2.0",
+  "lockfileVersion": 1,
+  "dependencies": {
+    "@greenkeeper/flags": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@greenkeeper/flags/-/flags-3.0.1.tgz",
+      "integrity": "sha1-N7of8sdiohFCvX6oxmzjTbP0zAk=",
+      "dev": true
+    },
+    "@greenkeeper/rc": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@greenkeeper/rc/-/rc-2.0.1.tgz",
+      "integrity": "sha1-Ruh7aU/hSEEAWrZSGlo5sIMK+IE=",
+      "dev": true
+    },
+    "abbrev": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
+      "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=",
+      "dev": true
+    },
+    "acorn": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz",
+      "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==",
+      "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,
+      "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
+        }
+      }
+    },
+    "ajv": {
+      "version": "4.11.8",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+      "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+      "dev": true
+    },
+    "ajv-keywords": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
+      "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=",
+      "dev": true
+    },
+    "align-text": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+      "dev": true
+    },
+    "almond": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/almond/-/almond-0.3.3.tgz",
+      "integrity": "sha1-oOfJWsdiTWQXtElLHmi/9pMWiiA=",
+      "dev": true
+    },
+    "ansi-align": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
+      "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+      "dev": true,
+      "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.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz",
+          "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=",
+          "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
+        }
+      }
+    },
+    "ansi-escapes": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
+      "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+      "dev": true
+    },
+    "ansicolors": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz",
+      "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=",
+      "dev": true
+    },
+    "anymatch": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz",
+      "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=",
+      "dev": true,
+      "optional": true
+    },
+    "aproba": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz",
+      "integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw==",
+      "dev": true
+    },
+    "archy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+      "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+      "dev": true
+    },
+    "are-we-there-yet": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
+      "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+      "dev": true
+    },
+    "argparse": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+      "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+      "dev": true
+    },
+    "arr-diff": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+      "dev": true,
+      "optional": 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,
+      "optional": true
+    },
+    "array-find-index": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+      "dev": true
+    },
+    "array-union": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+      "dev": true
+    },
+    "array-uniq": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+      "dev": true
+    },
+    "array-unique": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+      "dev": true,
+      "optional": true
+    },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+      "dev": true
+    },
+    "asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
+      "dev": true
+    },
+    "assert-plus": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+      "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
+      "dev": true
+    },
+    "async": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+      "dev": true
+    },
+    "async-each": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+      "dev": true,
+      "optional": true
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "awesomplete-avoid-xss": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/awesomplete-avoid-xss/-/awesomplete-avoid-xss-1.1.2.tgz",
+      "integrity": "sha1-+f4vrzmNZNniQYMlC3xKMTGcfGQ=",
+      "dev": true
+    },
+    "aws-sign2": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+      "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
+      "dev": true
+    },
+    "aws4": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+      "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
+      "dev": true
+    },
+    "babel-cli": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.24.1.tgz",
+      "integrity": "sha1-IHzXBbumFImy6kG1MSNBz2rKIoM=",
+      "dev": true
+    },
+    "babel-code-frame": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz",
+      "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=",
+      "dev": true
+    },
+    "babel-core": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz",
+      "integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=",
+      "dev": true
+    },
+    "babel-generator": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz",
+      "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=",
+      "dev": true
+    },
+    "babel-helper-builder-binary-assignment-operator-visitor": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
+      "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
+      "dev": true
+    },
+    "babel-helper-call-delegate": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+      "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+      "dev": true
+    },
+    "babel-helper-define-map": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz",
+      "integrity": "sha1-epdH8ljYlH0y1RX2qhx70CIEoIA=",
+      "dev": true
+    },
+    "babel-helper-explode-assignable-expression": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
+      "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
+      "dev": true
+    },
+    "babel-helper-function-name": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+      "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+      "dev": true
+    },
+    "babel-helper-get-function-arity": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+      "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+      "dev": true
+    },
+    "babel-helper-hoist-variables": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+      "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+      "dev": true
+    },
+    "babel-helper-optimise-call-expression": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+      "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+      "dev": true
+    },
+    "babel-helper-regex": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz",
+      "integrity": "sha1-024i+rEAjXnYhkjjIRaGgShFbOg=",
+      "dev": true
+    },
+    "babel-helper-remap-async-to-generator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
+      "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
+      "dev": true
+    },
+    "babel-helper-replace-supers": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+      "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+      "dev": true
+    },
+    "babel-helpers": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
+      "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+      "dev": true
+    },
+    "babel-messages": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+      "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+      "dev": true
+    },
+    "babel-plugin-check-es2015-constants": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+      "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+      "dev": true
+    },
+    "babel-plugin-syntax-async-functions": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
+      "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=",
+      "dev": true
+    },
+    "babel-plugin-syntax-exponentiation-operator": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
+      "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
+      "dev": true
+    },
+    "babel-plugin-syntax-trailing-function-commas": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
+      "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=",
+      "dev": true
+    },
+    "babel-plugin-transform-async-to-generator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
+      "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-arrow-functions": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+      "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-block-scoped-functions": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+      "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-block-scoping": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz",
+      "integrity": "sha1-dsKV3DpHQbFmWt/TFnIV3P8ypXY=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-classes": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+      "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-computed-properties": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+      "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-destructuring": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+      "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-duplicate-keys": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
+      "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-for-of": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+      "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-function-name": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+      "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-literals": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+      "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-modules-amd": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
+      "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-modules-commonjs": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz",
+      "integrity": "sha1-0+MQtA72ZKNmIiAAl8bUQCmPK/4=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-modules-systemjs": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
+      "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-modules-umd": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
+      "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-object-super": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+      "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-parameters": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+      "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-shorthand-properties": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+      "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-spread": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+      "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-sticky-regex": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
+      "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-template-literals": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+      "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-typeof-symbol": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
+      "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
+      "dev": true
+    },
+    "babel-plugin-transform-es2015-unicode-regex": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
+      "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
+      "dev": true
+    },
+    "babel-plugin-transform-exponentiation-operator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
+      "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
+      "dev": true
+    },
+    "babel-plugin-transform-regenerator": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz",
+      "integrity": "sha1-uNowWtQ8PJm0hI5P5AN7dw0jxBg=",
+      "dev": true
+    },
+    "babel-plugin-transform-strict-mode": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+      "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+      "dev": true
+    },
+    "babel-polyfill": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz",
+      "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=",
+      "dev": true
+    },
+    "babel-preset-env": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.0.tgz",
+      "integrity": "sha512-OVgtQRuOZKckrILgMA5rvctvFZPv72Gua9Rt006AiPoB0DJKGN07UmaQA+qRrYgK71MVct8fFhT0EyNWYorVew==",
+      "dev": true
+    },
+    "babel-preset-es2015": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
+      "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=",
+      "dev": true
+    },
+    "babel-preset-es2016": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-es2016/-/babel-preset-es2016-6.24.1.tgz",
+      "integrity": "sha1-+QC/k+LrwNJ235uKtZck6/2Vn4s=",
+      "dev": true
+    },
+    "babel-preset-es2017": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-es2017/-/babel-preset-es2017-6.24.1.tgz",
+      "integrity": "sha1-WXvq37n38gi8/YoS6bKym4svFNE=",
+      "dev": true
+    },
+    "babel-preset-latest": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-latest/-/babel-preset-latest-6.24.1.tgz",
+      "integrity": "sha1-Z33gaRVKdIXC0lxXfAL2JLhbheg=",
+      "dev": true
+    },
+    "babel-register": {
+      "version": "6.24.1",
+      "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz",
+      "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=",
+      "dev": true
+    },
+    "babel-runtime": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz",
+      "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=",
+      "dev": true
+    },
+    "babel-template": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz",
+      "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=",
+      "dev": true
+    },
+    "babel-traverse": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz",
+      "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=",
+      "dev": true
+    },
+    "babel-types": {
+      "version": "6.25.0",
+      "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz",
+      "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=",
+      "dev": true
+    },
+    "babylon": {
+      "version": "6.17.4",
+      "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz",
+      "integrity": "sha512-kChlV+0SXkjE0vUn9OZ7pBMWRFd8uq3mZe8x1K6jhuNcAFAtEnjchFAqB+dYEXKyd+JpT6eppRR78QAr5gTsUw==",
+      "dev": true
+    },
+    "backbone": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.3.3.tgz",
+      "integrity": "sha1-TMgOp8sWMaxHSInOQPL4vGg7KZk=",
+      "dev": true
+    },
+    "backbone.browserStorage": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/backbone.browserStorage/-/backbone.browserStorage-0.0.3.tgz",
+      "integrity": "sha1-ikIi8I2bHQslLR14/1CUuNCKc2s=",
+      "dev": true
+    },
+    "backbone.overview": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/backbone.overview/-/backbone.overview-0.0.3.tgz",
+      "integrity": "sha1-RW8stg8xkWVeMYOCpl2VZUTLZrI=",
+      "dev": true,
+      "dependencies": {
+        "backbone": {
+          "version": "1.2.3",
+          "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.2.3.tgz",
+          "integrity": "sha1-wiz9B/yG676uYdGJKe0RXpmdZbk=",
+          "dev": true
+        }
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+      "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+      "dev": true,
+      "optional": true
+    },
+    "binary-extensions": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz",
+      "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=",
+      "dev": true,
+      "optional": true
+    },
+    "boom": {
+      "version": "2.10.1",
+      "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+      "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+      "dev": true
+    },
+    "bootstrap": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.7.tgz",
+      "integrity": "sha1-WjiTlFSfIzMIdaOxUGVldPip63E=",
+      "dev": true
+    },
+    "bourbon": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/bourbon/-/bourbon-4.3.4.tgz",
+      "integrity": "sha1-TaOAAp6SwMj5dkx3lFGhNLEefMM=",
+      "dev": true
+    },
+    "boxen": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.2.0.tgz",
+      "integrity": "sha512-tfKK3nq0qXXOxvXEYW1k1XNRrDuQzO2oFPvLD3Fs1I58n0leuTNlftBmu3seUCyZvDfiqgRaxlqZs9WJAbSA7g==",
+      "dev": true,
+      "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
+        },
+        "ansi-styles": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz",
+          "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz",
+          "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==",
+          "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.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz",
+          "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=",
+          "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
+        },
+        "supports-color": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz",
+          "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==",
+          "dev": true
+        }
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
+      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+      "dev": true
+    },
+    "braces": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+      "dev": true,
+      "optional": true
+    },
+    "browserslist": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.1.5.tgz",
+      "integrity": "sha1-6IJVDfPRzW1IHBo+ADjyuvE6RxE=",
+      "dev": true
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+      "dev": true
+    },
+    "caller-path": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+      "dev": true
+    },
+    "callsites": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+      "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+      "dev": true
+    },
+    "camelcase-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+      "dev": true,
+      "dependencies": {
+        "camelcase": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+          "dev": true
+        }
+      }
+    },
+    "caniuse-lite": {
+      "version": "1.0.30000700",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000700.tgz",
+      "integrity": "sha1-YISHHsdcb6YjJ96XYiUU+V2dsmo=",
+      "dev": true
+    },
+    "capture-stack-trace": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
+      "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
+      "dev": true
+    },
+    "cardinal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-1.0.0.tgz",
+      "integrity": "sha1-UOIcGwqjdyn5N33vGWtanOyTLuk=",
+      "dev": true
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "center-align": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+      "dev": true
+    },
+    "chalk": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+      "dev": true
+    },
+    "char-spinner": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/char-spinner/-/char-spinner-1.0.1.tgz",
+      "integrity": "sha1-5upnvSR+EHESmDt6sEee02KAAIE=",
+      "dev": true
+    },
+    "chokidar": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
+      "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
+      "dev": true,
+      "optional": true
+    },
+    "circular-json": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz",
+      "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=",
+      "dev": true
+    },
+    "clean-css": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.6.tgz",
+      "integrity": "sha1-Wke+tSaZTLT3vzYYilXtO0VSjws=",
+      "dev": true
+    },
+    "clean-css-cli": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.1.6.tgz",
+      "integrity": "sha1-PvKidGsHT1XuBTkihxCnDbeng4k=",
+      "dev": true
+    },
+    "cli": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+      "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+      "dev": true
+    },
+    "cli-boxes": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+      "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
+      "dev": true
+    },
+    "cli-cursor": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
+      "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
+      "dev": true
+    },
+    "cli-md": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/cli-md/-/cli-md-1.2.0.tgz",
+      "integrity": "sha1-qZsfS6GkdrGmVPD+lX4ZAsSM2e4=",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+          "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+          "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+          "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
+          "dev": true
+        },
+        "has-ansi": {
+          "version": "0.1.0",
+          "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+          "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+          "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+          "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
+          "dev": true
+        }
+      }
+    },
+    "cli-table": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
+      "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
+      "dev": true
+    },
+    "cli-width": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz",
+      "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=",
+      "dev": true
+    },
+    "clite": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/clite/-/clite-0.3.0.tgz",
+      "integrity": "sha1-5/y8jMW9Pn+LhO1I2xLpR0zHNEE=",
+      "dev": true,
+      "dependencies": {
+        "boxen": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.3.1.tgz",
+          "integrity": "sha1-p9iYJDrmIvertrtgTXQKdsalRhs=",
+          "dev": true
+        },
+        "cliui": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+          "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+          "dev": true
+        },
+        "es6-promise": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+          "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
+          "dev": true
+        },
+        "update-notifier": {
+          "version": "0.6.3",
+          "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-0.6.3.tgz",
+          "integrity": "sha1-d23sjaoT6WKjQeih2YNUMGtnrgg=",
+          "dev": true
+        },
+        "window-size": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz",
+          "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=",
+          "dev": true
+        },
+        "yargs": {
+          "version": "4.8.1",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
+          "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=",
+          "dev": true
+        }
+      }
+    },
+    "cliui": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+      "dev": true,
+      "dependencies": {
+        "wordwrap": {
+          "version": "0.0.2",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+          "dev": true
+        }
+      }
+    },
+    "clone-deep": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz",
+      "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=",
+      "dev": true,
+      "dependencies": {
+        "for-own": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+          "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+          "dev": true
+        }
+      }
+    },
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+      "dev": true
+    },
+    "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=",
+      "dev": true
+    },
+    "coffee-script": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz",
+      "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=",
+      "dev": true
+    },
+    "color-convert": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
+      "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
+      "dev": true
+    },
+    "color-name": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.2.tgz",
+      "integrity": "sha1-XIq3K2S9IhXWF66VWeuxSEdc+Y0=",
+      "dev": true
+    },
+    "colors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+      "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+      "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
+      "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
+      "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=",
+      "dev": true
+    },
+    "concat-stream": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+      "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+      "dev": true
+    },
+    "configstore": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz",
+      "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=",
+      "dev": true,
+      "dependencies": {
+        "uuid": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+          "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
+          "dev": true
+        }
+      }
+    },
+    "console-browserify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+      "dev": true
+    },
+    "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=",
+      "dev": true
+    },
+    "convert-source-map": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz",
+      "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=",
+      "dev": true
+    },
+    "core-js": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
+      "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=",
+      "dev": true
+    },
+    "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=",
+      "dev": true
+    },
+    "corser": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
+      "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=",
+      "dev": true
+    },
+    "create-error-class": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+      "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+      "dev": true
+    },
+    "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
+    },
+    "cryptiles": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+      "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+      "dev": true
+    },
+    "cssfilter": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.9.tgz",
+      "integrity": "sha1-j1zrOqvXaNtTnaRYKyFS1j73cV4=",
+      "dev": true
+    },
+    "currently-unhandled": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+      "dev": true
+    },
+    "d": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
+      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+      "dev": true
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+          "dev": true
+        }
+      }
+    },
+    "date-now": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+      "dev": true
+    },
+    "dateformat": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+      "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+      "dev": true
+    },
+    "debug": {
+      "version": "2.6.8",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+      "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+      "dev": true
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "deep-extend": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
+      "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=",
+      "dev": true
+    },
+    "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
+    },
+    "del": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+      "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+      "dev": true
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+      "dev": true
+    },
+    "detect-indent": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+      "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+      "dev": true
+    },
+    "diff": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.0.tgz",
+      "integrity": "sha512-w0XZubFWn0Adlsapj9EAWX0FqWdO4tz8kc3RiYdWLh4k/V8PTb6i0SMgXt0vRM3zyKnT8tKO7mUlieRQHIjMNg==",
+      "dev": true
+    },
+    "doctrine": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
+      "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
+      "dev": true
+    },
+    "dom-serializer": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+      "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
+          "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
+          "dev": true
+        },
+        "entities": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
+          "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
+          "dev": true
+        }
+      }
+    },
+    "domelementtype": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
+      "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
+      "dev": true
+    },
+    "domhandler": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+      "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+      "dev": true
+    },
+    "domutils": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+      "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+      "dev": true
+    },
+    "dot-prop": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
+      "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
+      "dev": true
+    },
+    "duplexer2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+      "dev": true
+    },
+    "duplexify": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
+      "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
+      "dev": true
+    },
+    "ecc-jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+      "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+      "dev": true,
+      "optional": true
+    },
+    "ecstatic": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-2.2.1.tgz",
+      "integrity": "sha512-ztE4WqheoWLh3wv+HQwy7dACnvNY620coWpa+XqY6R2cVWgaAT2lUISU1Uf7JpdLLJCURktJOaA9av2AOzsyYQ==",
+      "dev": true,
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "electron-to-chromium": {
+      "version": "1.3.15",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.15.tgz",
+      "integrity": "sha1-CDl5NIkcvPrrvRi4KpW1pIETg2k=",
+      "dev": true
+    },
+    "emojione": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/emojione/-/emojione-3.1.1.tgz",
+      "integrity": "sha1-9WgPvuLLKlbpkEKxTZNePrhhswA=",
+      "dev": true
+    },
+    "encoding": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+      "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+      "dev": true
+    },
+    "end-of-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
+      "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=",
+      "dev": true,
+      "dependencies": {
+        "once": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+          "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
+          "dev": true
+        }
+      }
+    },
+    "entities": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+      "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+      "dev": true
+    },
+    "env-paths": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz",
+      "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=",
+      "dev": true
+    },
+    "error-ex": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+      "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+      "dev": true
+    },
+    "es5-ext": {
+      "version": "0.10.24",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.24.tgz",
+      "integrity": "sha1-pVh3yZJLwMjZvTwsvhdJWsFwmxQ=",
+      "dev": true
+    },
+    "es6-iterator": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz",
+      "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=",
+      "dev": true
+    },
+    "es6-map": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
+      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
+      "dev": true
+    },
+    "es6-promise": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz",
+      "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng==",
+      "dev": true
+    },
+    "es6-set": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
+      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
+      "dev": true
+    },
+    "es6-symbol": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+      "dev": true
+    },
+    "es6-weak-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
+      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
+      "dev": true
+    },
+    "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
+    },
+    "escope": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
+      "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=",
+      "dev": true
+    },
+    "eslint": {
+      "version": "3.19.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz",
+      "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=",
+      "dev": true,
+      "dependencies": {
+        "user-home": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
+          "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-lodash": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-lodash/-/eslint-plugin-lodash-2.4.4.tgz",
+      "integrity": "sha1-7MeyFv3Uh98vVCc06hIVIDy1Y+w=",
+      "dev": true
+    },
+    "espree": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz",
+      "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=",
+      "dev": true
+    },
+    "esprima": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
+      "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
+      "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
+      "dev": true
+    },
+    "esrecurse": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
+      "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
+      "dev": true
+    },
+    "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.2",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+      "dev": true
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+      "dev": true
+    },
+    "eventemitter2": {
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+      "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+      "dev": true
+    },
+    "eventemitter3": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
+      "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=",
+      "dev": true
+    },
+    "execa": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+      "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+      "dev": true
+    },
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+      "dev": true
+    },
+    "exit-hook": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
+      "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
+      "dev": true
+    },
+    "expand-brackets": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+      "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+      "dev": true,
+      "optional": true
+    },
+    "expand-range": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
+      "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
+      "dev": true,
+      "optional": true
+    },
+    "extend": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
+      "dev": true
+    },
+    "external-editor": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz",
+      "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=",
+      "dev": true
+    },
+    "extglob": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+      "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+      "dev": true,
+      "optional": true
+    },
+    "extsprintf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz",
+      "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=",
+      "dev": true
+    },
+    "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": "1.7.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+      "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+      "dev": true
+    },
+    "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
+    },
+    "filename-regex": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
+      "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
+      "dev": true,
+      "optional": true
+    },
+    "fill-range": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
+      "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
+      "dev": true,
+      "optional": true
+    },
+    "filled-array": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz",
+      "integrity": "sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q=",
+      "dev": true
+    },
+    "find-up": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+      "dev": true
+    },
+    "findup-sync": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+      "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+      "dev": true,
+      "dependencies": {
+        "glob": {
+          "version": "5.0.15",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+          "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+          "dev": true
+        }
+      }
+    },
+    "flat-cache": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz",
+      "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=",
+      "dev": true
+    },
+    "font-awesome": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
+      "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=",
+      "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
+    },
+    "for-own": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
+      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+      "dev": true,
+      "optional": true
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
+    },
+    "form-data": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
+      "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
+      "dev": true
+    },
+    "formatio": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz",
+      "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=",
+      "dev": true
+    },
+    "fs-readdir-recursive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz",
+      "integrity": "sha1-jNF0XItPiinIyuw5JHaSG6GV9WA=",
+      "dev": true
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
+      "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "abbrev": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
+          "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=",
+          "dev": true,
+          "optional": true
+        },
+        "ajv": {
+          "version": "4.11.8",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+          "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+          "dev": true,
+          "optional": true
+        },
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+          "dev": true
+        },
+        "aproba": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz",
+          "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=",
+          "dev": true,
+          "optional": true
+        },
+        "are-we-there-yet": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
+          "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+          "dev": true,
+          "optional": true
+        },
+        "asn1": {
+          "version": "0.2.3",
+          "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+          "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
+          "dev": true,
+          "optional": true
+        },
+        "assert-plus": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+          "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
+          "dev": true,
+          "optional": true
+        },
+        "asynckit": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+          "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+          "dev": true,
+          "optional": true
+        },
+        "aws-sign2": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+          "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
+          "dev": true,
+          "optional": true
+        },
+        "aws4": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+          "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
+          "dev": true,
+          "optional": true
+        },
+        "balanced-match": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+          "dev": true
+        },
+        "bcrypt-pbkdf": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+          "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+          "dev": true,
+          "optional": true
+        },
+        "block-stream": {
+          "version": "0.0.9",
+          "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+          "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+          "dev": true
+        },
+        "boom": {
+          "version": "2.10.1",
+          "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+          "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+          "dev": true
+        },
+        "brace-expansion": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
+          "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=",
+          "dev": true
+        },
+        "buffer-shims": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+          "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=",
+          "dev": true
+        },
+        "caseless": {
+          "version": "0.12.0",
+          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+          "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+          "dev": true,
+          "optional": true
+        },
+        "co": {
+          "version": "4.6.0",
+          "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+          "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+          "dev": true,
+          "optional": true
+        },
+        "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=",
+          "dev": true
+        },
+        "combined-stream": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+          "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+          "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=",
+          "dev": true
+        },
+        "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=",
+          "dev": true
+        },
+        "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=",
+          "dev": true
+        },
+        "cryptiles": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+          "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+          "dev": true,
+          "optional": true
+        },
+        "dashdash": {
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+          "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+          "dev": true,
+          "optional": true,
+          "dependencies": {
+            "assert-plus": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "debug": {
+          "version": "2.6.8",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+          "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+          "dev": true,
+          "optional": true
+        },
+        "deep-extend": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
+          "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=",
+          "dev": true,
+          "optional": true
+        },
+        "delayed-stream": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+          "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+          "dev": true
+        },
+        "delegates": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+          "dev": true,
+          "optional": true
+        },
+        "ecc-jsbn": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+          "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+          "dev": true,
+          "optional": true
+        },
+        "extend": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+          "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
+          "dev": true,
+          "optional": true
+        },
+        "extsprintf": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz",
+          "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=",
+          "dev": true
+        },
+        "forever-agent": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+          "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+          "dev": true,
+          "optional": true
+        },
+        "form-data": {
+          "version": "2.1.4",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
+          "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
+          "dev": true,
+          "optional": true
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+          "dev": true
+        },
+        "fstream": {
+          "version": "1.0.11",
+          "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
+          "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+          "dev": true
+        },
+        "fstream-ignore": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz",
+          "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=",
+          "dev": true,
+          "optional": true
+        },
+        "gauge": {
+          "version": "2.7.4",
+          "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+          "dev": true,
+          "optional": true
+        },
+        "getpass": {
+          "version": "0.1.7",
+          "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+          "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+          "dev": true,
+          "optional": true,
+          "dependencies": {
+            "assert-plus": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "glob": {
+          "version": "7.1.2",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+          "dev": true
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        },
+        "har-schema": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
+          "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
+          "dev": true,
+          "optional": true
+        },
+        "har-validator": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
+          "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
+          "dev": true,
+          "optional": true
+        },
+        "has-unicode": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+          "dev": true,
+          "optional": true
+        },
+        "hawk": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+          "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+          "dev": true,
+          "optional": true
+        },
+        "hoek": {
+          "version": "2.16.3",
+          "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+          "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
+          "dev": true
+        },
+        "http-signature": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+          "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+          "dev": true,
+          "optional": true
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+          "dev": true
+        },
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "ini": {
+          "version": "1.3.4",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
+          "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=",
+          "dev": true,
+          "optional": 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=",
+          "dev": true
+        },
+        "is-typedarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+          "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+          "dev": true,
+          "optional": true
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "dev": true
+        },
+        "isstream": {
+          "version": "0.1.2",
+          "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+          "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+          "dev": true,
+          "optional": true
+        },
+        "jodid25519": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz",
+          "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=",
+          "dev": true,
+          "optional": true
+        },
+        "jsbn": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+          "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+          "dev": true,
+          "optional": true
+        },
+        "json-schema": {
+          "version": "0.2.3",
+          "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+          "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+          "dev": true,
+          "optional": true
+        },
+        "json-stable-stringify": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+          "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+          "dev": true,
+          "optional": true
+        },
+        "json-stringify-safe": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+          "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+          "dev": true,
+          "optional": true
+        },
+        "jsonify": {
+          "version": "0.0.0",
+          "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+          "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+          "dev": true,
+          "optional": true
+        },
+        "jsprim": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz",
+          "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=",
+          "dev": true,
+          "optional": true,
+          "dependencies": {
+            "assert-plus": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "mime-db": {
+          "version": "1.27.0",
+          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
+          "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=",
+          "dev": true
+        },
+        "mime-types": {
+          "version": "2.1.15",
+          "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz",
+          "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=",
+          "dev": true
+        },
+        "minimatch": {
+          "version": "3.0.4",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+          "dev": true
+        },
+        "minimist": {
+          "version": "0.0.8",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+          "dev": true
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true,
+          "optional": true
+        },
+        "node-pre-gyp": {
+          "version": "0.6.36",
+          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz",
+          "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=",
+          "dev": true,
+          "optional": true
+        },
+        "nopt": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+          "dev": true,
+          "optional": true
+        },
+        "npmlog": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz",
+          "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==",
+          "dev": true,
+          "optional": true
+        },
+        "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=",
+          "dev": true
+        },
+        "oauth-sign": {
+          "version": "0.8.2",
+          "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+          "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+          "dev": true,
+          "optional": true
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+          "dev": true,
+          "optional": true
+        },
+        "once": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+          "dev": true
+        },
+        "os-homedir": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+          "dev": true,
+          "optional": 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=",
+          "dev": true,
+          "optional": true
+        },
+        "osenv": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
+          "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
+          "dev": true,
+          "optional": 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=",
+          "dev": true
+        },
+        "performance-now": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
+          "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
+          "dev": true,
+          "optional": true
+        },
+        "process-nextick-args": {
+          "version": "1.0.7",
+          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+          "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+          "dev": true
+        },
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true,
+          "optional": true
+        },
+        "qs": {
+          "version": "6.4.0",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
+          "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
+          "dev": true,
+          "optional": true
+        },
+        "rc": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz",
+          "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=",
+          "dev": true,
+          "optional": true,
+          "dependencies": {
+            "minimist": {
+              "version": "1.2.0",
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "readable-stream": {
+          "version": "2.2.9",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz",
+          "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=",
+          "dev": true
+        },
+        "request": {
+          "version": "2.81.0",
+          "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
+          "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
+          "dev": true,
+          "optional": true
+        },
+        "rimraf": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
+          "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
+          "dev": true
+        },
+        "safe-buffer": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
+          "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=",
+          "dev": true
+        },
+        "semver": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+          "dev": true,
+          "optional": true
+        },
+        "set-blocking": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+          "dev": true,
+          "optional": true
+        },
+        "signal-exit": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+          "dev": true,
+          "optional": true
+        },
+        "sntp": {
+          "version": "1.0.9",
+          "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+          "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+          "dev": true,
+          "optional": true
+        },
+        "sshpk": {
+          "version": "1.13.0",
+          "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz",
+          "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=",
+          "dev": true,
+          "optional": true,
+          "dependencies": {
+            "assert-plus": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+              "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "string_decoder": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz",
+          "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=",
+          "dev": true
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "dev": true
+        },
+        "stringstream": {
+          "version": "0.0.5",
+          "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+          "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
+          "dev": true,
+          "optional": true
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "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=",
+          "dev": true,
+          "optional": true
+        },
+        "tar": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+          "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
+          "dev": true
+        },
+        "tar-pack": {
+          "version": "3.4.0",
+          "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz",
+          "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=",
+          "dev": true,
+          "optional": true
+        },
+        "tough-cookie": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
+          "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
+          "dev": true,
+          "optional": true
+        },
+        "tunnel-agent": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+          "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+          "dev": true,
+          "optional": true
+        },
+        "tweetnacl": {
+          "version": "0.14.5",
+          "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+          "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+          "dev": true,
+          "optional": true
+        },
+        "uid-number": {
+          "version": "0.0.6",
+          "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
+          "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=",
+          "dev": true,
+          "optional": true
+        },
+        "util-deprecate": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+          "dev": true
+        },
+        "uuid": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
+          "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=",
+          "dev": true,
+          "optional": true
+        },
+        "verror": {
+          "version": "1.3.6",
+          "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
+          "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=",
+          "dev": true,
+          "optional": true
+        },
+        "wide-align": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
+          "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+          "dev": true,
+          "optional": true
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+          "dev": true
+        }
+      }
+    },
+    "gauge": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "dev": true
+    },
+    "generate-function": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
+      "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
+      "dev": true
+    },
+    "generate-object-property": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+      "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
+      "dev": true
+    },
+    "get-caller-file": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
+      "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
+      "dev": true
+    },
+    "get-stdin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+      "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
+    },
+    "getobject": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+      "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+      "dev": true
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+          "dev": true
+        }
+      }
+    },
+    "gettext-parser": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz",
+      "integrity": "sha1-LFpmONiTk0ubVQN9CtgstwBLJnk=",
+      "dev": true
+    },
+    "gitconfiglocal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-2.0.1.tgz",
+      "integrity": "sha1-tQCSyQUF05d7TZnXZPn1mcmA53Q=",
+      "dev": true
+    },
+    "github-slug": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/github-slug/-/github-slug-2.0.0.tgz",
+      "integrity": "sha1-yUdYSFYHVV+Xf/Ltf5Di8lltplI=",
+      "dev": true
+    },
+    "github-url-from-git": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz",
+      "integrity": "sha1-+YX+3MCpqledyI16/waNVcxiUaA=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+      "dev": true
+    },
+    "glob-base": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
+      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
+      "dev": true,
+      "optional": true
+    },
+    "glob-parent": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+      "dev": true
+    },
+    "globals": {
+      "version": "9.18.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+      "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+      "dev": true
+    },
+    "globby": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+      "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+      "dev": true
+    },
+    "got": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
+      "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
+      "dev": true
+    },
+    "graceful-fs": {
+      "version": "4.1.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+      "dev": true
+    },
+    "graceful-readlink": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+      "dev": true
+    },
+    "greenkeeper": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/greenkeeper/-/greenkeeper-4.2.1.tgz",
+      "integrity": "sha1-VsjFPjF4OdcuB4H8tU+yORekIFI=",
+      "dev": true,
+      "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
+        },
+        "esprima": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
+          "dev": true
+        },
+        "figures": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+          "dev": true
+        },
+        "inquirer": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-2.0.0.tgz",
+          "integrity": "sha1-4TUWh7kNFQykA86qPO+x4wZb70s=",
+          "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
+        },
+        "js-yaml": {
+          "version": "3.8.0",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.0.tgz",
+          "integrity": "sha1-9jYg0u5nY9z5UHi/e9uqAdjbTnM=",
+          "dev": true
+        },
+        "mute-stream": {
+          "version": "0.0.6",
+          "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz",
+          "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=",
+          "dev": true
+        },
+        "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
+        },
+        "string-width": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz",
+          "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=",
+          "dev": true,
+          "dependencies": {
+            "strip-ansi": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+              "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+              "dev": true
+            }
+          }
+        }
+      }
+    },
+    "grunt": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.1.tgz",
+      "integrity": "sha1-6HeHZOlEsY8yuw8QuQeEdcnftWs=",
+      "dev": true,
+      "dependencies": {
+        "esprima": {
+          "version": "2.7.3",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+          "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+          "dev": true
+        },
+        "glob": {
+          "version": "7.0.6",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+          "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+          "dev": true
+        },
+        "js-yaml": {
+          "version": "3.5.5",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
+          "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "2.2.8",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+          "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+          "dev": true
+        }
+      }
+    },
+    "grunt-cli": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+      "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+      "dev": true,
+      "dependencies": {
+        "resolve": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+          "dev": true
+        }
+      }
+    },
+    "grunt-json": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/grunt-json/-/grunt-json-0.2.0.tgz",
+      "integrity": "sha1-+dgHhWMZiqXDPJkE/yXQiO5TLUE=",
+      "dev": true
+    },
+    "grunt-known-options": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz",
+      "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=",
+      "dev": true
+    },
+    "grunt-legacy-log": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.0.tgz",
+      "integrity": "sha1-+4bxgJhHvAfcR4Q/ns1srLYt8tU=",
+      "dev": true,
+      "dependencies": {
+        "colors": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+          "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+          "dev": true
+        },
+        "lodash": {
+          "version": "3.10.1",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+          "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+          "dev": true
+        }
+      }
+    },
+    "grunt-legacy-log-utils": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz",
+      "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=",
+      "dev": true,
+      "dependencies": {
+        "lodash": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
+          "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
+          "dev": true
+        }
+      }
+    },
+    "grunt-legacy-util": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz",
+      "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=",
+      "dev": true,
+      "dependencies": {
+        "lodash": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
+          "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
+          "dev": true
+        }
+      }
+    },
+    "har-schema": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
+      "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
+      "dev": true
+    },
+    "har-validator": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
+      "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
+      "dev": true
+    },
+    "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
+    },
+    "has-color": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
+      "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+      "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
+      "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=",
+      "dev": true
+    },
+    "hasbin": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/hasbin/-/hasbin-1.2.3.tgz",
+      "integrity": "sha1-eMWSaJPIAhXCtWiuH9P8q3omlrA=",
+      "dev": true
+    },
+    "hawk": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+      "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+      "dev": true
+    },
+    "he": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+      "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+      "dev": true
+    },
+    "hide-secrets": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/hide-secrets/-/hide-secrets-1.1.0.tgz",
+      "integrity": "sha1-JAZoCU1cu+ZkAr4/rOMRwM7PrMI=",
+      "dev": true
+    },
+    "hoek": {
+      "version": "2.16.3",
+      "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+      "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
+      "dev": true
+    },
+    "home-or-tmp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
+      "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+      "dev": true
+    },
+    "hooker": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+      "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+      "dev": true
+    },
+    "hosted-git-info": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
+      "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
+      "dev": true
+    },
+    "htmlparser2": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+      "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+      "dev": true,
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "1.1.14",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+          "dev": true
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
+        }
+      }
+    },
+    "http-proxy": {
+      "version": "1.16.2",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz",
+      "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=",
+      "dev": true
+    },
+    "http-server": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.10.0.tgz",
+      "integrity": "sha1-sqRGsWqduH7TxiK6m+sbCFsSNKc=",
+      "dev": true
+    },
+    "http-signature": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+      "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+      "dev": true
+    },
+    "iconv-lite": {
+      "version": "0.4.18",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
+      "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==",
+      "dev": true
+    },
+    "ignore": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz",
+      "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=",
+      "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": "2.1.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+      "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+      "dev": true
+    },
+    "infinity-agent": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/infinity-agent/-/infinity-agent-2.0.3.tgz",
+      "integrity": "sha1-ReDi/3qesDCyfWK3SzdEt6esQhY=",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+      "dev": true
+    },
+    "ini": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
+      "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=",
+      "dev": true
+    },
+    "inquirer": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
+      "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=",
+      "dev": true
+    },
+    "install": {
+      "version": "0.8.9",
+      "resolved": "https://registry.npmjs.org/install/-/install-0.8.9.tgz",
+      "integrity": "sha1-n0tcDRhR74cunfheT3Fi1OXc2+0=",
+      "dev": true
+    },
+    "interpret": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz",
+      "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=",
+      "dev": true
+    },
+    "invariant": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
+      "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
+      "dev": true
+    },
+    "invert-kv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+      "dev": true
+    },
+    "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-binary-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+      "dev": true,
+      "optional": true
+    },
+    "is-buffer": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
+      "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
+      "dev": true
+    },
+    "is-builtin-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+      "dev": true
+    },
+    "is-dotfile": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
+      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
+      "dev": true,
+      "optional": true
+    },
+    "is-equal-shallow": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
+      "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
+      "dev": true,
+      "optional": 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": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+      "dev": true
+    },
+    "is-finite": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+      "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=",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+      "dev": true
+    },
+    "is-my-json-valid": {
+      "version": "2.16.0",
+      "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz",
+      "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=",
+      "dev": true
+    },
+    "is-npm": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+      "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=",
+      "dev": true
+    },
+    "is-number": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+      "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+      "dev": true,
+      "optional": true
+    },
+    "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-path-cwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+      "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+      "dev": true
+    },
+    "is-path-in-cwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
+      "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
+      "dev": true
+    },
+    "is-path-inside": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
+      "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
+      "dev": true
+    },
+    "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,
+      "dependencies": {
+        "isobject": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+          "dev": true
+        }
+      }
+    },
+    "is-posix-bracket": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
+      "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
+      "dev": true,
+      "optional": true
+    },
+    "is-primitive": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
+      "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
+      "dev": true
+    },
+    "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-property": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
+      "dev": true
+    },
+    "is-redirect": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+      "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
+      "dev": true
+    },
+    "is-resolvable": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
+      "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=",
+      "dev": true
+    },
+    "is-retry-allowed": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
+      "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=",
+      "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-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "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": "2.1.0",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+      "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+      "dev": true,
+      "optional": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
+    },
+    "jasmine-core": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.6.4.tgz",
+      "integrity": "sha1-3skmzQqfoof7bbXHVfpIfnTOysU=",
+      "dev": true
+    },
+    "jed": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/jed/-/jed-0.5.4.tgz",
+      "integrity": "sha1-rtFA1cTWWs89qli5Gd1WElP2QcI=",
+      "dev": true
+    },
+    "jquery": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.3.tgz",
+      "integrity": "sha1-ReB+QZAzTeNsnhpktDsfE3PZF1g=",
+      "dev": true
+    },
+    "jquery.browser": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/jquery.browser/-/jquery.browser-0.1.0.tgz",
+      "integrity": "sha1-nHKmCV/SgUtER26o9xZne3Kmors=",
+      "dev": true
+    },
+    "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
+    },
+    "js-yaml": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.0.tgz",
+      "integrity": "sha512-0LoUNELX4S+iofCT8f4uEHIiRBR+c2AINyC8qRWfC6QNruLtxVZRJaPcu/xwMgFIgDxF25tGHaDjvxzJCNE9yw==",
+      "dev": true
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true,
+      "optional": true
+    },
+    "jsesc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+      "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+      "dev": true
+    },
+    "jshint": {
+      "version": "2.9.5",
+      "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz",
+      "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=",
+      "dev": true,
+      "dependencies": {
+        "lodash": {
+          "version": "3.7.0",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz",
+          "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=",
+          "dev": true
+        },
+        "shelljs": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+          "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
+          "dev": true
+        },
+        "strip-json-comments": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+          "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+          "dev": true
+        }
+      }
+    },
+    "json-preserve-indent": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/json-preserve-indent/-/json-preserve-indent-1.1.3.tgz",
+      "integrity": "sha1-gNN5mXVAAB+4VpTboSkzEePS69o=",
+      "dev": true
+    },
+    "json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
+    },
+    "json-stable-stringify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+      "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+      "dev": true
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
+    },
+    "json5": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+      "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+      "dev": true
+    },
+    "jsonify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+      "dev": true
+    },
+    "jsonpointer": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
+      "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
+      "dev": true
+    },
+    "jsprim": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz",
+      "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+          "dev": true
+        }
+      }
+    },
+    "kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true
+    },
+    "latest-version": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz",
+      "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=",
+      "dev": true
+    },
+    "lazy-cache": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+      "dev": true
+    },
+    "lazy-req": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz",
+      "integrity": "sha1-va6+rTD42CQDnODOFJ1Nqge6H6w=",
+      "dev": true
+    },
+    "lcid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "dev": true
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true
+    },
+    "load-json-file": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+      "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+      "dev": true,
+      "dependencies": {
+        "strip-bom": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+          "dev": true
+        }
+      }
+    },
+    "lodash": {
+      "version": "4.17.4",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
+      "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
+      "dev": true
+    },
+    "lodash-template-loader": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lodash-template-loader/-/lodash-template-loader-2.0.0.tgz",
+      "integrity": "sha1-jsqfgFXIzS6E4wzrPlzZFQGJpfo=",
+      "dev": true
+    },
+    "lodash.assign": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
+      "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
+      "dev": true
+    },
+    "lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+      "dev": true
+    },
+    "lodash.defaults": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+      "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=",
+      "dev": true
+    },
+    "lodash.defaultsdeep": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz",
+      "integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=",
+      "dev": true
+    },
+    "lodash.mergewith": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
+      "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=",
+      "dev": true
+    },
+    "lodash.toarray": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+      "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=",
+      "dev": true
+    },
+    "lolex": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz",
+      "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=",
+      "dev": true
+    },
+    "longest": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+      "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+      "dev": true
+    },
+    "loose-envify": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+      "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+      "dev": true
+    },
+    "loud-rejection": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+      "dev": true
+    },
+    "lowercase-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
+      "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=",
+      "dev": true
+    },
+    "lru-cache": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
+      "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+      "dev": true
+    },
+    "map-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+      "dev": true
+    },
+    "marked": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz",
+      "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=",
+      "dev": true
+    },
+    "marked-terminal": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-1.7.0.tgz",
+      "integrity": "sha1-yMRgiBx3LHYEtkNnAH7l938SWQQ=",
+      "dev": true
+    },
+    "meow": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+      "dev": true,
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "micromatch": {
+      "version": "2.3.11",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+      "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+      "dev": true,
+      "optional": true
+    },
+    "mime": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz",
+      "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=",
+      "dev": true
+    },
+    "mime-db": {
+      "version": "1.27.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
+      "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.15",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz",
+      "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true
+    },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+      "dev": true
+    },
+    "mixin-object": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
+      "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
+      "dev": true,
+      "dependencies": {
+        "for-in": {
+          "version": "0.1.8",
+          "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
+          "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=",
+          "dev": true
+        }
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "dev": true
+    },
+    "moment": {
+      "version": "2.18.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
+      "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=",
+      "dev": true
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+      "dev": true
+    },
+    "mute-stream": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
+      "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
+      "dev": true
+    },
+    "nan": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
+      "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=",
+      "dev": true,
+      "optional": true
+    },
+    "native-promise-only": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
+      "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=",
+      "dev": true
+    },
+    "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
+    },
+    "nconf": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.7.2.tgz",
+      "integrity": "sha1-oF/fItwBw3jdXE3yfy3JC5qouwA=",
+      "dev": true,
+      "dependencies": {
+        "async": {
+          "version": "0.9.2",
+          "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+          "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
+          "dev": true
+        }
+      }
+    },
+    "nerf-dart": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz",
+      "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=",
+      "dev": true
+    },
+    "nested-error-stacks": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz",
+      "integrity": "sha1-GfYZWRUZ8JZ2mlupqG5u7sgjw88=",
+      "dev": true
+    },
+    "node-emoji": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.7.0.tgz",
+      "integrity": "sha512-dYx345sjhPJUpWaVQKjP0/43y+nTcfBRTZfSciM3ZEbRGaU/9AKaHBPf7AJ9vOKcK0W3v67AgI4m4oo02NLHhQ==",
+      "dev": true
+    },
+    "node-status-codes": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz",
+      "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=",
+      "dev": true
+    },
+    "nomnom": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
+      "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
+          "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
+          "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+          "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=",
+          "dev": true
+        },
+        "underscore": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+          "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=",
+          "dev": true
+        }
+      }
+    },
+    "nopt": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+      "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+      "dev": true
+    },
+    "normalize-package-data": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+      "dev": true
+    },
+    "normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "dev": true,
+      "optional": true
+    },
+    "npm": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/npm/-/npm-4.6.1.tgz",
+      "integrity": "sha1-+Osa0A3FilUUNjtBylNCgX8L1kY=",
+      "dev": true,
+      "dependencies": {
+        "abbrev": {
+          "version": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
+          "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=",
+          "dev": true
+        },
+        "ansi-regex": {
+          "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+          "dev": true
+        },
+        "ansicolors": {
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
+          "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=",
+          "dev": true
+        },
+        "ansistyles": {
+          "version": "0.1.3",
+          "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz",
+          "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=",
+          "dev": true
+        },
+        "aproba": {
+          "version": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz",
+          "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=",
+          "dev": true
+        },
+        "archy": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+          "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+          "dev": true
+        },
+        "asap": {
+          "version": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz",
+          "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8=",
+          "dev": true
+        },
+        "bluebird": {
+          "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
+          "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=",
+          "dev": true
+        },
+        "call-limit": {
+          "version": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.0.tgz",
+          "integrity": "sha1-b9YbA/PaQqLNDsK2DwK9DnGZH+o=",
+          "dev": true
+        },
+        "chownr": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
+          "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
+          "dev": true
+        },
+        "cmd-shim": {
+          "version": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-2.0.2.tgz",
+          "integrity": "sha1-b8vamUg6j9FdfTChlspp1oii79s=",
+          "dev": true
+        },
+        "columnify": {
+          "version": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz",
+          "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=",
+          "dev": true,
+          "dependencies": {
+            "wcwidth": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.0.tgz",
+              "integrity": "sha1-AtBZ/3qPx0Hg9rXaHmmytA2uym8=",
+              "dev": true,
+              "dependencies": {
+                "defaults": {
+                  "version": "1.0.3",
+                  "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
+                  "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+                  "dev": true,
+                  "dependencies": {
+                    "clone": {
+                      "version": "1.0.2",
+                      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz",
+                      "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=",
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "config-chain": {
+          "version": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz",
+          "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=",
+          "dev": true,
+          "dependencies": {
+            "proto-list": {
+              "version": "1.2.4",
+              "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+              "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
+              "dev": true
+            }
+          }
+        },
+        "debuglog": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
+          "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
+          "dev": true
+        },
+        "dezalgo": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
+          "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+          "dev": true
+        },
+        "editor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz",
+          "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=",
+          "dev": true
+        },
+        "fs-vacuum": {
+          "version": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz",
+          "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=",
+          "dev": true
+        },
+        "fs-write-stream-atomic": {
+          "version": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+          "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+          "dev": true
+        },
+        "fstream": {
+          "version": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
+          "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+          "dev": true
+        },
+        "fstream-npm": {
+          "version": "https://registry.npmjs.org/fstream-npm/-/fstream-npm-1.2.0.tgz",
+          "integrity": "sha1-0sPIkQE0aYLWTlcJHDhIe9qRb84=",
+          "dev": true,
+          "dependencies": {
+            "fstream-ignore": {
+              "version": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz",
+              "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=",
+              "dev": true,
+              "dependencies": {
+                "minimatch": {
+                  "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
+                  "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=",
+                  "dev": true,
+                  "dependencies": {
+                    "brace-expansion": {
+                      "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
+                      "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=",
+                      "dev": true,
+                      "dependencies": {
+                        "balanced-match": {
+                          "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+                          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+                          "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=",
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "glob": {
+          "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+          "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+          "dev": true,
+          "dependencies": {
+            "fs.realpath": {
+              "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+              "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+              "dev": true
+            },
+            "minimatch": {
+              "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
+              "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=",
+              "dev": true,
+              "dependencies": {
+                "brace-expansion": {
+                  "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
+                  "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=",
+                  "dev": true,
+                  "dependencies": {
+                    "balanced-match": {
+                      "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+                      "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+                      "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=",
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "path-is-absolute": {
+              "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+              "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+              "dev": true
+            }
+          }
+        },
+        "graceful-fs": {
+          "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        },
+        "has-unicode": {
+          "version": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+          "dev": true
+        },
+        "hosted-git-info": {
+          "version": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz",
+          "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=",
+          "dev": true
+        },
+        "iferr": {
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+          "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+          "dev": true
+        },
+        "imurmurhash": {
+          "version": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+          "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+          "dev": true
+        },
+        "inflight": {
+          "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+          "dev": true
+        },
+        "inherits": {
+          "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "ini": {
+          "version": "1.3.4",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
+          "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=",
+          "dev": true
+        },
+        "init-package-json": {
+          "version": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.1.tgz",
+          "integrity": "sha1-zYc6FneWvvuZYSsodioLY5P9j2o=",
+          "dev": true,
+          "dependencies": {
+            "promzard": {
+              "version": "0.3.0",
+              "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz",
+              "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=",
+              "dev": true
+            }
+          }
+        },
+        "JSONStream": {
+          "version": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
+          "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
+          "dev": true,
+          "dependencies": {
+            "jsonparse": {
+              "version": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.0.tgz",
+              "integrity": "sha1-hfwkWx2SWazGlBlguQWt9k594Og=",
+              "dev": true
+            },
+            "through": {
+              "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+              "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+              "dev": true
+            }
+          }
+        },
+        "lazy-property": {
+          "version": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz",
+          "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=",
+          "dev": true
+        },
+        "lockfile": {
+          "version": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.3.tgz",
+          "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k=",
+          "dev": true
+        },
+        "lodash._baseindexof": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz",
+          "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=",
+          "dev": true
+        },
+        "lodash._baseuniq": {
+          "version": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
+          "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=",
+          "dev": true,
+          "dependencies": {
+            "lodash._createset": {
+              "version": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
+              "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=",
+              "dev": true
+            },
+            "lodash._root": {
+              "version": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
+              "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=",
+              "dev": true
+            }
+          }
+        },
+        "lodash._bindcallback": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
+          "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=",
+          "dev": true
+        },
+        "lodash._cacheindexof": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz",
+          "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=",
+          "dev": true
+        },
+        "lodash._createcache": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz",
+          "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=",
+          "dev": true
+        },
+        "lodash._getnative": {
+          "version": "3.9.1",
+          "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+          "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
+          "dev": true
+        },
+        "lodash.clonedeep": {
+          "version": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+          "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+          "dev": true
+        },
+        "lodash.restparam": {
+          "version": "3.6.1",
+          "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
+          "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=",
+          "dev": true
+        },
+        "lodash.union": {
+          "version": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+          "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=",
+          "dev": true
+        },
+        "lodash.uniq": {
+          "version": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+          "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+          "dev": true
+        },
+        "lodash.without": {
+          "version": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
+          "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=",
+          "dev": true
+        },
+        "mississippi": {
+          "version": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.0.tgz",
+          "integrity": "sha1-0gFYPrEjJ+PFwWQqQEqcrPlONPU=",
+          "dev": true,
+          "dependencies": {
+            "concat-stream": {
+              "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+              "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+              "dev": true,
+              "dependencies": {
+                "typedarray": {
+                  "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+                  "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+                  "dev": true
+                }
+              }
+            },
+            "duplexify": {
+              "version": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
+              "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
+              "dev": true,
+              "dependencies": {
+                "end-of-stream": {
+                  "version": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
+                  "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=",
+                  "dev": true,
+                  "dependencies": {
+                    "once": {
+                      "version": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                      "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
+                      "dev": true
+                    }
+                  }
+                },
+                "stream-shift": {
+                  "version": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+                  "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+                  "dev": true
+                }
+              }
+            },
+            "end-of-stream": {
+              "version": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz",
+              "integrity": "sha1-6TUyWLqpEIll78QcsO+K3i88+wc=",
+              "dev": true,
+              "dependencies": {
+                "once": {
+                  "version": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+                  "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
+                  "dev": true
+                }
+              }
+            },
+            "flush-write-stream": {
+              "version": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz",
+              "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=",
+              "dev": true
+            },
+            "from2": {
+              "version": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+              "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+              "dev": true
+            },
+            "parallel-transform": {
+              "version": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
+              "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+              "dev": true,
+              "dependencies": {
+                "cyclist": {
+                  "version": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
+                  "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
+                  "dev": true
+                }
+              }
+            },
+            "pump": {
+              "version": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz",
+              "integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=",
+              "dev": true
+            },
+            "pumpify": {
+              "version": "https://registry.npmjs.org/pumpify/-/pumpify-1.3.5.tgz",
+              "integrity": "sha1-G2ccYZlAq8rqwK0OOjwWS+dgmTs=",
+              "dev": true
+            },
+            "stream-each": {
+              "version": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.0.tgz",
+              "integrity": "sha1-HpXUdXP1gNgU3A/4zQ9m8c5TyZE=",
+              "dev": true,
+              "dependencies": {
+                "stream-shift": {
+                  "version": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+                  "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+                  "dev": true
+                }
+              }
+            },
+            "through2": {
+              "version": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+              "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+              "dev": true,
+              "dependencies": {
+                "xtend": {
+                  "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+                  "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+                  "dev": true
+                }
+              }
+            }
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+          "dev": true,
+          "dependencies": {
+            "minimist": {
+              "version": "0.0.8",
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+              "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+              "dev": true
+            }
+          }
+        },
+        "move-concurrently": {
+          "version": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+          "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+          "dev": true,
+          "dependencies": {
+            "copy-concurrently": {
+              "version": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.3.tgz",
+              "integrity": "sha1-Rft4ZiSaHKiJqlcI5svSc+dbslA=",
+              "dev": true
+            },
+            "run-queue": {
+              "version": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+              "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+              "dev": true
+            }
+          }
+        },
+        "node-gyp": {
+          "version": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.0.tgz",
+          "integrity": "sha1-dHT2OjoFARYd2gtjQfAi8UxCP6Y=",
+          "dev": true,
+          "dependencies": {
+            "minimatch": {
+              "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
+              "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=",
+              "dev": true,
+              "dependencies": {
+                "brace-expansion": {
+                  "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
+                  "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=",
+                  "dev": true,
+                  "dependencies": {
+                    "balanced-match": {
+                      "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+                      "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+                      "dev": true
+                    },
+                    "concat-map": {
+                      "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+                      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "nopt": {
+              "version": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+              "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+              "dev": true
+            }
+          }
+        },
+        "nopt": {
+          "version": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+          "dev": true,
+          "dependencies": {
+            "osenv": {
+              "version": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
+              "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
+              "dev": true,
+              "dependencies": {
+                "os-homedir": {
+                  "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+                  "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+                  "dev": true
+                },
+                "os-tmpdir": {
+                  "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+                  "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+                  "dev": true
+                }
+              }
+            }
+          }
+        },
+        "normalize-git-url": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/normalize-git-url/-/normalize-git-url-3.0.2.tgz",
+          "integrity": "sha1-jl8Uvgva7bc+ByADEKpBbCc1D8Q=",
+          "dev": true
+        },
+        "normalize-package-data": {
+          "version": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz",
+          "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=",
+          "dev": true,
+          "dependencies": {
+            "is-builtin-module": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+              "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+              "dev": true,
+              "dependencies": {
+                "builtin-modules": {
+                  "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+                  "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+                  "dev": true
+                }
+              }
+            }
+          }
+        },
+        "npm-cache-filename": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz",
+          "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=",
+          "dev": true
+        },
+        "npm-install-checks": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.0.tgz",
+          "integrity": "sha1-1K7N/VGlPjcjt7L5Oy7ijjB7wNc=",
+          "dev": true
+        },
+        "npm-package-arg": {
+          "version": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-4.2.1.tgz",
+          "integrity": "sha1-WTMD/eqF98Qid18X+et2cPaA4+w=",
+          "dev": true
+        },
+        "npm-registry-client": {
+          "version": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.1.1.tgz",
+          "integrity": "sha1-gxR2RVQjygomXG/9thAPzAQrNs8=",
+          "dev": true,
+          "dependencies": {
+            "concat-stream": {
+              "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+              "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+              "dev": true,
+              "dependencies": {
+                "typedarray": {
+                  "version": "0.0.6",
+                  "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+                  "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+                  "dev": true
+                }
+              }
+            }
+          }
+        },
+        "npm-user-validate": {
+          "version": "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-0.1.5.tgz",
+          "integrity": "sha1-UkZdUMLSApSlcSW5lrrtv1bFAEs=",
+          "dev": true
+        },
+        "npmlog": {
+          "version": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz",
+          "integrity": "sha1-0DlQ4OeM4VJ7om0qdZLpNIrD518=",
+          "dev": true,
+          "dependencies": {
+            "are-we-there-yet": {
+              "version": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
+              "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+              "dev": true,
+              "dependencies": {
+                "delegates": {
+                  "version": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+                  "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+                  "dev": true
+                }
+              }
+            },
+            "console-control-strings": {
+              "version": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+              "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+              "dev": true
+            },
+            "gauge": {
+              "version": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+              "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+              "dev": true,
+              "dependencies": {
+                "object-assign": {
+                  "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+                  "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+                  "dev": true
+                },
+                "signal-exit": {
+                  "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+                  "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+                  "dev": true
+                },
+                "string-width": {
+                  "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                  "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                  "dev": true,
+                  "dependencies": {
+                    "code-point-at": {
+                      "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+                      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+                      "dev": true
+                    },
+                    "is-fullwidth-code-point": {
+                      "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+                      "dev": true,
+                      "dependencies": {
+                        "number-is-nan": {
+                          "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+                          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "wide-align": {
+                  "version": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz",
+                  "integrity": "sha1-QO3egCpx/qHwcNo+YtzaLnrdlq0=",
+                  "dev": true
+                }
+              }
+            },
+            "set-blocking": {
+              "version": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+              "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+              "dev": true
+            }
+          }
+        },
+        "once": {
+          "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+          "dev": true
+        },
+        "opener": {
+          "version": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz",
+          "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=",
+          "dev": true
+        },
+        "osenv": {
+          "version": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
+          "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
+          "dev": true,
+          "dependencies": {
+            "os-homedir": {
+              "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+              "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+              "dev": true
+            },
+            "os-tmpdir": {
+              "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+              "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+              "dev": true
+            }
+          }
+        },
+        "path-is-inside": {
+          "version": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+          "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+          "dev": true
+        },
+        "read": {
+          "version": "1.0.7",
+          "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+          "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+          "dev": true,
+          "dependencies": {
+            "mute-stream": {
+              "version": "0.0.5",
+              "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
+              "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
+              "dev": true
+            }
+          }
+        },
+        "read-cmd-shim": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz",
+          "integrity": "sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs=",
+          "dev": true
+        },
+        "read-installed": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
+          "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
+          "dev": true,
+          "dependencies": {
+            "util-extend": {
+              "version": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
+              "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=",
+              "dev": true
+            }
+          }
+        },
+        "read-package-json": {
+          "version": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.0.5.tgz",
+          "integrity": "sha1-+Tpk5kFSnfaKCMZN5GOJ6KP4iEU=",
+          "dev": true,
+          "dependencies": {
+            "json-parse-helpfulerror": {
+              "version": "1.0.3",
+              "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz",
+              "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=",
+              "dev": true,
+              "dependencies": {
+                "jju": {
+                  "version": "https://registry.npmjs.org/jju/-/jju-1.3.0.tgz",
+                  "integrity": "sha1-2t2e8BkkvHKLA/L3l5vb1i96Kqo=",
+                  "dev": true
+                }
+              }
+            }
+          }
+        },
+        "read-package-tree": {
+          "version": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.1.5.tgz",
+          "integrity": "sha1-rOfmOBx2hPlwqqmPx8XStmat2rY=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz",
+          "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=",
+          "dev": true,
+          "dependencies": {
+            "buffer-shims": {
+              "version": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+              "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=",
+              "dev": true
+            },
+            "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=",
+              "dev": true
+            },
+            "isarray": {
+              "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+              "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+              "dev": true
+            },
+            "process-nextick-args": {
+              "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+              "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+              "dev": true
+            },
+            "string_decoder": {
+              "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz",
+              "integrity": "sha1-8G9BFXtmTYYGn4S9vcmw2KsoFmc=",
+              "dev": true
+            },
+            "util-deprecate": {
+              "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+              "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+              "dev": true
+            }
+          }
+        },
+        "readdir-scoped-modules": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz",
+          "integrity": "sha1-n6+jfShr5dksuuve4DDcm19AZ0c=",
+          "dev": true
+        },
+        "realize-package-specifier": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/realize-package-specifier/-/realize-package-specifier-3.0.3.tgz",
+          "integrity": "sha1-0N74gpUrjeP2frpekRmWYScfQfQ=",
+          "dev": true
+        },
+        "request": {
+          "version": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
+          "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
+          "dev": true,
+          "dependencies": {
+            "aws-sign2": {
+              "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+              "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
+              "dev": true
+            },
+            "aws4": {
+              "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+              "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
+              "dev": true
+            },
+            "caseless": {
+              "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+              "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+              "dev": true
+            },
+            "combined-stream": {
+              "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+              "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+              "dev": true,
+              "dependencies": {
+                "delayed-stream": {
+                  "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+                  "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+                  "dev": true
+                }
+              }
+            },
+            "extend": {
+              "version": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz",
+              "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=",
+              "dev": true
+            },
+            "forever-agent": {
+              "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+              "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+              "dev": true
+            },
+            "form-data": {
+              "version": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz",
+              "integrity": "sha1-icNTQAi5fq2ky7FX1Y9vXfAl6uQ=",
+              "dev": true,
+              "dependencies": {
+                "asynckit": {
+                  "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+                  "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+                  "dev": true
+                }
+              }
+            },
+            "har-validator": {
+              "version": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
+              "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
+              "dev": true,
+              "dependencies": {
+                "ajv": {
+                  "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.4.tgz",
+                  "integrity": "sha1-6/OlXUsTLqYP9YR66F0u8GmWC0U=",
+                  "dev": true,
+                  "dependencies": {
+                    "co": {
+                      "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+                      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+                      "dev": true
+                    },
+                    "json-stable-stringify": {
+                      "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+                      "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+                      "dev": true,
+                      "dependencies": {
+                        "jsonify": {
+                          "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+                          "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "har-schema": {
+                  "version": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
+                  "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
+                  "dev": true
+                }
+              }
+            },
+            "hawk": {
+              "version": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+              "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+              "dev": true,
+              "dependencies": {
+                "boom": {
+                  "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+                  "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+                  "dev": true
+                },
+                "cryptiles": {
+                  "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+                  "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+                  "dev": true
+                },
+                "hoek": {
+                  "version": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+                  "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
+                  "dev": true
+                },
+                "sntp": {
+                  "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+                  "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+                  "dev": true
+                }
+              }
+            },
+            "http-signature": {
+              "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+              "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+              "dev": true,
+              "dependencies": {
+                "assert-plus": {
+                  "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+                  "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
+                  "dev": true
+                },
+                "jsprim": {
+                  "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.1.tgz",
+                  "integrity": "sha1-KnJW9wQSop7jZwqspiWZTE3P8lI=",
+                  "dev": true,
+                  "dependencies": {
+                    "extsprintf": {
+                      "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz",
+                      "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=",
+                      "dev": true
+                    },
+                    "json-schema": {
+                      "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+                      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+                      "dev": true
+                    },
+                    "verror": {
+                      "version": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
+                      "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=",
+                      "dev": true
+                    }
+                  }
+                },
+                "sshpk": {
+                  "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz",
+                  "integrity": "sha1-LY1eu0pvqyj/ujf6YqkPSj6lnXc=",
+                  "dev": true,
+                  "dependencies": {
+                    "asn1": {
+                      "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+                      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
+                      "dev": true
+                    },
+                    "assert-plus": {
+                      "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+                      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+                      "dev": true
+                    },
+                    "bcrypt-pbkdf": {
+                      "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+                      "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+                      "dev": true,
+                      "optional": true
+                    },
+                    "dashdash": {
+                      "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+                      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+                      "dev": true
+                    },
+                    "ecc-jsbn": {
+                      "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+                      "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+                      "dev": true,
+                      "optional": true
+                    },
+                    "getpass": {
+                      "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz",
+                      "integrity": "sha1-KD/9n8ElaECHUxHBtg6MQBhxEOY=",
+                      "dev": true
+                    },
+                    "jodid25519": {
+                      "version": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz",
+                      "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=",
+                      "dev": true,
+                      "optional": true
+                    },
+                    "jsbn": {
+                      "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+                      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+                      "dev": true,
+                      "optional": true
+                    },
+                    "tweetnacl": {
+                      "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+                      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+                      "dev": true,
+                      "optional": true
+                    }
+                  }
+                }
+              }
+            },
+            "is-typedarray": {
+              "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+              "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+              "dev": true
+            },
+            "isstream": {
+              "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+              "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+              "dev": true
+            },
+            "json-stringify-safe": {
+              "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+              "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+              "dev": true
+            },
+            "mime-types": {
+              "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz",
+              "integrity": "sha1-9+99l1g/yvO30oK2+LVnnaselO4=",
+              "dev": true,
+              "dependencies": {
+                "mime-db": {
+                  "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz",
+                  "integrity": "sha1-6v/NDk/Gk1z4E02iRuLmw1MFrf8=",
+                  "dev": true
+                }
+              }
+            },
+            "oauth-sign": {
+              "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+              "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+              "dev": true
+            },
+            "performance-now": {
+              "version": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
+              "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
+              "dev": true
+            },
+            "qs": {
+              "version": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
+              "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
+              "dev": true
+            },
+            "safe-buffer": {
+              "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
+              "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=",
+              "dev": true
+            },
+            "stringstream": {
+              "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+              "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
+              "dev": true
+            },
+            "tough-cookie": {
+              "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
+              "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
+              "dev": true,
+              "dependencies": {
+                "punycode": {
+                  "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+                  "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+                  "dev": true
+                }
+              }
+            },
+            "tunnel-agent": {
+              "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+              "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+              "dev": true
+            }
+          }
+        },
+        "retry": {
+          "version": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
+          "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
+          "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
+          "dev": true
+        },
+        "semver": {
+          "version": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+          "dev": true
+        },
+        "sha": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/sha/-/sha-2.0.1.tgz",
+          "integrity": "sha1-YDCCL70smCOUn49y7WQR7lzyWq4=",
+          "dev": true
+        },
+        "slide": {
+          "version": "1.1.6",
+          "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+          "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
+          "dev": true
+        },
+        "sorted-object": {
+          "version": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz",
+          "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=",
+          "dev": true
+        },
+        "sorted-union-stream": {
+          "version": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz",
+          "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=",
+          "dev": true,
+          "dependencies": {
+            "from2": {
+              "version": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz",
+              "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=",
+              "dev": true,
+              "dependencies": {
+                "readable-stream": {
+                  "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+                  "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+                  "dev": true,
+                  "dependencies": {
+                    "core-util-is": {
+                      "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+                      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+                      "dev": true
+                    },
+                    "isarray": {
+                      "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+                      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+                      "dev": true
+                    },
+                    "string_decoder": {
+                      "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+                      "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "stream-iterate": {
+              "version": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.1.1.tgz",
+              "integrity": "sha1-XX0ZeqUryeJxtEVHyeOIsrGzODY=",
+              "dev": true
+            }
+          }
+        },
+        "strip-ansi": {
+          "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "dev": true
+        },
+        "tar": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+          "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
+          "dev": true,
+          "dependencies": {
+            "block-stream": {
+              "version": "0.0.8",
+              "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.8.tgz",
+              "integrity": "sha1-Boj0baK7+c/wxPaCJaDLlcvopGs=",
+              "dev": true
+            }
+          }
+        },
+        "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
+        },
+        "uid-number": {
+          "version": "0.0.6",
+          "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
+          "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=",
+          "dev": true
+        },
+        "umask": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz",
+          "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=",
+          "dev": true
+        },
+        "unique-filename": {
+          "version": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz",
+          "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=",
+          "dev": true,
+          "dependencies": {
+            "unique-slug": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
+              "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
+              "dev": true
+            }
+          }
+        },
+        "unpipe": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+          "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+          "dev": true
+        },
+        "update-notifier": {
+          "version": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.1.0.tgz",
+          "integrity": "sha1-7AweU1NrdmR6JLd8uDlm2TFRI9k=",
+          "dev": true,
+          "dependencies": {
+            "boxen": {
+              "version": "https://registry.npmjs.org/boxen/-/boxen-1.0.0.tgz",
+              "integrity": "sha1-smlLrx9gX3CP8Bd8Ehk7IvKaqqs=",
+              "dev": true,
+              "dependencies": {
+                "ansi-align": {
+                  "version": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz",
+                  "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=",
+                  "dev": true,
+                  "dependencies": {
+                    "string-width": {
+                      "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                      "dev": true,
+                      "dependencies": {
+                        "code-point-at": {
+                          "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+                          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+                          "dev": true
+                        },
+                        "is-fullwidth-code-point": {
+                          "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+                          "dev": true,
+                          "dependencies": {
+                            "number-is-nan": {
+                              "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+                              "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "camelcase": {
+                  "version": "https://registry.npmjs.org/camelcase/-/camelcase-4.0.0.tgz",
+                  "integrity": "sha1-iw+Q1Evl4oG5A7mIc0m5JZXvB/I=",
+                  "dev": true
+                },
+                "cli-boxes": {
+                  "version": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+                  "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
+                  "dev": true
+                },
+                "string-width": {
+                  "version": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz",
+                  "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=",
+                  "dev": true,
+                  "dependencies": {
+                    "is-fullwidth-code-point": {
+                      "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                      "dev": true
+                    }
+                  }
+                },
+                "term-size": {
+                  "version": "https://registry.npmjs.org/term-size/-/term-size-0.1.1.tgz",
+                  "integrity": "sha1-hzYLljlsq1dgljcUzaDQy+7K2co=",
+                  "dev": true,
+                  "dependencies": {
+                    "execa": {
+                      "version": "https://registry.npmjs.org/execa/-/execa-0.4.0.tgz",
+                      "integrity": "sha1-TrZGejaglfq7KXD/nV4/t7zm68M=",
+                      "dev": true,
+                      "dependencies": {
+                        "cross-spawn-async": {
+                          "version": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz",
+                          "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=",
+                          "dev": true,
+                          "dependencies": {
+                            "lru-cache": {
+                              "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
+                              "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=",
+                              "dev": true,
+                              "dependencies": {
+                                "pseudomap": {
+                                  "version": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+                                  "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+                                  "dev": true
+                                },
+                                "yallist": {
+                                  "version": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz",
+                                  "integrity": "sha1-MGxUODXwnuGkyyO3vOmrNByRzdQ=",
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "is-stream": {
+                          "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+                          "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+                          "dev": true
+                        },
+                        "npm-run-path": {
+                          "version": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz",
+                          "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=",
+                          "dev": true
+                        },
+                        "object-assign": {
+                          "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+                          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+                          "dev": true
+                        },
+                        "path-key": {
+                          "version": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz",
+                          "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=",
+                          "dev": true
+                        },
+                        "strip-eof": {
+                          "version": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+                          "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "widest-line": {
+                  "version": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz",
+                  "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=",
+                  "dev": true,
+                  "dependencies": {
+                    "string-width": {
+                      "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                      "dev": true,
+                      "dependencies": {
+                        "code-point-at": {
+                          "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+                          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+                          "dev": true
+                        },
+                        "is-fullwidth-code-point": {
+                          "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+                          "dev": true,
+                          "dependencies": {
+                            "number-is-nan": {
+                              "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+                              "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "chalk": {
+              "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+              "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+              "dev": true,
+              "dependencies": {
+                "ansi-styles": {
+                  "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+                  "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+                  "dev": true
+                },
+                "escape-string-regexp": {
+                  "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+                  "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+                  "dev": true
+                },
+                "has-ansi": {
+                  "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+                  "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+                  "dev": true
+                },
+                "supports-color": {
+                  "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+                  "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+                  "dev": true
+                }
+              }
+            },
+            "configstore": {
+              "version": "https://registry.npmjs.org/configstore/-/configstore-3.0.0.tgz",
+              "integrity": "sha1-4bhmnBgDzMULVF6S+ObnmqgOAZY=",
+              "dev": true,
+              "dependencies": {
+                "dot-prop": {
+                  "version": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.1.1.tgz",
+                  "integrity": "sha1-qEk/C3te7sglJbXHWH+n3nyoWcE=",
+                  "dev": true,
+                  "dependencies": {
+                    "is-obj": {
+                      "version": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+                      "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
+                      "dev": true
+                    }
+                  }
+                },
+                "unique-string": {
+                  "version": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+                  "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+                  "dev": true,
+                  "dependencies": {
+                    "crypto-random-string": {
+                      "version": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+                      "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "is-npm": {
+              "version": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+              "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=",
+              "dev": true
+            },
+            "latest-version": {
+              "version": "https://registry.npmjs.org/latest-version/-/latest-version-3.0.0.tgz",
+              "integrity": "sha1-MQTwCMDDkQhBB/haNEvGHjiXBkk=",
+              "dev": true,
+              "dependencies": {
+                "package-json": {
+                  "version": "https://registry.npmjs.org/package-json/-/package-json-3.1.0.tgz",
+                  "integrity": "sha1-zigZAP6AUhUMxnCcbABsGP2y83k=",
+                  "dev": true,
+                  "dependencies": {
+                    "got": {
+                      "version": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+                      "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+                      "dev": true,
+                      "dependencies": {
+                        "create-error-class": {
+                          "version": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+                          "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+                          "dev": true,
+                          "dependencies": {
+                            "capture-stack-trace": {
+                              "version": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
+                              "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
+                              "dev": true
+                            }
+                          }
+                        },
+                        "duplexer3": {
+                          "version": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+                          "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
+                          "dev": true
+                        },
+                        "get-stream": {
+                          "version": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+                          "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+                          "dev": true
+                        },
+                        "is-redirect": {
+                          "version": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+                          "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
+                          "dev": true
+                        },
+                        "is-retry-allowed": {
+                          "version": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
+                          "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=",
+                          "dev": true
+                        },
+                        "is-stream": {
+                          "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+                          "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+                          "dev": true
+                        },
+                        "lowercase-keys": {
+                          "version": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
+                          "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=",
+                          "dev": true
+                        },
+                        "safe-buffer": {
+                          "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
+                          "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=",
+                          "dev": true
+                        },
+                        "timed-out": {
+                          "version": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+                          "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
+                          "dev": true
+                        },
+                        "unzip-response": {
+                          "version": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
+                          "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
+                          "dev": true
+                        },
+                        "url-parse-lax": {
+                          "version": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+                          "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+                          "dev": true,
+                          "dependencies": {
+                            "prepend-http": {
+                              "version": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+                              "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "registry-auth-token": {
+                      "version": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.1.0.tgz",
+                      "integrity": "sha1-mXwIJW4MeZmDe5DpRNs52KeQJ2s=",
+                      "dev": true,
+                      "dependencies": {
+                        "rc": {
+                          "version": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz",
+                          "integrity": "sha1-xepWS7B6/5/TpbMukGwdOmWUD+o=",
+                          "dev": true,
+                          "dependencies": {
+                            "deep-extend": {
+                              "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz",
+                              "integrity": "sha1-7+QRPQgIX05vlod1mBD4B0aeIlM=",
+                              "dev": true
+                            },
+                            "minimist": {
+                              "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+                              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+                              "dev": true
+                            },
+                            "strip-json-comments": {
+                              "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+                              "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "registry-url": {
+                      "version": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+                      "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+                      "dev": true,
+                      "dependencies": {
+                        "rc": {
+                          "version": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz",
+                          "integrity": "sha1-xepWS7B6/5/TpbMukGwdOmWUD+o=",
+                          "dev": true,
+                          "dependencies": {
+                            "deep-extend": {
+                              "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz",
+                              "integrity": "sha1-7+QRPQgIX05vlod1mBD4B0aeIlM=",
+                              "dev": true
+                            },
+                            "minimist": {
+                              "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+                              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+                              "dev": true
+                            },
+                            "strip-json-comments": {
+                              "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+                              "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "lazy-req": {
+              "version": "https://registry.npmjs.org/lazy-req/-/lazy-req-2.0.0.tgz",
+              "integrity": "sha1-yUUKNj7N2i5vDHATKtTzf48G8rQ=",
+              "dev": true
+            },
+            "semver-diff": {
+              "version": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+              "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+              "dev": true
+            },
+            "xdg-basedir": {
+              "version": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+              "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
+              "dev": true
+            }
+          }
+        },
+        "uuid": {
+          "version": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
+          "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=",
+          "dev": true
+        },
+        "validate-npm-package-license": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+          "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+          "dev": true,
+          "dependencies": {
+            "spdx-correct": {
+              "version": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+              "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+              "dev": true,
+              "dependencies": {
+                "spdx-license-ids": {
+                  "version": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.0.tgz",
+                  "integrity": "sha1-tUndD2Pct0Whfi6joHQC4OMy0eI=",
+                  "dev": true
+                }
+              }
+            },
+            "spdx-expression-parse": {
+              "version": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.2.tgz",
+              "integrity": "sha1-1SsUtelnB3FECvIlvLVjEirEUvY=",
+              "dev": true,
+              "dependencies": {
+                "spdx-exceptions": {
+                  "version": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-1.0.4.tgz",
+                  "integrity": "sha1-IguEI5EZrpBFqJLbgag/TOFvgP0=",
+                  "dev": true
+                },
+                "spdx-license-ids": {
+                  "version": "1.2.0",
+                  "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.0.tgz",
+                  "integrity": "sha1-tUndD2Pct0Whfi6joHQC4OMy0eI=",
+                  "dev": true
+                }
+              }
+            }
+          }
+        },
+        "validate-npm-package-name": {
+          "version": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
+          "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+          "dev": true,
+          "dependencies": {
+            "builtins": {
+              "version": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
+              "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
+              "dev": true
+            }
+          }
+        },
+        "which": {
+          "version": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
+          "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+          "dev": true,
+          "dependencies": {
+            "isexe": {
+              "version": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+              "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+              "dev": true
+            }
+          }
+        },
+        "wrappy": {
+          "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+          "dev": true
+        },
+        "write-file-atomic": {
+          "version": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.3.tgz",
+          "integrity": "sha1-gx3SLUkb3BNRgLuZag6z+L9Yd5E=",
+          "dev": true
+        }
+      }
+    },
+    "npm-package-arg": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-4.2.1.tgz",
+      "integrity": "sha1-WTMD/eqF98Qid18X+et2cPaA4+w=",
+      "dev": true
+    },
+    "npm-registry-client": {
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-7.5.0.tgz",
+      "integrity": "sha1-D23W5dEUJM+pn85bkw/q8JtPfwQ=",
+      "dev": true
+    },
+    "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
+    },
+    "npmlog": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+      "dev": true
+    },
+    "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=",
+      "dev": true
+    },
+    "oauth-sign": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+      "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+      "dev": true
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true
+    },
+    "object.omit": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
+      "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
+      "dev": true,
+      "optional": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true
+    },
+    "onetime": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+      "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
+      "dev": true
+    },
+    "open": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz",
+      "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=",
+      "dev": true
+    },
+    "opener": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz",
+      "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=",
+      "dev": true
+    },
+    "optimist": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+      "dev": true,
+      "dependencies": {
+        "wordwrap": {
+          "version": "0.0.3",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+          "dev": true
+        }
+      }
+    },
+    "optionator": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+      "dev": true
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+      "dev": true
+    },
+    "os-locale": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+      "dev": true
+    },
+    "os-name": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/os-name/-/os-name-1.0.3.tgz",
+      "integrity": "sha1-GzefZINa98Wn9JizV8uVIVwVnt8=",
+      "dev": true
+    },
+    "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=",
+      "dev": true
+    },
+    "osenv": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
+      "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
+      "dev": true
+    },
+    "osx-release": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/osx-release/-/osx-release-1.1.0.tgz",
+      "integrity": "sha1-8heRGigTaUmvG/kwiyQeJzfTzWw=",
+      "dev": true,
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "otr": {
+      "version": "0.2.16",
+      "resolved": "https://registry.npmjs.org/otr/-/otr-0.2.16.tgz",
+      "integrity": "sha1-BKdTRPUi38sHeMVDjA9V5p0+i8E=",
+      "dev": true
+    },
+    "output-file-sync": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz",
+      "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=",
+      "dev": true
+    },
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+      "dev": true
+    },
+    "package-json": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz",
+      "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=",
+      "dev": true
+    },
+    "parse-glob": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
+      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+      "dev": true,
+      "optional": true
+    },
+    "parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "dev": true
+    },
+    "path-exists": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+      "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=",
+      "dev": true
+    },
+    "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.5",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+      "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+      "dev": true
+    },
+    "path-to-regexp": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+      "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+      "dev": true,
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        }
+      }
+    },
+    "path-type": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+      "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+      "dev": true
+    },
+    "performance-now": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
+      "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
+      "dev": true
+    },
+    "pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+      "dev": true
+    },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true
+    },
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "dev": true
+    },
+    "pluggable.js": {
+      "version": "git+https://github.com/jcbrand/pluggable.js.git#8f8c8235816f44cda0f855d6ca879445aaa486a1",
+      "dev": true
+    },
+    "pluralize": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
+      "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=",
+      "dev": true
+    },
+    "po2json": {
+      "version": "0.4.5",
+      "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz",
+      "integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=",
+      "dev": true
+    },
+    "portfinder": {
+      "version": "1.0.13",
+      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz",
+      "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=",
+      "dev": true
+    },
+    "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
+    },
+    "prepend-http": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+      "dev": true
+    },
+    "preserve": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
+      "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
+      "dev": true,
+      "optional": true
+    },
+    "private": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz",
+      "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+      "dev": true
+    },
+    "progress": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+      "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+      "dev": true
+    },
+    "promise": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+      "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": "1.4.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
+      "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
+      "dev": true
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+      "dev": true
+    },
+    "random-string": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/random-string/-/random-string-0.1.2.tgz",
+      "integrity": "sha1-LWr7YHZlExasW4pi1jEG2zHrjUQ=",
+      "dev": true
+    },
+    "randomatic": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
+      "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "optional": true,
+          "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,
+              "optional": true
+            }
+          }
+        },
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "optional": true
+        }
+      }
+    },
+    "rc": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz",
+      "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=",
+      "dev": true,
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "read-all-stream": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz",
+      "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=",
+      "dev": true
+    },
+    "read-pkg": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+      "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+      "dev": true
+    },
+    "read-pkg-up": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+      "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+      "dev": true
+    },
+    "readable-stream": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+      "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+      "dev": true
+    },
+    "readdirp": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
+      "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
+      "dev": true,
+      "optional": true
+    },
+    "readline2": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
+      "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=",
+      "dev": true
+    },
+    "rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+      "dev": true
+    },
+    "redent": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+      "dev": true
+    },
+    "redeyed": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz",
+      "integrity": "sha1-6WwZO0DAgWsArshCaY5hGF5VSYo=",
+      "dev": true,
+      "dependencies": {
+        "esprima": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz",
+          "integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k=",
+          "dev": true
+        }
+      }
+    },
+    "regenerate": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz",
+      "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=",
+      "dev": true
+    },
+    "regenerator-runtime": {
+      "version": "0.10.5",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
+      "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
+      "dev": true
+    },
+    "regenerator-transform": {
+      "version": "0.9.11",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz",
+      "integrity": "sha1-On0GdSDLe3F2dp61/4aGkb7+EoM=",
+      "dev": true
+    },
+    "regex-cache": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz",
+      "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=",
+      "dev": true,
+      "optional": true
+    },
+    "regexpu-core": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
+      "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
+      "dev": true
+    },
+    "registry-auth-token": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz",
+      "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=",
+      "dev": true
+    },
+    "registry-url": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+      "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+      "dev": true
+    },
+    "regjsgen": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+      "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+      "dev": true
+    },
+    "regjsparser": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+      "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+      "dev": true,
+      "dependencies": {
+        "jsesc": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+          "dev": true
+        }
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz",
+      "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=",
+      "dev": true,
+      "optional": true
+    },
+    "repeat-element": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
+      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
+      "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
+    },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+      "dev": true
+    },
+    "request": {
+      "version": "2.81.0",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
+      "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
+      "dev": true
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+      "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
+    },
+    "requirejs": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.3.tgz",
+      "integrity": "sha1-qln9OgKH6vQHlZoTgigES13WpqM=",
+      "dev": true
+    },
+    "requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz",
+      "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=",
+      "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
+    },
+    "restore-cursor": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
+      "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
+      "dev": true
+    },
+    "retry": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
+      "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
+      "dev": true
+    },
+    "right-align": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+      "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
+      "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
+      "dev": true
+    },
+    "run-async": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
+      "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=",
+      "dev": true
+    },
+    "run-headless-chromium": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/run-headless-chromium/-/run-headless-chromium-0.1.1.tgz",
+      "integrity": "sha1-eeYQoA3wIosztrNK5qP8ps6eWv8=",
+      "dev": true,
+      "dependencies": {
+        "rimraf": {
+          "version": "2.2.8",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+          "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+          "dev": true
+        },
+        "which": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmjs.org/which/-/which-1.0.5.tgz",
+          "integrity": "sha1-VjDWgZ3aaS8UZEYueVbLQsCEJzk=",
+          "dev": true
+        }
+      }
+    },
+    "rx": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
+      "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=",
+      "dev": true
+    },
+    "rx-lite": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
+      "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=",
+      "dev": true
+    },
+    "safe-buffer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+      "dev": true
+    },
+    "samsam": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz",
+      "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=",
+      "dev": true
+    },
+    "semver": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+      "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+      "dev": true
+    },
+    "semver-diff": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+      "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+      "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=",
+      "dev": true
+    },
+    "set-immediate-shim": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+      "dev": true,
+      "optional": true
+    },
+    "shallow-clone": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
+      "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz",
+          "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=",
+          "dev": true
+        },
+        "lazy-cache": {
+          "version": "0.2.7",
+          "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz",
+          "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=",
+          "dev": true
+        }
+      }
+    },
+    "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
+    },
+    "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
+    },
+    "shelljs": {
+      "version": "0.7.8",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
+      "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+      "dev": true
+    },
+    "sinon": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.8.tgz",
+      "integrity": "sha1-Md4G/tj7o6Zx5XbdltClhjeW8lw=",
+      "dev": true
+    },
+    "slash": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+      "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+      "dev": true
+    },
+    "sleep": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz",
+      "integrity": "sha1-vk0XxXk2DgfgTtgXK6KxCmkFTfM=",
+      "dev": true,
+      "optional": true
+    },
+    "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
+    },
+    "slide": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+      "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
+      "dev": true
+    },
+    "sntp": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+      "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+      "dev": true
+    },
+    "snyk": {
+      "version": "1.36.2",
+      "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.36.2.tgz",
+      "integrity": "sha1-iL5yqNp1oC6SDyvn9YMEArXg60E=",
+      "dev": true,
+      "dependencies": {
+        "configstore": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/configstore/-/configstore-1.4.0.tgz",
+          "integrity": "sha1-w1eB0FAdJowlxUuLF/YkDopPsCE=",
+          "dev": true,
+          "dependencies": {
+            "uuid": {
+              "version": "2.0.3",
+              "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+              "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
+              "dev": true
+            }
+          }
+        },
+        "es6-promise": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+          "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
+          "dev": true
+        },
+        "got": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz",
+          "integrity": "sha1-5dDtSvVfw+701WAHdp2YGSvLLso=",
+          "dev": true,
+          "dependencies": {
+            "object-assign": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
+              "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=",
+              "dev": true
+            }
+          }
+        },
+        "inquirer": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.0.3.tgz",
+          "integrity": "sha1-6+OglIVxvMRszMvi+bzsJR6YS9A=",
+          "dev": true
+        },
+        "latest-version": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-1.0.1.tgz",
+          "integrity": "sha1-cs/Ebj6NG+ZR4eu1Tqn26pbzdLs=",
+          "dev": true
+        },
+        "mute-stream": {
+          "version": "0.0.6",
+          "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz",
+          "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=",
+          "dev": true
+        },
+        "package-json": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/package-json/-/package-json-1.2.0.tgz",
+          "integrity": "sha1-yOysCUInzfdqMWh07QXifMk5oOA=",
+          "dev": true
+        },
+        "repeating": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz",
+          "integrity": "sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw=",
+          "dev": true
+        },
+        "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
+        },
+        "timed-out": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz",
+          "integrity": "sha1-84sK6B03R9YoAB9B2vxlKs5nHAo=",
+          "dev": true
+        },
+        "update-notifier": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-0.5.0.tgz",
+          "integrity": "sha1-B7XcIGazYnqztPUwEw9+3doHpMw=",
+          "dev": true
+        }
+      }
+    },
+    "snyk-config": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/snyk-config/-/snyk-config-1.0.1.tgz",
+      "integrity": "sha1-8nrsJJiyQCescZIUAmUhWRERUI8=",
+      "dev": true
+    },
+    "snyk-gradle-plugin": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-1.0.2.tgz",
+      "integrity": "sha512-mFGMmSLj3lIBZay0pJ+8+Y5QB6B55ywOaLYA85TJHQfsoRp3A5LpQvTd6jBWDCo+oZw65wrDQxu4EsGEew+Y6Q==",
+      "dev": true
+    },
+    "snyk-module": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-1.8.1.tgz",
+      "integrity": "sha1-MdUID7HA39b6hWfdNKUj/QK/H8o=",
+      "dev": true
+    },
+    "snyk-mvn-plugin": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-1.0.0.tgz",
+      "integrity": "sha512-23K3VGMKS3W2l53cyQyb+EnbScKW3idUmylKRJRYEcK95ACzghdpI7tU4XBv5FH9LX60QybOskHYs0Phkim2Aw==",
+      "dev": true
+    },
+    "snyk-policy": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/snyk-policy/-/snyk-policy-1.7.1.tgz",
+      "integrity": "sha1-5BO2vUr2BQxeX0RSh5CeTpigmyI=",
+      "dev": true,
+      "dependencies": {
+        "es6-promise": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+          "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
+          "dev": true
+        }
+      }
+    },
+    "snyk-python-plugin": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/snyk-python-plugin/-/snyk-python-plugin-1.2.2.tgz",
+      "integrity": "sha512-JVQS77d7hRtsqGFPe7rVk8EsgqEPhBYJvtfLsRZzrLdk++T161rUVxXdYkWpUdrUO+uCUENqQX1ypi65Aj6tbg==",
+      "dev": true
+    },
+    "snyk-recursive-readdir": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/snyk-recursive-readdir/-/snyk-recursive-readdir-2.0.0.tgz",
+      "integrity": "sha1-XLWelGmBaeAgWmDn1qUG0LTVL/M=",
+      "dev": true,
+      "dependencies": {
+        "minimatch": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+          "integrity": "sha1-DzmKcwDqRB6cNIyD2Yq4ydv5xAo=",
+          "dev": true
+        }
+      }
+    },
+    "snyk-resolve": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/snyk-resolve/-/snyk-resolve-1.0.0.tgz",
+      "integrity": "sha1-u+kZbTf1fDklHmvnXM3Vsgl+maI=",
+      "dev": true
+    },
+    "snyk-resolve-deps": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/snyk-resolve-deps/-/snyk-resolve-deps-1.7.0.tgz",
+      "integrity": "sha1-E3Q6BYQ33/iQuq9DfDM8lmp0PLY=",
+      "dev": true,
+      "dependencies": {
+        "ansicolors": {
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
+          "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=",
+          "dev": true
+        },
+        "es6-promise": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+          "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
+          "dev": true
+        },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "snyk-sbt-plugin": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/snyk-sbt-plugin/-/snyk-sbt-plugin-1.0.2.tgz",
+      "integrity": "sha512-j+vVwqlrbMr6LSEM6sLOCykdOsRXhhJ23y8FdJHo22UfTVbRMCr6MFLrlMkNmsHcWhIFuzgqIdjkP5LMRdRktA==",
+      "dev": true
+    },
+    "snyk-tree": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/snyk-tree/-/snyk-tree-1.0.0.tgz",
+      "integrity": "sha1-D7cxdtvzLngvGRAClBYESPkRHMg=",
+      "dev": true
+    },
+    "snyk-try-require": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/snyk-try-require/-/snyk-try-require-1.2.0.tgz",
+      "integrity": "sha1-MPwrEcBwZFke41eAyCa+kTEvIUQ=",
+      "dev": true,
+      "dependencies": {
+        "es6-promise": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+          "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
+          "dev": true
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+      "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+      "dev": true
+    },
+    "source-map-support": {
+      "version": "0.4.15",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz",
+      "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=",
+      "dev": true
+    },
+    "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
+    },
+    "spdx-correct": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+      "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+      "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=",
+      "dev": true
+    },
+    "spdx-license-ids": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+      "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
+      "dev": true
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "sshpk": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
+      "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+          "dev": true
+        }
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+      "dev": true
+    },
+    "string_decoder": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+      "dev": true
+    },
+    "string-length": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz",
+      "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=",
+      "dev": true
+    },
+    "string-width": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "dev": true
+    },
+    "string.prototype.codepointat": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz",
+      "integrity": "sha1-aybpvTr8qnvjtCabUm3huCAArHg=",
+      "dev": true
+    },
+    "stringstream": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+      "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
+      "dev": true
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "dev": true
+    },
+    "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-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+      "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=",
+      "dev": true
+    },
+    "strophe.js": {
+      "version": "1.2.14",
+      "resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.2.14.tgz",
+      "integrity": "sha1-fO7sUbMnLMXGxq53R0eApYQPGqc=",
+      "dev": true
+    },
+    "strophejs-plugin-disco": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/strophejs-plugin-disco/-/strophejs-plugin-disco-0.0.1.tgz",
+      "integrity": "sha1-JVoXdsZDFOnZ5OxS1XM+6vUWx/k=",
+      "dev": true
+    },
+    "strophejs-plugin-ping": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/strophejs-plugin-ping/-/strophejs-plugin-ping-0.0.1.tgz",
+      "integrity": "sha1-NXEmxTZZSwZmjhh4c4Ey+sNciJY=",
+      "dev": true
+    },
+    "strophejs-plugin-register": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/strophejs-plugin-register/-/strophejs-plugin-register-0.0.1.tgz",
+      "integrity": "sha1-0oXTPH1oK40co3ZHgJrCZ8CgiaM=",
+      "dev": true
+    },
+    "strophejs-plugin-rsm": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/strophejs-plugin-rsm/-/strophejs-plugin-rsm-0.0.1.tgz",
+      "integrity": "sha1-zBPqgHWmbJ2rdt04G37d/bG64Hs=",
+      "dev": true
+    },
+    "strophejs-plugin-vcard": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/strophejs-plugin-vcard/-/strophejs-plugin-vcard-0.0.1.tgz",
+      "integrity": "sha1-BDfoyYdZr5fKJ07GuAC1W52IciM=",
+      "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
+    },
+    "table": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
+      "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
+      "dev": true,
+      "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.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz",
+          "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=",
+          "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
+        }
+      }
+    },
+    "temp": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
+      "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=",
+      "dev": true,
+      "dependencies": {
+        "rimraf": {
+          "version": "2.2.8",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+          "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+          "dev": true
+        }
+      }
+    },
+    "tempfile": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz",
+      "integrity": "sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=",
+      "dev": true,
+      "dependencies": {
+        "uuid": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+          "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
+          "dev": true
+        }
+      }
+    },
+    "term-size": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+      "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+      "dev": true
+    },
+    "text": {
+      "version": "github:requirejs/text#d04de4ffd7bf5ba6cb80cdca2d40d4f6f52a1b1f",
+      "dev": true
+    },
+    "text-encoding": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+      "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+      "dev": true
+    },
+    "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
+    },
+    "then-fs": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/then-fs/-/then-fs-2.0.0.tgz",
+      "integrity": "sha1-cveS3Z0xcFqRrhnr/Piz+WjIHaI=",
+      "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
+    },
+    "timed-out": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz",
+      "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=",
+      "dev": true
+    },
+    "tmp": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz",
+      "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=",
+      "dev": true
+    },
+    "to-fast-properties": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+      "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+      "dev": true
+    },
+    "tough-cookie": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
+      "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
+      "dev": true
+    },
+    "traverse": {
+      "version": "0.6.6",
+      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
+      "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=",
+      "dev": true
+    },
+    "trim-newlines": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+      "dev": true
+    },
+    "trim-right": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+      "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+      "dev": true
+    },
+    "tryit": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
+      "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
+      "dev": true
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true,
+      "optional": 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
+    },
+    "type-detect": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz",
+      "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=",
+      "dev": true
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "uglify-es": {
+      "version": "3.0.24",
+      "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.24.tgz",
+      "integrity": "sha512-5BfuaLWF0idSXopD0iAK6osMvyo1jEyuQ8MtSpOdLvqUDjlfDDf18qEyz3M7athqorybUuAoKpKW1DhFIhxP2w==",
+      "dev": true,
+      "dependencies": {
+        "commander": {
+          "version": "2.9.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+          "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+          "dev": true
+        }
+      }
+    },
+    "undefsafe": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-0.0.3.tgz",
+      "integrity": "sha1-7Mo6A+VrmvFzhbqsgSrIO5lKli8=",
+      "dev": true
+    },
+    "underscore": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
+      "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
+      "dev": true
+    },
+    "underscore.string": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz",
+      "integrity": "sha1-gGmSYzZl1eX8tNsfs6hi62jp5to=",
+      "dev": true
+    },
+    "union": {
+      "version": "0.4.6",
+      "resolved": "https://registry.npmjs.org/union/-/union-0.4.6.tgz",
+      "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=",
+      "dev": true,
+      "dependencies": {
+        "qs": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz",
+          "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=",
+          "dev": true
+        }
+      }
+    },
+    "unzip-response": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz",
+      "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=",
+      "dev": true
+    },
+    "update-notifier": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz",
+      "integrity": "sha1-j5LFFUgr1oMbfJMBPnD4dVLHz1o=",
+      "dev": true,
+      "dependencies": {
+        "ansi-align": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz",
+          "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=",
+          "dev": true
+        },
+        "boxen": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz",
+          "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=",
+          "dev": true
+        },
+        "camelcase": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+          "dev": true
+        }
+      }
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "dev": true,
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+          "dev": true
+        }
+      }
+    },
+    "url-join": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.2.tgz",
+      "integrity": "sha1-wHJ1aWetJLi1nldBVRyqx49QuLc=",
+      "dev": true
+    },
+    "url-parse-lax": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+      "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+      "dev": true
+    },
+    "user-home": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
+      "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
+      "dev": true
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
+    },
+    "uuid": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
+      "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
+      "dev": true
+    },
+    "v8flags": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz",
+      "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=",
+      "dev": true
+    },
+    "valid-url": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
+      "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=",
+      "dev": true
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+      "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+      "dev": true
+    },
+    "validator": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
+      "integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g=",
+      "dev": true
+    },
+    "verror": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
+      "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=",
+      "dev": true
+    },
+    "wait-until-promise": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wait-until-promise/-/wait-until-promise-1.0.0.tgz",
+      "integrity": "sha1-03Uy1bfv9oJwIMtE2OyqIzyWeMU=",
+      "dev": true
+    },
+    "webworker-threads": {
+      "version": "0.4.13",
+      "resolved": "https://registry.npmjs.org/webworker-threads/-/webworker-threads-0.4.13.tgz",
+      "integrity": "sha1-1zvf0AIb9wkxy4XCTMP0zvvrH5g=",
+      "dependencies": {
+        "nan": {
+          "version": "0.8.0",
+          "resolved": "https://registry.npmjs.org/nan/-/nan-0.8.0.tgz",
+          "integrity": "sha1-AiqPpen+hCCWSsH7PclOF/RJ9f0="
+        }
+      }
+    },
+    "which": {
+      "version": "1.2.14",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
+      "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+      "dev": true
+    },
+    "which-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "dev": true
+    },
+    "wide-align": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
+      "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+      "dev": true
+    },
+    "widest-line": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz",
+      "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=",
+      "dev": true
+    },
+    "win-release": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz",
+      "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=",
+      "dev": true
+    },
+    "window-size": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
+      "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=",
+      "dev": true
+    },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "write": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+      "dev": true
+    },
+    "write-file-atomic": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
+      "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
+      "dev": true
+    },
+    "xdg-basedir": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz",
+      "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=",
+      "dev": true
+    },
+    "xss": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/xss/-/xss-0.3.3.tgz",
+      "integrity": "sha1-oBQ2De4QMXMx+edCWBQfftA/x4Q=",
+      "dev": true
+    },
+    "xtend": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+      "dev": true
+    },
+    "xvfb": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/xvfb/-/xvfb-0.2.3.tgz",
+      "integrity": "sha1-VYipaHVFk5E/M8DA4su3N0EjWDI=",
+      "dev": true
+    },
+    "y18n": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+      "dev": true
+    },
+    "yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "dev": true
+    },
+    "yargs": {
+      "version": "3.15.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.15.0.tgz",
+      "integrity": "sha1-PZRG7yH7N5GzmFaQZi5LloPH8YE=",
+      "dev": true,
+      "dependencies": {
+        "camelcase": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+          "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+          "dev": true
+        }
+      }
+    },
+    "yargs-parser": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz",
+      "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=",
+      "dev": true,
+      "dependencies": {
+        "camelcase": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+          "dev": true
+        }
+      }
+    }
+  }
+}

+ 8 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "converse.js",
-  "version": "3.1.1",
+  "version": "3.2.0",
   "description": "Browser based XMPP instant messaging client",
   "main": "main.js",
   "directories": {
@@ -34,12 +34,16 @@
   "devDependencies": {
     "almond": "~0.3.3",
     "awesomplete-avoid-xss": "^1.1.2",
+    "babel-cli": "^6.18.0",
+    "babel-preset-env": "^1.5.2",
+    "babel-preset-latest": "^6.16.0",
     "backbone": "1.3.3",
     "backbone.browserStorage": "0.0.3",
     "backbone.overview": "0.0.3",
     "bootstrap": "^3.3.7",
     "bourbon": "^4.3.2",
     "clean-css-cli": "^4.0.10",
+    "emojione": "^3.0.3",
     "es6-promise": "^4.1.0",
     "eslint": "^3.19.0",
     "eslint-plugin-lodash": "^2.3.3",
@@ -53,7 +57,6 @@
     "jasmine-core": "2.6.4",
     "jed": "0.5.4",
     "jquery": "2.2.3",
-    "jquery-easing": "0.0.1",
     "jquery.browser": ">=0.1.0",
     "jshint": "^2.9.4",
     "lodash": "4.17.4",
@@ -74,7 +77,9 @@
     "strophejs-plugin-rsm": "0.0.1",
     "strophejs-plugin-vcard": "0.0.1",
     "text": "requirejs/text#2.0.15",
-    "wait-until-promise": "^1.0.0"
+    "uglify-es": "^3.0.24",
+    "wait-until-promise": "^1.0.0",
+    "xss": "^0.3.3"
   },
   "dependencies": {}
 }

+ 103 - 78
sass/_chatbox.scss

@@ -100,10 +100,11 @@
         }
         .chat-title {
             color: $chat-head-text-color;
-            line-height: 15px;
             display: block;
-            text-overflow: ellipsis;
+            line-height: 15px;
             overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
             a {
                 color: $chat-head-text-color;
                 width: 100%;
@@ -156,24 +157,25 @@
                 font-style: italic;
             }
             .chat-message {
-                margin: 0.3em;
-                span {
-                    &.chat-msg-author {
-                        font-weight: bold;
-                        white-space: nowrap;
-                        float: left;
-                        text-overflow: ellipsis;
-                        overflow: hidden;
-                    }
-                    &.chat-msg-them {
-                        color: $message-them-color;
-                    }
-                    &.chat-msg-me {
-                        color: $link-color;
-                    }
-                    &.chat-msg-content {
-                        max-width: 100%;
-                        word-wrap: break-word;
+                overflow: auto; // Ensures that content stays inside
+                .chat-msg-author {
+                    font-weight: bold;
+                    white-space: nowrap;
+                    float: left;
+                    text-overflow: ellipsis;
+                    overflow: hidden;
+                }
+                .chat-msg-them {
+                    color: $message-them-color;
+                }
+                .chat-msg-me {
+                    color: $link-color;
+                }
+                .chat-msg-content {
+                    max-width: 100%;
+                    word-wrap: break-word;
+                    .emojione {
+                        margin-bottom: -6px;
                     }
                 }
             }
@@ -272,11 +274,6 @@
                     text-decoration: none;
                     text-shadow: none;
                 }
-                .toolbar-picker-panel {
-                    a {
-                        color: $link-color;
-                    }
-                }
                 .chat-toolbar-text {
                     font-size: 12px;
                     padding-right: 3px;
@@ -284,7 +281,7 @@
                 .unencrypted a,
                 .unencrypted {
                     color: $text-color;
-                    .toolbar-picker-panel {
+                    .toolbar-menu {
                         a {
                             color: $link-color;
                         }
@@ -301,73 +298,101 @@
                 .toggle-occupants,
                 .toggle-clear,
                 .toggle-otr {
+
                     float: right;
                 }
                 li {
+                    cursor: pointer;
                     display: inline-block;
                     list-style: none;
-                    padding: 0 3px 0 3px;
-                    cursor: pointer;
                     margin-top: 1px;
-                }
-                li:hover {
-                    cursor: pointer;
-                }
-                ul {
-                    background: #fff;
-                    bottom: 100%;
-                    box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
-                    display: none;
-                    font-size: 12px;
-                    margin: 0;
-                    position: absolute;
-                    right: 0;
-                    li {
+                    padding: 0 3px 0 3px;
+                    &:hover {
                         cursor: pointer;
-                        list-style: none;
-                        position: relative;
-                        a:hover {
-                            color: #8f2831;
-                        }
                     }
-                }
-                li {
-                    margin-left: 0;
-                }
-                .toggle-smiley {
-                    color: $text-color;
-                    padding-left: 5px;
-                    ul {
-                        left: 0;
-                        li {
-                            font-size: $font-size;
-                            padding: 5px;
-                            z-index: 98;
+                    .toolbar-menu {
+                        background-color: #fff;
+                        bottom: 100%;
+                        box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
+                        font-size: 12px;
+                        margin: 0;
+                        position: absolute;
+                        right: 0;
+                        a {
+                            color: $link-color;
                         }
-                        li:hover {
-                            background-color: $highlight-color;
+                        ul {
+                            &.emoji-picker {
+                                height: $emoji-picker-height;
+                                overflow: scroll;
+                                padding: 0.5em;
+                            }
+                            &.emoji-toolbar {
+                                /* offset-x | offset-y | blur-radius | spread-radius | color */
+                                box-shadow: 0 -1px 2px 0 rgba(0, 0, 0, 0.4);
+                            }
+                            &.emoji-toolbar {
+                                overflow: hidden;
+                                left: 0;
+                                .picked {
+                                    background-color: $highlight-color;
+                                }
+                                li {
+                                    height: $emoji_height + 2*5px;
+                                    padding: 4px;
+                                    z-index: 98;
+                                    &.emoji {
+                                        a {
+                                            font-size: $font-size-huge;
+                                        }
+                                    }
+                                }
+                            }
+                            li {
+                                &.insert-emoji {
+                                    padding: 0.3em;
+                                    &:hover {
+                                        background-color: $highlight-color;
+                                    }
+                                }
+                                margin-left: 0;
+                                cursor: pointer;
+                                list-style: none;
+                                position: relative;
+                                a:hover {
+                                    color: #8f2831;
+                                }
+                            }
                         }
                     }
-                }
-                .toggle-otr {
-                    ul {
-                        li {
-                            padding: 7px;
-                            background-color: white;
-                            display: block;
+                    &.toggle-toolbar-menu {
+                        color: $text-color;
+                    }
+                    &.toggle-smiley {
+                        padding-left: 5px;
+                        .emoji-toolbar {
+                            .emoji-category-picker,
+                            .emoji-skintone-picker {
+                                li:hover {
+                                    background-color: $highlight-color;
+                                }
+                            }
+                        }
+                    }
+                    &.toggle-otr {
+                        ul {
                             z-index: 99;
-                            a {
-                                -moz-transition: background-color 0.2s ease-in-out;
-                                -webkit-transition: background-color 0.2s ease-in-out;
-                                transition: background-color 0.2s ease-in-out;
+                            li {
+                                &:hover {
+                                    background-color: $highlight-color;
+                                }
                                 display: block;
-                                padding: 1px;
-                                text-decoration: none;
+                                padding: 7px;
+                                a {
+                                    display: block;
+                                }
                             }
                         }
-                        li:hover {
-                            background-color: $highlight-color;
-                        }
                     }
                 }
             }

+ 17 - 11
sass/_chatrooms.scss

@@ -19,17 +19,23 @@
                 color: $chatroom-head-color;
             }
         }
-
-        .chatroom-description {
-            color: white;
-            font-size: 80%;
-            font-style: italic;
-            height: 1.3em;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            margin: 0;
-            margin-top: 0.3em;
+        .chat-title {
+            color: $chatroom-color-lightest;
+            .chatroom-name {
+                color: white;
+            }
+            .chatroom-jid {
+                font-size: $font-size-small;
+            }
+            .chatroom-description {
+                color: white;
+                font-size: 80%;
+                font-style: italic;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                white-space: nowrap;
+                margin: 0.3em 0;
+            }
         }
     }
 

+ 17 - 11
sass/_controlbox.scss

@@ -152,7 +152,8 @@
                         padding-bottom: 0.5em;
                         text-shadow: 0 1px 0 $text-shadow-color;
                     }
-                    dd.available-chatroom {
+                    .available-chatroom,
+                    .open-chatroom {
                         border: none;
                         clear: both;
                         color: $text-color;
@@ -165,6 +166,7 @@
                             color: $dark-link-color;
                         }
                         &.unread-msgs {
+                            .available-room,
                             .open-room {
                                 max-width: 55%;
                                 width: auto;
@@ -178,14 +180,18 @@
                                 }
                             }
                             &.open-room {
-                                float: left;
                                 width: 68%;
+                                float: left;
                                 overflow: hidden;
                                 text-overflow: ellipsis;
                                 white-space: nowrap;
                                 padding-right: 0.5em;
                             }
+                            &.available-room {
+                                width: 85%;
+                            }
                         }
+                        .add-bookmark,
                         .remove-bookmark {
                             &.button-on {
                                 color: $link-color;
@@ -236,7 +242,6 @@
                     position: absolute;
                     left: 0;
                     top: 0;
-                    border: 1px solid $light-background-border-color;
                     width: 100%;
                     z-index: 21;
                     background-color: $light-background-color;
@@ -246,14 +251,16 @@
                 }
             }
 
-            /* Custom addition for CSP */
             dd.search-xmpp {
-                display: none;
-                width: 100%;
-            }
-
-            dd.search-xmpp ul {
-                box-shadow: 1px 4px 10px 1px rgba(0, 0, 0, 0.4);
+                height: 0;
+                .contact-form-container {
+                    position: absolute;
+                    z-index: 22;
+                    form {
+                        box-shadow: 1px 4px 10px 1px rgba(0, 0, 0, 0.4);
+                        background-color: white;
+                    }
+                }
                 li:hover {
                     background-color: $light-background-color;
                 }
@@ -420,7 +427,6 @@
         }
 
         .add-xmpp-contact {
-            background: none;
             padding: 1em 0.5em;
             input {
                 margin: 0 0 1rem;

+ 11 - 3
sass/_core.scss

@@ -52,9 +52,6 @@
         -webkit-touch-callout: none;
         @include user-select(none);
     }
-    .emoticon {
-        font-size: $font-size;
-    }
 
     @keyframes fadein {
         0% { opacity: 0 }
@@ -77,6 +74,10 @@
         opacity: 0;
         display: none;
     }
+    .collapsed {
+        height: 0;
+        overflow: hidden;
+    }
 
     .locked {
         padding-right: 22px;
@@ -91,6 +92,10 @@
         }
     }
 
+    .emojione {
+        height: $emoji_height;
+    }
+
     .spinner {
         @include animation(spin 2s infinite, linear);
         display: block;
@@ -164,6 +169,9 @@
     .activated {
         display: block !important;
     }
+    .pure-form-message {
+        padding: 0.5em 0;
+    }
     .pure-button {
         border-radius: $chatbox-border-radius;
     }

+ 45 - 0
sass/converse/_chatbox.scss

@@ -12,4 +12,49 @@
             border-top-right-radius: 0;
         }
     }
+    .chatbox {
+        .chat-body {
+            .chat-message {
+                margin: 0.3em;
+                line-height: $line-height-large;
+                .chat-msg-author {
+                    line-height: $line-height-large;
+                }
+                .chat-msg-content {
+                    line-height: $line-height-large;
+                    .emojione {
+                        margin-bottom: -5px;
+                    }
+                }
+            }
+        }
+    }
+    .chatbox {
+        form.sendXMPPMessage {
+            .chat-toolbar {
+                li {
+                    .toolbar-menu {
+                        ul {
+                            &.emoji-toolbar {
+                                width: 100%;
+                                .emoji-category {
+                                    float: left;
+                                }
+                                li {
+                                    padding: 2px;
+                                }
+                            }
+                        }
+                    }
+                    &.toggle-smiley {
+                        ul {
+                            li {
+                                padding: 2px;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }

+ 9 - 1
sass/converse/_variables.scss

@@ -42,7 +42,7 @@ $global-background-color: $light-blue !default;
 $inverse-link-color: white !default;
 $link-shadow-color: #FAFAFA !default;
 $text-shadow-color: #FAFAFA !default;
-$text-color: #818479 !default;
+$text-color: #777 !default;
 $light-text-color: #A8ABA1 !default;
 $border-color: #CCC !default;
 $icon-color: $blue !default;
@@ -50,8 +50,11 @@ $save-button-color: $green !default;
 $chat-textarea-height: 70px !default;
 $send-button-height: 27px !default;
 $send-button-margin: 3px !default;
+
 $message-them-color: $red !default;
 
+$emoji_height : 20px !default;
+
 $roster-height: 194px !default;
 $roster-item-height: 60px !default;
 
@@ -91,13 +94,17 @@ $font-size-tiny: 10px !default;
 $font-size-small: 12px !default;
 $font-size: 14px !default;
 $font-size-large: 16px !default;
+$font-size-huge: 20px !default;
 $legend-font-size: 16px !default;
 
 $toolbar-height: 25px !default;
 $toolbar-color: $greenish-white !default;
 
+$emoji-picker-height: 100px !default;
+
 $line-height-small:  14px !default;
 $line-height:  16px !default;
+$line-height-large:  20px !default;
 
 $controlbox-width: 200px !default;
 $chat-width: 200px !default;
@@ -114,6 +121,7 @@ $font-path: "../fonticons/fonts/" !default;
 $chatroom-width: 300px !default;
 $chatroom-head-color: $red !default;
 $chatroom-color-light: $light-red !default;
+$chatroom-color-lightest: $light-red !default;
 $chatroom-color-dark: $darkest-red !default;
 $chatroom-message-them-color: $green !default;
 $chatroom-toolbar-color: $reddish-white !default;

+ 15 - 3
sass/inverse/_chatbox.scss

@@ -43,9 +43,15 @@
             border-top-right-radius: $chatbox-border-radius;
 
             .chat-message {
+                line-height: $line-height;
                 font-size: $font-size-small;
-                line-height: $line-height-small;
                 margin: 0.5em 0;
+                .chat-msg-author {
+                    line-height: $line-height;
+                }
+                .chat-msg-content {
+                    line-height: $line-height;
+                }
             }
         }
         .chat-content {
@@ -65,8 +71,14 @@
             .toggle-smiley {
                 padding-left: 0.5em;
                 ul {
-                    li {
-                        padding: 0.5em;
+                    &.emoji-toolbar {
+                        .emoji-category-picker {
+                            margin-right: 5em;
+                        }
+                        .emoji-category {
+                            padding-left: 10px;
+                            padding-right: 10px;
+                        }
                     }
                 }
             }

+ 4 - 3
sass/inverse/_chatrooms.scss

@@ -5,9 +5,10 @@
         .close-chatbox-button:before {
             content: "\e601"; // Leave icon
         }
-        .chatroom-description {
-            font-size: 66%;
-            margin-top: 3px;
+        .chat-title {
+            .chatroom-description {
+                font-size: 65%;
+            }
         }
     }
 

+ 6 - 1
sass/inverse/_variables.scss

@@ -42,15 +42,19 @@ $global-background-color: $light-blue !default;
 $inverse-link-color: white !default;
 $link-shadow-color: #FAFAFA !default;
 $text-shadow-color: #FAFAFA !default;
-$text-color: #818479 !default;
+$text-color: #777 !default;
 $light-text-color: #A8ABA1 !default;
 $border-color: #CCC !default;
 $icon-color: $blue !default;
 $save-button-color: $green !default;
 $send-button-height: 27px !default;
 $send-button-margin: 3px !default;
+
 $message-them-color: $red !default;
 
+$emoji_height: 22px !default;
+$emoji-picker-height: 150px !default;
+
 $roster-height: 194px !default;
 $roster-item-height: 30px !default;
 
@@ -121,6 +125,7 @@ $chatroom-head-height: 62px !default;
 $chatroom-width: 300px !default;
 $chatroom-head-color: $red !default;
 $chatroom-color-light: $light-red !default;
+$chatroom-color-lightest: $light-red !default;
 $chatroom-color-dark: $darkest-red !default;
 $chatroom-message-them-color: $green !default;
 $chatroom-toolbar-color: $reddish-white !default;

+ 56 - 23
spec/bookmarks.js

@@ -17,7 +17,9 @@
 
     describe("A chat room", function () {
 
-        it("can be bookmarked", mock.initConverse(function (_converse) {
+        it("can be bookmarked", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             var sent_stanza, IQ_id;
             var sendIQ = _converse.connection.sendIQ;
             spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -124,9 +126,12 @@
             _converse.connection._dataRecv(test_utils.createRequest(stanza));
             // We ignore this IQ stanza... (unless it's an error stanza), so
             // nothing to test for here.
+            done();
         }));
 
-        it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverse(function (_converse) {
+        it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             var jid = 'lounge@localhost';
             _converse.bookmarks.create({
                 'jid': jid,
@@ -144,11 +149,14 @@
                 'nick': ' Othello'
             });
             expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
+            done();
         }));
 
         describe("when bookmarked", function () {
 
-            it("displays that it's bookmarked through its bookmark icon", mock.initConverse(function (_converse) {
+            it("displays that it's bookmarked through its bookmark icon", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
                 var $bookmark_icon = view.$('.icon-pushpin');
@@ -157,9 +165,12 @@
                 expect($bookmark_icon.hasClass('button-on')).toBeTruthy();
                 view.model.set('bookmarked', false);
                 expect($bookmark_icon.hasClass('button-on')).toBeFalsy();
+                done();
             }));
 
-            it("can be unbookmarked", mock.initConverse(function (_converse) {
+            it("can be unbookmarked", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 var sent_stanza, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 test_utils.openChatRoom(_converse, 'theplay', 'conference.shakespeare.lit', 'JC');
@@ -216,6 +227,7 @@
                         "</pubsub>"+
                     "</iq>"
                 );
+                done();
             }));
         });
 
@@ -293,8 +305,9 @@
              */
         }));
 
-        it("can be retrieved from the XMPP server",
-                mock.initConverseWithConnectionSpies(['send'], function (_converse) {
+        it("can be retrieved from the XMPP server", mock.initConverseWithPromises(
+            ['send'], ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             /* Client requests all items
              * -------------------------
              *
@@ -306,7 +319,7 @@
              */
             var IQ_id;
             expect(_.filter(_converse.connection.send.calls.all(), function (call) {
-                var stanza = call.args[0]
+                var stanza = call.args[0];
                 if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
                     return;
                 }
@@ -366,14 +379,17 @@
             expect(_converse.bookmarks.models.length).toBe(2);
             expect(_converse.bookmarks.findWhere({'jid': 'theplay@conference.shakespeare.lit'}).get('autojoin')).toBe(true);
             expect(_converse.bookmarks.findWhere({'jid': 'another@conference.shakespeare.lit'}).get('autojoin')).toBe(false);
+            done();
         }));
 
         describe("The rooms panel", function () {
 
-            it("shows a list of bookmarks", mock.initConverseWithConnectionSpies(['send'], function (_converse) {
+            it("shows a list of bookmarks", mock.initConverseWithPromises(
+                ['send'], ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 var IQ_id;
                 expect(_.filter(_converse.connection.send.calls.all(), function (call) {
-                    var stanza = call.args[0]
+                    var stanza = call.args[0];
                     if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
                         return;
                     }
@@ -414,14 +430,21 @@
                                         'jid': 'another@conference.shakespeare.lit'
                                     }).c('nick').t('JC').up().up();
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                expect($('#chatrooms dl.bookmarks dd').length).toBe(3);
+
+                test_utils.waitUntil(function () {
+                    return $('#chatrooms dl.bookmarks dd').length;
+                }, 300).then(function () {
+                    expect($('#chatrooms dl.bookmarks dd').length).toBe(3);
+                    done();
+                });
             }));
 
-            it("remembers the toggle state of the bookmarks list",
-                    mock.initConverseWithConnectionSpies(['send'], function (_converse) {
+            it("remembers the toggle state of the bookmarks list", mock.initConverseWithPromises(
+                ['send'], ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 var IQ_id;
                 expect(_.filter(_converse.connection.send.calls.all(), function (call) {
-                    var stanza = call.args[0]
+                    var stanza = call.args[0];
                     if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
                         return;
                     }
@@ -454,23 +477,32 @@
                     'nick': ''
                 });
                 test_utils.openControlBox().openRoomsPanel(_converse);
-                expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(1);
-                expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
-                $('#chatrooms .bookmarks-toggle').click();
-                expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(0);
-                expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.CLOSED);
-                $('#chatrooms .bookmarks-toggle').click();
-                expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(1);
-                expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
+
+                test_utils.waitUntil(function () {
+                    return $('#chatrooms dl.bookmarks dd:visible').length;
+                }, 300).then(function () {
+                    expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(1);
+                    expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
+                    $('#chatrooms .bookmarks-toggle').click();
+                    expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(0);
+                    expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.CLOSED);
+                    $('#chatrooms .bookmarks-toggle').click();
+                    expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(1);
+                    expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
+                    done();
+                });
             }));
         });
     });
 
     describe("When hide_open_bookmarks is true and a bookmarked room is opened", function () {
 
-        it("can be closed", mock.initConverse({ hide_open_bookmarks: true }, function (_converse) {
-            test_utils.openControlBox().openRoomsPanel(_converse);
+        it("can be closed", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'],
+            { hide_open_bookmarks: true },
+            function (done, _converse) {
 
+            test_utils.openControlBox().openRoomsPanel(_converse);
             // XXX Create bookmarks view here, otherwise we need to mock stanza
             // traffic for it to get created.
             _converse.bookmarksview = new _converse.BookmarksView(
@@ -502,6 +534,7 @@
             view.close();
             room_els = _converse.bookmarksview.el.querySelectorAll(".open-room");
             expect(room_els.length).toBe(1);
+            done();
         }));
     });
 }));

Dosya farkı çok büyük olduğundan ihmal edildi
+ 334 - 169
spec/chatbox.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 524 - 435
spec/chatroom.js


+ 238 - 52
spec/controlbox.js

@@ -1,8 +1,7 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
     var $pres = converse.env.$pres;
     var $msg = converse.env.$msg;
     var $iq = converse.env.$iq;
@@ -25,7 +24,11 @@
 
     describe("The Control Box", function () {
 
-        it("can be opened by clicking a DOM element with class 'toggle-controlbox'", mock.initConverse(function (_converse) {
+        it("can be opened by clicking a DOM element with class 'toggle-controlbox'",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             // This spec will only pass if the controlbox is not currently
             // open yet.
             expect($("div#controlbox").is(':visible')).toBe(false);
@@ -39,18 +42,28 @@
             expect(_converse.controlboxtoggle.showControlBox).toHaveBeenCalled();
             expect(_converse.emit).toHaveBeenCalledWith('controlBoxOpened', jasmine.any(Object));
             expect($("div#controlbox").is(':visible')).toBe(true);
+            done();
         }));
 
         describe("The Status Widget", function () {
 
-            it("shows the user's chat status, which is online by default", mock.initConverse(function (_converse) {
+            it("shows the user's chat status, which is online by default",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var view = _converse.xmppstatusview;
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('online')).toBe(true);
                 expect(view.$el.find('a.choose-xmpp-status').attr('data-value')).toBe('I am online');
+                done();
             }));
 
-            it("can be used to set the current user's chat status", mock.initConverse(function (_converse) {
+            it("can be used to set the current user's chat status",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var view = _converse.xmppstatusview;
                 spyOn(view, 'toggleOptions').and.callThrough();
@@ -68,9 +81,14 @@
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('online')).toBe(false);
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('dnd')).toBe(true);
                 expect(view.$el.find('a.choose-xmpp-status').attr('data-value')).toBe('I am busy');
+                done();
             }));
 
-            it("can be used to set a custom status message", mock.initConverse(function (_converse) {
+            it("can be used to set a custom status message",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var view = _converse.xmppstatusview;
                 _converse.xmppstatus.save({'status': 'online'});
@@ -87,6 +105,7 @@
                 expect(_converse.emit).toHaveBeenCalledWith('statusMessageChanged', msg);
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('online')).toBe(true);
                 expect(view.$el.find('a.choose-xmpp-status').attr('data-value')).toBe(msg);
+                done();
             }));
         });
     });
@@ -95,7 +114,11 @@
 
         describe("The live filter", function () {
 
-            it("will only appear when roster contacts flow over the visible area", mock.initConverseWithAsync(function (done, _converse) {
+            it("will only appear when roster contacts flow over the visible area",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter = _converse.rosterview.$('.roster-filter');
                 var names = mock.cur_names;
                 test_utils.openControlBox();
@@ -120,13 +143,17 @@
                         } else {
                             return !$filter.is(':visible');
                         }
-                }).then(function () {
-                    done();
-                });
+                    }).then(function () {
+                        done();
+                    });
                 });
             }));
 
-            it("can be used to filter the contacts shown", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to filter the contacts shown",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter;
                 var $roster;
                 _converse.roster_groups = true;
@@ -180,7 +207,11 @@
                 });
             }));
 
-            it("can be used to filter the groups shown", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to filter the groups shown",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter;
                 var $roster;
                 var $type;
@@ -226,7 +257,11 @@
                 });
             }));
 
-            it("has a button with which its contents can be cleared", mock.initConverseWithAsync(function (done, _converse) {
+            it("has a button with which its contents can be cleared",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 test_utils.openControlBox();
                 test_utils.createGroupedContacts(_converse);
@@ -250,7 +285,11 @@
                 });
             }));
 
-            it("can be used to filter contacts by their chat state", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to filter contacts by their chat state",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter;
                 var $roster;
                 _converse.roster_groups = true;
@@ -287,7 +326,11 @@
 
         describe("A Roster Group", function () {
 
-            it("can be used to organize existing contacts", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to organize existing contacts",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
@@ -321,7 +364,11 @@
                 });
             }));
 
-            it("can share contacts with other roster groups", mock.initConverseWithAsync(function (done, _converse) {
+            it("can share contacts with other roster groups", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 var groups = ['colleagues', 'friends'];
                 spyOn(_converse, 'emit');
@@ -351,7 +398,11 @@
                 });
             }));
 
-            it("remembers whether it is closed or opened", mock.initConverse(function (_converse) {
+            it("remembers whether it is closed or opened",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 var i=0, j=0;
                 var groups = {
@@ -378,6 +429,7 @@
                 expect(view.model.get('state')).toBe('closed');
                 $toggle.click();
                 expect(view.model.get('state')).toBe('opened');
+                done();
             }));
         });
 
@@ -388,7 +440,11 @@
                 test_utils.createContacts(_converse, 'pending').openControlBox().openContactsPanel(_converse);
             }
 
-            it("can be collapsed under their own header", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be collapsed under their own header", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dd').length;
@@ -399,7 +455,11 @@
                 });
             }));
 
-            it("can be added to the roster", mock.initConverse(function (_converse) {
+            it("can be added to the roster",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 test_utils.openControlBox();
@@ -410,9 +470,14 @@
                     fullname: mock.pend_names[0]
                 });
                 expect(_converse.rosterview.update).toHaveBeenCalled();
+                done();
             }));
 
-            it("are shown in the roster when show_only_online_users", mock.initConverseWithAsync(function (done, _converse) {
+            it("are shown in the roster when show_only_online_users", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.show_only_online_users = true;
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 _addContacts(_converse);
@@ -428,7 +493,11 @@
                 });
             }));
 
-            it("are shown in the roster when hide_offline_users", mock.initConverseWithAsync(function (done, _converse) {
+            it("are shown in the roster when hide_offline_users", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.hide_offline_users = true;
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 _addContacts(_converse);
@@ -444,7 +513,11 @@
                 });
             }));
 
-            it("can be removed by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be removed by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 var name = mock.pend_names[0];
                 var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -474,7 +547,11 @@
                 });
             }));
 
-            it("do not have a header if there aren't any", mock.initConverseWithAsync(function (done, _converse) {
+            it("do not have a header if there aren't any", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var name = mock.pend_names[0];
                 _converse.roster.create({
@@ -500,7 +577,11 @@
                 });
             }));
 
-            it("will lose their own header once the last one has been removed", mock.initConverse(function (_converse) {
+            it("is shown when a new private message is received",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 var name;
                 spyOn(window, 'confirm').and.returnValue(true);
@@ -510,9 +591,14 @@
                         .parent().siblings('.remove-xmpp-contact').click();
                 }
                 expect(_converse.rosterview.$el.find('dt#pending-xmpp-contacts').is(':visible')).toBeFalsy();
+                done();
             }));
 
-            it("can be added to the roster and they will be sorted alphabetically", mock.initConverse(function (_converse) {
+            it("can be added to the roster and they will be sorted alphabetically",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var i, t;
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
@@ -530,6 +616,7 @@
                     return result + _.trim(value.textContent);
                 }, '');
                 expect(t).toEqual(mock.pend_names.slice(0,i+1).sort().join(''));
+                done();
             }));
         });
 
@@ -538,7 +625,11 @@
                 test_utils.createContacts(_converse, 'current').openControlBox().openContactsPanel(_converse);
             };
 
-            it("can be collapsed under their own header", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be collapsed under their own header", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dd:visible').length;
@@ -549,7 +640,11 @@
                 });
             }));
 
-            it("will be hidden when appearing under a collapsed group", mock.initConverseWithAsync(function (done, _converse) {
+            it("will be hidden when appearing under a collapsed group", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = false;
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
@@ -572,7 +667,11 @@
                 });
             }));
 
-            it("can be added to the roster and they will be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be added to the roster and they will be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 for (var i=0; i<mock.cur_names.length; i++) {
                     _converse.roster.create({
@@ -595,7 +694,11 @@
                 });
             }));
 
-            it("can be removed by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be removed by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dd').length;
@@ -620,7 +723,11 @@
                 });
             }));
 
-            it("do not have a header if there aren't any", mock.initConverseWithAsync(function (done, _converse) {
+            it("do not have a header if there aren't any", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var name = mock.cur_names[0];
                 var contact;
                 contact = _converse.roster.create({
@@ -650,7 +757,11 @@
                 });
             }));
 
-            it("can change their status to online and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to online and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -674,7 +785,11 @@
                 });
             }));
 
-            it("can change their status to busy and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to busy and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -698,7 +813,11 @@
                 });
             }));
 
-            it("can change their status to away and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to away and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -722,7 +841,11 @@
                 });
             }));
 
-            it("can change their status to xa and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to xa and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -746,7 +869,11 @@
                 });
             }));
 
-            it("can change their status to unavailable and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to unavailable and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -770,7 +897,11 @@
                 });
             }));
 
-            it("are ordered according to status: online, busy, away, xa, unavailable, offline", mock.initConverseWithAsync(function (done, _converse) {
+            it("are ordered according to status: online, busy, away, xa, unavailable, offline", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -860,7 +991,11 @@
 
         describe("Requesting Contacts", function () {
 
-            it("can be added to the roster and they will be sorted alphabetically", mock.initConverse(function (_converse) {
+            it("can be added to the roster and they will be sorted alphabetically",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var i, children;
                 var names = [];
                 var addName = function (idx, item) {
@@ -887,9 +1022,14 @@
                 names = [];
                 children.each(addName);
                 expect(names.join('')).toEqual(mock.req_names.slice(0,mock.req_names.length+1).sort().join(''));
+                done();
             }));
 
-            it("do not have a header if there aren't any", mock.initConverseWithAsync(function (done, _converse) {
+            it("do not have a header if there aren't any", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openContactsPanel(_converse);
                 var name = mock.req_names[0];
                 spyOn(window, 'confirm').and.returnValue(true);
@@ -914,7 +1054,11 @@
                 });
             }));
 
-            it("can be collapsed under their own header", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be collapsed under their own header", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'requesting').openControlBox();
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -925,7 +1069,11 @@
                 });
             }));
 
-            it("can have their requests accepted by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can have their requests accepted by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'requesting').openControlBox();
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -950,7 +1098,11 @@
                 });
             }));
 
-            it("can have their requests denied by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can have their requests denied by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'requesting').openControlBox();
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -973,7 +1125,9 @@
                 });
             }));
 
-            it("are persisted even if other contacts' change their presence ", mock.initConverse(function (_converse) {
+            it("are persisted even if other contacts' change their presence ", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 /* This is a regression test.
                  * https://github.com/jcbrand/_converse.js/issues/262
                  */
@@ -1009,12 +1163,17 @@
                 }).c('group').t('Friends');
                 _converse.roster.onReceivedFromServer(stanza.tree());
                 expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
+                done();
             }));
         });
 
         describe("All Contacts", function () {
 
-            it("are saved to, and can be retrieved from browserStorage", mock.initConverse(function (_converse) {
+            it("are saved to, and can be retrieved from browserStorage",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.openContactsPanel(_converse);
                 var new_attrs, old_attrs, attrs;
@@ -1036,9 +1195,14 @@
                     // comparison
                     expect(_.isEqual(new_attrs.sort(), old_attrs.sort())).toEqual(true);
                 }
+                done();
             }));
 
-            it("will show fullname and jid properties on tooltip", mock.initConverseWithAsync(function (done, _converse) {
+            it("will show fullname and jid properties on tooltip", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
@@ -1064,7 +1228,11 @@
 
     describe("The 'Add Contact' widget", function () {
 
-        it("opens up an add form when you click on it", mock.initConverse(function (_converse) {
+        it("opens up an add form when you click on it",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             var panel = _converse.chatboxviews.get('controlbox').contactspanel;
             spyOn(panel, 'toggleContactForm').and.callThrough();
             panel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
@@ -1072,9 +1240,14 @@
             expect(panel.toggleContactForm).toHaveBeenCalled();
             // XXX: Awaiting more tests, close it again for now...
             panel.$el.find('a.toggle-xmpp-contact-form').click();
+            done();
         }));
 
-        it("can be used to add contact and it checks for case-sensivity", mock.initConverseWithAsync(function (done, _converse) {
+        it("can be used to add contact and it checks for case-sensivity", 
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             spyOn(_converse, 'emit');
             spyOn(_converse.rosterview, 'update').and.callThrough();
             test_utils.openControlBox();
@@ -1101,12 +1274,15 @@
                 done();
             });
         }));
-
     });
 
     describe("The Controlbox Tabs", function () {
 
-        it("contains two tabs, 'Contacts' and 'ChatRooms'", mock.initConverse(function (_converse) {
+        it("contains two tabs, 'Contacts' and 'ChatRooms'",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             var cbview = _converse.chatboxviews.get('controlbox');
             var $panels = cbview.$el.find('.controlbox-panes');
@@ -1115,9 +1291,14 @@
             expect($panels.children().first().is(':visible')).toBe(true);
             expect($panels.children().last().attr('id')).toBe('chatrooms');
             expect($panels.children().last().is(':visible')).toBe(false);
+            done();
         }));
 
-        it("remembers which tab was open last", mock.initConverse(function (_converse) {
+        it("remembers which tab was open last",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             var cbview = _converse.chatboxviews.get('controlbox');
             var $tabs = cbview.$el.find('#controlbox-tabs');
@@ -1126,11 +1307,16 @@
             expect(cbview.model.get('active-panel')).toBe('chatrooms');
             $tabs.find('li').first().find('a').click();
             expect(cbview.model.get('active-panel')).toBe('users');
+            done();
         }));
 
         describe("The \"Contacts\" Panel", function () {
 
-            it("shows the number of unread mentions received", mock.initConverse(function (_converse) {
+            it("shows the number of unread mentions received",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.openContactsPanel(_converse);
 
@@ -1168,8 +1354,8 @@
                 chatview.model.set({'minimized': false});
                 expect(_.includes(contacts_panel.tab_el.firstChild.classList, 'unread-msgs')).toBeFalsy();
                 expect(_.isNull(contacts_panel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+                done();
             }));
-
         });
     });
 }));

+ 39 - 29
spec/converse.js

@@ -7,7 +7,6 @@
 } (this, function (jasmine, converse, mock, test_utils) {
     var b64_sha1 = converse.env.b64_sha1;
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
 
     describe("Converse", function() {
         
@@ -54,7 +53,11 @@
 
         describe("A chat state indication", function () {
 
-            it("are sent out when the client becomes or stops being idle", mock.initConverse(function (_converse) {
+            it("are sent out when the client becomes or stops being idle",
+                mock.initConverseWithPromises(
+                    null, ['discoInitialized'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'sendCSI').and.callThrough();
                 var sent_stanza;
                 spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
@@ -62,7 +65,7 @@
                 });
                 var i = 0;
                 _converse.idle_seconds = 0; // Usually initialized by registerIntervalHandler
-                _converse.features['urn:xmpp:csi:0'] = true; // Mock that the server supports CSI
+                _converse.disco_entities.get(_converse.domain).features['urn:xmpp:csi:0'] = true; // Mock that the server supports CSI
 
                 _converse.csi_waiting_time = 3; // The relevant config option
                 while (i <= _converse.csi_waiting_time) {
@@ -79,10 +82,10 @@
                 expect(sent_stanza.toLocaleString()).toBe(
                     "<active xmlns='urn:xmpp:csi:0'/>"
                 );
-
                 // Reset values
                 _converse.csi_waiting_time = 0;
-                _converse.features['urn:xmpp:csi:0'] = false;
+                _converse.disco_entities.get(_converse.domain).features['urn:xmpp:csi:0'] = false;
+                done();
             }));
         });
 
@@ -272,32 +275,38 @@
 
         describe("The \"chats\" API", function() {
 
-            it("has a method 'get' which returns a wrapped chat box", mock.initConverse(function (_converse) {
-                test_utils.createContacts(_converse, 'current');
-                // Test on chat that doesn't exist.
-                expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
-                var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                // Test on chat that's not open
-                var box = _converse.api.chats.get(jid);
-                expect(typeof box === 'undefined').toBeTruthy();
-                var chatboxview = _converse.chatboxviews.get(jid);
-                // Test for single JID
-                test_utils.openChatBoxFor(_converse, jid);
-                box = _converse.api.chats.get(jid);
-                expect(box instanceof Object).toBeTruthy();
-                expect(box.model.get('box_id')).toBe(b64_sha1(jid));
-                chatboxview = _converse.chatboxviews.get(jid);
-                expect(chatboxview.$el.is(':visible')).toBeTruthy();
-                // Test for multiple JIDs
-                var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
-                test_utils.openChatBoxFor(_converse, jid2);
-                var list = _converse.api.chats.get([jid, jid2]);
-                expect(_.isArray(list)).toBeTruthy();
-                expect(list[0].model.get('box_id')).toBe(b64_sha1(jid));
-                expect(list[1].model.get('box_id')).toBe(b64_sha1(jid2));
+            it("has a method 'get' which returns a wrapped chat box", mock.initConverseWithPromises(
+                null, ['rosterInitialized'], {}, function (done, _converse) {
+                    test_utils.openControlBox();
+                    test_utils.createContacts(_converse, 'current');
+                    // Test on chat that doesn't exist.
+                    expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
+                    var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+                    // Test on chat that's not open
+                    var box = _converse.api.chats.get(jid);
+                    expect(typeof box === 'undefined').toBeTruthy();
+                    var chatboxview = _converse.chatboxviews.get(jid);
+                    // Test for single JID
+                    test_utils.openChatBoxFor(_converse, jid);
+                    box = _converse.api.chats.get(jid);
+                    expect(box instanceof Object).toBeTruthy();
+                    expect(box.model.get('box_id')).toBe(b64_sha1(jid));
+                    chatboxview = _converse.chatboxviews.get(jid);
+                    expect(chatboxview.$el.is(':visible')).toBeTruthy();
+                    // Test for multiple JIDs
+                    var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
+                    test_utils.openChatBoxFor(_converse, jid2);
+                    var list = _converse.api.chats.get([jid, jid2]);
+                    expect(_.isArray(list)).toBeTruthy();
+                    expect(list[0].model.get('box_id')).toBe(b64_sha1(jid));
+                    expect(list[1].model.get('box_id')).toBe(b64_sha1(jid2));
+                    done();
             }));
 
-            it("has a method 'open' which opens and returns a wrapped chat box", mock.initConverse(function (_converse) {
+            it("has a method 'open' which opens and returns a wrapped chat box", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
+                test_utils.openControlBox();
                 test_utils.createContacts(_converse, 'current');
                 var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 var chatboxview;
@@ -318,6 +327,7 @@
                 expect(_.isArray(list)).toBeTruthy();
                 expect(list[0].model.get('box_id')).toBe(b64_sha1(jid));
                 expect(list[1].model.get('box_id')).toBe(b64_sha1(jid2));
+                done();
             }));
         });
 

+ 166 - 2
spec/disco.js

@@ -8,14 +8,178 @@
 } (this, function (jasmine, $, converse, mock, test_utils) {
     "use strict";
     var Strophe = converse.env.Strophe;
+    var $iq = converse.env.$iq;
+    var _ = converse.env._;
 
     describe("Service Discovery", function () {
+
+        describe("Whenever converse.js queries a server for its features", function () {
+            it("stores the features it receives", mock.initConverseWithAsync(function (done, _converse) {
+                var IQ_stanzas = _converse.connection.IQ_stanzas;
+                var IQ_ids =  _converse.connection.IQ_ids;
+                test_utils.waitUntil(function () {
+                    return _.filter(IQ_stanzas, function (iq) {
+                        return iq.nodeTree.querySelector('query[xmlns="http://jabber.org/protocol/disco#info"]');
+                    }).length > 0;
+                }, 300).then(function () {
+                    /* <iq type='result'
+                     *      from='plays.shakespeare.lit'
+                     *      to='romeo@montague.net/orchard'
+                     *      id='info1'>
+                     *  <query xmlns='http://jabber.org/protocol/disco#info'>
+                     *      <identity
+                     *          category='server'
+                     *          type='im'/>
+                     *      <identity
+                     *          category='conference'
+                     *          type='text'
+                     *          name='Play-Specific Chatrooms'/>
+                     *      <identity
+                     *          category='directory'
+                     *          type='chatroom'
+                     *          name='Play-Specific Chatrooms'/>
+                     *      <feature var='http://jabber.org/protocol/disco#info'/>
+                     *      <feature var='http://jabber.org/protocol/disco#items'/>
+                     *      <feature var='http://jabber.org/protocol/muc'/>
+                     *      <feature var='jabber:iq:register'/>
+                     *      <feature var='jabber:iq:search'/>
+                     *      <feature var='jabber:iq:time'/>
+                     *      <feature var='jabber:iq:version'/>
+                     *  </query>
+                     *  </iq>
+                     */
+                    var info_IQ_id = IQ_ids[0];
+                    var stanza = $iq({
+                        'type': 'result',
+                        'from': 'localhost',
+                        'to': 'dummy@localhost/resource',
+                        'id': info_IQ_id
+                    }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
+                        .c('identity', {
+                            'category': 'server',
+                            'type': 'im'}).up()
+                        .c('identity', {
+                            'category': 'conference',
+                            'type': 'text',
+                            'name': 'Play-Specific Chatrooms'}).up()
+                        .c('identity', {
+                            'category': 'directory',
+                            'type': 'chatroom',
+                            'name': 'Play-Specific Chatrooms'}).up()
+                        .c('feature', {
+                            'var': 'http://jabber.org/protocol/disco#info'}).up()
+                        .c('feature', {
+                            'var': 'http://jabber.org/protocol/disco#items'}).up()
+                        .c('feature', {
+                            'var': 'jabber:iq:register'}).up()
+                        .c('feature', {
+                            'var': 'jabber:iq:time'}).up()
+                        .c('feature', {
+                            'var': 'jabber:iq:version'});
+                    _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+                    var entities = _converse.disco_entities;
+                    expect(entities.length).toBe(1);
+                    expect(entities.get(_converse.domain).features.length).toBe(5);
+                    expect(entities.get(_converse.domain).identities.length).toBe(3);
+                    expect(entities.get('localhost').features.where({'var': 'jabber:iq:version'}).length).toBe(1);
+                    expect(entities.get('localhost').features.where({'var': 'jabber:iq:time'}).length).toBe(1);
+                    expect(entities.get('localhost').features.where({'var': 'jabber:iq:register'}).length).toBe(1);
+                    expect(entities.get('localhost').features.where(
+                        {'var': 'http://jabber.org/protocol/disco#items'}).length).toBe(1);
+                    expect(entities.get('localhost').features.where(
+                        {'var': 'http://jabber.org/protocol/disco#info'}).length).toBe(1);
+
+
+                test_utils.waitUntil(function () {
+                    // Converse.js sees that the entity has a disco#items feature,
+                    // so it will make a query for it.
+                    return _.filter(IQ_stanzas, function (iq) {
+                        return iq.nodeTree.querySelector('query[xmlns="http://jabber.org/protocol/disco#items"]');
+                    }).length > 0;
+                }, 300).then(function () {
+                    /* <iq type='result'
+                     *     from='catalog.shakespeare.lit'
+                     *     to='romeo@montague.net/orchard'
+                     *     id='items2'>
+                     * <query xmlns='http://jabber.org/protocol/disco#items'>
+                     *     <item jid='people.shakespeare.lit'
+                     *         name='Directory of Characters'/>
+                     *     <item jid='plays.shakespeare.lit'
+                     *         name='Play-Specific Chatrooms'/>
+                     *     <item jid='mim.shakespeare.lit'
+                     *         name='Gateway to Marlowe IM'/>
+                     *     <item jid='words.shakespeare.lit'
+                     *         name='Shakespearean Lexicon'/>
+                     *
+                     *     <item jid='catalog.shakespeare.lit'
+                     *         node='books'
+                     *         name='Books by and about Shakespeare'/>
+                     *     <item jid='catalog.shakespeare.lit'
+                     *         node='clothing'
+                     *         name='Wear your literary taste with pride'/>
+                     *     <item jid='catalog.shakespeare.lit'
+                     *         node='music'
+                     *         name='Music from the time of Shakespeare'/>
+                     * </query>
+                     * </iq>
+                     */
+                   var items_IQ_id = IQ_ids.pop();
+                   stanza = $iq({
+                       'type': 'result',
+                       'from': 'localhost',
+                       'to': 'dummy@localhost/resource',
+                       'id': items_IQ_id
+                   }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#items'})
+                       .c('item', {
+                           'jid': 'people.shakespeare.lit',
+                           'name': 'Directory of Characters'}).up()
+                       .c('item', {
+                           'jid': 'plays.shakespeare.lit',
+                           'name': 'Play-Specific Chatrooms'}).up()
+                       .c('item', {
+                           'jid': 'words.shakespeare.lit',
+                           'name': 'Gateway to Marlowe IM'}).up()
+                       .c('item', {
+                           'jid': 'localhost',
+                           'name': 'Shakespearean Lexicon'}).up()
+
+                       .c('item', {
+                           'jid': 'localhost',
+                           'node': 'books',
+                           'name': 'Books by and about Shakespeare'}).up()
+                       .c('item', {
+                           'node': 'localhost',
+                           'name': 'Wear your literary taste with pride'}).up()
+                       .c('item', {
+                           'jid': 'localhost',
+                           'node': 'music',
+                           'name': 'Music from the time of Shakespeare'
+                       });
+                    _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+                    entities = _converse.disco_entities;
+                    expect(entities.length).toBe(4);
+                    expect(entities.get(_converse.domain).identities.where({'category': 'conference'}).length).toBe(1);
+                    expect(entities.get(_converse.domain).identities.where({'category': 'directory'}).length).toBe(1);
+                    done();
+                });
+                });
+            }));
+        });
+
         describe("Whenever converse.js discovers a new server feature", function () {
-           it("emits the serviceDiscovered event", mock.initConverse(function (_converse) {
+           it("emits the serviceDiscovered event",
+                mock.initConverseWithPromises(
+                    null, ['discoInitialized'], {},
+                    function (done, _converse) {
+
                 sinon.spy(_converse, 'emit');
-                _converse.features.create({'var': Strophe.NS.MAM});
+                _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 expect(_converse.emit.called).toBe(true);
+                expect(_converse.emit.args[0][0]).toBe('serviceDiscovered');
                 expect(_converse.emit.args[0][1].get('var')).toBe(Strophe.NS.MAM);
+                done();
             }));
         });
     });

+ 7 - 2
spec/headline.js

@@ -40,7 +40,9 @@
             utils.isHeadlineMessage.restore();
         }));
 
-        it("will open and display headline messages", mock.initConverse(function (_converse) {
+        it("will open and display headline messages", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             /* <message from='notify.example.com'
              *          to='romeo@im.example.com'
              *          type='headline'
@@ -74,9 +76,12 @@
             expect(utils.isHeadlineMessage.called).toBeTruthy();
             expect(utils.isHeadlineMessage.returned(true)).toBeTruthy();
             utils.isHeadlineMessage.restore(); // unwraps
+            done();
         }));
 
-        it("will not show a headline messages from a full JID if allow_non_roster_messaging is false", mock.initConverse(function (_converse) {
+        it("will not show a headline messages from a full JID if allow_non_roster_messaging is false",
+            mock.initConverse(function (_converse) {
+
             _converse.allow_non_roster_messaging = false;
             sinon.spy(utils, 'isHeadlineMessage');
             var stanza = $msg({

+ 34 - 29
spec/mam.js

@@ -1,9 +1,9 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     "use strict";
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
+    var Backbone = converse.env.Backbone;
     var Strophe = converse.env.Strophe;
     var $iq = converse.env.$iq;
     var $msg = converse.env.$msg;
@@ -15,20 +15,25 @@
 
         describe("The archive.query API", function () {
 
-           it("can be used to query for all archived messages", mock.initConverse(function (_converse) {
+           it("can be used to query for all archived messages",
+                mock.initConverseWithPromises(
+                    null, ['discoInitialized'], {},
+                    function (done, _converse) {
+
                 var sent_stanza, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 _converse.api.archive.query();
                 var queryid = $(sent_stanza.toString()).find('query').attr('queryid');
                 expect(sent_stanza.toString()).toBe(
                     "<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'><query xmlns='urn:xmpp:mam:2' queryid='"+queryid+"'/></iq>");
+                done();
             }));
 
            it("can be used to query for all messages to/from a particular JID", mock.initConverse(function (_converse) {
@@ -38,8 +43,8 @@
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 _converse.api.archive.query({'with':'juliet@capulet.lit'});
                 var queryid = $(sent_stanza.toString()).find('query').attr('queryid');
@@ -66,8 +71,8 @@
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 var start = '2010-06-07T00:00:00Z';
                 var end = '2010-07-07T13:23:54Z';
@@ -97,8 +102,8 @@
            }));
 
            it("throws a TypeError if an invalid date is provided", mock.initConverse(function (_converse) {
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 expect(_.partial(_converse.api.archive.query, {'start': 'not a real date'})).toThrow(
                     new TypeError('archive.query: invalid date provided for: start')
@@ -112,8 +117,8 @@
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 var start = '2010-06-07T00:00:00Z';
                 _converse.api.archive.query({'start': start});
@@ -141,8 +146,8 @@
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 var start = '2010-06-07T00:00:00Z';
                 _converse.api.archive.query({'start': start, 'max':10});
@@ -173,8 +178,8 @@
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 var start = '2010-06-07T00:00:00Z';
                 _converse.api.archive.query({
@@ -210,8 +215,8 @@
                     sent_stanza = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 _converse.api.archive.query({'before': '', 'max':10});
                 var queryid = $(sent_stanza.toString()).find('query').attr('queryid');
@@ -237,8 +242,8 @@
                 // and pass it in. However, in the callback method an RSM object is
                 // returned which can be reused for easy paging. This test is
                 // more for that usecase.
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 var sent_stanza, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
@@ -247,7 +252,7 @@
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
                 var rsm =  new Strophe.RSM({'max': '10'});
-                rsm['with'] = 'romeo@montague.lit';
+                rsm['with'] = 'romeo@montague.lit'; // eslint-disable-line dot-notation
                 rsm.start = '2010-06-07T00:00:00Z';
                 _converse.api.archive.query(rsm);
 
@@ -275,8 +280,8 @@
            }));
 
            it("accepts a callback function, which it passes the messages and a Strophe.RSM object", mock.initConverse(function (_converse) {
-                if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
-                    _converse.features.create({'var': Strophe.NS.MAM});
+                if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
+                    _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
                 }
                 var sent_stanza, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
@@ -351,7 +356,7 @@
                 expect(args[0].length).toBe(2);
                 expect(args[0][0].outerHTML).toBe(msg1.nodeTree.outerHTML);
                 expect(args[0][1].outerHTML).toBe(msg2.nodeTree.outerHTML);
-                expect(args[1]['with']).toBe('romeo@capulet.lit');
+                expect(args[1]['with']).toBe('romeo@capulet.lit'); // eslint-disable-line dot-notation
                 expect(args[1].max).toBe('10');
                 expect(args[1].count).toBe('16');
                 expect(args[1].first).toBe('23452-4534-1');
@@ -372,11 +377,11 @@
                 spyOn(_converse, 'onMAMPreferences').and.callThrough();
                 _converse.message_archiving = 'never';
 
-                var feature = new _converse.Feature({
+                var feature = new Backbone.Model({
                     'var': Strophe.NS.MAM
                 });
                 spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set
-                _converse.features.onFeatureAdded(feature);
+                _converse.disco_entities.get(_converse.domain).onFeatureAdded(feature);
 
                 expect(_converse.connection.sendIQ).toHaveBeenCalled();
                 expect(sent_stanza.toLocaleString()).toBe(
@@ -430,7 +435,7 @@
                     .c('never').up();
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
                 expect(feature.save).toHaveBeenCalled();
-                expect(feature.get('preferences')['default']).toBe('never');
+                expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation
 
                 // Restore
                 _converse.message_archiving = 'never';

+ 41 - 20
spec/minchats.js

@@ -6,7 +6,11 @@
 
     describe("The Minimized Chats Widget", function () {
 
-        it("shows chats that have been minimized",  mock.initConverse(function (_converse) {
+        it("shows chats that have been minimized",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
@@ -34,9 +38,14 @@
             expect(_converse.minimized_chats.$el.is(':visible')).toBeTruthy();
             expect(_converse.minimized_chats.keys().length).toBe(2);
             expect(_.includes(_converse.minimized_chats.keys(), contact_jid)).toBeTruthy();
+            done();
         }));
 
-        it("can be toggled to hide or show minimized chats", mock.initConverse(function (_converse) {
+        it("can be toggled to hide or show minimized chats",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
@@ -56,9 +65,14 @@
             _converse.minimized_chats.$('#toggle-minimized-chats').click();
             expect(_converse.minimized_chats.$('.minimized-chats-flyout').is(':visible')).toBeFalsy();
             expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeTruthy();
+            done();
         }));
 
-        it("shows the number messages received to minimized chats", mock.initConverse(function (_converse) {
+        it("shows the number messages received to minimized chats",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
@@ -120,28 +134,35 @@
                 id: (new Date()).getTime()
             }).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
             expect(_converse.minimized_chats.toggleview.$('.unread-message-count').text()).toBe((i).toString());
+            done();
         }));
 
-        it("shows the number messages received to minimized groupchats", mock.initConverse(function (_converse) {
+        it("shows the number messages received to minimized groupchats",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             var room_jid = 'kitchen@conference.shakespeare.lit';
             test_utils.openAndEnterChatRoom(
-                _converse, 'kitchen', 'conference.shakespeare.lit', 'fires');
-            var view = _converse.chatboxviews.get(room_jid);
-            view.model.set({'minimized': true});
+                _converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
+                var view = _converse.chatboxviews.get(room_jid);
+                view.model.set({'minimized': true});
 
-            var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
-            var message = 'fires: Your attention is required';
-            var nick = mock.chatroom_names[0],
-                msg = $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t(message).tree();
-            view.handleMUCMessage(msg);
-
-            expect(_converse.minimized_chats.toggleview.$('.unread-message-count').is(':visible')).toBeTruthy();
-            expect(_converse.minimized_chats.toggleview.$('.unread-message-count').text()).toBe('1');
+                var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
+                var message = 'fires: Your attention is required';
+                var nick = mock.chatroom_names[0],
+                    msg = $msg({
+                        from: room_jid+'/'+nick,
+                        id: (new Date()).getTime(),
+                        to: 'dummy@localhost',
+                        type: 'groupchat'
+                    }).c('body').t(message).tree();
+                view.handleMUCMessage(msg);
+
+                expect(_converse.minimized_chats.toggleview.$('.unread-message-count').is(':visible')).toBeTruthy();
+                expect(_converse.minimized_chats.toggleview.$('.unread-message-count').text()).toBe('1');
+                done();
+            });
         }));
     });
 }));

+ 90 - 68
spec/notification.js

@@ -12,7 +12,11 @@
             describe("And the desktop is not focused", function () {
                 describe("an HTML5 Notification", function () {
 
-                    it("is shown when a new private message is received", mock.initConverse(function (_converse) {
+                    it("is shown when a new private message is received",
+                        mock.initConverseWithPromises(
+                            null, ['rosterGroupsFetched'], {},
+                            function (done, _converse) {
+
                         // TODO: not yet testing show_desktop_notifications setting
                         test_utils.createContacts(_converse, 'current');
                         spyOn(_converse, 'showMessageNotification');
@@ -30,42 +34,53 @@
                         _converse.chatboxes.onMessage(msg); // This will emit 'message'
                         expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
                         expect(_converse.showMessageNotification).toHaveBeenCalled();
+                        done();
                     }));
 
-                    it("is shown when you are mentioned in a chat room", mock.initConverse(function (_converse) {
+                    it("is shown when you are mentioned in a chat room",
+                        mock.initConverseWithPromises(
+                            null, ['rosterGroupsFetched'], {},
+                            function (done, _converse) {
+
                         test_utils.createContacts(_converse, 'current');
-                        test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
-                        var view = _converse.chatboxviews.get('lounge@localhost');
-                        if (!view.$el.find('.chat-area').length) { view.renderChatArea(); }
-                        var no_notification = false;
-                        if (typeof window.Notification === 'undefined') {
-                            no_notification = true;
-                            window.Notification = function () {
-                                return {
-                                    'close': function () {}
+                        test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
+                            var view = _converse.chatboxviews.get('lounge@localhost');
+                            if (!view.$el.find('.chat-area').length) { view.renderChatArea(); }
+                            var no_notification = false;
+                            if (typeof window.Notification === 'undefined') {
+                                no_notification = true;
+                                window.Notification = function () {
+                                    return {
+                                        'close': function () {}
+                                    };
                                 };
-                            };
-                        }
-                        spyOn(_converse, 'showMessageNotification').and.callThrough();
-                        spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
-                        
-                        var message = 'dummy: This message will show a desktop notification';
-                        var nick = mock.chatroom_names[0],
-                            msg = $msg({
-                                from: 'lounge@localhost/'+nick,
-                                id: (new Date()).getTime(),
-                                to: 'dummy@localhost',
-                                type: 'groupchat'
-                            }).c('body').t(message).tree();
-                        _converse.chatboxes.onMessage(msg); // This will emit 'message'
-                        expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
-                        expect(_converse.showMessageNotification).toHaveBeenCalled();
-                        if (no_notification) {
-                            delete window.Notification;
-                        }
+                            }
+                            spyOn(_converse, 'showMessageNotification').and.callThrough();
+                            spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
+                            
+                            var message = 'dummy: This message will show a desktop notification';
+                            var nick = mock.chatroom_names[0],
+                                msg = $msg({
+                                    from: 'lounge@localhost/'+nick,
+                                    id: (new Date()).getTime(),
+                                    to: 'dummy@localhost',
+                                    type: 'groupchat'
+                                }).c('body').t(message).tree();
+                            _converse.chatboxes.onMessage(msg); // This will emit 'message'
+                            expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
+                            expect(_converse.showMessageNotification).toHaveBeenCalled();
+                            if (no_notification) {
+                                delete window.Notification;
+                            }
+                            done();
+                        });
                     }));
 
-                    it("is shown for headline messages", mock.initConverse(function (_converse) {
+                    it("is shown for headline messages",
+                        mock.initConverseWithPromises(
+                            null, ['rosterGroupsFetched'], {},
+                            function (done, _converse) {
+
                         spyOn(_converse, 'showMessageNotification').and.callThrough();
                         spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
                         var stanza = $msg({
@@ -84,6 +99,7 @@
                                 'notify.example.com')
                             ).toBeTruthy();
                         expect(_converse.showMessageNotification).toHaveBeenCalled();
+                        done();
                     }));
 
                     it("is not shown for full JID headline messages if allow_non_roster_messaging is false", mock.initConverse(function (_converse) {
@@ -137,44 +153,50 @@
         describe("When play_sounds is set to true", function () {
             describe("A notification sound", function () {
 
-                it("is played when the current user is mentioned in a chat room", mock.initConverse(function (_converse) {
+                it("is played when the current user is mentioned in a chat room",
+                    mock.initConverseWithPromises(
+                        null, ['rosterGroupsFetched'], {},
+                        function (done, _converse) {
+
                     test_utils.createContacts(_converse, 'current');
-                    test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
-                    _converse.play_sounds = true;
-                    spyOn(_converse, 'playSoundNotification');
-                    var view = _converse.chatboxviews.get('lounge@localhost');
-                    if (!view.$el.find('.chat-area').length) { view.renderChatArea(); }
-                    var text = 'This message will play a sound because it mentions dummy';
-                    var message = $msg({
-                        from: 'lounge@localhost/otheruser',
-                        id: '1',
-                        to: 'dummy@localhost',
-                        type: 'groupchat'
-                    }).c('body').t(text);
-                    view.onChatRoomMessage(message.nodeTree);
-                    expect(_converse.playSoundNotification).toHaveBeenCalled();
-
-                    text = "This message won't play a sound";
-                    message = $msg({
-                        from: 'lounge@localhost/otheruser',
-                        id: '2',
-                        to: 'dummy@localhost',
-                        type: 'groupchat'
-                    }).c('body').t(text);
-                    view.onChatRoomMessage(message.nodeTree);
-                    expect(_converse.playSoundNotification, 1);
-                    _converse.play_sounds = false;
-
-                    text = "This message won't play a sound because it is sent by dummy";
-                    message = $msg({
-                        from: 'lounge@localhost/dummy',
-                        id: '3',
-                        to: 'dummy@localhost',
-                        type: 'groupchat'
-                    }).c('body').t(text);
-                    view.onChatRoomMessage(message.nodeTree);
-                    expect(_converse.playSoundNotification, 1);
-                    _converse.play_sounds = false;
+                    test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
+                        _converse.play_sounds = true;
+                        spyOn(_converse, 'playSoundNotification');
+                        var view = _converse.chatboxviews.get('lounge@localhost');
+                        if (!view.$el.find('.chat-area').length) { view.renderChatArea(); }
+                        var text = 'This message will play a sound because it mentions dummy';
+                        var message = $msg({
+                            from: 'lounge@localhost/otheruser',
+                            id: '1',
+                            to: 'dummy@localhost',
+                            type: 'groupchat'
+                        }).c('body').t(text);
+                        view.onChatRoomMessage(message.nodeTree);
+                        expect(_converse.playSoundNotification).toHaveBeenCalled();
+
+                        text = "This message won't play a sound";
+                        message = $msg({
+                            from: 'lounge@localhost/otheruser',
+                            id: '2',
+                            to: 'dummy@localhost',
+                            type: 'groupchat'
+                        }).c('body').t(text);
+                        view.onChatRoomMessage(message.nodeTree);
+                        expect(_converse.playSoundNotification, 1);
+                        _converse.play_sounds = false;
+
+                        text = "This message won't play a sound because it is sent by dummy";
+                        message = $msg({
+                            from: 'lounge@localhost/dummy',
+                            id: '3',
+                            to: 'dummy@localhost',
+                            type: 'groupchat'
+                        }).c('body').t(text);
+                        view.onChatRoomMessage(message.nodeTree);
+                        expect(_converse.playSoundNotification, 1);
+                        _converse.play_sounds = false;
+                        done();
+                    });
                 }));
             });
         });

+ 14 - 5
spec/otr.js

@@ -1,13 +1,16 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
-    var $ = converse.env.jQuery;
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     var Strophe = converse.env.Strophe;
     var b64_sha1 = converse.env.b64_sha1;
 
     return describe("The OTR module", function() {
 
-        it("will add processing hints to sent out encrypted <message> stanzas", mock.initConverse(function (_converse) {
+        it("will add processing hints to sent out encrypted <message> stanzas",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
             test_utils.createContacts(_converse, 'current');
@@ -25,11 +28,16 @@
             expect($hints.get(1).tagName).toBe('no-permanent-store');
             expect($hints.get(2).tagName).toBe('no-copy');
             chatview.model.set('otr_status', UNENCRYPTED); // Reset again to UNENCRYPTED
+            done();
         }));
 
         describe("An OTR Chat Message", function () {
 
-            it("will not be carbon copied when it's sent out", mock.initConverse(function (_converse) {
+            it("will not be carbon copied when it's sent out",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 test_utils.openContactsPanel(_converse);
                 test_utils.createContacts(_converse, 'current');
@@ -46,6 +54,7 @@
                 expect($sent.find('private').length).toBe(1);
                 expect($sent.find('private').attr('xmlns')).toBe('urn:xmpp:carbons:2');
                 chatbox.set('otr_status', 0); // Reset again to UNENCRYPTED
+                done();
             }));
         });
     });

+ 12 - 2
spec/ping.js

@@ -6,20 +6,30 @@
     describe("XMPP Ping", function () {
         describe("Ping and pong handlers", function () {
 
-            it("are registered when _converse.js is connected", mock.initConverse(function (_converse) {
+            it("are registered when _converse.js is connected",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'registerPingHandler').and.callThrough();
                 spyOn(_converse, 'registerPongHandler').and.callThrough();
                 _converse.emit('connected');
                 expect(_converse.registerPingHandler).toHaveBeenCalled();
                 expect(_converse.registerPongHandler).toHaveBeenCalled();
+                done();
             }));
 
-            it("are registered when _converse.js reconnected", mock.initConverse(function (_converse) {
+            it("are registered when _converse.js reconnected",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'registerPingHandler').and.callThrough();
                 spyOn(_converse, 'registerPongHandler').and.callThrough();
                 _converse.emit('reconnected');
                 expect(_converse.registerPingHandler).toHaveBeenCalled();
                 expect(_converse.registerPongHandler).toHaveBeenCalled();
+                done();
             }));
         });
 

+ 6 - 1
spec/presence.js

@@ -49,7 +49,11 @@
 
     describe("A received presence stanza", function () {
 
-        it("has its priority taken into account", mock.initConverse(function (_converse) {
+        it("has its priority taken into account",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
             var contact_jid = mock.cur_names[8].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -218,6 +222,7 @@
             _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
             expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('offline');
             expect(_.keys(contact.get('resources')).length).toBe(0);
+            done();
         }));
     });
 }));

+ 50 - 15
spec/protocol.js

@@ -10,6 +10,7 @@
     var Strophe = converse.env.Strophe;
     var $iq = converse.env.$iq;
     var $pres = converse.env.$pres;
+    var _ = converse.env._;
     // See:
     // https://xmpp.org/rfcs/rfc3921.html
 
@@ -47,11 +48,15 @@
              * that session. A client MUST acknowledge each roster push with an IQ
              * stanza of type "result".
              */
-            it("Subscribe to contact, contact accepts and subscribes back", mock.initConverseWithAsync(function (done, _converse) {
+            it("Subscribe to contact, contact accepts and subscribes back",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'],
+                    { roster_groups: false },
+                    function (done, _converse) {
+
                 /* The process by which a user subscribes to a contact, including
                 * the interaction between roster items and subscription states.
                 */
-                _converse.roster_groups = false;
                 var contact, stanza, sent_stanza, IQ_id;
                 test_utils.openControlBox(_converse);
                 var panel = _converse.chatboxviews.get('controlbox').contactspanel;
@@ -68,15 +73,20 @@
                 panel.delegateEvents(); // Rebind all events so that our spy gets called
 
                 /* Add a new contact through the UI */
-                var $form = panel.$('form.add-xmpp-contact');
-                expect($form.is(":visible")).toBeFalsy();
+                var form = panel.el.querySelector('form.add-xmpp-contact');
+                expect(_.isNull(form)).toBeTruthy();
+
                 // Click the "Add a contact" link.
                 panel.$('.toggle-xmpp-contact-form').click();
-                // Check that the $form appears
-                expect($form.is(":visible")).toBeTruthy();
+
+                // Check that the form appears
+                form = panel.el.querySelector('form.add-xmpp-contact');
+                expect(form.parentElement.offsetHeight).not.toBe(0);
+                expect(_.includes(form.parentElement.classList, 'collapsed')).toBeFalsy();
+
                 // Fill in the form and submit
-                $form.find('input').val('contact@example.org');
-                $form.submit();
+                $(form).find('input').val('contact@example.org');
+                $(form).submit();
 
                 /* In preparation for being able to render the contact in the
                 * user's client interface and for the server to keep track of the
@@ -86,8 +96,11 @@
                 expect(panel.addContactFromForm).toHaveBeenCalled();
                 expect(_converse.roster.addAndSubscribe).toHaveBeenCalled();
                 expect(_converse.roster.addContact).toHaveBeenCalled();
-                // The form should not be visible anymore.
-                expect($form.is(":visible")).toBeFalsy();
+
+                // The form should not be visible anymore (by virtue of its
+                // parent being collapsed)
+                expect(form.parentElement.offsetHeight).toBe(0);
+                expect(_.includes(form.parentElement.classList, 'collapsed')).toBeTrue;
 
                 /* _converse request consists of sending an IQ
                 * stanza of type='set' containing a <query/> element qualified by
@@ -134,8 +147,10 @@
                 * </iq>
                 */
                 var create = _converse.roster.create;
+                var sent_stanzas = [];
                 spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
                     sent_stanza = stanza;
+                    sent_stanzas.push(stanza.toLocaleString());
                 });
                 spyOn(_converse.roster, 'create').and.callFake(function () {
                     contact = create.apply(_converse.roster, arguments);
@@ -165,6 +180,11 @@
                 *
                 *  <presence to='contact@example.org' type='subscribe'/>
                 */
+
+                test_utils.waitUntil(function () {
+                    return sent_stanzas.length == 1;
+                }, 300).then(function () {
+
                 expect(contact.subscribe).toHaveBeenCalled();
                 expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
                     "<presence to='contact@example.org' type='subscribe' xmlns='jabber:client'>"+
@@ -204,7 +224,8 @@
                 // contact in the roster.
                 test_utils.waitUntil(function () {
                     return $('a:contains("Pending contacts")').length;
-                }).then(function () {
+                }, 300).then(function () {
+
                     var $header = $('a:contains("Pending contacts")');
                     expect($header.length).toBe(1);
                     expect($header.is(":visible")).toBeTruthy();
@@ -347,9 +368,14 @@
                     expect($contacts.hasClass('both')).toBeTruthy();
                     done();
                 });
+                });
             }));
 
-            it("Alternate Flow: Contact Declines Subscription Request", mock.initConverse(function (_converse) {
+            it("Alternate Flow: Contact Declines Subscription Request",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 /* The process by which a user subscribes to a contact, including
                 * the interaction between roster items and subscription states.
                 */
@@ -429,11 +455,16 @@
                         "</query>"+
                     "</iq>"
                 );
+                done();
             }));
 
-            it("Unsubscribe to a contact when subscription is mutual", mock.initConverseWithAsync(function (done, _converse) {
+            it("Unsubscribe to a contact when subscription is mutual",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'],
+                    { roster_groups: false },
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id, jid = 'annegreet.gomez@localhost';
-                _converse.roster_groups = false;
                 test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current');
                 spyOn(window, 'confirm').and.returnValue(true);
@@ -490,7 +521,10 @@
                 });
             }));
 
-            it("Receiving a subscription request", mock.initConverse(function (_converse) {
+            it("Receiving a subscription request", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
                 test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
                 spyOn(_converse, "emit");
@@ -516,6 +550,7 @@
                     expect($header.is(":visible")).toBeTruthy();
                     var $contacts = $header.parent().nextUntil('dt', 'dd');
                     expect($contacts.length).toBe(1);
+                    done();
                 });
             }));
         });

+ 2 - 3
spec/register.js

@@ -1,7 +1,6 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
-    var $ = converse.env.jQuery;
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     var Strophe = converse.env.Strophe;
     var $iq = converse.env.$iq;
 

+ 71 - 57
spec/roomslist.js

@@ -3,15 +3,17 @@
 } (this, function (jasmine, mock, converse, roomslist, test_utils) {
     var _ = converse.env._;
     var $msg = converse.env.$msg;
+    var Promise = converse.env.Promise;
 
     describe("The converse-roomslist plugin", function () {
 
-        it("is shown under a list of open rooms in the \"Rooms\" panel", mock.initConverse(
+        it("is shown under a list of open rooms in the \"Rooms\" panel", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'],
             { whitelisted_plugins: ['converse-roomslist'],
               allow_bookmarks: false // Makes testing easier, otherwise we
                                      // have to mock stanza traffic.
             },
-            function (_converse) {
+            function (done, _converse) {
                 test_utils.openControlBox().openRoomsPanel(_converse);
                 var controlbox = _converse.chatboxviews.get('controlbox');
 
@@ -45,18 +47,20 @@
 
                 list = controlbox.el.querySelector('div.rooms-list-container');
                 expect(_.includes(list.classList, 'hidden')).toBeTruthy();
+                done();
             }
         ));
     });
 
     describe("An room shown in the rooms list", function () {
 
-        it("can be closed", mock.initConverse(
+        it("can be closed", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'],
             { whitelisted_plugins: ['converse-roomslist'],
               allow_bookmarks: false // Makes testing easier, otherwise we
                                      // have to mock stanza traffic.
             },
-            function (_converse) {
+            function (done, _converse) {
 
             spyOn(window, 'confirm').and.callFake(function () {
                 return true;
@@ -71,67 +75,77 @@
             var close_el = _converse.rooms_list_view.el.querySelector(".close-room");
             close_el.click();
             expect(window.confirm).toHaveBeenCalledWith(
-                'Are you sure you want to leave the room ""?');
+                'Are you sure you want to leave the room "lounge"?');
             room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
             expect(room_els.length).toBe(0);
             expect(_converse.chatboxes.length).toBe(1);
+            done();
         }));
 
-        it("shows unread messages directed at the user", mock.initConverse(
+        it("shows unread messages directed at the user", mock.initConverseWithAsync(
             { whitelisted_plugins: ['converse-roomslist'],
               allow_bookmarks: false // Makes testing easier, otherwise we
                                      // have to mock stanza traffic.
-            }, function (_converse) {
-
-            var room_jid = 'kitchen@conference.shakespeare.lit';
-            test_utils.openAndEnterChatRoom(
-                _converse, 'kitchen', 'conference.shakespeare.lit', 'romeo');
-            var view = _converse.chatboxviews.get(room_jid);
-            view.model.set({'minimized': true});
-
-            var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
-            var nick = mock.chatroom_names[0];
-            view.handleMUCMessage(
-                $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t('foo').tree());
-
-            // If the user isn't mentioned, the counter doesn't get incremented, but the text of the room is bold
-            var room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
-            expect(_.includes(room_el.classList, 'unread-msgs'));
-
-            // If the user is mentioned, the counter also gets updated
-            view.handleMUCMessage(
-                $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t('romeo: Your attention is required').tree()
-            );
-            var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
-            expect(indicator_el.textContent).toBe('1');
-
-            view.handleMUCMessage(
-                $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t('romeo: and another thing...').tree()
-            );
-            indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
-            expect(indicator_el.textContent).toBe('2');
-
-            // When the chat gets maximized again, the unread indicators are removed
-            view.model.set({'minimized': false});
-            indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
-            expect(_.isNull(indicator_el));
-            room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
-            expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
+            }, function (done, _converse) {
+
+            test_utils.waitUntil(function () {
+                    return !_.isUndefined(_converse.rooms_list_view)
+                }, 500)
+            .then(function () {
+                var room_jid = 'kitchen@conference.shakespeare.lit';
+                test_utils.openAndEnterChatRoom(
+                    _converse, 'kitchen', 'conference.shakespeare.lit', 'romeo').then(function () {
+
+                    var view = _converse.chatboxviews.get(room_jid);
+                    view.model.set({'minimized': true});
+                    var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
+                    var nick = mock.chatroom_names[0];
+                    view.handleMUCMessage(
+                        $msg({
+                            from: room_jid+'/'+nick,
+                            id: (new Date()).getTime(),
+                            to: 'dummy@localhost',
+                            type: 'groupchat'
+                        }).c('body').t('foo').tree());
+
+                    // If the user isn't mentioned, the counter doesn't get incremented, but the text of the room is bold
+                    var room_el = _converse.rooms_list_view.el.querySelector(
+                        ".available-chatroom"
+                    );
+                    expect(_.includes(room_el.classList, 'unread-msgs'));
+
+                    // If the user is mentioned, the counter also gets updated
+                    view.handleMUCMessage(
+                        $msg({
+                            from: room_jid+'/'+nick,
+                            id: (new Date()).getTime(),
+                            to: 'dummy@localhost',
+                            type: 'groupchat'
+                        }).c('body').t('romeo: Your attention is required').tree()
+                    );
+                    var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                    expect(indicator_el.textContent).toBe('1');
+
+                    view.handleMUCMessage(
+                        $msg({
+                            from: room_jid+'/'+nick,
+                            id: (new Date()).getTime(),
+                            to: 'dummy@localhost',
+                            type: 'groupchat'
+                        }).c('body').t('romeo: and another thing...').tree()
+                    );
+                    indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                    expect(indicator_el.textContent).toBe('2');
+
+                    // When the chat gets maximized again, the unread indicators are removed
+                    view.model.set({'minimized': false});
+                    indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                    expect(_.isNull(indicator_el));
+                    room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
+                    expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
+                    done();
+                });
+            });
         }));
     });
 }));

+ 2 - 2
spec/transcripts.js

@@ -1,5 +1,6 @@
 (function (root, factory) {
     define([
+        "jquery.noconflict",
         "converse-core",
         "mock",
         "test_utils",
@@ -7,10 +8,9 @@
         "transcripts"
         ], factory
     );
-} (this, function (converse, mock, test_utils, utils, transcripts) {
+} (this, function ($, converse, mock, test_utils, utils, transcripts) {
     var Strophe = converse.env.Strophe;
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
     var IGNORED_TAGS = [
         'stream:features',
         'auth',

+ 3 - 3
spec/utils.js

@@ -14,7 +14,7 @@
                 chatview_avatar_height: 32,
                 auto_join_rooms: [],
                 visible_toolbar_buttons: {
-                    'emoticons': true,
+                    'emojis': true,
                     'call': false,
                     'clear': true,
                     'toggle_occupants': true
@@ -31,7 +31,7 @@
                     'anonymous@conference.nomnom.im',
                 ],
                 visible_toolbar_buttons: {
-                    'emoticons': false,
+                    'emojis': false,
                     'call': false,
                     'toggle_occupants':false,
                     'invalid': false 
@@ -44,7 +44,7 @@
             expect(context.chatview_avatar_width).toBe(32);
             expect(context.chatview_avatar_height).toBe(48);
             expect(_.keys(context.visible_toolbar_buttons)).toEqual(_.keys(settings.visible_toolbar_buttons));
-            expect(context.visible_toolbar_buttons.emoticons).toBeFalsy();
+            expect(context.visible_toolbar_buttons.emojis).toBeFalsy();
             expect(context.visible_toolbar_buttons.call).toBeFalsy();
             expect(context.visible_toolbar_buttons.toggle_occupants).toBeFalsy();
             expect(context.visible_toolbar_buttons.invalid).toBeFalsy();

+ 2 - 3
spec/xmppstatus.js

@@ -1,7 +1,6 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
-    var $ = converse.env.jQuery;
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
 
     return describe("The XMPPStatus model", function() {
 

+ 9 - 0
src/build-esnext.js

@@ -0,0 +1,9 @@
+({
+    baseUrl: "../",
+    name: "almond",
+    mainConfigFile: 'config.js',
+    wrap: {
+        startFile: "start.frag",
+        endFile: "end.frag"
+    }
+});

+ 9 - 0
src/build-inverse-esnext.js

@@ -0,0 +1,9 @@
+({
+    baseUrl: "../",
+    name: "almond",
+    mainConfigFile: 'config.js',
+    wrap: {
+        startFile: "start.frag",
+        endFile: "inverse-end.frag"
+    }
+})

+ 22 - 0
src/build-inverse.js

@@ -2,6 +2,28 @@
     baseUrl: "../",
     name: "almond",
     mainConfigFile: 'config.js',
+    paths: {
+        "converse-bookmarks":       "builds/converse-bookmarks",
+        "converse-chatview":        "builds/converse-chatview",
+        "converse-controlbox":      "builds/converse-controlbox",
+        "converse-core":            "builds/converse-core",
+        "converse-dragresize":      "builds/converse-dragresize",
+        "converse-headline":        "builds/converse-headline",
+        "converse-inverse":         "builds/converse-inverse",
+        "converse-mam":             "builds/converse-mam",
+        "converse-minimize":        "builds/converse-minimize",
+        "converse-muc":             "builds/converse-muc",
+        "converse-muc-embedded":    "builds/converse-muc-embedded",
+        "converse-notification":    "builds/converse-notification",
+        "converse-otr":             "builds/converse-otr",
+        "converse-ping":            "builds/converse-ping",
+        "converse-register":        "builds/converse-register",
+        "converse-roomslist":       "builds/converse-roomslist",
+        "converse-rosterview":      "builds/converse-rosterview",
+        "converse-singleton":       "builds/converse-singleton",
+        "converse-vcard":           "builds/converse-vcard",
+        "utils":                    "builds/utils"
+    },
     wrap: {
         startFile: "start.frag",
         endFile: "inverse-end.frag"

+ 26 - 2
src/build-no-dependencies.js

@@ -36,11 +36,35 @@
         "strophe.vcard",
         "strophe.ping",
         "otr",
-        "lodash"
+        "lodash",
+        "lodash.converter",
+        "lodash.noconflict"
     ],
+    paths: {
+        "converse-bookmarks":       "builds/converse-bookmarks",
+        "converse-chatview":        "builds/converse-chatview",
+        "converse-controlbox":      "builds/converse-controlbox",
+        "converse-core":            "builds/converse-core",
+        "converse-dragresize":      "builds/converse-dragresize",
+        "converse-headline":        "builds/converse-headline",
+        "converse-inverse":         "builds/converse-inverse",
+        "converse-mam":             "builds/converse-mam",
+        "converse-minimize":        "builds/converse-minimize",
+        "converse-muc":             "builds/converse-muc",
+        "converse-muc-embedded":    "builds/converse-muc-embedded",
+        "converse-notification":    "builds/converse-notification",
+        "converse-otr":             "builds/converse-otr",
+        "converse-ping":            "builds/converse-ping",
+        "converse-register":        "builds/converse-register",
+        "converse-roomslist":       "builds/converse-roomslist",
+        "converse-rosterview":      "builds/converse-rosterview",
+        "converse-singleton":       "builds/converse-singleton",
+        "converse-vcard":           "builds/converse-vcard",
+        "utils":                    "builds/utils"
+    },
     wrap: {
         startFile: "start.frag",
         endFile: "end-no-dependencies.frag"
     },
     mainConfigFile: "config.js"
-})
+});

+ 23 - 1
src/build.js

@@ -2,8 +2,30 @@
     baseUrl: "../",
     name: "almond",
     mainConfigFile: 'config.js',
+    paths: {
+        "converse-bookmarks":       "builds/converse-bookmarks",
+        "converse-chatview":        "builds/converse-chatview",
+        "converse-controlbox":      "builds/converse-controlbox",
+        "converse-core":            "builds/converse-core",
+        "converse-dragresize":      "builds/converse-dragresize",
+        "converse-headline":        "builds/converse-headline",
+        "converse-inverse":         "builds/converse-inverse",
+        "converse-mam":             "builds/converse-mam",
+        "converse-minimize":        "builds/converse-minimize",
+        "converse-muc":             "builds/converse-muc",
+        "converse-muc-embedded":    "builds/converse-muc-embedded",
+        "converse-notification":    "builds/converse-notification",
+        "converse-otr":             "builds/converse-otr",
+        "converse-ping":            "builds/converse-ping",
+        "converse-register":        "builds/converse-register",
+        "converse-roomslist":       "builds/converse-roomslist",
+        "converse-rosterview":      "builds/converse-rosterview",
+        "converse-singleton":       "builds/converse-singleton",
+        "converse-vcard":           "builds/converse-vcard",
+        "utils":                    "builds/utils"
+    },
     wrap: {
         startFile: "start.frag",
         endFile: "end.frag"
     }
-})
+});

+ 21 - 8
src/config.js

@@ -17,16 +17,20 @@ require.config({
     paths: {
         "almond":                   "node_modules/almond/almond",
         "awesomplete":              "node_modules/awesomplete-avoid-xss/awesomplete",
+        "babel":                    "node_modules/requirejs-babel/babel-5.8.34.min",
         "backbone":                 "node_modules/backbone/backbone",
-        "backbone.noconflict":      "src/backbone.noconflict",
         "backbone.browserStorage":  "node_modules/backbone.browserStorage/backbone.browserStorage",
+        "backbone.noconflict":      "src/backbone.noconflict",
         "backbone.overview":        "node_modules/backbone.overview/backbone.overview",
+        "emojione":                 "node_modules/emojione/lib/js/emojione",
+        "es6-promise":              "node_modules/es6-promise/dist/es6-promise.auto",
         "eventemitter":             "node_modules/otr/build/dep/eventemitter",
-        "es6-promise":              "node_modules/es6-promise/dist/es6-promise",
         "jquery":                   "node_modules/jquery/dist/jquery",
-        "jquery.noconflict":        "src/jquery.noconflict",
         "jquery.browser":           "node_modules/jquery.browser/dist/jquery.browser",
-        "jquery.easing":            "node_modules/jquery-easing/jquery.easing.1.3.umd", // XXX: Only required for https://conversejs.org website
+        "jquery.noconflict":        "src/jquery.noconflict",
+        "lodash":                   "node_modules/lodash/lodash",
+        "lodash.converter":         "3rdparty/lodash.fp",
+        "lodash.noconflict":        "src/lodash.noconflict",
         "pluggable":                "node_modules/pluggable.js/dist/pluggable",
         "polyfill":                 "src/polyfill",
         "sizzle":                   "node_modules/jquery/sizzle/dist/sizzle",
@@ -38,11 +42,10 @@ require.config({
         "text":                     "node_modules/text/text",
         "tpl":                      "node_modules/lodash-template-loader/loader",
         "typeahead":                "components/typeahead.js/index",
-        "lodash":                   "node_modules/lodash/lodash",
-        "lodash.converter":         "3rdparty/lodash.fp",
-        "lodash.noconflict":        "src/lodash.noconflict",
         "underscore":               "src/underscore-shim",
         "utils":                    "src/utils",
+        "xss.noconflict":           "src/xss.noconflict",
+        "xss":                      "node_modules/xss/dist/xss",
 
         // Converse
         "converse":                 "src/converse",
@@ -52,6 +55,7 @@ require.config({
         "converse-chatview":        "src/converse-chatview",
         "converse-controlbox":      "src/converse-controlbox",
         "converse-core":            "src/converse-core",
+        "converse-disco":           "src/converse-disco",
         "converse-dragresize":      "src/converse-dragresize",
         "converse-headline":        "src/converse-headline",
         "converse-inverse":         "src/converse-inverse",
@@ -136,6 +140,15 @@ require.config({
 
     // define module dependencies for modules not using define
     shim: {
-        'awesomplete':          { exports: 'Awesomplete' }
+        'awesomplete':          { exports: 'Awesomplete'},
+        'emojione':             { exports: 'emojione'},
+        'xss':                  {
+            init: function (xss_noconflict) {
+                return {
+                    filterXSS: window.filterXSS,
+                    filterCSS: window.filterCSS
+                }
+            }
+        }
     }
 });

+ 116 - 97
src/converse-bookmarks.js

@@ -10,7 +10,8 @@
  * in XEP-0048.
  */
 (function (root, factory) {
-    define(["utils",
+    define(["jquery.noconflict",
+            "utils",
             "converse-core",
             "converse-muc",
             "tpl!chatroom_bookmark_form",
@@ -20,6 +21,7 @@
         ],
         factory);
 }(this, function (
+        $,
         utils,
         converse,
         muc,
@@ -29,13 +31,7 @@
         tpl_bookmarks_list
     ) {
 
-    var $ = converse.env.jQuery,
-        Backbone = converse.env.Backbone,
-        Strophe = converse.env.Strophe,
-        $iq = converse.env.$iq,
-        b64_sha1 = converse.env.b64_sha1,
-        sizzle = converse.env.sizzle,
-        _ = converse.env._;
+    const { Backbone, Promise, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
 
     converse.plugins.add('converse-bookmarks', {
         overrides: {
@@ -45,11 +41,12 @@
             //
             // New functions which don't exist yet can also be added.
 
-            clearSession: function () {
+            clearSession () {
                 this.__super__.clearSession.apply(this, arguments);
                 if (!_.isUndefined(this.bookmarks)) {
                     this.bookmarks.reset();
                     this.bookmarks.browserStorage._clear();
+                    window.sessionStorage.removeItem(this.bookmarks.fetched_flag);
                 }
             },
 
@@ -58,20 +55,20 @@
                     'click .toggle-bookmark': 'toggleBookmark'
                 },
 
-                initialize: function () {
+                initialize () {
                     this.__super__.initialize.apply(this, arguments);
                     this.model.on('change:bookmarked', this.onBookmarked, this);
                     this.setBookmarkState();
                 },
 
-                generateHeadingHTML: function () {
-                    var _converse = this.__super__._converse,
-                        __ = _converse.__,
+                generateHeadingHTML () {
+                    const { _converse } = this.__super__,
+                        { __ } = _converse,
                         html = this.__super__.generateHeadingHTML.apply(this, arguments);
                     if (_converse.allow_bookmarks) {
-                        var div = document.createElement('div');
+                        const div = document.createElement('div');
                         div.innerHTML = html;
-                        var bookmark_button = tpl_chatroom_bookmark_toggle(
+                        const bookmark_button = tpl_chatroom_bookmark_toggle(
                             _.assignIn(
                                 this.model.toJSON(),
                                 {
@@ -79,23 +76,23 @@
                                     bookmarked: this.model.get('bookmarked')
                                 }
                             ));
-                        var close_button = div.querySelector('.close-chatbox-button');
+                        const close_button = div.querySelector('.close-chatbox-button');
                         close_button.insertAdjacentHTML('afterend', bookmark_button);
                         return div.innerHTML;
                     }
                     return html;
                 },
 
-                checkForReservedNick: function () {
+                checkForReservedNick () {
                     /* Check if the user has a bookmark with a saved nickanme
                      * for this room, and if so use it.
                      * Otherwise delegate to the super method.
                      */
-                    var _converse = this.__super__._converse;
+                    const { _converse } = this.__super__;
                     if (_.isUndefined(_converse.bookmarks) || !_converse.allow_bookmarks) {
                         return this.__super__.checkForReservedNick.apply(this, arguments);
                     }
-                    var model = _converse.bookmarks.findWhere({'jid': this.model.get('jid')});
+                    const model = _converse.bookmarks.findWhere({'jid': this.model.get('jid')});
                     if (!_.isUndefined(model) && model.get('nick')) {
                         this.join(model.get('nick'));
                     } else {
@@ -103,7 +100,7 @@
                     }
                 },
 
-                onBookmarked: function () {
+                onBookmarked () {
                     if (this.model.get('bookmarked')) {
                         this.$('.icon-pushpin').addClass('button-on');
                     } else {
@@ -111,12 +108,12 @@
                     }
                 },
 
-                setBookmarkState: function () {
+                setBookmarkState () {
                     /* Set whether the room is bookmarked or not.
                      */
-                    var _converse = this.__super__._converse;
+                    const { _converse } = this.__super__;
                     if (!_.isUndefined(_converse.bookmarks)) {
-                        var models = _converse.bookmarks.where({'jid': this.model.get('jid')});
+                        const models = _converse.bookmarks.where({'jid': this.model.get('jid')});
                         if (!models.length) {
                             this.model.save('bookmarked', false);
                         } else {
@@ -125,9 +122,9 @@
                     }
                 },
 
-                renderBookmarkForm: function () {
-                    var _converse = this.__super__._converse,
-                        __ = _converse.__,
+                renderBookmarkForm () {
+                    const { _converse } = this.__super__,
+                        { __ } = _converse,
                         $body = this.$('.chatroom-body');
                     $body.children().addClass('hidden');
                     // Remove any existing forms
@@ -146,10 +143,10 @@
                     this.$('.chatroom-form .button-cancel').on('click', this.cancelConfiguration.bind(this));
                 },
 
-                onBookmarkFormSubmitted: function (ev) {
+                onBookmarkFormSubmitted (ev) {
                     ev.preventDefault();
-                    var _converse = this.__super__._converse;
-                    var $form = $(ev.target), that = this;
+                    const { _converse } = this.__super__;
+                    const $form = $(ev.target), that = this;
                     _converse.bookmarks.createBookmark({
                         'jid': this.model.get('jid'),
                         'autojoin': $form.find('input[name="autojoin"]').prop('checked'),
@@ -163,13 +160,13 @@
                         });
                 },
 
-                toggleBookmark: function (ev) {
+                toggleBookmark (ev) {
                     if (ev) {
                         ev.preventDefault();
                         ev.stopPropagation();
                     }
-                    var _converse = this.__super__._converse;
-                    var models = _converse.bookmarks.where({'jid': this.model.get('jid')});
+                    const { _converse } = this.__super__;
+                    const models = _converse.bookmarks.where({'jid': this.model.get('jid')});
                     if (!models.length) {
                         this.renderBookmarkForm();
                     } else {
@@ -182,13 +179,13 @@
             }
         },
 
-        initialize: function () {
+        initialize () {
             /* The initialize function gets called as soon as the plugin is
              * loaded by converse.js's plugin machinery.
              */
-            var _converse = this._converse,
-                __ = _converse.__,
-                ___ = _converse.___;
+            const { _converse } = this,
+                { __,
+                ___ } = _converse;
 
             // Configuration values for this plugin
             // ====================================
@@ -201,6 +198,31 @@
             // Promises exposed by this plugin
             _converse.api.promises.add('bookmarksInitialized');
 
+            // Pure functions on the _converse object
+            _.extend(_converse, {
+                removeBookmarkViaEvent (ev) {
+                    /* Remove a bookmark as determined by the passed in
+                     * event.
+                     */
+                    ev.preventDefault();
+                    const name = ev.target.getAttribute('data-bookmark-name');
+                    const jid = ev.target.getAttribute('data-room-jid');
+                    if (confirm(__(___("Are you sure you want to remove the bookmark \"%1$s\"?"), name))) {
+                        _.invokeMap(_converse.bookmarks.where({'jid': jid}), Backbone.Model.prototype.destroy);
+                    }
+                },
+
+                addBookmarkViaEvent (ev) {
+                    /* Add a bookmark as determined by the passed in
+                     * event.
+                     */
+                    ev.preventDefault();
+                    const jid = ev.target.getAttribute('data-room-jid');
+                    const chatroom = _converse.openChatRoom({'jid': jid}, true);
+                    _converse.chatboxviews.get(jid).renderBookmarkForm();
+                },
+            });
+
             _converse.Bookmark = Backbone.Model;
 
             _converse.BookmarksList = Backbone.Model.extend({
@@ -212,56 +234,55 @@
             _converse.Bookmarks = Backbone.Collection.extend({
                 model: _converse.Bookmark,
 
-                initialize: function () {
+                initialize () {
                     this.on('add', _.flow(this.openBookmarkedRoom, this.markRoomAsBookmarked));
                     this.on('remove', this.markRoomAsUnbookmarked, this);
                     this.on('remove', this.sendBookmarkStanza, this);
 
-                    var cache_key = 'converse.room-bookmarks'+_converse.bare_jid;
-                    this.cached_flag = b64_sha1(cache_key+'fetched');
+                    const cache_key = `converse.room-bookmarks${_converse.bare_jid}`;
+                    this.fetched_flag = b64_sha1(cache_key+'fetched');
                     this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
                         b64_sha1(cache_key)
                     );
                 },
 
-                openBookmarkedRoom: function (bookmark) {
+                openBookmarkedRoom (bookmark) {
                     if (bookmark.get('autojoin')) {
                         _converse.api.rooms.open(bookmark.get('jid'), bookmark.get('nick'));
                     }
                     return bookmark;
                 },
 
-                fetchBookmarks: function () {
-                    var deferred = new $.Deferred();
-                    var promise = deferred.promise();
-                    if (window.sessionStorage.getItem(this.browserStorage.name)) {
+                fetchBookmarks () {
+                    const deferred = utils.getWrappedPromise();
+                    if (this.browserStorage.records.length > 0) {
                         this.fetch({
                             'success': _.bind(this.onCachedBookmarksFetched, this, deferred),
                             'error':  _.bind(this.onCachedBookmarksFetched, this, deferred)
                         });
-                    } else if (! window.sessionStorage.getItem(this.cached_flag)) {
-                        // There aren't any cached bookmarks, and the cache is
-                        // not set to null. So we query the XMPP server.
+                    } else if (! window.sessionStorage.getItem(this.fetched_flag)) {
+                        // There aren't any cached bookmarks and the
+                        // `fetched_flag` is off, so we query the XMPP server.
                         // If nothing is returned from the XMPP server, we set
-                        // the cache to null to avoid calling the server again.
+                        // the `fetched_flag` to avoid calling the server again.
                         this.fetchBookmarksFromServer(deferred);
                     } else {
                         deferred.resolve();
                     }
-                    return promise;
+                    return deferred.promise;
                 },
 
-                onCachedBookmarksFetched: function (deferred) {
+                onCachedBookmarksFetched (deferred) {
                     return deferred.resolve();
                 },
 
-                createBookmark: function (options) {
+                createBookmark (options) {
                     _converse.bookmarks.create(options);
                     _converse.bookmarks.sendBookmarkStanza();
                 },
 
-                sendBookmarkStanza: function () {
-                    var stanza = $iq({
+                sendBookmarkStanza () {
+                    let stanza = $iq({
                             'type': 'set',
                             'from': _converse.connection.jid,
                         })
@@ -288,7 +309,7 @@
                     _converse.connection.sendIQ(stanza, null, this.onBookmarkError.bind(this));
                 },
 
-                onBookmarkError: function (iq) {
+                onBookmarkError (iq) {
                     _converse.log("Error while trying to add bookmark", Strophe.LogLevel.ERROR);
                     _converse.log(iq);
                     // We remove all locally cached bookmarks and fetch them
@@ -298,8 +319,8 @@
                     window.alert(__("Sorry, something went wrong while trying to save your bookmark."));
                 },
 
-                fetchBookmarksFromServer: function (deferred) {
-                    var stanza = $iq({
+                fetchBookmarksFromServer (deferred) {
+                    const stanza = $iq({
                         'from': _converse.connection.jid,
                         'type': 'get',
                     }).c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
@@ -311,25 +332,25 @@
                     );
                 },
 
-                markRoomAsBookmarked: function (bookmark) {
-                    var room = _converse.chatboxes.get(bookmark.get('jid'));
+                markRoomAsBookmarked (bookmark) {
+                    const room = _converse.chatboxes.get(bookmark.get('jid'));
                     if (!_.isUndefined(room)) {
                         room.save('bookmarked', true);
                     }
                 },
 
-                markRoomAsUnbookmarked: function (bookmark) {
-                    var room = _converse.chatboxes.get(bookmark.get('jid'));
+                markRoomAsUnbookmarked (bookmark) {
+                    const room = _converse.chatboxes.get(bookmark.get('jid'));
                     if (!_.isUndefined(room)) {
                         room.save('bookmarked', false);
                     }
                 },
 
-                onBookmarksReceived: function (deferred, iq) {
-                    var bookmarks = $(iq).find(
+                onBookmarksReceived (deferred, iq) {
+                    const bookmarks = $(iq).find(
                         'items[node="storage:bookmarks"] item[id="current"] storage conference'
                     );
-                    var that = this;
+                    const that = this;
                     _.forEach(bookmarks, function (bookmark) {
                         that.create({
                             'jid': bookmark.getAttribute('jid'),
@@ -343,10 +364,10 @@
                     }
                 },
 
-                onBookmarksReceivedError: function (deferred, iq) {
-                    window.sessionStorage.setItem(this.cached_flag, true);
+                onBookmarksReceivedError (deferred, iq) {
+                    window.sessionStorage.setItem(this.fetched_flag, true);
                     _converse.log('Error while fetching bookmarks', Strophe.LogLevel.ERROR);
-                    _converse.log(iq);
+                    _converse.log(iq, Strophe.LogLevel.DEBUG);
                     if (!_.isNil(deferred)) {
                         return deferred.reject();
                     }
@@ -357,17 +378,18 @@
                 tagName: 'div',
                 className: 'bookmarks-list, rooms-list-container',
                 events: {
-                    'click .remove-bookmark': 'removeBookmark',
-                    'click .bookmarks-toggle': 'toggleBookmarksList'
+                    'click .add-bookmark': 'addBookmark',
+                    'click .bookmarks-toggle': 'toggleBookmarksList',
+                    'click .remove-bookmark': 'removeBookmark'
                 },
 
-                initialize: function () {
+                initialize () {
                     this.model.on('add', this.renderBookmarkListElement, this);
                     this.model.on('remove', this.removeBookmarkListElement, this);
                     _converse.chatboxes.on('add', this.renderBookmarkListElement, this);
                     _converse.chatboxes.on('remove', this.renderBookmarkListElement, this);
 
-                    var cachekey = 'converse.room-bookmarks'+_converse.bare_jid+'-list-model';
+                    const cachekey = `converse.room-bookmarks${_converse.bare_jid}-list-model`;
                     this.list_model = new _converse.BookmarksList();
                     this.list_model.id = cachekey;
                     this.list_model.browserStorage = new Backbone.BrowserStorage[_converse.storage](
@@ -377,7 +399,7 @@
                     this.render();
                 },
 
-                render: function () {
+                render () {
                     this.$el.html(tpl_bookmarks_list({
                         'toggle_state': this.list_model.get('toggle-state'),
                         'desc_bookmarks': __('Click to toggle the bookmarks list'),
@@ -387,23 +409,17 @@
                         this.$('.bookmarks').hide();
                     }
                     this.model.each(this.renderBookmarkListElement.bind(this));
-                    var controlboxview = _converse.chatboxviews.get('controlbox');
+                    const controlboxview = _converse.chatboxviews.get('controlbox');
                     if (!_.isUndefined(controlboxview)) {
                         this.$el.prependTo(controlboxview.$('#chatrooms'));
                     }
                     return this.$el;
                 },
 
-                removeBookmark: function (ev) {
-                    ev.preventDefault();
-                    var name = $(ev.target).data('bookmarkName');
-                    var jid = $(ev.target).data('roomJid');
-                    if (confirm(__(___("Are you sure you want to remove the bookmark \"%1$s\"?"), name))) {
-                        _.invokeMap(_converse.bookmarks.where({'jid': jid}), Backbone.Model.prototype.destroy);
-                    }
-                },
+                removeBookmark: _converse.removeBookmarkViaEvent,
+                addBookmark: _converse.addBookmarkViaEvent,
 
-                renderBookmarkListElement: function (item) {
+                renderBookmarkListElement (item) {
                     if (item instanceof _converse.ChatBox) {
                         item = _.head(this.model.where({'jid': item.get('jid')}));
                         if (_.isNil(item)) {
@@ -420,8 +436,8 @@
                         return;
                     }
 
-                    var list_el = this.el.querySelector('.bookmarks');
-                    var div = document.createElement('div');
+                    const list_el = this.el.querySelector('.bookmarks');
+                    const div = document.createElement('div');
                     div.innerHTML = tpl_bookmark({
                         'bookmarked': true,
                         'info_leave_room': __('Leave this room'),
@@ -432,8 +448,8 @@
                         'name': item.get('name'),
                         'open_title': __('Click to open this room')
                     });
-                    var el = _.head(sizzle(
-                        '.available-chatroom[data-room-jid="'+item.get('jid')+'"]',
+                    const el = _.head(sizzle(
+                        `.available-chatroom[data-room-jid="${item.get('jid')}"]`,
                         list_el));
 
                     if (el) {
@@ -444,19 +460,19 @@
                     this.show();
                 },
 
-                show: function () {
+                show () {
                     if (!this.$el.is(':visible')) {
                         this.$el.show();
                     }
                 },
 
-                hide: function () {
+                hide () {
                     this.$el.hide();
                 },
 
-                removeBookmarkListElement: function (item) {
-                    var list_el = this.el.querySelector('.bookmarks');
-                    var el = _.head(sizzle('.available-chatroom[data-room-jid="'+item.get('jid')+'"]', list_el));
+                removeBookmarkListElement (item) {
+                    const list_el = this.el.querySelector('.bookmarks');
+                    const el = _.head(sizzle(`.available-chatroom[data-room-jid="${item.get('jid')}"]`, list_el));
                     if (el) {
                         list_el.removeChild(el);
                     }
@@ -465,9 +481,9 @@
                     }
                 },
 
-                toggleBookmarksList: function (ev) {
+                toggleBookmarksList (ev) {
                     if (ev && ev.preventDefault) { ev.preventDefault(); }
-                    var $el = $(ev.target);
+                    const $el = $(ev.target);
                     if ($el.hasClass("icon-opened")) {
                         this.$('.bookmarks').slideUp('fast');
                         this.list_model.save({'toggle-state': _converse.CLOSED});
@@ -480,22 +496,25 @@
                 }
             });
 
-            var initBookmarks = function () {
+            const initBookmarks = function () {
                 if (!_converse.allow_bookmarks) {
                     return;
                 }
                 _converse.bookmarks = new _converse.Bookmarks();
-                _converse.bookmarks.fetchBookmarks().always(function () {
+                _converse.bookmarks.fetchBookmarks().then(function () {
                     _converse.bookmarksview = new _converse.BookmarksView(
                         {'model': _converse.bookmarks}
                     );
                     _converse.emit('bookmarksInitialized');
                 });
             };
-            $.when(_converse.api.waitUntil('chatBoxesFetched'),
-                   _converse.api.waitUntil('roomsPanelRendered')).then(initBookmarks);
 
-            var afterReconnection = function () {
+            Promise.all([
+                _converse.api.waitUntil('chatBoxesFetched'),
+                _converse.api.waitUntil('roomsPanelRendered')
+            ]).then(initBookmarks);
+
+            const afterReconnection = function () {
                 if (!_converse.allow_bookmarks) {
                     return;
                 }

+ 318 - 176
src/converse-chatview.js

@@ -8,10 +8,14 @@
 
 (function (root, factory) {
     define([
+            "jquery.noconflict",
             "converse-core",
+            "emojione",
+            "xss",
             "tpl!chatbox",
             "tpl!new_day",
             "tpl!action",
+            "tpl!emojis",
             "tpl!message",
             "tpl!help_message",
             "tpl!toolbar",
@@ -19,10 +23,14 @@
             "tpl!spinner"
     ], factory);
 }(this, function (
+            $,
             converse,
+            emojione,
+            xss,
             tpl_chatbox,
             tpl_new_day,
             tpl_action,
+            tpl_emojis,
             tpl_message,
             tpl_help_message,
             tpl_toolbar,
@@ -30,20 +38,13 @@
             tpl_spinner
     ) {
     "use strict";
-    var $ = converse.env.jQuery,
-        $msg = converse.env.$msg,
-        Backbone = converse.env.Backbone,
-        Strophe = converse.env.Strophe,
-        _ = converse.env._,
-        moment = converse.env.moment,
-        utils = converse.env.utils;
-
-    var KEY = {
+    const { $msg, Backbone, Strophe, _, b64_sha1, moment, utils } = converse.env;
+
+    const KEY = {
         ENTER: 13,
         FORWARD_SLASH: 47
     };
 
-
     converse.plugins.add('converse-chatview', {
 
         overrides: {
@@ -52,11 +53,26 @@
             // relevant objects or classes.
             //
             // New functions which don't exist yet can also be added.
+            //
+            registerGlobalEventHandlers: function () {
+                this.__super__.registerGlobalEventHandlers();
+                document.addEventListener(
+                    'click', function (ev) {
+                        if (_.includes(ev.target.classList, 'toggle-toolbar-menu') ||
+                            _.includes(ev.target.classList, 'insert-emoji')) {
+                            return;
+                        }
+                        utils.slideInAllElements(
+                            document.querySelectorAll('.toolbar-menu')
+                        )
+                    }
+                );
+            },
 
             ChatBoxViews: {
-                onChatBoxAdded: function (item) {
-                    var _converse = this.__super__._converse;
-                    var view = this.get(item.get('id'));
+                onChatBoxAdded (item) {
+                    const { _converse } = this.__super__;
+                    let view = this.get(item.get('id'));
                     if (!view) {
                         view = new _converse.ChatBoxView({model: item});
                         this.add(item.get('id'), view);
@@ -68,35 +84,138 @@
             }
         },
 
-
-        initialize: function () {
+        initialize () {
             /* The initialize function gets called as soon as the plugin is
              * loaded by converse.js's plugin machinery.
              */
-            var _converse = this._converse,
-                __ = _converse.__;
+            const { _converse } = this,
+                { __ } = _converse;
 
             _converse.api.settings.update({
-                chatview_avatar_height: 32,
-                chatview_avatar_width: 32,
-                show_toolbar: true,
-                time_format: 'HH:mm',
-                visible_toolbar_buttons: {
-                    'emoticons': true,
+                'use_emojione': true,
+                'emojione_image_path': emojione.imagePathPNG,
+                'chatview_avatar_height': 32,
+                'chatview_avatar_width': 32,
+                'show_toolbar': true,
+                'time_format': 'HH:mm',
+                'visible_toolbar_buttons': {
+                    'emoji': true,
                     'call': false,
                     'clear': true
                 },
             });
+            emojione.imagePathPNG = _converse.emojione_image_path;
+            emojione.ascii = true;
 
-            var onWindowStateChanged = function (data) {
-                var state = data.state;
+            function onWindowStateChanged (data) {
                 _converse.chatboxviews.each(function (chatboxview) {
-                    chatboxview.onWindowStateChanged(state);
-                })
-            };
-
+                    chatboxview.onWindowStateChanged(data.state);
+                });
+            }
             _converse.api.listen.on('windowStateChanged', onWindowStateChanged);
 
+            _converse.EmojiPicker = Backbone.Model.extend({ 
+                defaults: {
+                    'current_category': 'people',
+                    'current_skintone': '',
+                    'scroll_position': 0
+                },
+                initialize () {
+                    const id = `converse.emoji-${_converse.bare_jid}`;
+                    this.id = id;
+                    this.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
+                }
+            });
+
+            _converse.EmojiPickerView = Backbone.View.extend({
+                className: 'emoji-picker-container toolbar-menu collapsed',
+                events: {
+                    'click .emoji-category-picker li.emoji-category': 'chooseCategory',
+                    'click .emoji-skintone-picker li.emoji-skintone': 'chooseSkinTone'
+                },
+
+                initialize () {
+                    this.model.on('change:current_skintone', this.render, this);
+                    this.model.on('change:current_category', this.render, this);
+                    this.setScrollPosition = _.debounce(this.setScrollPosition, 50);
+                },
+
+                render () {
+                    var emojis_html = tpl_emojis(
+                        _.extend(
+                            this.model.toJSON(), {
+                                'transform': _converse.use_emojione ? emojione.shortnameToImage : emojione.shortnameToUnicode,
+                                'emojis_by_category': utils.getEmojisByCategory(_converse, emojione),
+                                'toned_emojis': utils.getTonedEmojis(_converse),
+                                'skintones': ['tone1', 'tone2', 'tone3', 'tone4', 'tone5'],
+                                'shouldBeHidden': this.shouldBeHidden
+                            }
+                        ));
+                    this.el.innerHTML = emojis_html;
+                    _.forEach(this.el.querySelectorAll('.emoji-picker'), (el) => {
+                        el.addEventListener('scroll', this.setScrollPosition.bind(this));
+                    });
+                    this.restoreScrollPosition();
+                    return this;
+                },
+
+                shouldBeHidden (shortname, current_skintone, toned_emojis) {
+                    /* Helper method for the template which decides whether an
+                     * emoji should be hidden, based on which skin tone is
+                     * currently being applied.
+                     */
+                    if (_.includes(shortname, '_tone')) {
+                        if (!current_skintone || !_.includes(shortname, current_skintone)) {
+                            return true;
+                        }
+                    } else {
+                        if (current_skintone && _.includes(toned_emojis, shortname)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                },
+
+                restoreScrollPosition () {
+                    const current_picker = _.difference(
+                        this.el.querySelectorAll('.emoji-picker'),
+                        this.el.querySelectorAll('.emoji-picker.hidden')
+                    );
+                    if (current_picker.length === 1 && this.model.get('scroll_position')) {
+                        current_picker[0].scrollTop = this.model.get('scroll_position');
+                    }
+                },
+
+                setScrollPosition (ev) {
+                    this.model.save('scroll_position', ev.target.scrollTop);
+                },
+
+                chooseSkinTone (ev) {
+                    ev.preventDefault();
+                    ev.stopPropagation();
+                    const target = ev.target.nodeName === 'IMG' ?
+                        ev.target.parentElement : ev.target;
+                    const skintone = target.getAttribute("data-skintone").trim();
+                    if (this.model.get('current_skintone') === skintone) {
+                        this.model.save({'current_skintone': ''});
+                    } else {
+                        this.model.save({'current_skintone': skintone});
+                    }
+                },
+
+                chooseCategory (ev) {
+                    ev.preventDefault();
+                    ev.stopPropagation();
+                    const target = ev.target.nodeName === 'IMG' ?
+                        ev.target.parentElement : ev.target;
+                    const category = target.getAttribute("data-category").trim();
+                    this.model.save({
+                        'current_category': category,
+                        'scroll_position': 0
+                    });
+                }
+            });
+
             _converse.ChatBoxView = Backbone.View.extend({
                 length: 200,
                 tagName: 'div',
@@ -106,15 +225,18 @@
                 events: {
                     'click .close-chatbox-button': 'close',
                     'keypress .chat-textarea': 'keyPressed',
-                    'click .send-button': 'onSendButtonClicked',
-                    'click .toggle-smiley': 'toggleEmoticonMenu',
-                    'click .toggle-smiley ul li': 'insertEmoticon',
+                    'click .send-button': 'onFormSubmitted',
+                    'click .toggle-smiley': 'toggleEmojiMenu',
+                    'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
                     'click .toggle-clear': 'clearMessages',
                     'click .toggle-call': 'toggleCall',
                     'click .new-msgs-indicator': 'viewUnreadMessages'
                 },
 
-                initialize: function () {
+                initialize () {
+                    this.markScrolled = _.debounce(this.markScrolled, 100);
+
+                    this.createEmojiPicker();
                     this.model.messages.on('add', this.onMessageAdded, this);
                     this.model.on('show', this.show, this);
                     this.model.on('destroy', this.hide, this);
@@ -129,7 +251,7 @@
                     _converse.emit('chatBoxInitialized', this);
                 },
 
-                render: function () {
+                render () {
                     this.$el.attr('id', this.model.get('box_id'))
                         .html(tpl_chatbox(
                                 _.extend(this.model.toJSON(), {
@@ -152,15 +274,26 @@
                     return this.showStatusMessage();
                 },
 
-                afterMessagesFetched: function () {
+                createEmojiPicker () {
+                    if (_.isUndefined(_converse.emojipicker)) {
+                        _converse.emojipicker = new _converse.EmojiPicker();
+                        _converse.emojipicker.fetch();
+                    }
+                    this.emoji_picker_view = new _converse.EmojiPickerView({
+                        'model': _converse.emojipicker
+                    });
+                },
+
+                afterMessagesFetched () {
                     this.insertIntoDOM();
                     this.scrollDown();
                     // We only start listening for the scroll event after
                     // cached messages have been fetched
                     this.$content.on('scroll', this.markScrolled.bind(this));
+                    _converse.emit('afterMessagesFetched', this);
                 },
 
-                fetchMessages: function () {
+                fetchMessages () {
                     this.model.messages.fetch({
                         'add': true,
                         'success': this.afterMessagesFetched.bind(this),
@@ -169,26 +302,26 @@
                     return this;
                 },
 
-                insertIntoDOM: function () {
+                insertIntoDOM () {
                     /* This method gets overridden in src/converse-controlbox.js if
                      * the controlbox plugin is active.
                      */
-                    var container = document.querySelector('#conversejs');
+                    const container = document.querySelector('#conversejs');
                     if (this.el.parentNode !== container) {
                         container.insertBefore(this.el, container.firstChild);
                     }
                     return this;
                 },
 
-                clearStatusNotification: function () {
+                clearStatusNotification () {
                     this.$content.find('div.chat-event').remove();
                 },
 
-                showStatusNotification: function (message, keep_old, permanent) {
+                showStatusNotification (message, keep_old, permanent) {
                     if (!keep_old) {
                         this.clearStatusNotification();
                     }
-                    var $el = $('<div class="chat-info"></div>').text(message);
+                    const $el = $('<div class="chat-info"></div>').text(message);
                     if (!permanent) {
                         $el.addClass('chat-event');
                     }
@@ -196,19 +329,19 @@
                     this.scrollDown();
                 },
 
-                addSpinner: function () {
+                addSpinner () {
                     if (_.isNull(this.el.querySelector('.spinner'))) {
                         this.$content.prepend(tpl_spinner);
                     }
                 },
 
-                clearSpinner: function () {
+                clearSpinner () {
                     if (this.$content.children(':first').is('span.spinner')) {
                         this.$content.children(':first').remove();
                     }
                 },
 
-                insertDayIndicator: function (date, prepend) {
+                insertDayIndicator (date, prepend) {
                     /* Appends (or prepends if "prepend" is truthy) an indicator
                      * into the chat area, showing the day as given by the
                      * passed in date.
@@ -216,15 +349,15 @@
                      * Parameters:
                      *  (String) date - An ISO8601 date string.
                      */
-                    var day_date = moment(date).startOf('day');
-                    var insert = prepend ? this.$content.prepend: this.$content.append;
+                    const day_date = moment(date).startOf('day');
+                    const insert = prepend ? this.$content.prepend: this.$content.append;
                     insert.call(this.$content, tpl_new_day({
                         isodate: day_date.format(),
                         datestring: day_date.format("dddd MMM Do YYYY")
                     }));
                 },
 
-                insertMessage: function (attrs, prepend) {
+                insertMessage (attrs, prepend) {
                     /* Helper method which appends a message (or prepends if the
                      * 2nd parameter is set to true) to the end of the chat box's
                      * content area.
@@ -232,18 +365,16 @@
                      * Parameters:
                      *  (Object) attrs: An object containing the message attributes.
                      */
-                    var that = this;
-                    var insert = prepend ? this.$content.prepend : this.$content.append;
-                    _.flow(
-                        function ($el) {
-                            insert.call(that.$content, $el);
+                    const insert = prepend ? this.$content.prepend : this.$content.append;
+                    _.flow(($el) => {
+                            insert.call(this.$content, $el);
                             return $el;
                         },
                         this.scrollDown.bind(this)
                     )(this.renderMessage(attrs));
                 },
 
-                showMessage: function (attrs) {
+                showMessage (attrs) {
                     /* Inserts a chat message into the content area of the chat box.
                      * Will also insert a new day indicator if the message is on a
                      * different day.
@@ -255,11 +386,9 @@
                      *  (Object) attrs: An object containing the message
                      *      attributes.
                      */
-                    var msg_dates,
-                        $first_msg = this.$content.find('.chat-message:first'),
-                        first_msg_date = $first_msg.data('isodate'),
-                        current_msg_date = moment(attrs.time) || moment,
-                        last_msg_date = this.$content.find('.chat-message:last').data('isodate');
+                    let current_msg_date = moment(attrs.time) || moment;
+                    const $first_msg = this.$content.find('.chat-message:first'),
+                          first_msg_date = $first_msg.data('isodate');
 
                     if (!first_msg_date) {
                         // This is the first received message, so we insert a
@@ -268,6 +397,8 @@
                         this.insertMessage(attrs);
                         return;
                     }
+
+                    const last_msg_date = this.$content.find('.chat-message:last').data('isodate');
                     if (current_msg_date.isAfter(last_msg_date) ||
                             current_msg_date.isSame(last_msg_date)) {
                         // The new message is after the last message
@@ -294,22 +425,24 @@
                     }
                     // Find the correct place to position the message
                     current_msg_date = current_msg_date.format();
-                    msg_dates = _.map(this.$content.find('.chat-message'), function (el) {
-                        return $(el).data('isodate');
-                    });
+                    const msg_dates = _.map(
+                        this.$content.find('.chat-message'),
+                        (el) => $(el).data('isodate')
+                    );
                     msg_dates.push(current_msg_date);
                     msg_dates.sort();
-                    var idx = msg_dates.indexOf(current_msg_date)-1;
-                    var $latest_message = this.$content.find('.chat-message[data-isodate="'+msg_dates[idx]+'"]:last');
-                    _.flow(
-                        function ($el) {
+
+                    const idx = msg_dates.indexOf(current_msg_date)-1;
+                    const $latest_message = this.$content.find(`.chat-message[data-isodate="${msg_dates[idx]}"]:last`);
+                    _.flow(($el) => {
                             $el.insertAfter($latest_message);
+                            return $el;
                         },
                         this.scrollDown.bind(this)
                     )(this.renderMessage(attrs));
                 },
 
-                getExtraMessageTemplateAttributes: function () {
+                getExtraMessageTemplateAttributes () {
                     /* Provides a hook for sending more attributes to the
                      * message template.
                      *
@@ -319,11 +452,11 @@
                     return {};
                 },
 
-                getExtraMessageClasses: function (attrs) {
+                getExtraMessageClasses (attrs) {
                     return attrs.delayed && 'delayed' || '';
                 },
 
-                renderMessage: function (attrs) {
+                renderMessage (attrs) {
                     /* Renders a chat message based on the passed in attributes.
                      *
                      * Parameters:
@@ -332,12 +465,11 @@
                      *  Returns:
                      *      The DOM element representing the message.
                      */
-                    var msg_time = moment(attrs.time) || moment,
-                        text = attrs.message,
-                        match = text.match(/^\/(.*?)(?: (.*))?$/),
+                    let text = attrs.message,
                         fullname = this.model.get('fullname') || attrs.fullname,
                         template, username;
 
+                    const match = text.match(/^\/(.*?)(?: (.*))?$/);
                     if ((match) && (match[1] === 'me')) {
                         text = text.replace(/^\/me/, '');
                         template = tpl_action;
@@ -361,7 +493,8 @@
                                "Output has been shortened."),
                             true, true);
                     }
-                    var $msg = $(template(
+                    const msg_time = moment(attrs.time) || moment;
+                    const $msg = $(template(
                         _.extend(this.getExtraMessageTemplateAttributes(attrs), {
                             'msgid': attrs.msgid,
                             'sender': attrs.sender,
@@ -371,21 +504,21 @@
                             'extra_classes': this.getExtraMessageClasses(attrs)
                         })
                     ));
-                    $msg.find('.chat-msg-content').first()
-                        .text(text)
-                        .addHyperlinks()
-                        .addEmoticons(_converse.visible_toolbar_buttons.emoticons);
+                    const msg_content = $msg[0].querySelector('.chat-msg-content');
+                    msg_content.innerHTML = utils.addEmoji(
+                        _converse, emojione, utils.addHyperlinks(xss.filterXSS(text, {'whiteList': {}}))
+                    );
+                    utils.renderImageURLs(msg_content);
                     return $msg;
                 },
 
-                showHelpMessages: function (msgs, type, spinner) {
-                    var i, msgs_length = msgs.length;
-                    for (i=0; i<msgs_length; i++) {
+                showHelpMessages (msgs, type, spinner) {
+                    _.each(msgs, (msg) => {
                         this.$content.append($(tpl_help_message({
                             'type': type||'info',
-                            'message': msgs[i]
+                            'message': msgs
                         })));
-                    }
+                    });
                     if (spinner === true) {
                         this.$content.append(tpl_spinner);
                     } else if (spinner === false) {
@@ -394,7 +527,7 @@
                     return this.scrollDown();
                 },
 
-                handleChatStateMessage: function (message) {
+                handleChatStateMessage (message) {
                     if (message.get('chat_state') === _converse.COMPOSING) {
                         if (message.get('sender') === 'me') {
                             this.showStatusNotification(__('Typing from another device'));
@@ -415,11 +548,11 @@
                     }
                 },
 
-                shouldShowOnTextMessage: function () {
+                shouldShowOnTextMessage () {
                     return !this.$el.is(':visible');
                 },
 
-                handleTextMessage: function (message) {
+                handleTextMessage (message) {
                     this.showMessage(_.clone(message.attributes));
                     if (utils.isNewMessage(message) && message.get('sender') === 'me') {
                         // We remove the "scrolled" flag so that the chat area
@@ -439,15 +572,15 @@
                     }
                 },
 
-                handleErrorMessage: function (message) {
-                    var $message = $('[data-msgid='+message.get('msgid')+']');
+                handleErrorMessage (message) {
+                    const $message = $(`[data-msgid=${message.get('msgid')}]`);
                     if ($message.length) {
                         $message.after($('<div class="chat-info chat-error"></div>').text(message.get('message')));
                         this.scrollDown();
                     }
                 },
 
-                onMessageAdded: function (message) {
+                onMessageAdded (message) {
                     /* Handler that gets called when a new message object is created.
                      *
                      * Parameters:
@@ -470,7 +603,7 @@
                     });
                 },
 
-                createMessageStanza: function (message) {
+                createMessageStanza (message) {
                     return $msg({
                                 from: _converse.connection.jid,
                                 to: this.model.get('jid'),
@@ -480,7 +613,7 @@
                             .c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
                 },
 
-                sendMessage: function (message) {
+                sendMessage (message) {
                     /* Responsible for sending off a text message.
                      *
                      *  Parameters:
@@ -488,7 +621,7 @@
                      */
                     // TODO: We might want to send to specfic resources.
                     // Especially in the OTR case.
-                    var messageStanza = this.createMessageStanza(message);
+                    const messageStanza = this.createMessageStanza(message);
                     _converse.connection.send(messageStanza);
                     if (_converse.forward_messages) {
                         // Forward the message, so that other connected resources are also aware of it.
@@ -501,7 +634,7 @@
                     }
                 },
 
-                onMessageSubmitted: function (text) {
+                onMessageSubmitted (text) {
                     /* This method gets called once the user has typed a message
                      * and then pressed enter in a chat box.
                      *
@@ -515,25 +648,26 @@
                             'error'
                         );
                     }
-                    var match = text.replace(/^\s*/, "").match(/^\/(.*)\s*$/), msgs;
+                    const match = text.replace(/^\s*/, "").match(/^\/(.*)\s*$/);
                     if (match) {
                         if (match[1] === "clear") {
                             return this.clearMessages();
                         }
                         else if (match[1] === "help") {
-                            msgs = [
-                                '<strong>/help</strong>:'+__('Show this menu')+'',
-                                '<strong>/me</strong>:'+__('Write in the third person')+'',
-                                '<strong>/clear</strong>:'+__('Remove messages')+''
+                            const msgs = [
+                                `<strong>/help</strong>:${__('Show this menu')}`,
+                                `<strong>/me</strong>:${__('Write in the third person')}`,
+                                `<strong>/clear</strong>:${__('Remove messages')}`
                                 ];
                             this.showHelpMessages(msgs);
                             return;
                         }
                     }
-                    var fullname = _converse.xmppstatus.get('fullname');
+                    let fullname = _converse.xmppstatus.get('fullname');
                     fullname = _.isEmpty(fullname)? _converse.bare_jid: fullname;
-                    var message = this.model.messages.create({
-                        fullname: fullname,
+
+                    const message = this.model.messages.create({
+                        fullname,
                         sender: 'me',
                         time: moment().format(),
                         message: text
@@ -541,7 +675,7 @@
                     this.sendMessage(message);
                 },
 
-                sendChatState: function () {
+                sendChatState () {
                     /* Sends a message with the status of the user in this chat session
                      * as taken from the 'chat_state' attribute of the chat box.
                      * See XEP-0085 Chat State Notifications.
@@ -554,7 +688,7 @@
                     );
                 },
 
-                setChatState: function (state, no_save) {
+                setChatState (state, no_save) {
                     /* Mutator for setting the chat state of this chat session.
                      * Handles clearing of any chat state notification timeouts and
                      * setting new ones if necessary.
@@ -583,34 +717,10 @@
                     return this;
                 },
 
-                keyPressed: function (ev) {
-                    /* Event handler for when a key is pressed in a chat box textarea.
-                     */
-                    var textarea = ev.target, message;
-                    if (ev.keyCode === KEY.ENTER) {
-                        ev.preventDefault();
-                        message = textarea.value;
-                        textarea.value = '';
-                        textarea.focus();
-                        if (message !== '') {
-                            this.onMessageSubmitted(message);
-                            _converse.emit('messageSend', message);
-                        }
-                        this.setChatState(_converse.ACTIVE);
-                    } else {
-                        // Set chat state to composing if keyCode is not a forward-slash
-                        // (which would imply an internal command and not a message).
-                        this.setChatState(_converse.COMPOSING, ev.keyCode === KEY.FORWARD_SLASH);
-                    }
-                },
-
-                onSendButtonClicked: function(ev) {
-                    /* Event handler for when a send button is clicked in a chat box textarea.
-                     */
+                onFormSubmitted (ev) {
                     ev.preventDefault();
-                    var textarea = this.el.querySelector('.chat-textarea'),
-                        message = textarea.value;
-
+                    const textarea = this.el.querySelector('.chat-textarea'),
+                          message = textarea.value;
                     textarea.value = '';
                     textarea.focus();
                     if (message !== '') {
@@ -620,9 +730,21 @@
                     this.setChatState(_converse.ACTIVE);
                 },
 
-                clearMessages: function (ev) {
+                keyPressed (ev) {
+                    /* Event handler for when a key is pressed in a chat box textarea.
+                     */
+                    if (ev.keyCode === KEY.ENTER) {
+                        this.onFormSubmitted(ev);
+                    } else {
+                        // Set chat state to composing if keyCode is not a forward-slash
+                        // (which would imply an internal command and not a message).
+                        this.setChatState(_converse.COMPOSING, ev.keyCode === KEY.FORWARD_SLASH);
+                    }
+                },
+
+                clearMessages (ev) {
                     if (ev && ev.preventDefault) { ev.preventDefault(); }
-                    var result = confirm(__("Are you sure you want to clear the messages from this chat box?"));
+                    const result = confirm(__("Are you sure you want to clear the messages from this chat box?"));
                     if (result === true) {
                         this.$content.empty();
                         this.model.messages.reset();
@@ -631,29 +753,44 @@
                     return this;
                 },
 
-                insertIntoTextArea: function (value) {
-                    var $textbox = this.$el.find('textarea.chat-textarea');
-                    var existing = $textbox.val();
+                insertIntoTextArea (value) {
+                    const $textbox = this.$el.find('textarea.chat-textarea');
+                    let existing = $textbox.val();
                     if (existing && (existing[existing.length-1] !== ' ')) {
                         existing = existing + ' ';
                     }
                     $textbox.focus().val(existing+value+' ');
                 },
 
-                insertEmoticon: function (ev) {
+                insertEmoji (ev) {
                     ev.stopPropagation();
-                    this.$el.find('.toggle-smiley ul').slideToggle(200);
-                    var $target = $(ev.target);
-                    $target = $target.is('a') ? $target : $target.children('a');
-                    this.insertIntoTextArea($target.data('emoticon'));
+                    this.toggleEmojiMenu();
+                    const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target;
+                    this.insertIntoTextArea(target.getAttribute('data-emoji'));
                 },
 
-                toggleEmoticonMenu: function (ev) {
-                    ev.stopPropagation();
-                    this.$el.find('.toggle-smiley ul').slideToggle(200);
+                toggleEmojiMenu (ev) {
+                    if (!_.isUndefined(ev)) {
+                        ev.stopPropagation();
+                        if (ev.target.classList.contains('emoji-category-picker') ||
+                            ev.target.classList.contains('emoji-skintone-picker') ||
+                                ev.target.classList.contains('emoji-category')) {
+                            return;
+                        }
+                    }
+                    const elements = _.difference(
+                        document.querySelectorAll('.toolbar-menu'),
+                        [this.emoji_picker_view.el]
+                    );
+                    utils.slideInAllElements(elements).then(
+                        _.partial(
+                            utils.slideToggleElement,
+                            this.emoji_picker_view.el
+                        )
+                    );
                 },
 
-                toggleCall: function (ev) {
+                toggleCall (ev) {
                     ev.stopPropagation();
                     _converse.emit('callButtonClicked', {
                         connection: _converse.connection,
@@ -661,9 +798,9 @@
                     });
                 },
 
-                onChatStatusChanged: function (item) {
-                    var chat_status = item.get('chat_status'),
-                        fullname = item.get('fullname');
+                onChatStatusChanged (item) {
+                    const chat_status = item.get('chat_status');
+                    let fullname = item.get('fullname');
                     fullname = _.isEmpty(fullname)? item.get('jid'): fullname;
                     if (this.$el.is(':visible')) {
                         if (chat_status === 'offline') {
@@ -678,7 +815,7 @@
                     }
                 },
 
-                onStatusChanged: function (item) {
+                onStatusChanged (item) {
                     this.showStatusMessage();
                     _converse.emit('contactStatusMessageChanged', {
                         'contact': item.attributes,
@@ -686,7 +823,7 @@
                     });
                 },
 
-                showStatusMessage: function (msg) {
+                showStatusMessage (msg) {
                     msg = msg || this.model.get('status');
                     if (_.isString(msg)) {
                         this.$el.find('p.user-custom-message').text(msg).attr('title', msg);
@@ -694,7 +831,7 @@
                     return this;
                 },
 
-                close: function (ev) {
+                close (ev) {
                     if (ev && ev.preventDefault) { ev.preventDefault(); }
                     if (_converse.connection.connected) {
                         // Immediately sending the chat state, because the
@@ -712,35 +849,39 @@
                     return this;
                 },
 
-                getToolbarOptions: function (options) {
+                getToolbarOptions (options) {
                     return _.extend(options || {}, {
                         'label_clear': __('Clear all messages'),
                         'label_insert_smiley': __('Insert a smiley'),
                         'label_start_call': __('Start a call'),
                         'show_call_button': _converse.visible_toolbar_buttons.call,
                         'show_clear_button': _converse.visible_toolbar_buttons.clear,
-                        'show_emoticons': _converse.visible_toolbar_buttons.emoticons,
+                        'use_emoji': _converse.visible_toolbar_buttons.emoji,
                     });
                 },
 
-                renderToolbar: function (toolbar, options) {
+                renderToolbar (toolbar, options) {
                     if (!_converse.show_toolbar) { return; }
                     toolbar = toolbar || tpl_toolbar;
-                    options = _.extend(
+                    options = _.assign(
                         this.model.toJSON(),
                         this.getToolbarOptions(options || {})
                     );
-                    this.$el.find('.chat-toolbar').html(toolbar(options));
+                    this.el.querySelector('.chat-toolbar').innerHTML = toolbar(options);
+
+                    var toggle = this.el.querySelector('.toggle-smiley');
+                    toggle.innerHTML = '';
+                    toggle.appendChild(this.emoji_picker_view.render().el);
                     return this;
                 },
 
-                renderAvatar: function () {
+                renderAvatar () {
                     if (!this.model.get('image')) {
                         return;
                     }
-                    var width = _converse.chatview_avatar_width;
-                    var height = _converse.chatview_avatar_height;
-                    var img_src = 'data:'+this.model.get('image_type')+';base64,'+this.model.get('image'),
+                    const width = _converse.chatview_avatar_width;
+                    const height = _converse.chatview_avatar_height;
+                    const img_src = `data:${this.model.get('image_type')};base64,${this.model.get('image')}`,
                         canvas = $(tpl_avatar({
                             'width': width,
                             'height': height
@@ -749,10 +890,10 @@
                     if (!(canvas.getContext && canvas.getContext('2d'))) {
                         return this;
                     }
-                    var ctx = canvas.getContext('2d');
-                    var img = new Image();   // Create new Image object
+                    const ctx = canvas.getContext('2d');
+                    const img = new Image();   // Create new Image object
                     img.onload = function () {
-                        var ratio = img.width/img.height;
+                        const ratio = img.width/img.height;
                         if (ratio < 1) {
                             ctx.drawImage(img, 0,0, width, height*(1/ratio));
                         } else {
@@ -765,19 +906,19 @@
                     return this;
                 },
 
-                focus: function () {
+                focus () {
                     this.$el.find('.chat-textarea').focus();
                     _converse.emit('chatBoxFocused', this);
                     return this;
                 },
 
-                hide: function () {
+                hide () {
                     this.el.classList.add('hidden');
                     utils.refreshWebkit();
                     return this;
                 },
 
-                afterShown: function (focus) {
+                afterShown (focus) {
                     if (utils.isPersistableModel(this.model)) {
                         this.model.save();
                     }
@@ -788,7 +929,7 @@
                     }
                 },
 
-                _show: function (focus) {
+                _show (focus) {
                     /* Inner show method that gets debounced */
                     if (this.$el.is(':visible') && this.$el.css('opacity') === "1") {
                         if (focus) { this.focus(); }
@@ -797,7 +938,7 @@
                     utils.fadeIn(this.el, _.bind(this.afterShown, this, focus));
                 },
 
-                show: function (focus) {
+                show (focus) {
                     if (_.isUndefined(this.debouncedShow)) {
                         /* We wrap the method in a debouncer and set it on the
                          * instance, so that we have it debounced per instance.
@@ -809,14 +950,14 @@
                     return this;
                 },
 
-                hideNewMessagesIndicator: function () {
-                    var new_msgs_indicator = this.el.querySelector('.new-msgs-indicator');
+                hideNewMessagesIndicator () {
+                    const new_msgs_indicator = this.el.querySelector('.new-msgs-indicator');
                     if (!_.isNull(new_msgs_indicator)) {
                         new_msgs_indicator.classList.add('hidden');
                     }
                 },
 
-                markScrolled: _.debounce(function (ev) {
+                markScrolled: function (ev) {
                     /* Called when the chat content is scrolled up or down.
                      * We want to record when the user has scrolled away from
                      * the bottom, so that we don't automatically scroll away
@@ -831,23 +972,24 @@
                         });
                         return;
                     }
-                    var scrolled = true;
-                    var is_at_bottom =
+                    let scrolled = true;
+                    const is_at_bottom =
                         (this.$content.scrollTop() + this.$content.innerHeight()) >=
                             this.$content[0].scrollHeight-10;
+
                     if (is_at_bottom) {
                         scrolled = false;
                         this.onScrolledDown();
                     }
                     utils.safeSave(this.model, {'scrolled': scrolled});
-                }, 150),
+                },
 
-                viewUnreadMessages: function () {
+                viewUnreadMessages () {
                     this.model.save('scrolled', false);
                     this.scrollDown();
                 },
 
-                _scrollDown: function () {
+                _scrollDown () {
                     /* Inner method that gets debounced */
                     if (this.$content.is(':visible') && !this.model.get('scrolled')) {
                         this.$content.scrollTop(this.$content[0].scrollHeight);
@@ -856,7 +998,7 @@
                     }
                 },
 
-                onScrolledDown: function() {
+                onScrolledDown() {
                     this.hideNewMessagesIndicator();
                     if (_converse.windowState !== 'hidden') {
                         this.model.clearUnreadMsgCounter();
@@ -864,7 +1006,7 @@
                     _converse.emit('chatBoxScrolledDown', {'chatbox': this.model});
                 },
 
-                scrollDown: function () {
+                scrollDown () {
                     if (_.isUndefined(this.debouncedScrollDown)) {
                         /* We wrap the method in a debouncer and set it on the
                          * instance, so that we have it debounced per instance.
@@ -876,7 +1018,7 @@
                     return this;
                 },
 
-                onWindowStateChanged: function (state) {
+                onWindowStateChanged (state) {
                     if (this.model.get('num_unread', 0) && !this.model.newMessageWillBeHidden()) {
                         this.model.clearUnreadMsgCounter();
                     }

+ 138 - 134
src/converse-controlbox.js

@@ -7,7 +7,8 @@
 /*global define */
 
 (function (root, factory) {
-    define(["converse-core",
+    define(["jquery.noconflict",
+            "converse-core",
             "tpl!add_contact_dropdown",
             "tpl!add_contact_form",
             "tpl!change_status_message",
@@ -25,6 +26,7 @@
             "converse-rosterview"
     ], factory);
 }(this, function (
+            $,
             converse,
             tpl_add_contact_dropdown,
             tpl_add_contact_form,
@@ -42,17 +44,9 @@
         ) {
     "use strict";
 
-    var USERS_PANEL_ID = 'users';
-    var CHATBOX_TYPE = 'chatbox';
-    // Strophe methods for building stanzas
-    var Strophe = converse.env.Strophe,
-        Backbone = converse.env.Backbone,
-        utils = converse.env.utils;
-    // Other necessary globals
-    var $ = converse.env.jQuery,
-        _ = converse.env._,
-        fp = converse.env.fp,
-        moment = converse.env.moment;
+    const USERS_PANEL_ID = 'users';
+    const CHATBOX_TYPE = 'chatbox';
+    const { Strophe, Backbone, utils, _, fp, moment } = converse.env;
 
 
     converse.plugins.add('converse-controlbox', {
@@ -64,19 +58,19 @@
             //
             // New functions which don't exist yet can also be added.
 
-            initChatBoxes: function () {
+            initChatBoxes () {
                 this.__super__.initChatBoxes.apply(this, arguments);
                 this.controlboxtoggle = new this.ControlBoxToggle();
             },
 
-            initConnection: function () {
+            initConnection () {
                 this.__super__.initConnection.apply(this, arguments);
                 if (this.connection) {
                     this.addControlBox();
                 }
             },
 
-            _tearDown: function () {
+            _tearDown () {
                 this.__super__._tearDown.apply(this, arguments);
                 if (this.rosterview) {
                     // Removes roster groups
@@ -89,9 +83,9 @@
                 }
             },
 
-            clearSession: function () {
+            clearSession () {
                 this.__super__.clearSession.apply(this, arguments);
-                var controlbox = this.chatboxes.get('controlbox');
+                const controlbox = this.chatboxes.get('controlbox');
                 if (controlbox &&
                         controlbox.collection &&
                         controlbox.collection.browserStorage) {
@@ -100,13 +94,13 @@
             },
 
             ChatBoxes: {
-                chatBoxMayBeShown: function (chatbox) {
+                chatBoxMayBeShown (chatbox) {
                     return this.__super__.chatBoxMayBeShown.apply(this, arguments) &&
                            chatbox.get('id') !== 'controlbox';
                 },
 
-                onChatBoxesFetched: function (collection, resp) {
-                    var _converse = this.__super__._converse;
+                onChatBoxesFetched (collection, resp) {
+                    const { _converse } = this.__super__;
                     this.__super__.onChatBoxesFetched.apply(this, arguments);
                     if (!_.includes(_.map(collection, 'id'), 'controlbox')) {
                         _converse.addControlBox();
@@ -116,10 +110,10 @@
             },
 
             ChatBoxViews: {
-                onChatBoxAdded: function (item) {
-                    var _converse = this.__super__._converse;
+                onChatBoxAdded (item) {
+                    const { _converse } = this.__super__;
                     if (item.get('box_id') === 'controlbox') {
-                        var view = this.get(item.get('id'));
+                        let view = this.get(item.get('id'));
                         if (view) {
                             view.model = item;
                             view.initialize();
@@ -133,8 +127,8 @@
                     }
                 },
 
-                closeAllChatBoxes: function () {
-                    var _converse = this.__super__._converse;
+                closeAllChatBoxes () {
+                    const { _converse } = this.__super__;
                     this.each(function (view) {
                         if (view.model.get('id') === 'controlbox' &&
                                 (_converse.disconnection_cause !== _converse.LOGOUT || _converse.show_controlbox_by_default)) {
@@ -145,9 +139,9 @@
                     return this;
                 },
 
-                getChatBoxWidth: function (view) {
-                    var _converse = this.__super__._converse;
-                    var controlbox = this.get('controlbox');
+                getChatBoxWidth (view) {
+                    const { _converse } = this.__super__;
+                    const controlbox = this.get('controlbox');
                     if (view.model.get('id') === 'controlbox') {
                         /* We return the width of the controlbox or its toggle,
                          * depending on which is visible.
@@ -165,7 +159,7 @@
 
 
             ChatBox: {
-                initialize: function () {
+                initialize () {
                     if (this.get('id') === 'controlbox') {
                         this.set({'time_opened': moment(0).valueOf()});
                     } else {
@@ -176,20 +170,20 @@
 
 
             ChatBoxView: {
-                insertIntoDOM: function () {
-                    var _converse = this.__super__._converse;
+                insertIntoDOM () {
+                    const { _converse } = this.__super__;
                     this.$el.insertAfter(_converse.chatboxviews.get("controlbox").$el);
                     return this;
                 }
             }
         },
 
-        initialize: function () {
+        initialize () {
             /* The initialize function gets called as soon as the plugin is
              * loaded by converse.js's plugin machinery.
              */
-            var _converse = this._converse,
-                __ = _converse.__;
+            const { _converse } = this,
+                { __ } = _converse;
 
             _converse.api.settings.update({
                 allow_logout: true,
@@ -200,15 +194,16 @@
                 xhr_user_search_url: ''
             });
 
-            var LABEL_CONTACTS = __('Contacts');
+            const LABEL_CONTACTS = __('Contacts');
 
-            _converse.addControlBox = function () {
-                return _converse.chatboxes.add({
+            _converse.addControlBox = () =>
+                _converse.chatboxes.add({
                     id: 'controlbox',
                     box_id: 'controlbox',
+                    type: 'controlbox',
                     closed: !_converse.show_controlbox_by_default
-                });
-            };
+                })
+            ;
 
             _converse.ControlBoxView = _converse.ChatBoxView.extend({
                 tagName: 'div',
@@ -219,7 +214,7 @@
                     'click ul#controlbox-tabs li a': 'switchTab',
                 },
 
-                initialize: function () {
+                initialize () {
                     this.$el.insertAfter(_converse.controlboxtoggle.$el);
                     this.model.on('change:connected', this.onConnected, this);
                     this.model.on('destroy', this.hide, this);
@@ -232,7 +227,7 @@
                     }
                 },
 
-                render: function () {
+                render () {
                     if (this.model.get('connected')) {
                         if (_.isUndefined(this.model.get('closed'))) {
                             this.model.set('closed', !_converse.show_controlbox_by_default);
@@ -259,21 +254,21 @@
                     return this;
                 },
 
-                onConnected: function () {
+                onConnected () {
                     if (this.model.get('connected')) {
                         this.render().insertRoster();
                         this.model.save();
                     }
                 },
 
-                insertRoster: function () {
+                insertRoster () {
                     /* Place the rosterview inside the "Contacts" panel.
                      */
                     this.contactspanel.$el.append(_converse.rosterview.$el);
                     return this;
                 },
 
-                renderLoginPanel: function () {
+                renderLoginPanel () {
                     this.loginpanel = new _converse.LoginPanel({
                         '$parent': this.$el.find('.controlbox-panes'),
                         'model': this
@@ -282,7 +277,7 @@
                     return this;
                 },
 
-                renderContactsPanel: function () {
+                renderContactsPanel () {
                     if (_.isUndefined(this.model.get('active-panel'))) {
                         this.model.save({'active-panel': USERS_PANEL_ID});
                     }
@@ -297,7 +292,7 @@
                     _converse.xmppstatusview.render();
                 },
 
-                close: function (ev) {
+                close (ev) {
                     if (ev && ev.preventDefault) { ev.preventDefault(); }
                     if (_converse.sticky_controlbox) {
                         return;
@@ -311,7 +306,7 @@
                     return this;
                 },
 
-                ensureClosedState: function () {
+                ensureClosedState () {
                     if (this.model.get('closed')) {
                         this.hide();
                     } else {
@@ -319,7 +314,7 @@
                     }
                 },
 
-                hide: function (callback) {
+                hide (callback) {
                     if (_converse.sticky_controlbox) {
                         return;
                     }
@@ -333,8 +328,8 @@
                     return this;
                 },
 
-                onControlBoxToggleHidden: function () {
-                    var that = this;
+                onControlBoxToggleHidden () {
+                    const that = this;
                     utils.fadeIn(this.el, function () {
                         _converse.controlboxtoggle.updateOnlineCount();
                         utils.refreshWebkit();
@@ -343,17 +338,17 @@
                     });
                 },
 
-                show: function () {
+                show () {
                     _converse.controlboxtoggle.hide(
                         this.onControlBoxToggleHidden.bind(this)
                     );
                     return this;
                 },
 
-                switchTab: function (ev) {
+                switchTab (ev) {
                     // TODO: automatically focus the relevant input
                     if (ev && ev.preventDefault) { ev.preventDefault(); }
-                    var $tab = $(ev.target),
+                    const $tab = $(ev.target),
                         $sibling = $tab.parent().siblings('li').children('a'),
                         $tab_panel = $($tab.attr('href'));
                     $($sibling.attr('href')).addClass('hidden');
@@ -366,7 +361,7 @@
                     return this;
                 },
 
-                showHelpMessages: function () {
+                showHelpMessages () {
                     /* Override showHelpMessages in ChatBoxView, for now do nothing.
                      *
                      * Parameters:
@@ -385,7 +380,7 @@
                     'submit form#converse-login': 'authenticate'
                 },
 
-                initialize: function (cfg) {
+                initialize (cfg) {
                     cfg.$parent.html(this.$el.html(
                         tpl_login_panel({
                             'ANONYMOUS': _converse.ANONYMOUS,
@@ -405,7 +400,7 @@
                     this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
                 },
 
-                render: function () {
+                render () {
                     this.$tabs.append(tpl_login_tab({label_sign_in: __('Sign in')}));
                     this.$el.find('input#jid').focus();
                     if (!this.$el.is(':visible')) {
@@ -414,20 +409,21 @@
                     return this;
                 },
 
-                authenticate: function (ev) {
+                authenticate (ev) {
                     if (ev && ev.preventDefault) { ev.preventDefault(); }
-                    var $form = $(ev.target);
+                    const $form = $(ev.target);
                     if (_converse.authentication === _converse.ANONYMOUS) {
                         this.connect($form, _converse.jid, null);
                         return;
                     }
-                    var $jid_input = $form.find('input[name=jid]'),
-                        jid = $jid_input.val(),
-                        $pw_input = $form.find('input[name=password]'),
-                        password = $pw_input.val(),
+                    const $jid_input = $form.find('input[name=jid]');
+                    const $pw_input = $form.find('input[name=password]');
+                    const password = $pw_input.val();
+
+                    let jid = $jid_input.val(),
                         errors = false;
 
-                    if (!jid) {
+                    if (!jid || _.filter(jid.split('@')).length < 2) {
                         errors = true;
                         $jid_input.addClass('error');
                     }
@@ -445,8 +441,8 @@
                     return false;
                 },
 
-                connect: function ($form, jid, password) {
-                    var resource;
+                connect ($form, jid, password) {
+                    let resource;
                     if ($form) {
                         $form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
                     }
@@ -462,7 +458,7 @@
                     _converse.connection.connect(jid, password, _converse.onConnectStatusChanged);
                 },
 
-                remove: function () {
+                remove () {
                     this.$tabs.empty();
                     this.$el.parent().empty();
                 }
@@ -478,19 +474,18 @@
                     "click .dropdown dd ul li a": "setStatus"
                 },
 
-                initialize: function () {
+                initialize () {
                     this.model.on("change:status", this.updateStatusUI, this);
                     this.model.on("change:status_message", this.updateStatusUI, this);
                     this.model.on("update-status-ui", this.updateStatusUI, this);
                 },
 
-                render: function () {
+                render () {
                     // Replace the default dropdown with something nicer
-                    var $select = this.$el.find('select#select-xmpp-status'),
-                        chat_status = this.model.get('status') || 'offline',
-                        options = $('option', $select),
-                        $options_target,
-                        options_list = [];
+                    const $select = this.$el.find('select#select-xmpp-status');
+                    const chat_status = this.model.get('status') || 'offline';
+                    const options = $('option', $select);
+                    const options_list = [];
                     this.$el.html(tpl_choose_status());
                     this.$el.find('#fancy-xmpp-status-select')
                             .html(tpl_chat_status({
@@ -506,39 +501,42 @@
                             'text': this.text
                         }));
                     });
-                    $options_target = this.$el.find("#target dd ul").hide();
+                    const $options_target = this.$el.find("#target dd ul").hide();
                     $options_target.append(options_list.join(''));
                     $select.remove();
                     return this;
                 },
 
-                toggleOptions: function (ev) {
+                toggleOptions (ev) {
                     ev.preventDefault();
+                    utils.slideInAllElements(
+                        document.querySelectorAll('#conversejs .contact-form-container')
+                    );
                     $(ev.target).parent().parent().siblings('dd').find('ul').toggle('fast');
                 },
 
-                renderStatusChangeForm: function (ev) {
+                renderStatusChangeForm (ev) {
                     ev.preventDefault();
-                    var status_message = _converse.xmppstatus.get('status_message') || '';
-                    var input = tpl_change_status_message({
+                    const status_message = _converse.xmppstatus.get('status_message') || '';
+                    const input = tpl_change_status_message({
                         'status_message': status_message,
                         'label_custom_status': __('Custom status'),
                         'label_save': __('Save')
                     });
-                    var $xmppstatus = this.$el.find('.xmpp-status');
+                    const $xmppstatus = this.$el.find('.xmpp-status');
                     $xmppstatus.parent().addClass('no-border');
                     $xmppstatus.replaceWith(input);
                     this.$el.find('.custom-xmpp-status').focus().focus();
                 },
 
-                setStatusMessage: function (ev) {
+                setStatusMessage (ev) {
                     ev.preventDefault();
                     this.model.setStatusMessage($(ev.target).find('input').val());
                 },
 
-                setStatus: function (ev) {
+                setStatus (ev) {
                     ev.preventDefault();
-                    var $el = $(ev.currentTarget),
+                    const $el = $(ev.currentTarget),
                         value = $el.attr('data-value');
                     if (value === 'logout') {
                         this.$el.find(".dropdown dd ul").hide();
@@ -549,7 +547,7 @@
                     }
                 },
 
-                getPrettyStatus: function (stat) {
+                getPrettyStatus (stat) {
                     if (stat === 'chat') {
                         return __('online');
                     } else if (stat === 'dnd') {
@@ -565,11 +563,11 @@
                     }
                 },
 
-                updateStatusUI: function (model) {
-                    var stat = model.get('status');
+                updateStatusUI (model) {
+                    const stat = model.get('status');
                     // For translators: the %1$s part gets replaced with the status
                     // Example, I am online
-                    var status_message = model.get('status_message') || __("I am %1$s", this.getPrettyStatus(stat));
+                    const status_message = model.get('status_message') || __("I am %1$s", this.getPrettyStatus(stat));
                     this.$el.find('#fancy-xmpp-status-select').removeClass('no-border').html(
                         tpl_chat_status({
                             'chat_status': stat,
@@ -592,17 +590,17 @@
                     'click a.subscribe-to-user': 'addContactFromList'
                 },
 
-                initialize: function (cfg) {
+                initialize (cfg) {
                     this.parent_el = cfg.$parent[0];
                     this.tab_el = document.createElement('li');
                     _converse.chatboxes.on('change:num_unread', this.renderTab, this);
                     _converse.chatboxes.on('add', _.debounce(this.renderTab, 100), this);
                 },
 
-                render: function () {
+                render () {
                     this.renderTab();
 
-                    var widgets = tpl_contacts_panel({
+                    let widgets = tpl_contacts_panel({
                         label_online: __('Online'),
                         label_busy: __('Busy'),
                         label_away: __('Away'),
@@ -619,16 +617,16 @@
                     }
                     this.el.innerHTML = widgets;
 
-                    var controlbox = _converse.chatboxes.get('controlbox');
+                    const controlbox = _converse.chatboxes.get('controlbox');
                     if (controlbox.get('active-panel') !== USERS_PANEL_ID) {
                         this.el.classList.add('hidden');
                     }
                     return this;
                 },
 
-                renderTab: function () {
-                    var controlbox = _converse.chatboxes.get('controlbox');
-                    var chats = fp.filter(_.partial(utils.isOfType, CHATBOX_TYPE), _converse.chatboxes.models);
+                renderTab () {
+                    const controlbox = _converse.chatboxes.get('controlbox');
+                    const chats = fp.filter(_.partial(utils.isOfType, CHATBOX_TYPE), _converse.chatboxes.models);
                     this.tab_el.innerHTML = tpl_contacts_tab({
                         'label_contacts': LABEL_CONTACTS,
                         'is_current': controlbox.get('active-panel') === USERS_PANEL_ID,
@@ -636,53 +634,54 @@
                     });
                 },
 
-                insertIntoDOM: function () {
+                insertIntoDOM () {
                     this.parent_el.appendChild(this.render().el);
                     this.tabs = this.parent_el.parentNode.querySelector('#controlbox-tabs');
                     this.tabs.appendChild(this.tab_el);
-                    this.$('.search-xmpp ul').append(
-                        this.generateAddContactHTML()
-                    );
                     return this;
                 },
 
-                generateAddContactHTML: function () {
+                generateAddContactHTML (settings={}) {
                     if (_converse.xhr_user_search) {
                         return tpl_search_contact({
                             label_contact_name: __('Contact name'),
                             label_search: __('Search')
                         });
                     } else {
-                        return tpl_add_contact_form({
+                        return tpl_add_contact_form(_.assign({
+                            error_message: null,
                             label_contact_username: __('e.g. user@example.org'),
-                            label_add: __('Add')
-                        });
+                            label_add: __('Add'),
+                            value: ''
+                        }, settings));
                     }
                 },
 
-                toggleContactForm: function (ev) {
+                toggleContactForm (ev) {
                     ev.preventDefault();
-                    this.$el.find('.search-xmpp').toggle('fast', function () {
-                        if ($(this).is(':visible')) {
-                            $(this).find('input.username').focus();
+                    this.el.querySelector('.search-xmpp div').innerHTML = this.generateAddContactHTML();
+                    var dropdown = this.el.querySelector('.contact-form-container');
+                    utils.slideToggleElement(dropdown).then(() => {
+                        if ($(dropdown).is(':visible')) {
+                            $(dropdown).find('input.username').focus();
                         }
                     });
                 },
 
-                searchContacts: function (ev) {
+                searchContacts (ev) {
                     ev.preventDefault();
                     $.getJSON(_converse.xhr_user_search_url+ "?q=" + $(ev.target).find('input.username').val(), function (data) {
-                        var $ul= $('.search-xmpp ul');
+                        const $ul= $('.search-xmpp ul');
                         $ul.find('li.found-user').remove();
                         $ul.find('li.chat-info').remove();
                         if (!data.length) {
-                            $ul.append('<li class="chat-info">'+__('No users found')+'</li>');
+                            $ul.append(`<li class="chat-info">${__('No users found')}</li>`);
                         }
                         $(data).each(function (idx, obj) {
                             $ul.append(
                                 $('<li class="found-user"></li>')
                                 .append(
-                                    $('<a class="subscribe-to-user" href="#" title="'+__('Click to add as a chat contact')+'"></a>')
+                                    $(`<a class="subscribe-to-user" href="#" title="${__('Click to add as a chat contact')}"></a>`)
                                     .attr('data-recipient', Strophe.getNodeFromJid(obj.id)+"@"+Strophe.getDomainFromJid(obj.id))
                                     .text(obj.fullname)
                                 )
@@ -691,27 +690,32 @@
                     });
                 },
 
-                addContactFromForm: function (ev) {
+                addContactFromForm (ev) {
                     ev.preventDefault();
-                    var $input = $(ev.target).find('input');
-                    var jid = $input.val();
-                    if (! jid) {
-                        // this is not a valid JID
-                        $input.addClass('error');
+                    const $input = $(ev.target).find('input');
+                    const jid = $input.val();
+                    if (!jid || _.filter(jid.split('@')).length < 2) {
+                        this.el.querySelector('.search-xmpp div').innerHTML =
+                            this.generateAddContactHTML({
+                                error_message: __('Please enter a valid XMPP username'),
+                                label_contact_username: __('e.g. user@example.org'),
+                                label_add: __('Add'),
+                                value: jid
+                            });
                         return;
                     }
                     _converse.roster.addAndSubscribe(jid);
-                    $('.search-xmpp').hide();
+                    utils.slideIn(this.el.querySelector('.contact-form-container'));
                 },
 
-                addContactFromList: function (ev) {
+                addContactFromList (ev) {
                     ev.preventDefault();
-                    var $target = $(ev.target),
+                    const $target = $(ev.target),
                         jid = $target.attr('data-recipient'),
                         name = $target.text();
                     _converse.roster.addAndSubscribe(jid, name);
                     $target.parent().remove();
-                    $('.search-xmpp').hide();
+                    utils.slideIn(this.el.querySelector('.contact-form-container'));
                 }
             });
 
@@ -727,10 +731,10 @@
                     'href': "#"
                 },
 
-                initialize: function () {
+                initialize () {
                     _converse.chatboxviews.$el.prepend(this.render());
                     this.updateOnlineCount();
-                    var that = this;
+                    const that = this;
                     _converse.on('initialized', function () {
                         _converse.roster.on("add", that.updateOnlineCount, that);
                         _converse.roster.on('change', that.updateOnlineCount, that);
@@ -739,7 +743,7 @@
                     });
                 },
 
-                render: function () {
+                render () {
                     // We let the render method of ControlBoxView decide whether
                     // the ControlBox or the Toggle must be shown. This prevents
                     // artifacts (i.e. on page load the toggle is shown only to then
@@ -755,24 +759,24 @@
                     if (_.isUndefined(_converse.roster)) {
                         return;
                     }
-                    var $count = this.$('#online-count');
-                    $count.text('('+_converse.roster.getNumOnlineContacts()+')');
+                    const $count = this.$('#online-count');
+                    $count.text(`(${_converse.roster.getNumOnlineContacts()})`);
                     if (!$count.is(':visible')) {
                         $count.show();
                     }
                 }, _converse.animate ? 100 : 0),
 
-                hide: function (callback) {
+                hide (callback) {
                     this.el.classList.add('hidden');
                     callback();
                 },
 
-                show: function (callback) {
+                show (callback) {
                     utils.fadeIn(this.el, callback);
                 },
 
-                showControlBox: function () {
-                    var controlbox = _converse.chatboxes.get('controlbox');
+                showControlBox () {
+                    let controlbox = _converse.chatboxes.get('controlbox');
                     if (!controlbox) {
                         controlbox = _converse.addControlBox();
                     }
@@ -783,10 +787,10 @@
                     }
                 },
 
-                onClick: function (e) {
+                onClick (e) {
                     e.preventDefault();
                     if ($("div#controlbox").is(':visible')) {
-                        var controlbox = _converse.chatboxes.get('controlbox');
+                        const controlbox = _converse.chatboxes.get('controlbox');
                         if (_converse.connection.connected) {
                             controlbox.save({closed: true});
                         } else {
@@ -798,23 +802,23 @@
                 }
             });
 
-            var disconnect =  function () {
+            const disconnect =  function () {
                 /* Upon disconnection, set connected to `false`, so that if
                  * we reconnect,
                  * "onConnected" will be called, to fetch the roster again and
                  * to send out a presence stanza.
                  */
-                var view = _converse.chatboxviews.get('controlbox');
+                const view = _converse.chatboxviews.get('controlbox');
                 view.model.set({connected:false});
                 view.$('#controlbox-tabs').empty();
                 view.renderLoginPanel();
             };
             _converse.on('disconnected', disconnect);
 
-            var afterReconnected = function () {
+            const afterReconnected = function () {
                 /* After reconnection makes sure the controlbox's is aware.
                  */
-                var view = _converse.chatboxviews.get('controlbox');
+                const view = _converse.chatboxviews.get('controlbox');
                 if (view.model.get('connected')) {
                     _converse.chatboxviews.get("controlbox").onConnected();
                 } else {

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor