Ver código fonte

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

JC Brand 8 anos atrás
pai
commit
a717481b2c
100 arquivos alterados com 94358 adições e 84767 exclusões
  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>

Diferenças do arquivo suprimidas por serem muito extensas
+ 26281 - 25118
dist/converse-mobile.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1125 - 1739
dist/converse-no-dependencies.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 26285 - 25122
dist/converse.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 26285 - 25122
dist/inverse.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 210 - 273
locale/ca/LC_MESSAGES/converse.po


Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
-            ""
          ]
       }
    }

Diferenças do arquivo suprimidas por serem muito extensas
+ 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 ?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
             "האם אתה בטוח כי ברצונך לסרב את בקשת איש קשר זה?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
-            ""
          ]
       }
    }

Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
-            ""
          ]
       }
    }

Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
-            ""
          ]
       }
    }

Diferenças do arquivo suprimidas por serem muito extensas
+ 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?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
-            ""
          ]
       }
    }

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
             "Вы уверены, что хотите отклонить запрос от этого контакта?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
             "Ви впевнені, що хочете відхилити цей запит контакту?"

Diferenças do arquivo suprimidas por serem muito extensas
+ 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,
-            ""
          ]
       }
    }

Diferenças do arquivo suprimidas por serem muito extensas
+ 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();
         }));
     });
 }));

Diferenças do arquivo suprimidas por serem muito extensas
+ 334 - 169
spec/chatbox.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 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 {

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff