Commit 7f842c42 authored by JC Brand's avatar JC Brand

Update some bower dependencies and rebuilt dist files

fixes #437
parent f55ab176
...@@ -25,6 +25,9 @@ JSHINTEXCEPTIONS = $(GENERATED) \ ...@@ -25,6 +25,9 @@ JSHINTEXCEPTIONS = $(GENERATED) \
src/bigint.js src/bigint.js
CHECKSOURCES = $(filter-out $(JSHINTEXCEPTIONS),$(SOURCES)) CHECKSOURCES = $(filter-out $(JSHINTEXCEPTIONS),$(SOURCES))
.PHONY: all
all: dev
.PHONY: help .PHONY: help
help: help:
@echo "Please use \`make <target>' where <target> is one of the following:" @echo "Please use \`make <target>' where <target> is one of the following:"
...@@ -50,8 +53,6 @@ help: ...@@ -50,8 +53,6 @@ help:
@echo " stamp-bundler Install Bundler (Ruby) dependencies and create the guard file stamp-bundler which will prevent those dependencies from being installed again." @echo " stamp-bundler Install Bundler (Ruby) dependencies and create the guard file stamp-bundler which will prevent those dependencies from being installed again."
@echo " watch Tells Sass to watch the .scss files for changes and then automatically update the CSS files." @echo " watch Tells Sass to watch the .scss files for changes and then automatically update the CSS files."
.PHONY: all
all: dev
######################################################################## ########################################################################
## Miscellaneous ## Miscellaneous
......
...@@ -8,29 +8,31 @@ ...@@ -8,29 +8,31 @@
"sinon": "^1.17.3" "sinon": "^1.17.3"
}, },
"dependencies": { "dependencies": {
"requirejs": "~2.1.15", "requirejs": "~2.2.0",
"jquery": "1.11.0", "jquery": "1.12.2",
"jed": "0.5.4", "jed": "1.1.0",
"underscore": "~1.8.3", "underscore": "~1.8.3",
"backbone": "1.1.2", "backbone": "1.1.2",
"backbone.browserStorage": "0.0.2", "backbone.browserStorage": "0.0.2",
"backbone.overview": "0.0.2", "backbone.overview": "0.0.2",
"otr": "0.2.12", "otr": "0.2.16",
"crypto-js-evanvosberg": "https://github.com/evanvosberg/crypto-js.git#release-3.1.2-5", "crypto-js-evanvosberg": "https://github.com/evanvosberg/crypto-js.git#release-3.1.2-5",
"almond": "~0.3.0", "almond": "~0.3.1",
"requirejs-text": "~2.0.12", "requirejs-text": "~2.0.14",
"requirejs-tpl-jcbrand": "*", "requirejs-tpl-jcbrand": "*",
"momentjs": "~2.10.6", "momentjs": "~2.12.0",
"jquery.browser": ">=0.0.7", "jquery.browser": ">=0.1.0",
"bootstrap": "~3.2.0", "bootstrap": "~3.2.0",
"fontawesome": "~4.1.0", "fontawesome": "~4.1.0",
"typeahead.js": "https://raw.githubusercontent.com/jcbrand/typeahead.js/eedfb10505dd3a20123d1fafc07c1352d83f0ab3/dist/typeahead.jquery.js", "typeahead.js": "https://raw.githubusercontent.com/jcbrand/typeahead.js/eedfb10505dd3a20123d1fafc07c1352d83f0ab3/dist/typeahead.jquery.js",
"strophejs": "1.2.4", "strophejs": "1.2.5",
"strophejs-plugins": "https://github.com/strophe/strophejs-plugins.git#amd", "strophejs-plugins": "https://github.com/strophe/strophejs-plugins.git#amd",
"bourbon": "~4.2.3" "bourbon": "~4.2.6"
}, },
"resolutions": { "resolutions": {
"backbone": "1.1.2" "backbone": "1.1.2",
"requirejs": "~2.2.0",
"jquery": "1.12.2"
}, },
"exportsOverride": {}, "exportsOverride": {},
"ignore": [ "ignore": [
......
...@@ -27,4 +27,4 @@ ...@@ -27,4 +27,4 @@
Copyright 2014 Yahoo! Inc. All rights reserved. Copyright 2014 Yahoo! Inc. All rights reserved.
Licensed under the BSD License. Licensed under the BSD License.
https://github.com/yahoo/pure/blob/master/LICENSE.md https://github.com/yahoo/pure/blob/master/LICENSE.md
*/-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#conversejs html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}#conversejs body{margin:0}#conversejs article,#conversejs aside,#conversejs details,#conversejs figcaption,#conversejs figure,#conversejs footer,#conversejs header,#conversejs hgroup,#conversejs main,#conversejs menu,#conversejs nav,#conversejs section,#conversejs summary{display:block}#conversejs audio,#conversejs canvas,#conversejs progress,#conversejs video{display:inline-block;vertical-align:baseline}#conversejs audio:not([controls]){display:none;height:0}#conversejs [hidden],#conversejs template{display:none}#conversejs a{background-color:transparent}#conversejs a:active,#conversejs a:hover{outline:0}#conversejs abbr[title]{border-bottom:1px dotted}#conversejs b,#conversejs strong{font-weight:700}#conversejs dfn{font-style:italic}#conversejs mark{background:#ff0;color:#000}#conversejs small{font-size:80%}#conversejs sub,#conversejs sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}#conversejs sup{top:-.5em}#conversejs sub{bottom:-.25em}#conversejs svg:not(:root){overflow:hidden}#conversejs hr{box-sizing:content-box;height:0}#conversejs pre{overflow:auto}#conversejs code,#conversejs kbd,#conversejs pre,#conversejs samp{font-family:monospace,monospace;font-size:1em}#conversejs button,#conversejs input,#conversejs optgroup,#conversejs select,#conversejs textarea{color:inherit;font:inherit;margin:0}#conversejs button{overflow:visible}#conversejs button,#conversejs select{text-transform:none}#conversejs button,#conversejs html input[type=button],#conversejs input[type=reset],#conversejs input[type=submit]{-webkit-appearance:button;cursor:pointer}#conversejs button[disabled],#conversejs html input[disabled]{cursor:default}#conversejs button::-moz-focus-inner,#conversejs input::-moz-focus-inner{border:0;padding:0}#conversejs input{line-height:normal}#conversejs input[type=checkbox],#conversejs input[type=radio]{box-sizing:border-box;padding:0}#conversejs input[type=number]::-webkit-inner-spin-button,#conversejs input[type=number]::-webkit-outer-spin-button{height:auto}#conversejs input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}#conversejs input[type=search]::-webkit-search-cancel-button,#conversejs input[type=search]::-webkit-search-decoration{-webkit-appearance:none}#conversejs textarea{overflow:auto}#conversejs optgroup{font-weight:700}#conversejs table{border-collapse:collapse;border-spacing:0}#conversejs .hidden,#conversejs [hidden]{display:none!important}#conversejs .pure-img{max-width:100%;height:auto;display:block}#conversejs .pure-form input[type=color],#conversejs .pure-form input[type=date],#conversejs .pure-form input[type=datetime-local],#conversejs .pure-form input[type=datetime],#conversejs .pure-form input[type=email],#conversejs .pure-form input[type=month],#conversejs .pure-form input[type=number],#conversejs .pure-form input[type=password],#conversejs .pure-form input[type=search],#conversejs .pure-form input[type=tel],#conversejs .pure-form input[type=text],#conversejs .pure-form input[type=time],#conversejs .pure-form input[type=url],#conversejs .pure-form input[type=week],#conversejs .pure-form select,#conversejs .pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#conversejs .pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#conversejs .pure-form input[type=color]{padding:.2em .5em}#conversejs .pure-form input:not([type]):focus,#conversejs .pure-form input[type=color]:focus,#conversejs .pure-form input[type=date]:focus,#conversejs .pure-form input[type=datetime-local]:focus,#conversejs .pure-form input[type=datetime]:focus,#conversejs .pure-form input[type=email]:focus,#conversejs .pure-form input[type=month]:focus,#conversejs .pure-form input[type=number]:focus,#conversejs .pure-form input[type=password]:focus,#conversejs .pure-form input[type=search]:focus,#conversejs .pure-form input[type=tel]:focus,#conversejs .pure-form input[type=text]:focus,#conversejs .pure-form input[type=time]:focus,#conversejs .pure-form input[type=url]:focus,#conversejs .pure-form input[type=week]:focus,#conversejs .pure-form select:focus,#conversejs .pure-form textarea:focus{outline:0;border-color:#1A9707}#conversejs .pure-form input[type=checkbox]:focus,#conversejs .pure-form input[type=file]:focus,#conversejs .pure-form input[type=radio]:focus{outline:thin solid #1A9707;outline:1px auto #1A9707}#conversejs .pure-form .pure-checkbox,#conversejs .pure-form .pure-radio{margin:.5em 0;display:block}#conversejs .pure-form input:not([type])[disabled],#conversejs .pure-form input[type=color][disabled],#conversejs .pure-form input[type=date][disabled],#conversejs .pure-form input[type=datetime-local][disabled],#conversejs .pure-form input[type=datetime][disabled],#conversejs .pure-form input[type=email][disabled],#conversejs .pure-form input[type=month][disabled],#conversejs .pure-form input[type=number][disabled],#conversejs .pure-form input[type=password][disabled],#conversejs .pure-form input[type=search][disabled],#conversejs .pure-form input[type=tel][disabled],#conversejs .pure-form input[type=text][disabled],#conversejs .pure-form input[type=time][disabled],#conversejs .pure-form input[type=url][disabled],#conversejs .pure-form input[type=week][disabled],#conversejs .pure-form select[disabled],#conversejs .pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}#conversejs .pure-form input[readonly],#conversejs .pure-form select[readonly],#conversejs .pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}#conversejs .pure-form input:focus:invalid,#conversejs .pure-form select:focus:invalid,#conversejs .pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}#conversejs .pure-form input[type=checkbox]:focus:invalid:focus,#conversejs .pure-form input[type=file]:focus:invalid:focus,#conversejs .pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}#conversejs .pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}#conversejs .pure-form select[multiple]{height:auto}#conversejs .pure-form label{margin:.5em 0 .2em}#conversejs .pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}#conversejs .pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}#conversejs .pure-form-stacked input:not([type]),#conversejs .pure-form-stacked input[type=color],#conversejs .pure-form-stacked input[type=date],#conversejs .pure-form-stacked input[type=datetime-local],#conversejs .pure-form-stacked input[type=datetime],#conversejs .pure-form-stacked input[type=email],#conversejs .pure-form-stacked input[type=file],#conversejs .pure-form-stacked input[type=month],#conversejs .pure-form-stacked input[type=number],#conversejs .pure-form-stacked input[type=password],#conversejs .pure-form-stacked input[type=search],#conversejs .pure-form-stacked input[type=tel],#conversejs .pure-form-stacked input[type=text],#conversejs .pure-form-stacked input[type=time],#conversejs .pure-form-stacked input[type=url],#conversejs .pure-form-stacked input[type=week],#conversejs .pure-form-stacked label,#conversejs .pure-form-stacked select,#conversejs .pure-form-stacked textarea{display:block;margin:.25em 0}#conversejs .pure-form-aligned .pure-help-inline,#conversejs .pure-form-aligned input,#conversejs .pure-form-aligned select,#conversejs .pure-form-aligned textarea,#conversejs .pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}#conversejs .pure-form-aligned textarea{vertical-align:top}#conversejs .pure-form-aligned .pure-control-group{margin-bottom:.5em}#conversejs .pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}#conversejs .pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}#conversejs .pure-form .pure-input-rounded,#conversejs .pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}#conversejs .pure-form .pure-group fieldset{margin-bottom:10px}#conversejs .pure-form .pure-group input,#conversejs .pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}#conversejs .pure-form .pure-group input:focus,#conversejs .pure-form .pure-group textarea:focus{z-index:3}#conversejs .pure-form .pure-group input:first-child,#conversejs .pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}#conversejs .pure-form .pure-group input:first-child:last-child,#conversejs .pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}#conversejs .pure-form .pure-group input:last-child,#conversejs .pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}#conversejs .pure-form .pure-group button{margin:.35em 0}#conversejs .pure-form .pure-input-1{width:100%}#conversejs .pure-form .pure-input-3-4{width:75%}#conversejs .pure-form .pure-input-2-3{width:66%}#conversejs .pure-form .pure-input-1-2{width:50%}#conversejs .pure-form .pure-input-1-3{width:33%}#conversejs .pure-form .pure-input-1-4{width:25%}#conversejs .pure-form .pure-help-inline,#conversejs .pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}#conversejs .pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width:480px){#conversejs .pure-form button[type=submit]{margin:.7em 0 0}#conversejs .pure-form input:not([type]),#conversejs .pure-form input[type=color],#conversejs .pure-form input[type=date],#conversejs .pure-form input[type=datetime-local],#conversejs .pure-form input[type=datetime],#conversejs .pure-form input[type=email],#conversejs .pure-form input[type=month],#conversejs .pure-form input[type=number],#conversejs .pure-form input[type=password],#conversejs .pure-form input[type=search],#conversejs .pure-form input[type=tel],#conversejs .pure-form input[type=text],#conversejs .pure-form input[type=time],#conversejs .pure-form input[type=url],#conversejs .pure-form input[type=week],#conversejs .pure-form label{margin-bottom:.3em;display:block}#conversejs .pure-group input:not([type]),#conversejs .pure-group input[type=color],#conversejs .pure-group input[type=date],#conversejs .pure-group input[type=datetime-local],#conversejs .pure-group input[type=datetime],#conversejs .pure-group input[type=email],#conversejs .pure-group input[type=month],#conversejs .pure-group input[type=number],#conversejs .pure-group input[type=password],#conversejs .pure-group input[type=search],#conversejs .pure-group input[type=tel],#conversejs .pure-group input[type=text],#conversejs .pure-group input[type=time],#conversejs .pure-group input[type=url],#conversejs .pure-group input[type=week]{margin-bottom:0}#conversejs .pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}#conversejs .pure-form-aligned .pure-controls{margin:1.5em 0 0}#conversejs .pure-form .pure-help-inline,#conversejs .pure-form-message,#conversejs .pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}#conversejs .pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:none transparent;background-color:#E6E6E6;text-decoration:none}#conversejs .pure-button-hover,#conversejs .pure-button:focus,#conversejs .pure-button:hover{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05)40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05)0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05)40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05)40%,rgba(0,0,0,.1))}#conversejs .pure-button:focus{outline:0}#conversejs .pure-button-active,#conversejs .pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15)inset,0 0 6px rgba(0,0,0,.2)inset}#conversejs .pure-button-disabled,#conversejs .pure-button-disabled:active,#conversejs .pure-button-disabled:focus,#conversejs .pure-button-disabled:hover,#conversejs .pure-button[disabled]{border:none;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}#conversejs .pure-button-hidden{display:none}#conversejs .pure-button::-moz-focus-inner{padding:0;border:0}#conversejs .pure-button-primary,#conversejs .pure-button-selected,#conversejs a.pure-button-primary,#conversejs a.pure-button-selected{background-color:#0078e7;color:#fff}#conversejs *,#conversejs :after,#conversejs :before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@media screen and (max-width:480px){#conversejs{margin:0;right:10px;left:10px;bottom:5px}}#conversejs ul li{height:auto}#conversejs a,#conversejs article,#conversejs aside,#conversejs audio,#conversejs blockquote,#conversejs canvas,#conversejs caption,#conversejs dd,#conversejs details,#conversejs div,#conversejs dl,#conversejs dt,#conversejs em,#conversejs embed,#conversejs fieldset,#conversejs figcaption,#conversejs figure,#conversejs footer,#conversejs form,#conversejs h1,#conversejs h2,#conversejs h3,#conversejs h4,#conversejs h5,#conversejs h6,#conversejs header,#conversejs hgroup,#conversejs img,#conversejs label,#conversejs legend,#conversejs li,#conversejs mark,#conversejs menu,#conversejs nav,#conversejs ol,#conversejs output,#conversejs p,#conversejs pre,#conversejs ruby,#conversejs section,#conversejs span,#conversejs strong,#conversejs summary,#conversejs table,#conversejs tbody,#conversejs td,#conversejs tfoot,#conversejs th,#conversejs thead,#conversejs time,#conversejs tr,#conversejs ul,#conversejs video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}#conversejs button,#conversejs input[type=button],#conversejs input[type=password],#conversejs input[type=submit],#conversejs input[type=text],#conversejs textarea{font-size:14px;padding:.25em;min-height:0}#conversejs strong{font-weight:700}#conversejs ol,#conversejs ul{list-style:none}#conversejs li{height:10px}#conversejs dl,#conversejs ol,#conversejs ul{font:inherit;margin:0}#conversejs a,#conversejs a:visited{text-decoration:none;color:#2A9D8F;text-shadow:none}#conversejs{bottom:0;color:#818479;direction:ltr;display:block;font-size:14px;height:35px;position:fixed;left:0;right:0;width:auto;z-index:30}@media screen and (max-width:480px){#conversejs{width:100%;width:100vw}}#conversejs ::selection{background-color:#DCF9F6}#conversejs ::-moz-selection{background-color:#DCF9F6}#conversejs .no-text-select{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#conversejs .emoticon{font-size:14px}#conversejs .left{float:left}#conversejs .right{float:right}#conversejs .centered{text-align:center;display:block;margin:5em auto}#conversejs .hor_centered{text-align:center;display:block;margin:0 auto;clear:both}#conversejs .locked{padding-right:22px}@-webkit-keyframes spin{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes spin{from{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@keyframes spin{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);-o-transform:rotate(359deg);transform:rotate(359deg)}}#conversejs .spinner{-webkit-animation:spin 2s infinite,linear;-moz-animation:spin 2s infinite,linear;animation:spin 2s infinite,linear;display:block;text-align:center;margin:5px}#conversejs .spinner:before{font-size:24px;font-family:Converse-js!important;content:"\231b"}#conversejs .button-group,#conversejs .input-button-group{display:table}#conversejs .button-group{width:100%}#conversejs .input-button-group button,#conversejs .input-button-group input{display:table-cell}#conversejs .error{color:red}#conversejs .reg-feedback{font-size:85%}#conversejs #converse-login .conn-feedback,#conversejs .reg-feedback{display:block;text-align:center;width:100%}#conversejs a.restore-chat{padding:1px 0 1px 5px;color:#fff;line-height:15px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#conversejs a.restore-chat:visited{color:#fff}#conversejs .activated{display:block!important}#conversejs .pure-button{border-radius:4px}#conversejs .button-primary{color:#fff;background-color:#2AC611}#conversejs .button-secondary{color:#fff;background-color:#83A0D6}#conversejs .button-cancel{color:#fff;background-color:#D24E2B}#conversejs form.pure-form.converse-form{background:#fff;margin:1em}#conversejs form.pure-form.converse-form legend{color:#818479}#conversejs form.pure-form.converse-form label{margin-top:1em}#conversejs form.pure-form.converse-form input[type=button],#conversejs form.pure-form.converse-form input[type=number],#conversejs form.pure-form.converse-form input[type=password],#conversejs form.pure-form.converse-form input[type=submit],#conversejs form.pure-form.converse-form input[type=text]{height:2.2em}#conversejs form.pure-form.converse-form input[type=button],#conversejs form.pure-form.converse-form input[type=submit]{padding-left:1em;padding-right:1em;margin-right:1em}#conversejs form.pure-form.converse-form input.error{border:1px solid red;color:#818479}#conversejs form.pure-form.converse-form .form-help{color:gray;font-size:85%;padding-top:.5em}#conversejs form.pure-form.converse-form .form-help:hover{color:#818479}#conversejs .chat-textarea-chatbox-selected{border:1px solid #578308;margin:0}#conversejs .chat-textarea-chatroom-selected{border:2px solid #2A9D8F;margin:0}#conversejs .dropdown dt,#conversejs .dropdown ul{margin:0;padding:0}#conversejs .flyout{border-radius:4px;bottom:6px;display:block;position:absolute}#conversejs .chat-head{border-top-left-radius:4px;border-top-right-radius:4px;color:#fff;font-size:100%;height:55px;margin:0;padding:5px;position:relative}#conversejs .chat-head .avatar{margin-right:.5em;border-radius:50%;float:left}#conversejs .chat-head.chat-head-chatbox{background-color:#F4A261}#conversejs .chat-head .user-custom-message{clear:left;color:#fff;font-size:80%;font-style:italic;height:1.3em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0;padding-top:.2em}#conversejs .chatbox-btn{border-radius:50%;border:1px solid #fff;color:#fff;cursor:pointer;display:inline-block;float:right;font-size:9px;margin:0;margin-right:.2em;padding:.5em .5em .3em;text-decoration:none}#conversejs .chatbox-btn:active{position:relative;top:1px}#conversejs .chatbox{display:block;float:right;height:35px;margin:0 .5em;width:200px}@media screen and (max-width:480px){#conversejs .chatbox{margin:0;width:100%}}#conversejs .chatbox .box-flyout{background-color:#fff;box-shadow:1px 3px 5px 3px rgba(0,0,0,.4);height:450px;min-height:225px;min-width:200px;width:200px;z-index:1}@media screen and (max-width:480px){#conversejs .chatbox .box-flyout{width:100%;height:99vh}}#conversejs .chatbox .chat-title{color:#fff;line-height:15px;display:block;text-overflow:ellipsis;overflow:hidden;height:2em}#conversejs .chatbox .chat-title a{color:#fff;width:100%}#conversejs .chatbox .chat-body{background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-top:0;height:289px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px)}#conversejs .chatbox .chat-body p{color:#818479;font-size:14px;margin:0;padding:5px}#conversejs .chatbox .chat-body .chat-info{color:#D24E2B;margin:.3em}#conversejs .chatbox .chat-body .chat-info.chat-event{clear:left;font-style:italic}#conversejs .chatbox .chat-body .chat-info.chat-error{color:#D24E2B;font-weight:700}#conversejs .chatbox .chat-body .chat-info.chat-date{display:inline-block;margin-top:2em}#conversejs .chatbox .chat-body .chat-message{margin:.3em}#conversejs .chatbox .chat-body .chat-message span{display:inline-block}#conversejs .chatbox .chat-body .chat-message span.chat-msg-author{max-width:100%;font-weight:700;white-space:nowrap;float:left;text-overflow:ellipsis;overflow:hidden}#conversejs .chatbox .chat-body .chat-message span.chat-msg-them{color:#1A9707}#conversejs .chatbox .chat-body .chat-message span.chat-msg-me{color:#2A9D8F}#conversejs .chatbox .chat-body .chat-message span.chat-msg-content{max-width:100%;word-wrap:break-word}#conversejs .chatbox .chat-body .delayed .chat-msg-them{color:#FB5D50}#conversejs .chatbox .chat-body .delayed .chat-msg-me{color:#7EABBB}#conversejs .chatbox .chat-content{position:relative;padding:.5em;font-size:13px;color:#818479;overflow-y:auto;border:0;background-color:#fff;line-height:1.3em;height:206px;height:calc(100% - 96px)}#conversejs .chatbox .dropdown{background-color:#FCFDFD}#conversejs .chatbox .dropdown dd{margin:0;padding:0;position:relative}#conversejs .chatbox form.sendXMPPMessage{-moz-background-clip:padding;-webkit-background-clip:padding-box;border-bottom-left-radius:4px;border-bottom-right-radius:4px;background:#fff;border:0;margin:0;padding:0;position:relative;height:95px;min-width:200px}@media screen and (max-width:480px){#conversejs .chatbox form.sendXMPPMessage{width:100%}}#conversejs .chatbox form.sendXMPPMessage .chat-textarea{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:0;height:70px;padding:.5em;width:100%;resize:none}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar{font-size:14px;margin:0;padding:5px;height:25px;display:block;background-color:#FFF5EE}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar a{color:#2A9D8F}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .chat-toolbar-text{font-size:12px;padding-right:3px;text-shadow:0 1px 0 #fff}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a{color:#D24E2B}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unverified,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unverified a{color:#cf5300}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .private,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .private a{color:#4b7003}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-clear,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-occupants,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr{float:right}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar li{display:inline-block;list-style:none;padding:0 3px;cursor:pointer;margin-top:1px}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar li:hover{cursor:pointer}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul{background:#fff;bottom:100%;box-shadow:-1px -1px 2px 0 rgba(0,0,0,.4);display:none;font-size:12px;margin:0 0 1px;position:absolute;right:0}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li{cursor:pointer;list-style:none;position:relative}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li a:hover{color:#8f2831}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley{color:#2A9D8F;padding-left:5px}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li{font-size:14px;padding:5px;z-index:98}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li:hover{background-color:#DCF9F6}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li{padding:7px;background-color:#fff;display:block;z-index:99}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li a{-moz-transition:background-color .2s ease-in-out;-webkit-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out;display:block;padding:1px;text-decoration:none}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li:hover{background-color:#DCF9F6}#conversejs .chatbox .dragresize{background:0 0;border:0;margin:0;position:absolute;top:0;z-index:20}#conversejs .chatbox .dragresize-top{cursor:n-resize;height:5px;width:100%}#conversejs .chatbox .dragresize-left{cursor:w-resize;width:5px;height:100%;left:0}#conversejs .chatbox .dragresize-topleft{cursor:nw-resize;width:15px;height:15px;top:0;left:0}#conversejs #controlbox{display:none;margin-right:1em}@media screen and (max-width:480px){#conversejs #controlbox{margin:0}}#conversejs #controlbox .controlbox-head{background-color:#577BDD;border-top-left-radius:4px;border-top-right-radius:4px;color:#fff;height:55px;margin:0;padding:6px 6px 6px 0}#conversejs #controlbox form.search-xmpp-contact{margin:0;padding:0 0 5px 5px}#conversejs #controlbox form.search-xmpp-contact input{width:8em}#conversejs #controlbox a.subscribe-to-user{padding-left:2em;font-weight:700}#conversejs #controlbox #converse-register{background:#fff}#conversejs #controlbox #converse-register .title{font-weight:700}#conversejs #controlbox #converse-register .info{font-style:italic;color:green;font-size:85%;margin:5px 0}#conversejs #controlbox #converse-register .form-errors{color:red;display:none}#conversejs #controlbox #converse-register .provider-title{font-size:22px}#conversejs #controlbox #converse-register .provider-score{width:178px;margin-bottom:8px}#conversejs #controlbox #converse-register .form-help .url{font-weight:700;color:#2A9D8F}#conversejs #controlbox #converse-register .input-group{display:table;margin:auto;width:100%}#conversejs #controlbox #converse-register .input-group span{overflow-x:hidden;text-overflow:ellipsis;max-width:110px}#conversejs #controlbox #converse-register .input-group input[name=username],#conversejs #controlbox #converse-register .input-group span{display:table-cell;text-align:left}#conversejs #controlbox #converse-register .instructions{color:gray;font-size:85%}#conversejs #controlbox #converse-register .instructions:hover{color:#818479}#conversejs #controlbox #converse-login,#conversejs #controlbox #converse-register{margin-top:2em}#conversejs #controlbox #converse-login .login-anon,#conversejs #controlbox #converse-register .login-anon{height:auto;white-space:normal}#conversejs #controlbox #converse-login .save-submit,#conversejs #controlbox #converse-register .save-submit{color:#436F64}#conversejs #controlbox #converse-login input,#conversejs #controlbox #converse-register input{width:100%;margin:.5em 0}#conversejs #controlbox #users .add-converse-contact{margin:0 1em .75em}#conversejs #controlbox #chatrooms form.add-chatroom input[type=button],#conversejs #controlbox #chatrooms form.add-chatroom input[type=submit],#conversejs #controlbox #chatrooms form.add-chatroom input[type=text]{width:100%}#conversejs #controlbox #chatrooms #available-chatrooms{padding:0 1em 2em;text-align:left}#conversejs #controlbox #chatrooms #available-chatrooms dt{border:none;color:#818479;font-weight:400;padding:0;padding-bottom:.5em;text-shadow:0 1px 0 #FAFAFA}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom{border:none;clear:both;color:#818479;display:block;overflow:hidden;padding:.4em;text-shadow:0 1px 0 #FAFAFA;word-wrap:break-word}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom:hover{background-color:#DCF9F6}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom a.room-info{clear:right;display:block}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom a.room-info:before{font-size:15px}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom a.open-room{float:left;width:85%}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom .room-info{font-size:11px;font-style:normal;font-weight:400}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom li.room-info{display:block;margin-left:5px}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom p.room-info{margin:0;padding:0;display:block;white-space:normal}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom div.room-info{clear:left;width:100%}#conversejs #controlbox .dropdown a{width:148px;display:inline-block;line-height:25px}#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;border:1px solid #B1BFC4;width:100%;z-index:21;background-color:#FCFDFD}#conversejs #controlbox .dropdown dd ul li:hover{background-color:#DCF9F6}#conversejs #controlbox .dropdown dd.search-xmpp ul{box-shadow:1px 4px 10px 1px rgba(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 #select-xmpp-status{float:right;margin-right:.5em}#conversejs #controlbox #set-custom-xmpp-status{float:left;padding:0}#conversejs #controlbox #set-custom-xmpp-status fieldset{padding:0;margin-top:-1px}#conversejs #controlbox #set-custom-xmpp-status input{height:26px;width:-webkit-calc(100% - 40px);width:calc(100% - 40px);padding:0 0 0 .5em}#conversejs #controlbox #set-custom-xmpp-status input[type=submit]{height:26px;width:40px;padding:1px;float:right}#conversejs #controlbox #controlbox-tabs{text-align:center;display:inline;overflow:hidden;font-size:12px;list-style-type:none}#conversejs #controlbox #controlbox-tabs li{float:left;list-style:none;padding-left:0;text-shadow:#fff 0 1px 0;width:38%}#conversejs #controlbox #controlbox-tabs li a{background-color:#fff;border-bottom:1px solid #CCC;border-top-left-radius:4px;border-top-right-radius:4px;box-shadow:inset 2px -2px 20px rgba(0,0,0,.3);color:#818479;display:block;font-size:12px;height:54px;line-height:54px;margin:0;text-align:center;text-decoration:none}#conversejs #controlbox #controlbox-tabs li a:hover{color:#818479}#conversejs #controlbox #controlbox-tabs li a.current,#conversejs #controlbox #controlbox-tabs li a.current:hover{box-shadow:none;border-bottom:0;height:55px;cursor:default;color:#818479}#conversejs #controlbox .fancy-dropdown{border:1px solid #B1BFC4;height:25px;border-radius:4px;text-align:left;padding:0;padding-left:.3em}#conversejs #controlbox .fancy-dropdown .choose-xmpp-status{width:155px}#conversejs #controlbox .fancy-dropdown .choose-xmpp-status,#conversejs #controlbox .fancy-dropdown .toggle-xmpp-contact-form{text-shadow:0 1px 0 #fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline}#conversejs #controlbox .fancy-dropdown.no-border{border:0}#conversejs #controlbox #fancy-xmpp-status-select{padding-left:0}#conversejs #controlbox #fancy-xmpp-status-select .xmpp-status{margin-left:.3em;display:inline}#conversejs #controlbox #fancy-xmpp-status-select a.change-xmpp-status-message{float:right;clear:right;width:12px;margin-right:.3em;color:#2A9D8F}#conversejs #controlbox .controlbox-pane{background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:0;font-size:14px;position:absolute;text-align:center;width:100%;height:289px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px);overflow-y:auto;overflow-x:hidden}#conversejs #controlbox .controlbox-pane label{font-size:14px;font-weight:700;height:auto;margin:4px}#conversejs #controlbox .controlbox-pane dd{margin-left:0;margin-bottom:0}#conversejs #controlbox .controlbox-pane dd.odd{background-color:#DCEAC5}#conversejs #controlbox #users{overflow-y:hidden}#conversejs #controlbox .add-xmpp-contact{background:0 0;padding:1em}#conversejs #controlbox .add-xmpp-contact input{margin:0 0 1rem;width:100%}#conversejs #controlbox .add-xmpp-contact button{width:100%}#conversejs #controlbox .xmpp-status-menu{text-align:left;box-shadow:1px 4px 10px 1px rgba(0,0,0,.4)}#conversejs #controlbox .xmpp-status-menu li{padding:2px}#conversejs #controlbox .xmpp-status-menu li a{width:100%;padding:0 8px}#conversejs #controlbox .xmpp-status-menu li a.logout,#conversejs #controlbox .xmpp-status-menu li a.logout span{color:#D24E2B}#conversejs #controlbox .set-xmpp-status{background:0 0;margin:1em 1em .5em}#conversejs #controlbox .set-xmpp-status .dropdown dd ul{z-index:22}#conversejs .toggle-controlbox{background-color:#2A9D8F;border-top-left-radius:4px;border-top-right-radius:4px;color:#0a0a0a;float:right;height:100%;margin:0 .5em;padding:10px 8px 0}#conversejs .toggle-controlbox span{color:#fff}#conversejs #converse-roster{text-align:left;width:100%;position:relative;margin:1em 0 0;height:194px;height:calc(100% - 50px - 20px);overflow:hidden;padding:0;padding-bottom:3em}#conversejs #converse-roster.no-contact-requests{height:calc(100% - 25px - 20px)}#conversejs #converse-roster .search-xmpp ul li.chat-info{padding-left:10px}#conversejs #converse-roster .roster-filter-group{margin:0 1em;width:100%;padding-right:2em}#conversejs #converse-roster .roster-filter-group .roster-filter{float:left;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAABNSURBVHjaXI7BDcAwCAMvyQjMyQ6dAbZiKfqoUK34g2zJh1dENIC7M8pMAPYdzAVY3d0ajNz9aypS/b5R6o+ZPdqoKgCq6h80KH3xDgBqNR97p8oAGQAAAABJRU5ErkJggg==) no-repeat right -20px center;border:1px solid #999;font-size:14px;height:25px;margin:0;padding:0;padding-left:.4em;width:53%}#conversejs #converse-roster .roster-filter-group .roster-filter.x{background-position:right 3px center}#conversejs #converse-roster .roster-filter-group .roster-filter.onX{cursor:pointer}#conversejs #converse-roster .roster-filter-group .filter-type{display:table-cell;float:right;font-size:calc(14px - 2px);height:25px;padding:0;width:47%;border-radius:0;border:1px solid}#conversejs #converse-roster .roster-contacts{margin:0;height:100%;overflow-x:hidden;overflow-y:auto}#conversejs #converse-roster .roster-contacts dt.roster-group{border:none;color:#818479;display:none;font-weight:400;margin-top:.5em;padding:.5em 1em;text-shadow:0 1px 0 #FAFAFA}#conversejs #converse-roster .roster-contacts dt.roster-group:hover{background-color:#DCF9F6}#conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle{color:#818479;display:block;width:100%}#conversejs #converse-roster .roster-contacts dd{border:none;clear:both;color:#818479;display:block;height:24px;overflow-y:hidden;padding:.3em 0 .3em 1em;text-shadow:0 1px 0 #FAFAFA;line-height:14px;width:100%}#conversejs #converse-roster .roster-contacts dd .open-chat{max-width:90%}#conversejs #converse-roster .roster-contacts dd:hover{background-color:#DCF9F6}#conversejs #converse-roster .roster-contacts dd:hover .remove-xmpp-contact{display:inline-block}#conversejs #converse-roster .roster-contacts dd:hover .open-chat{width:80%}#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact.request-actions{margin-left:.5em;margin-bottom:.3em;float:right}#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .req-contact-name{width:69%;padding:0}#conversejs #converse-roster .roster-contacts dd.current-xmpp-contact span{font-size:16px;float:left;color:#2A9D8F}#conversejs #converse-roster .roster-contacts dd.odd{background-color:#DCEAC5}#conversejs #converse-roster .roster-contacts dd a,#conversejs #converse-roster .roster-contacts dd span{text-shadow:0 1px 0 #FAFAFA;display:inline-block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#conversejs #converse-roster .roster-contacts dd span{padding:0 .5em 0 0;height:100%}#conversejs #converse-roster .roster-contacts dd a.decline-xmpp-request{margin-left:5px}#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact{float:right;margin-right:1em;display:none;color:#818479}#conversejs #converse-roster span.pending-contact-name{width:80%}#conversejs .chat-head-chatroom{background-color:#E76F51}#conversejs .chat-head-chatroom .chatroom-topic{color:#fff;font-size:80%;font-style:italic;height:1.3em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0;margin-top:.3em}#conversejs .chatroom{width:300px}@media screen and (max-width:480px){#conversejs .chatroom{width:100%}#conversejs .chatroom .box-flyout{min-width:100%;width:100%}}#conversejs .chatroom .box-flyout{min-width:300px;width:300px}#conversejs .chatroom .box-flyout .chatroom-body{border-bottom-left-radius:4px;border-bottom-right-radius:4px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px);background-color:#fff;border-top:0;width:100%}#conversejs .chatroom .box-flyout .chatroom-body .chat-area{word-wrap:break-word;height:100%;max-width:70%;float:left;min-width:200px}#conversejs .chatroom .box-flyout .chatroom-body .chat-area .chat-content{padding:0 .5em}#conversejs .chatroom .box-flyout .chatroom-body .chat-area.full{max-width:100%}#conversejs .chatroom .box-flyout .chatroom-body .mentioned{font-weight:700}#conversejs .chatroom .box-flyout .chatroom-body .chat-msg-room{color:#1A9707}#conversejs .chatroom .box-flyout .chatroom-body .occupants{vertical-align:top;background-color:#fff;overflow:hidden;border-left:1px solid #818479;border-bottom-right-radius:4px;min-width:30%;height:100%}#conversejs .chatroom .box-flyout .chatroom-body .occupants.hidden{display:none}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupants-heading{padding:.3em;font-weight:700}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupant-list{height:85%;height:calc(100% - 70px);overflow-x:hidden;overflow-y:auto;list-style:none}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupant-list li{cursor:default;display:block;font-size:12px;overflow:hidden;padding:2px 5px;text-overflow:ellipsis;white-space:nowrap;width:100px}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupant-list li.moderator{color:#D24E2B}#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container{background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:0;color:#818479;font-size:14px;height:289px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px);overflow-y:auto;position:absolute}#conversejs .chatroom .chat-textarea{border-bottom-right-radius:0}#conversejs .chatroom .room-invite{margin:.3em}#conversejs .chatroom .room-invite .invited-contact{margin:-1px 0 0 -1px;width:100%;border:1px solid #999}#conversejs .chatroom .room-invite .invited-contact.tt-input{width:100%;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkBCjE0uzKkOgAAAidJREFUKM+N0k+IEnEUB/D3cyscdagkWpHV0WGWREXm0AgOGkSJ07kh2UXYU5cOewm6Bp0KXG/tpSCv6hyEFQIhMEaE3yERYfwTOoqKGLQxDAbqYadLgu7J7/XxeY/3ez8EACDLsgljfMfj8ZxUKhXXYDAAnueBoqgyAMipVOovXAuSZdnUaDQeDofDs16vFyUIAjRNUwmCoG02G1AUdZ5IJN7GYrHfm3AvEAjcnUwmX0ajUdRqtV74fL6sruufKYoa6bp+fzabPUMI7ZfL5eImNHk8npNerxc1m80XHMe98fv9H3K5XDkSibxjWfb1arWaYoyPMMbCFqxUKi6CIODw8LDmdDq7oigaAACiKK5omv7KcdylpmlIkiTHFlRVFTRNUxVFqa/ROqIoGoqi5A3DgFartfU4Jp7ngSAI2uVyPZIk6dZmUZKk2w6H4xghBPF4HK7vWLbZbDCdTp+rqvpUkiS0RvV6/bTf7x8wDHMViURqm/AGAMgURZ232+1X1Wr102KxuEwmk3lZlo/7/f7BcrkkSZKs2e12tHXH/x/gHsY4jTE+0jQNGYYBCCFgGOaKJMkfjUaDZximGQ6HXzSbzZ+ZTMbY6oIxFgqFgqPT6YAgCMBxXM1ut6N0Op0fj8chi8XyjWXZ98Fg8DuCHZLNZh+USqWP8/n8idvt/hUKhV7u7QK9Xu8fmqanAJBQVXUfAGY7TQQAKBaLN8fjsdDtdh/run72Dzhf7XLe2UevAAAAAElFTkSuQmCC) no-repeat right 3px center}#conversejs .chatroom .room-invite .invited-contact.tt-input:focus{border-color:#E76F51}#conversejs .chatroom .room-invite .invited-contact.tt-hint{color:transparent;background-color:#fff}#conversejs .chatroom .room-invite .tt-dropdown-menu{width:96%;max-height:250px;background:#E76F51;border-bottom-right-radius:4px;border-bottom-left-radius:4px;overflow-y:auto}#conversejs .chatroom .room-invite .tt-dropdown-menu .tt-suggestion p{color:#fff;cursor:pointer;font-size:11px;text-overflow:ellipsis;overflow-x:hidden}#conversejs .chatroom .room-invite .tt-dropdown-menu .tt-suggestion p:hover{background-color:#FF977C}#conversejs .chatroom .room-invite .tt-dropdown-menu .tt-suggestion .tt-highlight{background-color:#D24E2B}#conversejs #minimized-chats{border-top-left-radius:4px;border-top-right-radius:4px;color:#fff;display:none;float:right;font-weight:700;height:100%;margin:0 .5em;padding:0;width:130px}#conversejs #minimized-chats #toggle-minimized-chats{border-top-left-radius:4px;border-top-right-radius:4px;background-color:#2A9D8F;color:#fff;position:relative;padding:10px 0 0;display:block;width:100%;height:100%;text-align:center}#conversejs #minimized-chats .minimized-chats-flyout{height:auto;bottom:35px}#conversejs #minimized-chats .minimized-chats-flyout .chat-head{padding:.3em;border-radius:4px;width:130px;height:35px;margin-bottom:.2em;box-shadow:1px 3px 5px 3px rgba(0,0,0,.4)}#conversejs #minimized-chats .minimized-chats-flyout.minimized{height:auto}#conversejs #minimized-chats .chat-head-message-count,#conversejs #minimized-chats .unread-message-count{font-weight:700;background-color:#fff;border:1px solid;text-shadow:1px 1px 0 #FAFAFA;color:#D24E2B;border-radius:5px;padding:2px 4px;font-size:16px;text-align:center;position:absolute;right:116px;bottom:10px} */-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#conversejs html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}#conversejs body{margin:0}#conversejs article,#conversejs aside,#conversejs details,#conversejs figcaption,#conversejs figure,#conversejs footer,#conversejs header,#conversejs hgroup,#conversejs main,#conversejs menu,#conversejs nav,#conversejs section,#conversejs summary{display:block}#conversejs audio,#conversejs canvas,#conversejs progress,#conversejs video{display:inline-block;vertical-align:baseline}#conversejs audio:not([controls]){display:none;height:0}#conversejs [hidden],#conversejs template{display:none}#conversejs a{background-color:transparent}#conversejs a:active,#conversejs a:hover{outline:0}#conversejs abbr[title]{border-bottom:1px dotted}#conversejs b,#conversejs strong{font-weight:700}#conversejs dfn{font-style:italic}#conversejs mark{background:#ff0;color:#000}#conversejs small{font-size:80%}#conversejs sub,#conversejs sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}#conversejs sup{top:-.5em}#conversejs sub{bottom:-.25em}#conversejs svg:not(:root){overflow:hidden}#conversejs hr{box-sizing:content-box;height:0}#conversejs pre{overflow:auto}#conversejs code,#conversejs kbd,#conversejs pre,#conversejs samp{font-family:monospace,monospace;font-size:1em}#conversejs button,#conversejs input,#conversejs optgroup,#conversejs select,#conversejs textarea{color:inherit;font:inherit;margin:0}#conversejs button{overflow:visible}#conversejs button,#conversejs select{text-transform:none}#conversejs button,#conversejs html input[type=button],#conversejs input[type=reset],#conversejs input[type=submit]{-webkit-appearance:button;cursor:pointer}#conversejs button[disabled],#conversejs html input[disabled]{cursor:default}#conversejs button::-moz-focus-inner,#conversejs input::-moz-focus-inner{border:0;padding:0}#conversejs input{line-height:normal}#conversejs input[type=checkbox],#conversejs input[type=radio]{box-sizing:border-box;padding:0}#conversejs input[type=number]::-webkit-inner-spin-button,#conversejs input[type=number]::-webkit-outer-spin-button{height:auto}#conversejs input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}#conversejs input[type=search]::-webkit-search-cancel-button,#conversejs input[type=search]::-webkit-search-decoration{-webkit-appearance:none}#conversejs textarea{overflow:auto}#conversejs optgroup{font-weight:700}#conversejs table{border-collapse:collapse;border-spacing:0}#conversejs .hidden,#conversejs [hidden]{display:none!important}#conversejs .pure-img{max-width:100%;height:auto;display:block}#conversejs .pure-form input[type=color],#conversejs .pure-form input[type=date],#conversejs .pure-form input[type=datetime-local],#conversejs .pure-form input[type=datetime],#conversejs .pure-form input[type=email],#conversejs .pure-form input[type=month],#conversejs .pure-form input[type=number],#conversejs .pure-form input[type=password],#conversejs .pure-form input[type=search],#conversejs .pure-form input[type=tel],#conversejs .pure-form input[type=text],#conversejs .pure-form input[type=time],#conversejs .pure-form input[type=url],#conversejs .pure-form input[type=week],#conversejs .pure-form select,#conversejs .pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#conversejs .pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#conversejs .pure-form input[type=color]{padding:.2em .5em}#conversejs .pure-form input:not([type]):focus,#conversejs .pure-form input[type=color]:focus,#conversejs .pure-form input[type=date]:focus,#conversejs .pure-form input[type=datetime-local]:focus,#conversejs .pure-form input[type=datetime]:focus,#conversejs .pure-form input[type=email]:focus,#conversejs .pure-form input[type=month]:focus,#conversejs .pure-form input[type=number]:focus,#conversejs .pure-form input[type=password]:focus,#conversejs .pure-form input[type=search]:focus,#conversejs .pure-form input[type=tel]:focus,#conversejs .pure-form input[type=text]:focus,#conversejs .pure-form input[type=time]:focus,#conversejs .pure-form input[type=url]:focus,#conversejs .pure-form input[type=week]:focus,#conversejs .pure-form select:focus,#conversejs .pure-form textarea:focus{outline:0;border-color:#1A9707}#conversejs .pure-form input[type=checkbox]:focus,#conversejs .pure-form input[type=file]:focus,#conversejs .pure-form input[type=radio]:focus{outline:thin solid #1A9707;outline:1px auto #1A9707}#conversejs .pure-form .pure-checkbox,#conversejs .pure-form .pure-radio{margin:.5em 0;display:block}#conversejs .pure-form input:not([type])[disabled],#conversejs .pure-form input[type=color][disabled],#conversejs .pure-form input[type=date][disabled],#conversejs .pure-form input[type=datetime-local][disabled],#conversejs .pure-form input[type=datetime][disabled],#conversejs .pure-form input[type=email][disabled],#conversejs .pure-form input[type=month][disabled],#conversejs .pure-form input[type=number][disabled],#conversejs .pure-form input[type=password][disabled],#conversejs .pure-form input[type=search][disabled],#conversejs .pure-form input[type=tel][disabled],#conversejs .pure-form input[type=text][disabled],#conversejs .pure-form input[type=time][disabled],#conversejs .pure-form input[type=url][disabled],#conversejs .pure-form input[type=week][disabled],#conversejs .pure-form select[disabled],#conversejs .pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}#conversejs .pure-form input[readonly],#conversejs .pure-form select[readonly],#conversejs .pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}#conversejs .pure-form input:focus:invalid,#conversejs .pure-form select:focus:invalid,#conversejs .pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}#conversejs .pure-form input[type=checkbox]:focus:invalid:focus,#conversejs .pure-form input[type=file]:focus:invalid:focus,#conversejs .pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}#conversejs .pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}#conversejs .pure-form select[multiple]{height:auto}#conversejs .pure-form label{margin:.5em 0 .2em}#conversejs .pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}#conversejs .pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}#conversejs .pure-form-stacked input:not([type]),#conversejs .pure-form-stacked input[type=color],#conversejs .pure-form-stacked input[type=date],#conversejs .pure-form-stacked input[type=datetime-local],#conversejs .pure-form-stacked input[type=datetime],#conversejs .pure-form-stacked input[type=email],#conversejs .pure-form-stacked input[type=file],#conversejs .pure-form-stacked input[type=month],#conversejs .pure-form-stacked input[type=number],#conversejs .pure-form-stacked input[type=password],#conversejs .pure-form-stacked input[type=search],#conversejs .pure-form-stacked input[type=tel],#conversejs .pure-form-stacked input[type=text],#conversejs .pure-form-stacked input[type=time],#conversejs .pure-form-stacked input[type=url],#conversejs .pure-form-stacked input[type=week],#conversejs .pure-form-stacked label,#conversejs .pure-form-stacked select,#conversejs .pure-form-stacked textarea{display:block;margin:.25em 0}#conversejs .pure-form-aligned .pure-help-inline,#conversejs .pure-form-aligned input,#conversejs .pure-form-aligned select,#conversejs .pure-form-aligned textarea,#conversejs .pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}#conversejs .pure-form-aligned textarea{vertical-align:top}#conversejs .pure-form-aligned .pure-control-group{margin-bottom:.5em}#conversejs .pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}#conversejs .pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}#conversejs .pure-form .pure-input-rounded,#conversejs .pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}#conversejs .pure-form .pure-group fieldset{margin-bottom:10px}#conversejs .pure-form .pure-group input,#conversejs .pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}#conversejs .pure-form .pure-group input:focus,#conversejs .pure-form .pure-group textarea:focus{z-index:3}#conversejs .pure-form .pure-group input:first-child,#conversejs .pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}#conversejs .pure-form .pure-group input:first-child:last-child,#conversejs .pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}#conversejs .pure-form .pure-group input:last-child,#conversejs .pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}#conversejs .pure-form .pure-group button{margin:.35em 0}#conversejs .pure-form .pure-input-1{width:100%}#conversejs .pure-form .pure-input-3-4{width:75%}#conversejs .pure-form .pure-input-2-3{width:66%}#conversejs .pure-form .pure-input-1-2{width:50%}#conversejs .pure-form .pure-input-1-3{width:33%}#conversejs .pure-form .pure-input-1-4{width:25%}#conversejs .pure-form .pure-help-inline,#conversejs .pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}#conversejs .pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width:480px){#conversejs .pure-form button[type=submit]{margin:.7em 0 0}#conversejs .pure-form input:not([type]),#conversejs .pure-form input[type=color],#conversejs .pure-form input[type=date],#conversejs .pure-form input[type=datetime-local],#conversejs .pure-form input[type=datetime],#conversejs .pure-form input[type=email],#conversejs .pure-form input[type=month],#conversejs .pure-form input[type=number],#conversejs .pure-form input[type=password],#conversejs .pure-form input[type=search],#conversejs .pure-form input[type=tel],#conversejs .pure-form input[type=text],#conversejs .pure-form input[type=time],#conversejs .pure-form input[type=url],#conversejs .pure-form input[type=week],#conversejs .pure-form label{margin-bottom:.3em;display:block}#conversejs .pure-group input:not([type]),#conversejs .pure-group input[type=color],#conversejs .pure-group input[type=date],#conversejs .pure-group input[type=datetime-local],#conversejs .pure-group input[type=datetime],#conversejs .pure-group input[type=email],#conversejs .pure-group input[type=month],#conversejs .pure-group input[type=number],#conversejs .pure-group input[type=password],#conversejs .pure-group input[type=search],#conversejs .pure-group input[type=tel],#conversejs .pure-group input[type=text],#conversejs .pure-group input[type=time],#conversejs .pure-group input[type=url],#conversejs .pure-group input[type=week]{margin-bottom:0}#conversejs .pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}#conversejs .pure-form-aligned .pure-controls{margin:1.5em 0 0}#conversejs .pure-form .pure-help-inline,#conversejs .pure-form-message,#conversejs .pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}#conversejs .pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:none transparent;background-color:#E6E6E6;text-decoration:none}#conversejs .pure-button-hover,#conversejs .pure-button:focus,#conversejs .pure-button:hover{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05)40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05)0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05)40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05)40%,rgba(0,0,0,.1))}#conversejs .pure-button:focus{outline:0}#conversejs .pure-button-active,#conversejs .pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15)inset,0 0 6px rgba(0,0,0,.2)inset}#conversejs .pure-button-disabled,#conversejs .pure-button-disabled:active,#conversejs .pure-button-disabled:focus,#conversejs .pure-button-disabled:hover,#conversejs .pure-button[disabled]{border:none;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}#conversejs .pure-button-hidden{display:none}#conversejs .pure-button::-moz-focus-inner{padding:0;border:0}#conversejs .pure-button-primary,#conversejs .pure-button-selected,#conversejs a.pure-button-primary,#conversejs a.pure-button-selected{background-color:#0078e7;color:#fff}#conversejs *,#conversejs :after,#conversejs :before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@media screen and (max-width:480px){#conversejs{margin:0;right:10px;left:10px;bottom:5px}}#conversejs ul li{height:auto}#conversejs a,#conversejs article,#conversejs aside,#conversejs audio,#conversejs blockquote,#conversejs canvas,#conversejs caption,#conversejs dd,#conversejs details,#conversejs div,#conversejs dl,#conversejs dt,#conversejs em,#conversejs embed,#conversejs fieldset,#conversejs figcaption,#conversejs figure,#conversejs footer,#conversejs form,#conversejs h1,#conversejs h2,#conversejs h3,#conversejs h4,#conversejs h5,#conversejs h6,#conversejs header,#conversejs hgroup,#conversejs img,#conversejs label,#conversejs legend,#conversejs li,#conversejs mark,#conversejs menu,#conversejs nav,#conversejs ol,#conversejs output,#conversejs p,#conversejs pre,#conversejs ruby,#conversejs section,#conversejs span,#conversejs strong,#conversejs summary,#conversejs table,#conversejs tbody,#conversejs td,#conversejs tfoot,#conversejs th,#conversejs thead,#conversejs time,#conversejs tr,#conversejs ul,#conversejs video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}#conversejs button,#conversejs input[type=button],#conversejs input[type=password],#conversejs input[type=submit],#conversejs input[type=text],#conversejs textarea{font-size:14px;padding:.25em;min-height:0}#conversejs strong{font-weight:700}#conversejs ol,#conversejs ul{list-style:none}#conversejs li{height:10px}#conversejs dl,#conversejs ol,#conversejs ul{font:inherit;margin:0}#conversejs a,#conversejs a:visited{text-decoration:none;color:#2A9D8F;text-shadow:none}#conversejs{bottom:0;color:#818479;direction:ltr;display:block;font-size:14px;height:35px;position:fixed;left:0;right:0;width:auto;z-index:30}@media screen and (max-width:480px){#conversejs{width:100%;width:100vw}}#conversejs ::selection{background-color:#DCF9F6}#conversejs ::-moz-selection{background-color:#DCF9F6}#conversejs .no-text-select{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#conversejs .emoticon{font-size:14px}#conversejs .left{float:left}#conversejs .right{float:right}#conversejs .centered{text-align:center;display:block;margin:5em auto}#conversejs .hor_centered{text-align:center;display:block;margin:0 auto;clear:both}#conversejs .locked{padding-right:22px}@-webkit-keyframes spin{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes spin{from{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@keyframes spin{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);-o-transform:rotate(359deg);transform:rotate(359deg)}}#conversejs .spinner{-webkit-animation:spin 2s infinite,linear;-moz-animation:spin 2s infinite,linear;animation:spin 2s infinite,linear;display:block;text-align:center;margin:5px}#conversejs .spinner:before{font-size:24px;font-family:Converse-js!important;content:"\231b"}#conversejs .button-group,#conversejs .input-button-group{display:table}#conversejs .button-group{width:100%}#conversejs .input-button-group button,#conversejs .input-button-group input{display:table-cell}#conversejs .error{color:red}#conversejs .reg-feedback{font-size:85%}#conversejs #converse-login .conn-feedback,#conversejs .reg-feedback{display:block;text-align:center;width:100%}#conversejs a.restore-chat{padding:1px 0 1px 5px;color:#fff;line-height:15px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#conversejs a.restore-chat:visited{color:#fff}#conversejs .activated{display:block!important}#conversejs .pure-button{border-radius:4px}#conversejs .button-primary{color:#fff;background-color:#2AC611}#conversejs .button-secondary{color:#fff;background-color:#83A0D6}#conversejs .button-cancel{color:#fff;background-color:#D24E2B}#conversejs form.pure-form.converse-form{background:#fff;margin:1em}#conversejs form.pure-form.converse-form legend{color:#818479}#conversejs form.pure-form.converse-form label{margin-top:1em}#conversejs form.pure-form.converse-form input[type=button],#conversejs form.pure-form.converse-form input[type=number],#conversejs form.pure-form.converse-form input[type=password],#conversejs form.pure-form.converse-form input[type=submit],#conversejs form.pure-form.converse-form input[type=text]{height:2.2em}#conversejs form.pure-form.converse-form input[type=button],#conversejs form.pure-form.converse-form input[type=submit]{padding-left:1em;padding-right:1em;margin-right:1em}#conversejs form.pure-form.converse-form input.error{border:1px solid red;color:#818479}#conversejs form.pure-form.converse-form .form-help{color:gray;font-size:85%;padding-top:.5em}#conversejs form.pure-form.converse-form .form-help:hover{color:#818479}#conversejs .chat-textarea-chatbox-selected{border:1px solid #578308;margin:0}#conversejs .chat-textarea-chatroom-selected{border:2px solid #2A9D8F;margin:0}#conversejs .dropdown dt,#conversejs .dropdown ul{margin:0;padding:0}#conversejs .flyout{border-radius:4px;bottom:6px;display:block;position:absolute}#conversejs .chat-head{border-top-left-radius:4px;border-top-right-radius:4px;color:#fff;font-size:100%;height:55px;margin:0;padding:5px;position:relative}#conversejs .chat-head .avatar{margin-right:.5em;border-radius:50%;float:left}#conversejs .chat-head.chat-head-chatbox{background-color:#F4A261}#conversejs .chat-head .user-custom-message{clear:left;color:#fff;font-size:80%;font-style:italic;height:1.3em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0;padding-top:.2em}#conversejs .chatbox-btn{border-radius:50%;border:1px solid #fff;color:#fff;cursor:pointer;display:inline-block;float:right;font-size:9px;margin:0;margin-right:.2em;padding:.5em .5em .3em;text-decoration:none}#conversejs .chatbox-btn:active{position:relative;top:1px}#conversejs .chatbox{display:block;float:right;height:35px;margin:0 .5em;width:200px}@media screen and (max-width:480px){#conversejs .chatbox{margin:0;width:100%}}#conversejs .chatbox .box-flyout{background-color:#fff;box-shadow:1px 3px 5px 3px rgba(0,0,0,.4);height:450px;min-height:225px;min-width:200px;width:200px;z-index:1}@media screen and (max-width:480px){#conversejs .chatbox .box-flyout{width:100%;height:99vh}}#conversejs .chatbox .chat-title{color:#fff;line-height:15px;display:block;text-overflow:ellipsis;overflow:hidden;height:2em}#conversejs .chatbox .chat-title a{color:#fff;width:100%}#conversejs .chatbox .chat-body{background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-top:0;height:289px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px)}#conversejs .chatbox .chat-body p{color:#818479;font-size:14px;margin:0;padding:5px}#conversejs .chatbox .chat-body .chat-info{color:#D24E2B;margin:.3em}#conversejs .chatbox .chat-body .chat-info.chat-event{clear:left;font-style:italic}#conversejs .chatbox .chat-body .chat-info.chat-error{color:#D24E2B;font-weight:700}#conversejs .chatbox .chat-body .chat-info.chat-date{display:inline-block;margin-top:2em}#conversejs .chatbox .chat-body .chat-message{margin:.3em}#conversejs .chatbox .chat-body .chat-message span{display:inline-block}#conversejs .chatbox .chat-body .chat-message span.chat-msg-author{max-width:100%;font-weight:700;white-space:nowrap;float:left;text-overflow:ellipsis;overflow:hidden}#conversejs .chatbox .chat-body .chat-message span.chat-msg-them{color:#1A9707}#conversejs .chatbox .chat-body .chat-message span.chat-msg-me{color:#2A9D8F}#conversejs .chatbox .chat-body .chat-message span.chat-msg-content{max-width:100%;word-wrap:break-word}#conversejs .chatbox .chat-body .delayed .chat-msg-them{color:#FB5D50}#conversejs .chatbox .chat-body .delayed .chat-msg-me{color:#7EABBB}#conversejs .chatbox .chat-content{position:relative;padding:.5em;font-size:13px;color:#818479;overflow-y:auto;border:0;background-color:#fff;line-height:1.3em;height:206px;height:calc(100% - 96px)}#conversejs .chatbox .dropdown{background-color:#FCFDFD}#conversejs .chatbox .dropdown dd{margin:0;padding:0;position:relative}#conversejs .chatbox form.sendXMPPMessage{-moz-background-clip:padding;-webkit-background-clip:padding-box;border-bottom-left-radius:4px;border-bottom-right-radius:4px;background:#fff;border:0;margin:0;padding:0;position:relative;height:95px;min-width:200px}@media screen and (max-width:480px){#conversejs .chatbox form.sendXMPPMessage{width:100%}}#conversejs .chatbox form.sendXMPPMessage .chat-textarea{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:0;height:70px;padding:.5em;width:100%;resize:none}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar{font-size:14px;margin:0;padding:5px;height:25px;display:block;background-color:#FFF5EE}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar a{color:#2A9D8F}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .chat-toolbar-text{font-size:12px;padding-right:3px;text-shadow:0 1px 0 #fff}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unencrypted a{color:#D24E2B}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unverified,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .unverified a{color:#cf5300}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .private,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .private a{color:#4b7003}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-clear,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-occupants,#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr{float:right}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar li{display:inline-block;list-style:none;padding:0 3px;cursor:pointer;margin-top:1px}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar li:hover{cursor:pointer}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul{background:#fff;bottom:100%;box-shadow:-1px -1px 2px 0 rgba(0,0,0,.4);display:none;font-size:12px;margin:0 0 1px;position:absolute;right:0}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li{cursor:pointer;list-style:none;position:relative}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar ul li a:hover{color:#8f2831}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley{color:#2A9D8F;padding-left:5px}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li{font-size:14px;padding:5px;z-index:98}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li:hover{background-color:#DCF9F6}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li{padding:7px;background-color:#fff;display:block;z-index:99}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li a{-moz-transition:background-color .2s ease-in-out;-webkit-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out;display:block;padding:1px;text-decoration:none}#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-otr ul li:hover{background-color:#DCF9F6}#conversejs .chatbox .dragresize{background:0 0;border:0;margin:0;position:absolute;top:0;z-index:20}#conversejs .chatbox .dragresize-top{cursor:n-resize;height:5px;width:100%}#conversejs .chatbox .dragresize-left{cursor:w-resize;width:5px;height:100%;left:0}#conversejs .chatbox .dragresize-topleft{cursor:nw-resize;width:15px;height:15px;top:0;left:0}#conversejs #controlbox{display:none;margin-right:1em}@media screen and (max-width:480px){#conversejs #controlbox{margin:0}}#conversejs #controlbox .controlbox-head{background-color:#577BDD;border-top-left-radius:4px;border-top-right-radius:4px;color:#fff;height:55px;margin:0;padding:6px 6px 6px 0}#conversejs #controlbox form.search-xmpp-contact{margin:0;padding:0 0 5px 5px}#conversejs #controlbox form.search-xmpp-contact input{width:8em}#conversejs #controlbox a.subscribe-to-user{padding-left:2em;font-weight:700}#conversejs #controlbox #converse-register{background:#fff}#conversejs #controlbox #converse-register .title{font-weight:700}#conversejs #controlbox #converse-register .info{font-style:italic;color:green;font-size:85%;margin:5px 0}#conversejs #controlbox #converse-register .form-errors{color:red;display:none}#conversejs #controlbox #converse-register .provider-title{font-size:22px}#conversejs #controlbox #converse-register .provider-score{width:178px;margin-bottom:8px}#conversejs #controlbox #converse-register .form-help .url{font-weight:700;color:#2A9D8F}#conversejs #controlbox #converse-register .input-group{display:table;margin:auto;width:100%}#conversejs #controlbox #converse-register .input-group span{overflow-x:hidden;text-overflow:ellipsis;max-width:110px}#conversejs #controlbox #converse-register .input-group input[name=username],#conversejs #controlbox #converse-register .input-group span{display:table-cell;text-align:left}#conversejs #controlbox #converse-register .instructions{color:gray;font-size:85%}#conversejs #controlbox #converse-register .instructions:hover{color:#818479}#conversejs #controlbox #converse-login,#conversejs #controlbox #converse-register{margin-top:2em}#conversejs #controlbox #converse-login .login-anon,#conversejs #controlbox #converse-register .login-anon{height:auto;white-space:normal}#conversejs #controlbox #converse-login .save-submit,#conversejs #controlbox #converse-register .save-submit{color:#436F64}#conversejs #controlbox #converse-login input,#conversejs #controlbox #converse-register input{width:100%;margin:.5em 0}#conversejs #controlbox #users .add-converse-contact{margin:0 1em .75em}#conversejs #controlbox #chatrooms form.add-chatroom input[type=button],#conversejs #controlbox #chatrooms form.add-chatroom input[type=submit],#conversejs #controlbox #chatrooms form.add-chatroom input[type=text]{width:100%}#conversejs #controlbox #chatrooms #available-chatrooms{padding:0 1em 2em;text-align:left}#conversejs #controlbox #chatrooms #available-chatrooms dt{border:none;color:#818479;font-weight:400;padding:0;padding-bottom:.5em;text-shadow:0 1px 0 #FAFAFA}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom{border:none;clear:both;color:#818479;display:block;overflow:hidden;padding:.4em;text-shadow:0 1px 0 #FAFAFA;word-wrap:break-word}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom:hover{background-color:#DCF9F6}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom a.room-info{clear:right;display:block}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom a.room-info:before{font-size:15px}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom a.open-room{float:left;width:85%}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom .room-info{font-size:11px;font-style:normal;font-weight:400}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom li.room-info{display:block;margin-left:5px}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom p.room-info{margin:0;padding:0;display:block;white-space:normal}#conversejs #controlbox #chatrooms #available-chatrooms dd.available-chatroom div.room-info{clear:left;width:100%}#conversejs #controlbox .dropdown a{width:148px;display:inline-block;line-height:25px}#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;border:1px solid #B1BFC4;width:100%;z-index:21;background-color:#FCFDFD}#conversejs #controlbox .dropdown dd ul li:hover{background-color:#DCF9F6}#conversejs #controlbox .dropdown dd.search-xmpp ul{box-shadow:1px 4px 10px 1px rgba(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 #select-xmpp-status{float:right;margin-right:.5em}#conversejs #controlbox #set-custom-xmpp-status{float:left;padding:0}#conversejs #controlbox #set-custom-xmpp-status fieldset{padding:0;margin-top:-1px}#conversejs #controlbox #set-custom-xmpp-status input{height:26px;width:-webkit-calc(100% - 40px);width:calc(100% - 40px);padding:0 0 0 .5em}#conversejs #controlbox #set-custom-xmpp-status input[type=submit]{height:26px;width:40px;padding:1px;float:right}#conversejs #controlbox #controlbox-tabs{text-align:center;display:inline;overflow:hidden;font-size:12px;list-style-type:none}#conversejs #controlbox #controlbox-tabs li{float:left;list-style:none;padding-left:0;text-shadow:#fff 0 1px 0;width:38%}#conversejs #controlbox #controlbox-tabs li a{background-color:#fff;border-bottom:1px solid #CCC;border-top-left-radius:4px;border-top-right-radius:4px;box-shadow:inset 2px -2px 20px rgba(0,0,0,.3);color:#818479;display:block;font-size:12px;height:54px;line-height:54px;margin:0;text-align:center;text-decoration:none}#conversejs #controlbox #controlbox-tabs li a:hover{color:#818479}#conversejs #controlbox #controlbox-tabs li a.current,#conversejs #controlbox #controlbox-tabs li a.current:hover{box-shadow:none;border-bottom:0;height:55px;cursor:default;color:#818479}#conversejs #controlbox .fancy-dropdown{border:1px solid #B1BFC4;height:25px;border-radius:4px;text-align:left;padding:0;padding-left:.3em}#conversejs #controlbox .fancy-dropdown .choose-xmpp-status{width:155px}#conversejs #controlbox .fancy-dropdown .choose-xmpp-status,#conversejs #controlbox .fancy-dropdown .toggle-xmpp-contact-form{text-shadow:0 1px 0 #fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline}#conversejs #controlbox .fancy-dropdown.no-border{border:0}#conversejs #controlbox #fancy-xmpp-status-select{padding-left:0}#conversejs #controlbox #fancy-xmpp-status-select .xmpp-status{margin-left:.3em;display:inline}#conversejs #controlbox #fancy-xmpp-status-select a.change-xmpp-status-message{float:right;clear:right;width:12px;margin-right:.3em;color:#2A9D8F}#conversejs #controlbox .controlbox-pane{background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:0;font-size:14px;position:absolute;text-align:center;width:100%;height:289px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px);overflow-y:auto;overflow-x:hidden}#conversejs #controlbox .controlbox-pane label{font-size:14px;font-weight:700;height:auto;margin:4px}#conversejs #controlbox .controlbox-pane dd{margin-left:0;margin-bottom:0}#conversejs #controlbox .controlbox-pane dd.odd{background-color:#DCEAC5}#conversejs #controlbox #users{overflow-y:hidden}#conversejs #controlbox .add-xmpp-contact{background:0 0;padding:1em}#conversejs #controlbox .add-xmpp-contact input{margin:0 0 1rem;width:100%}#conversejs #controlbox .add-xmpp-contact button{width:100%}#conversejs #controlbox .xmpp-status-menu{text-align:left;box-shadow:1px 4px 10px 1px rgba(0,0,0,.4)}#conversejs #controlbox .xmpp-status-menu li{padding:2px}#conversejs #controlbox .xmpp-status-menu li a{width:100%;padding:0 8px}#conversejs #controlbox .xmpp-status-menu li a.logout,#conversejs #controlbox .xmpp-status-menu li a.logout span{color:#D24E2B}#conversejs #controlbox .set-xmpp-status{background:0 0;margin:1em 1em .5em}#conversejs #controlbox .set-xmpp-status .dropdown dd ul{z-index:22}#conversejs .toggle-controlbox{background-color:#2A9D8F;border-top-left-radius:4px;border-top-right-radius:4px;color:#0a0a0a;float:right;height:100%;margin:0 .5em;padding:10px 8px 0}#conversejs .toggle-controlbox span{color:#fff}#conversejs #converse-roster{text-align:left;width:100%;position:relative;margin:1em 0 0;height:194px;height:calc(100% - 50px - 20px);overflow:hidden;padding:0;padding-bottom:3em}#conversejs #converse-roster.no-contact-requests{height:calc(100% - 25px - 20px)}#conversejs #converse-roster .search-xmpp ul li.chat-info{padding-left:10px}#conversejs #converse-roster .roster-filter-group{margin:0 1em;width:100%;padding-right:2em}#conversejs #converse-roster .roster-filter-group .roster-filter{float:left;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAABNSURBVHjaXI7BDcAwCAMvyQjMyQ6dAbZiKfqoUK34g2zJh1dENIC7M8pMAPYdzAVY3d0ajNz9aypS/b5R6o+ZPdqoKgCq6h80KH3xDgBqNR97p8oAGQAAAABJRU5ErkJggg==) no-repeat right -20px center;border:1px solid #999;font-size:14px;height:25px;margin:0;padding:0;padding-left:.4em;width:53%}#conversejs #converse-roster .roster-filter-group .roster-filter.x{background-position:right 3px center}#conversejs #converse-roster .roster-filter-group .roster-filter.onX{cursor:pointer}#conversejs #converse-roster .roster-filter-group .filter-type{display:table-cell;float:right;font-size:calc(14px - 2px);height:25px;padding:0;width:47%;border-radius:0;border:1px solid}#conversejs #converse-roster .roster-contacts{margin:0;height:100%;overflow-x:hidden;overflow-y:auto}#conversejs #converse-roster .roster-contacts dt.roster-group{border:none;color:#818479;display:none;font-weight:400;margin-top:.5em;padding:.5em 1em;text-shadow:0 1px 0 #FAFAFA}#conversejs #converse-roster .roster-contacts dt.roster-group:hover{background-color:#DCF9F6}#conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle{color:#818479;display:block;width:100%}#conversejs #converse-roster .roster-contacts dd{border:none;clear:both;color:#818479;display:block;height:24px;overflow-y:hidden;padding:.3em 0 .3em 1em;text-shadow:0 1px 0 #FAFAFA;line-height:14px;width:100%}#conversejs #converse-roster .roster-contacts dd .open-chat{max-width:90%}#conversejs #converse-roster .roster-contacts dd:hover{background-color:#DCF9F6}#conversejs #converse-roster .roster-contacts dd:hover .remove-xmpp-contact{display:inline-block}#conversejs #converse-roster .roster-contacts dd:hover .open-chat{width:80%}#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact.request-actions{margin-left:.5em;margin-bottom:.3em;float:right}#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .req-contact-name{width:69%;padding:0}#conversejs #converse-roster .roster-contacts dd.current-xmpp-contact span{font-size:16px;float:left;color:#2A9D8F}#conversejs #converse-roster .roster-contacts dd.odd{background-color:#DCEAC5}#conversejs #converse-roster .roster-contacts dd a,#conversejs #converse-roster .roster-contacts dd span{text-shadow:0 1px 0 #FAFAFA;display:inline-block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#conversejs #converse-roster .roster-contacts dd span{padding:0 .5em 0 0;height:100%}#conversejs #converse-roster .roster-contacts dd a.decline-xmpp-request{margin-left:5px}#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact{float:right;margin-right:1em;display:none;color:#818479}#conversejs #converse-roster span.pending-contact-name{width:80%}#conversejs .chat-head-chatroom{background-color:#E76F51}#conversejs .chat-head-chatroom .chatroom-topic{color:#fff;font-size:80%;font-style:italic;height:1.3em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0;margin-top:.3em}#conversejs .chatroom{width:300px}@media screen and (max-width:480px){#conversejs .chatroom{width:100%}#conversejs .chatroom .box-flyout{min-width:100%;width:100%}}#conversejs .chatroom .box-flyout{min-width:300px;width:300px}#conversejs .chatroom .box-flyout .chatroom-body{border-bottom-left-radius:4px;border-bottom-right-radius:4px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px);background-color:#fff;border-top:0;width:100%}#conversejs .chatroom .box-flyout .chatroom-body .chat-area{word-wrap:break-word;height:100%;max-width:70%;float:left;min-width:200px}#conversejs .chatroom .box-flyout .chatroom-body .chat-area .chat-content{padding:0 .5em}#conversejs .chatroom .box-flyout .chatroom-body .chat-area.full{max-width:100%}#conversejs .chatroom .box-flyout .chatroom-body .mentioned{font-weight:700}#conversejs .chatroom .box-flyout .chatroom-body .chat-msg-room{color:#1A9707}#conversejs .chatroom .box-flyout .chatroom-body .occupants{vertical-align:top;background-color:#fff;overflow:hidden;border-left:1px solid #818479;border-bottom-right-radius:4px;min-width:30%;height:100%}#conversejs .chatroom .box-flyout .chatroom-body .occupants.hidden{display:none}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupants-heading{padding:.3em;font-weight:700}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupant-list{height:85%;height:calc(100% - 70px);overflow-x:hidden;overflow-y:auto;list-style:none}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupant-list li{cursor:default;display:block;font-size:12px;overflow:hidden;padding:2px 5px;text-overflow:ellipsis;white-space:nowrap;width:100px}#conversejs .chatroom .box-flyout .chatroom-body .occupants .occupant-list li.moderator{color:#D24E2B}#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container{background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;border:0;color:#818479;font-size:14px;height:289px;height:-webkit-calc(100% - 55px);height:calc(100% - 55px);overflow-y:auto;position:absolute}#conversejs .chatroom .chat-textarea{border-bottom-right-radius:0}#conversejs .chatroom .room-invite{margin:.3em}#conversejs .chatroom .room-invite .invited-contact{margin:-1px 0 0 -1px;width:100%;border:1px solid #999}#conversejs .chatroom .room-invite .invited-contact.tt-input{width:100%;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkBCjE0uzKkOgAAAidJREFUKM+N0k+IEnEUB/D3cyscdagkWpHV0WGWREXm0AgOGkSJ07kh2UXYU5cOewm6Bp0KXG/tpSCv6hyEFQIhMEaE3yERYfwTOoqKGLQxDAbqYadLgu7J7/XxeY/3ez8EACDLsgljfMfj8ZxUKhXXYDAAnueBoqgyAMipVOovXAuSZdnUaDQeDofDs16vFyUIAjRNUwmCoG02G1AUdZ5IJN7GYrHfm3AvEAjcnUwmX0ajUdRqtV74fL6sruufKYoa6bp+fzabPUMI7ZfL5eImNHk8npNerxc1m80XHMe98fv9H3K5XDkSibxjWfb1arWaYoyPMMbCFqxUKi6CIODw8LDmdDq7oigaAACiKK5omv7KcdylpmlIkiTHFlRVFTRNUxVFqa/ROqIoGoqi5A3DgFartfU4Jp7ngSAI2uVyPZIk6dZmUZKk2w6H4xghBPF4HK7vWLbZbDCdTp+rqvpUkiS0RvV6/bTf7x8wDHMViURqm/AGAMgURZ232+1X1Wr102KxuEwmk3lZlo/7/f7BcrkkSZKs2e12tHXH/x/gHsY4jTE+0jQNGYYBCCFgGOaKJMkfjUaDZximGQ6HXzSbzZ+ZTMbY6oIxFgqFgqPT6YAgCMBxXM1ut6N0Op0fj8chi8XyjWXZ98Fg8DuCHZLNZh+USqWP8/n8idvt/hUKhV7u7QK9Xu8fmqanAJBQVXUfAGY7TQQAKBaLN8fjsdDtdh/run72Dzhf7XLe2UevAAAAAElFTkSuQmCC) no-repeat right 3px center}#conversejs .chatroom .room-invite .invited-contact.tt-input:focus{border-color:#E76F51}#conversejs .chatroom .room-invite .invited-contact.tt-hint{color:transparent;background-color:#fff}#conversejs .chatroom .room-invite .tt-dropdown-menu{width:96%;max-height:250px;background:#E76F51;border-bottom-right-radius:4px;border-bottom-left-radius:4px;overflow-y:auto}#conversejs .chatroom .room-invite .tt-dropdown-menu .tt-suggestion p{color:#fff;cursor:pointer;font-size:11px;text-overflow:ellipsis;overflow-x:hidden}#conversejs .chatroom .room-invite .tt-dropdown-menu .tt-suggestion p:hover{background-color:#FF977C}#conversejs .chatroom .room-invite .tt-dropdown-menu .tt-suggestion .tt-highlight{background-color:#D24E2B}#conversejs .chatbox.headlines .chat-head.chat-head-chatbox{background-color:#2A9D8F}#conversejs #minimized-chats{border-top-left-radius:4px;border-top-right-radius:4px;color:#fff;display:none;float:right;font-weight:700;height:100%;margin:0 .5em;padding:0;width:130px}#conversejs #minimized-chats #toggle-minimized-chats{border-top-left-radius:4px;border-top-right-radius:4px;background-color:#2A9D8F;color:#fff;position:relative;padding:10px 0 0;display:block;width:100%;height:100%;text-align:center}#conversejs #minimized-chats .minimized-chats-flyout{height:auto;bottom:35px}#conversejs #minimized-chats .minimized-chats-flyout .chat-head{padding:.3em;border-radius:4px;width:130px;height:35px;margin-bottom:.2em;box-shadow:1px 3px 5px 3px rgba(0,0,0,.4)}#conversejs #minimized-chats .minimized-chats-flyout.minimized{height:auto}#conversejs #minimized-chats .chat-head-message-count,#conversejs #minimized-chats .unread-message-count{font-weight:700;background-color:#fff;border:1px solid;text-shadow:1px 1px 0 #FAFAFA;color:#D24E2B;border-radius:5px;padding:2px 4px;font-size:16px;text-align:center;position:absolute;right:116px;bottom:10px}
\ No newline at end of file \ No newline at end of file
...@@ -2232,6 +2232,21 @@ define("converse-templates", [ ...@@ -2232,6 +2232,21 @@ define("converse-templates", [
return str; return str;
}, },
isHeadlineMessage: function (message) {
var $message = $(message),
from_jid = $message.attr('from');
if ($message.attr('type') === 'headline' ||
// Some servers (I'm looking at you Prosody) don't set the message
// type to "headline" when sending server messages. For now we
// check if an @ signal is included, and if not, we assume it's
// a headline message.
(typeof from_jid !== 'undefined' && from_jid.indexOf('@') === -1)
) {
return true;
}
return false;
},
refreshWebkit: function () { refreshWebkit: function () {
/* This works around a webkit bug. Refreshes the browser's viewport, /* This works around a webkit bug. Refreshes the browser's viewport,
* otherwise chatboxes are not moved along when one is closed. * otherwise chatboxes are not moved along when one is closed.
...@@ -3036,6 +3051,7 @@ define("polyfill", function(){}); ...@@ -3036,6 +3051,7 @@ define("polyfill", function(){});
this.afterReconnected(); this.afterReconnected();
deferred.resolve(); deferred.resolve();
}.bind(this)); }.bind(this));
converse.emit('reconnected');
return deferred.promise(); return deferred.promise();
}; };
...@@ -3651,7 +3667,9 @@ define("polyfill", function(){}); ...@@ -3651,7 +3667,9 @@ define("polyfill", function(){});
} else { } else {
from = Strophe.getBareJidFromJid($message.attr('from')); from = Strophe.getBareJidFromJid($message.attr('from'));
} }
fullname = (_.isEmpty(fullname) ? from: fullname).split(' ')[0]; if (_.isEmpty(fullname)) {
fullname = from;
}
if (delayed) { if (delayed) {
stamp = $delay.attr('stamp'); stamp = $delay.attr('stamp');
time = stamp; time = stamp;
...@@ -3674,8 +3692,7 @@ define("polyfill", function(){}); ...@@ -3674,8 +3692,7 @@ define("polyfill", function(){});
}); });
} }
}); });
this.ChatBoxes = Backbone.Collection.extend({ this.ChatBoxes = Backbone.Collection.extend({
model: converse.ChatBox, model: converse.ChatBox,
comparator: 'time_opened', comparator: 'time_opened',
...@@ -3716,22 +3733,39 @@ define("polyfill", function(){}); ...@@ -3716,22 +3733,39 @@ define("polyfill", function(){});
}, },
onMessage: function (message) { onMessage: function (message) {
/* Handler method for all incoming single-user chat "message" stanzas. /* Handler method for all incoming single-user chat "message"
* stanzas.
*/ */
var $message = $(message), var $message = $(message),
contact_jid, $forwarded, $delay, from_bare_jid, from_resource, is_me, msgid, contact_jid, $forwarded, $delay, from_bare_jid,
from_resource, is_me, msgid,
chatbox, resource, chatbox, resource,
from_jid = $message.attr('from'), from_jid = $message.attr('from'),
to_jid = $message.attr('to'), to_jid = $message.attr('to'),
to_resource = Strophe.getResourceFromJid(to_jid); to_resource = Strophe.getResourceFromJid(to_jid);
if (to_resource && to_resource !== converse.resource) { if (to_resource && to_resource !== converse.resource) {
converse.log('Ignore incoming message intended for a different resource: '+to_jid, 'info'); converse.log(
'onMessage: Ignoring incoming message intended for a different resource: '+to_jid,
'info'
);
return true; return true;
} } else if (from_jid === converse.connection.jid) {
if (from_jid === converse.connection.jid) { // FIXME: Forwarded messages should be sent to specific
// FIXME: Forwarded messages should be sent to specific resources, not broadcasted // resources, not broadcasted
converse.log("Ignore incoming message sent from this client's JID: "+from_jid, 'info'); converse.log(
"onMessage: Ignoring incoming message sent from this client's JID: "+from_jid,
'info'
);
return true;
} else if (utils.isHeadlineMessage(message)) {
// XXX: Ideally we wouldn't have to check for headline
// messages, but Prosody sends headline messages with the
// wrong type ('chat'), so we need to filter them out here.
converse.log(
"onMessage: Ignoring incoming headline message sent with type 'chat' from JID: "+from_jid,
'info'
);
return true; return true;
} }
$forwarded = $message.find('forwarded'); $forwarded = $message.find('forwarded');
...@@ -3802,6 +3836,7 @@ define("polyfill", function(){}); ...@@ -3802,6 +3836,7 @@ define("polyfill", function(){});
initialize: function () { initialize: function () {
this.model.on("add", this.onChatBoxAdded, this); this.model.on("add", this.onChatBoxAdded, this);
this.model.on("destroy", this.removeChat, this);
}, },
_ensureElement: function () { _ensureElement: function () {
...@@ -3823,7 +3858,12 @@ define("polyfill", function(){}); ...@@ -3823,7 +3858,12 @@ define("polyfill", function(){});
onChatBoxAdded: function (item) { onChatBoxAdded: function (item) {
var view = this.get(item.get('id')); var view = this.get(item.get('id'));
// Views aren't created here, since the core code doesn't have
// contain any views. Instead, they're created in overrides in
// converse-chatiew.js and/or converse-muc.js
if (view) { if (view) {
// This is an optimization. We don't remove older views, so
// when one is available, we reuse it.
delete view.model; // Remove ref to old model to help garbage collection delete view.model; // Remove ref to old model to help garbage collection
view.model = item; view.model = item;
view.initialize(); view.initialize();
...@@ -3831,6 +3871,10 @@ define("polyfill", function(){}); ...@@ -3831,6 +3871,10 @@ define("polyfill", function(){});
return view; return view;
}, },
removeChat: function (item) {
this.remove(item.get('id'));
},
closeAllChatBoxes: function () { closeAllChatBoxes: function () {
/* This method gets overridden in src/converse-controlbox.js if /* This method gets overridden in src/converse-controlbox.js if
* the controlbox plugin is active. * the controlbox plugin is active.
...@@ -4488,11 +4532,10 @@ define("polyfill", function(){}); ...@@ -4488,11 +4532,10 @@ define("polyfill", function(){});
}; };
})); }));
/**
* @preserve jed.js https://github.com/SlexAxton/Jed
*/
/* /*
jed.js
v0.5.0beta
https://github.com/SlexAxton/Jed
----------- -----------
A gettext compatible i18n library for modern JavaScript Applications A gettext compatible i18n library for modern JavaScript Applications
...@@ -4581,7 +4624,9 @@ in order to offer easy upgrades -- jsgettext.berlios.de ...@@ -4581,7 +4624,9 @@ in order to offer easy upgrades -- jsgettext.berlios.de
} }
}, },
// The default domain if one is missing // The default domain if one is missing
"domain" : "messages" "domain" : "messages",
// enable debug mode to log untranslated strings to the console
"debug" : false
}; };
// Mix in the sent options with the default options // Mix in the sent options with the default options
...@@ -4626,7 +4671,7 @@ in order to offer easy upgrades -- jsgettext.berlios.de ...@@ -4626,7 +4671,7 @@ in order to offer easy upgrades -- jsgettext.berlios.de
}, },
fetch : function ( sArr ) { fetch : function ( sArr ) {
if ( {}.toString.call( sArr ) != '[object Array]' ) { if ( {}.toString.call( sArr ) != '[object Array]' ) {
sArr = [].slice.call(arguments); sArr = [].slice.call(arguments, 0);
} }
return ( sArr && sArr.length ? Jed.sprintf : function(x){ return x; } )( return ( sArr && sArr.length ? Jed.sprintf : function(x){ return x; } )(
this._i18n.dcnpgettext(this._domain, this._context, this._key, this._pkey, this._val), this._i18n.dcnpgettext(this._domain, this._context, this._key, this._pkey, this._val),
...@@ -4711,9 +4756,6 @@ in order to offer easy upgrades -- jsgettext.berlios.de ...@@ -4711,9 +4756,6 @@ in order to offer easy upgrades -- jsgettext.berlios.de
// isn't explicitly passed in // isn't explicitly passed in
domain = domain || this._textdomain; domain = domain || this._textdomain;
// Default the value to the singular case
val = typeof val == 'undefined' ? 1 : val;
var fallback; var fallback;
// Handle special cases // Handle special cases
...@@ -4747,23 +4789,34 @@ in order to offer easy upgrades -- jsgettext.berlios.de ...@@ -4747,23 +4789,34 @@ in order to offer easy upgrades -- jsgettext.berlios.de
throw new Error('No translation key found.'); throw new Error('No translation key found.');
} }
// Handle invalid numbers, but try casting strings for good measure
if ( typeof val != 'number' ) {
val = parseInt( val, 10 );
if ( isNaN( val ) ) {
throw new Error('The number that was passed in is not a number.');
}
}
var key = context ? context + Jed.context_delimiter + singular_key : singular_key, var key = context ? context + Jed.context_delimiter + singular_key : singular_key,
locale_data = this.options.locale_data, locale_data = this.options.locale_data,
dict = locale_data[ domain ], dict = locale_data[ domain ],
pluralForms = dict[""].plural_forms || (locale_data.messages || this.defaults.locale_data.messages)[""].plural_forms, defaultConf = (locale_data.messages || this.defaults.locale_data.messages)[""],
val_idx = getPluralFormFunc(pluralForms)(val) + 1, pluralForms = dict[""].plural_forms || dict[""]["Plural-Forms"] || dict[""]["plural-forms"] || defaultConf.plural_forms || defaultConf["Plural-Forms"] || defaultConf["plural-forms"],
val_list, val_list,
res; res;
var val_idx;
if (val === undefined) {
// No value passed in; assume singular key lookup.
val_idx = 0;
} else {
// Value has been passed in; use plural-forms calculations.
// Handle invalid numbers, but try casting strings for good measure
if ( typeof val != 'number' ) {
val = parseInt( val, 10 );
if ( isNaN( val ) ) {
throw new Error('The number that was passed in is not a number.');
}
}
val_idx = getPluralFormFunc(pluralForms)(val);
}
// Throw an error if a domain isn't found // Throw an error if a domain isn't found
if ( ! dict ) { if ( ! dict ) {
throw new Error('No domain named `' + domain + '` could be found.'); throw new Error('No domain named `' + domain + '` could be found.');
...@@ -4773,20 +4826,25 @@ in order to offer easy upgrades -- jsgettext.berlios.de ...@@ -4773,20 +4826,25 @@ in order to offer easy upgrades -- jsgettext.berlios.de
// If there is no match, then revert back to // If there is no match, then revert back to
// english style singular/plural with the keys passed in. // english style singular/plural with the keys passed in.
if ( ! val_list || val_idx >= val_list.length ) { if ( ! val_list || val_idx > val_list.length ) {
if (this.options.missing_key_callback) { if (this.options.missing_key_callback) {
this.options.missing_key_callback(key); this.options.missing_key_callback(key, domain);
}
res = [ singular_key, plural_key ];
// collect untranslated strings
if (this.options.debug===true) {
console.log(res[ getPluralFormFunc(pluralForms)( val ) ]);
} }
res = [ null, singular_key, plural_key ]; return res[ getPluralFormFunc()( val ) ];
return res[ getPluralFormFunc(pluralForms)( val ) + 1 ];
} }
res = val_list[ val_idx ]; res = val_list[ val_idx ];
// This includes empty strings on purpose // This includes empty strings on purpose
if ( ! res ) { if ( ! res ) {
res = [ null, singular_key, plural_key ]; res = [ singular_key, plural_key ];
return res[ getPluralFormFunc(pluralForms)( val ) + 1 ]; return res[ getPluralFormFunc()( val ) ];
} }
return res; return res;
} }
...@@ -5090,15 +5148,15 @@ performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { ...@@ -5090,15 +5148,15 @@ performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
var $0 = $$.length - 1; var $0 = $$.length - 1;
switch (yystate) { switch (yystate) {
case 1: return { type : 'GROUP', expr: $$[$0-1] }; case 1: return { type : 'GROUP', expr: $$[$0-1] };
break; break;
case 2:this.$ = { type: 'TERNARY', expr: $$[$0-4], truthy : $$[$0-2], falsey: $$[$0] }; case 2:this.$ = { type: 'TERNARY', expr: $$[$0-4], truthy : $$[$0-2], falsey: $$[$0] };
break; break;
case 3:this.$ = { type: "OR", left: $$[$0-2], right: $$[$0] }; case 3:this.$ = { type: "OR", left: $$[$0-2], right: $$[$0] };
break; break;
case 4:this.$ = { type: "AND", left: $$[$0-2], right: $$[$0] }; case 4:this.$ = { type: "AND", left: $$[$0-2], right: $$[$0] };
break; break;
case 5:this.$ = { type: 'LT', left: $$[$0-2], right: $$[$0] }; case 5:this.$ = { type: 'LT', left: $$[$0-2], right: $$[$0] };
break; break;
case 6:this.$ = { type: 'LTE', left: $$[$0-2], right: $$[$0] }; case 6:this.$ = { type: 'LTE', left: $$[$0-2], right: $$[$0] };
break; break;
...@@ -5112,11 +5170,11 @@ case 10:this.$ = { type: 'EQ', left: $$[$0-2], right: $$[$0] }; ...@@ -5112,11 +5170,11 @@ case 10:this.$ = { type: 'EQ', left: $$[$0-2], right: $$[$0] };
break; break;
case 11:this.$ = { type: 'MOD', left: $$[$0-2], right: $$[$0] }; case 11:this.$ = { type: 'MOD', left: $$[$0-2], right: $$[$0] };
break; break;
case 12:this.$ = { type: 'GROUP', expr: $$[$0-1] }; case 12:this.$ = { type: 'GROUP', expr: $$[$0-1] };
break; break;
case 13:this.$ = { type: 'VAR' }; case 13:this.$ = { type: 'VAR' };
break; break;
case 14:this.$ = { type: 'NUM', val: Number(yytext) }; case 14:this.$ = { type: 'NUM', val: Number(yytext) };
break; break;
} }
}, },
...@@ -5402,7 +5460,7 @@ next:function () { ...@@ -5402,7 +5460,7 @@ next:function () {
if (this._input === "") { if (this._input === "") {
return this.EOF; return this.EOF;
} else { } else {
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno}); {text: "", token: null, line: this.yylineno});
} }
}, },
...@@ -5535,15 +5593,12 @@ return parser; ...@@ -5535,15 +5593,12 @@ return parser;
ChatBoxViews: { ChatBoxViews: {
onChatBoxAdded: function (item) { onChatBoxAdded: function (item) {
var view = this.get(item.get('id')); var view = this.get(item.get('id'));
// FIXME: leaky abstraction from chatroom here, need to if (!view) {
// come up with a nicer solution for this.
// Perhaps change 'chatroom' to more generic non-boolean
if (!view && !item.get('chatroom')) {
view = new converse.ChatBoxView({model: item}); view = new converse.ChatBoxView({model: item});
this.add(item.get('id'), view); this.add(item.get('id'), view);
this.trimChats(view); return view;
} else { } else {
this._super.onChatBoxAdded.apply(this, arguments); return this._super.onChatBoxAdded.apply(this, arguments);
} }
} }
} }
...@@ -5566,7 +5621,6 @@ return parser; ...@@ -5566,7 +5621,6 @@ return parser;
events: { events: {
'click .close-chatbox-button': 'close', 'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'minimize',
'keypress textarea.chat-textarea': 'keyPressed', 'keypress textarea.chat-textarea': 'keyPressed',
'click .toggle-smiley': 'toggleEmoticonMenu', 'click .toggle-smiley': 'toggleEmoticonMenu',
'click .toggle-smiley ul li': 'insertEmoticon', 'click .toggle-smiley ul li': 'insertEmoticon',
...@@ -5586,7 +5640,6 @@ return parser; ...@@ -5586,7 +5640,6 @@ return parser;
this.model.on('change:chat_state', this.sendChatState, this); this.model.on('change:chat_state', this.sendChatState, this);
this.model.on('change:chat_status', this.onChatStatusChanged, this); this.model.on('change:chat_status', this.onChatStatusChanged, this);
this.model.on('change:image', this.renderAvatar, this); this.model.on('change:image', this.renderAvatar, this);
this.model.on('change:minimized', this.onMinimizedChanged, this);
this.model.on('change:status', this.onStatusChanged, this); this.model.on('change:status', this.onStatusChanged, this);
this.model.on('showHelpMessages', this.showHelpMessages, this); this.model.on('showHelpMessages', this.showHelpMessages, this);
this.model.on('sendMessage', this.sendMessage, this); this.model.on('sendMessage', this.sendMessage, this);
...@@ -5602,6 +5655,7 @@ return parser; ...@@ -5602,6 +5655,7 @@ return parser;
show_textarea: true, show_textarea: true,
title: this.model.get('fullname'), title: this.model.get('fullname'),
info_close: __('Close this chat box'), info_close: __('Close this chat box'),
// FIXME: leaky-abstraction from converse-minimize
info_minimize: __('Minimize this chat box'), info_minimize: __('Minimize this chat box'),
label_personal_message: __('Personal message') label_personal_message: __('Personal message')
} }
...@@ -5661,10 +5715,10 @@ return parser; ...@@ -5661,10 +5715,10 @@ return parser;
fetchArchivedMessages: function (options) { fetchArchivedMessages: function (options) {
/* Fetch archived chat messages from the XMPP server. /* Fetch archived chat messages from the XMPP server.
* *
* Then, upon receiving them, call onMessage on the chat box, * Then, upon receiving them, call onMessage on the chat box,
* so that they are displayed inside it. * so that they are displayed inside it.
*/ */
if (!converse.features.findWhere({'var': Strophe.NS.MAM})) { if (!converse.features.findWhere({'var': Strophe.NS.MAM})) {
converse.log("Attempted to fetch archived messages but this user's server doesn't support XEP-0313"); converse.log("Attempted to fetch archived messages but this user's server doesn't support XEP-0313");
return; return;
...@@ -5685,16 +5739,16 @@ return parser; ...@@ -5685,16 +5739,16 @@ return parser;
insertIntoPage: function () { insertIntoPage: function () {
/* This method gets overridden in src/converse-controlbox.js if /* This method gets overridden in src/converse-controlbox.js if
* the controlbox plugin is active. * the controlbox plugin is active.
*/ */
$('#conversejs').prepend(this.$el); $('#conversejs').prepend(this.$el);
return this; return this;
}, },
adjustToViewport: function () { adjustToViewport: function () {
/* Event handler called when viewport gets resized. We remove /* Event handler called when viewport gets resized. We remove
* custom width/height from chat boxes. * custom width/height from chat boxes.
*/ */
var viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); var viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
var viewport_height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); var viewport_height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
if (viewport_width <= 480) { if (viewport_width <= 480) {
...@@ -5709,8 +5763,8 @@ return parser; ...@@ -5709,8 +5763,8 @@ return parser;
initDragResize: function () { initDragResize: function () {
/* Determine and store the default box size. /* Determine and store the default box size.
* We need this information for the drag-resizing feature. * We need this information for the drag-resizing feature.
*/ */
var $flyout = this.$el.find('.box-flyout'); var $flyout = this.$el.find('.box-flyout');
if (typeof this.model.get('height') === 'undefined') { if (typeof this.model.get('height') === 'undefined') {
var height = $flyout.height(); var height = $flyout.height();
...@@ -5770,11 +5824,11 @@ return parser; ...@@ -5770,11 +5824,11 @@ return parser;
prependDayIndicator: function (date) { prependDayIndicator: function (date) {
/* Prepends an indicator into the chat area, showing the day as /* Prepends an indicator into the chat area, showing the day as
* given by the passed in date. * given by the passed in date.
* *
* Parameters: * Parameters:
* (String) date - An ISO8601 date string. * (String) date - An ISO8601 date string.
*/ */
var day_date = moment(date).startOf('day'); var day_date = moment(date).startOf('day');
this.$content.prepend(converse.templates.new_day({ this.$content.prepend(converse.templates.new_day({
isodate: day_date.format(), isodate: day_date.format(),
...@@ -5784,11 +5838,11 @@ return parser; ...@@ -5784,11 +5838,11 @@ return parser;
appendMessage: function (attrs) { appendMessage: function (attrs) {
/* Helper method which appends a message to the end of the chat /* Helper method which appends a message to the end of the chat
* box's content area. * box's content area.
* *
* Parameters: * Parameters:
* (Object) attrs: An object containing the message attributes. * (Object) attrs: An object containing the message attributes.
*/ */
_.compose( _.compose(
_.debounce(this.scrollDown.bind(this), 50), _.debounce(this.scrollDown.bind(this), 50),
this.$content.append.bind(this.$content) this.$content.append.bind(this.$content)
...@@ -5797,15 +5851,15 @@ return parser; ...@@ -5797,15 +5851,15 @@ return parser;
showMessage: function (attrs) { showMessage: function (attrs) {
/* Inserts a chat message into the content area of the chat box. /* 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 * Will also insert a new day indicator if the message is on a
* different day. * different day.
* *
* The message to show may either be newer than the newest * The message to show may either be newer than the newest
* message, or older than the oldest message. * message, or older than the oldest message.
* *
* Parameters: * Parameters:
* (Object) attrs: An object containing the message attributes. * (Object) attrs: An object containing the message attributes.
*/ */
var $first_msg = this.$content.children('.chat-message:first'), var $first_msg = this.$content.children('.chat-message:first'),
first_msg_date = $first_msg.data('isodate'), first_msg_date = $first_msg.data('isodate'),
last_msg_date, current_msg_date, day_date, $msgs, msg_dates, idx; last_msg_date, current_msg_date, day_date, $msgs, msg_dates, idx;
...@@ -5883,13 +5937,13 @@ return parser; ...@@ -5883,13 +5937,13 @@ return parser;
renderMessage: function (attrs) { renderMessage: function (attrs) {
/* Renders a chat message based on the passed in attributes. /* Renders a chat message based on the passed in attributes.
* *
* Parameters: * Parameters:
* (Object) attrs: An object containing the message attributes. * (Object) attrs: An object containing the message attributes.
* *
* Returns: * Returns:
* The DOM element representing the message. * The DOM element representing the message.
*/ */
var msg_time = moment(attrs.time) || moment, var msg_time = moment(attrs.time) || moment,
text = attrs.message, text = attrs.message,
match = text.match(/^\/(.*?)(?: (.*))?$/), match = text.match(/^\/(.*?)(?: (.*))?$/),
...@@ -5952,22 +6006,26 @@ return parser; ...@@ -5952,22 +6006,26 @@ return parser;
} }
}, },
shouldShowOnTextMessage: function () {
return !this.$el.is(':visible');
},
handleTextMessage: function (message) { handleTextMessage: function (message) {
this.showMessage(_.clone(message.attributes)); this.showMessage(_.clone(message.attributes));
if ((message.get('sender') !== 'me') && (converse.windowState === 'blur')) { if ((message.get('sender') !== 'me') && (converse.windowState === 'blur')) {
converse.incrementMsgCounter(); converse.incrementMsgCounter();
} }
if (!this.model.get('minimized') && !this.$el.is(':visible')) { if (this.shouldShowOnTextMessage()) {
this.show(); this.show();
} }
}, },
onMessageAdded: function (message) { onMessageAdded: function (message) {
/* Handler that gets called when a new message object is created. /* Handler that gets called when a new message object is created.
* *
* Parameters: * Parameters:
* (Object) message - The message Backbone object that was added. * (Object) message - The message Backbone object that was added.
*/ */
if (typeof this.clear_status_timeout !== 'undefined') { if (typeof this.clear_status_timeout !== 'undefined') {
window.clearTimeout(this.clear_status_timeout); window.clearTimeout(this.clear_status_timeout);
delete this.clear_status_timeout; delete this.clear_status_timeout;
...@@ -5991,10 +6049,10 @@ return parser; ...@@ -5991,10 +6049,10 @@ return parser;
sendMessage: function (message) { sendMessage: function (message) {
/* Responsible for sending off a text message. /* Responsible for sending off a text message.
* *
* Parameters: * Parameters:
* (Message) message - The chat message * (Message) message - The chat message
*/ */
// TODO: We might want to send to specfic resources. // TODO: We might want to send to specfic resources.
// Especially in the OTR case. // Especially in the OTR case.
var messageStanza = this.createMessageStanza(message); var messageStanza = this.createMessageStanza(message);
...@@ -6012,11 +6070,11 @@ return parser; ...@@ -6012,11 +6070,11 @@ return parser;
onMessageSubmitted: function (text) { onMessageSubmitted: function (text) {
/* This method gets called once the user has typed a message /* This method gets called once the user has typed a message
* and then pressed enter in a chat box. * and then pressed enter in a chat box.
* *
* Parameters: * Parameters:
* (string) text - The chat message text. * (string) text - The chat message text.
*/ */
if (!converse.connection.authenticated) { if (!converse.connection.authenticated) {
return this.showHelpMessages( return this.showHelpMessages(
['Sorry, the connection has been lost, '+ ['Sorry, the connection has been lost, '+
...@@ -6052,9 +6110,9 @@ return parser; ...@@ -6052,9 +6110,9 @@ return parser;
sendChatState: function () { sendChatState: function () {
/* Sends a message with the status of the user in this chat session /* Sends a message with the status of the user in this chat session
* as taken from the 'chat_state' attribute of the chat box. * as taken from the 'chat_state' attribute of the chat box.
* See XEP-0085 Chat State Notifications. * See XEP-0085 Chat State Notifications.
*/ */
converse.connection.send( converse.connection.send(
$msg({'to':this.model.get('jid'), 'type': 'chat'}) $msg({'to':this.model.get('jid'), 'type': 'chat'})
.c(this.model.get('chat_state'), {'xmlns': Strophe.NS.CHATSTATES}) .c(this.model.get('chat_state'), {'xmlns': Strophe.NS.CHATSTATES})
...@@ -6063,16 +6121,16 @@ return parser; ...@@ -6063,16 +6121,16 @@ return parser;
setChatState: function (state, no_save) { setChatState: function (state, no_save) {
/* Mutator for setting the chat state of this chat session. /* Mutator for setting the chat state of this chat session.
* Handles clearing of any chat state notification timeouts and * Handles clearing of any chat state notification timeouts and
* setting new ones if necessary. * setting new ones if necessary.
* Timeouts are set when the state being set is COMPOSING or PAUSED. * Timeouts are set when the state being set is COMPOSING or PAUSED.
* After the timeout, COMPOSING will become PAUSED and PAUSED will become INACTIVE. * After the timeout, COMPOSING will become PAUSED and PAUSED will become INACTIVE.
* See XEP-0085 Chat State Notifications. * See XEP-0085 Chat State Notifications.
* *
* Parameters: * Parameters:
* (string) state - The chat state (consts ACTIVE, COMPOSING, PAUSED, INACTIVE, GONE) * (string) state - The chat state (consts ACTIVE, COMPOSING, PAUSED, INACTIVE, GONE)
* (Boolean) no_save - Just do the cleanup or setup but don't actually save the state. * (Boolean) no_save - Just do the cleanup or setup but don't actually save the state.
*/ */
if (typeof this.chat_state_timeout !== 'undefined') { if (typeof this.chat_state_timeout !== 'undefined') {
window.clearTimeout(this.chat_state_timeout); window.clearTimeout(this.chat_state_timeout);
delete this.chat_state_timeout; delete this.chat_state_timeout;
...@@ -6092,14 +6150,15 @@ return parser; ...@@ -6092,14 +6150,15 @@ return parser;
keyPressed: function (ev) { keyPressed: function (ev) {
/* Event handler for when a key is pressed in a chat box textarea. /* Event handler for when a key is pressed in a chat box textarea.
*/ */
var $textarea = $(ev.target), message; var $textarea = $(ev.target), message;
if (ev.keyCode === KEY.ENTER) { if (ev.keyCode === KEY.ENTER) {
ev.preventDefault(); ev.preventDefault();
message = $textarea.val(); message = $textarea.val();
$textarea.val('').focus(); $textarea.val('').focus();
if (message !== '') { if (message !== '') {
if (this.model.get('chatroom')) { // XXX: leaky abstraction from MUC
if (this.model.get('type') === 'chatroom') {
this.onChatRoomMessageSubmitted(message); this.onChatRoomMessageSubmitted(message);
} else { } else {
this.onMessageSubmitted(message); this.onMessageSubmitted(message);
...@@ -6107,7 +6166,8 @@ return parser; ...@@ -6107,7 +6166,8 @@ return parser;
converse.emit('messageSend', message); converse.emit('messageSend', message);
} }
this.setChatState(converse.ACTIVE); this.setChatState(converse.ACTIVE);
} else if (!this.model.get('chatroom')) { // chat state data is currently only for single user chat // XXX: leaky abstraction from MUC
} else if (this.model.get('type') !== 'chatroom') { // chat state data is currently only for single user chat
// Set chat state to composing if keyCode is not a forward-slash // Set chat state to composing if keyCode is not a forward-slash
// (which would imply an internal command and not a message). // (which would imply an internal command and not a message).
this.setChatState(converse.COMPOSING, ev.keyCode === KEY.FORWARD_SLASH); this.setChatState(converse.COMPOSING, ev.keyCode === KEY.FORWARD_SLASH);
...@@ -6142,26 +6202,22 @@ return parser; ...@@ -6142,26 +6202,22 @@ return parser;
}, },
setChatBoxHeight: function (height) { setChatBoxHeight: function (height) {
if (!this.model.get('minimized')) { if (height) {
if (height) { height = converse.applyDragResistance(height, this.model.get('default_height'))+'px';
height = converse.applyDragResistance(height, this.model.get('default_height'))+'px'; } else {
} else { height = "";
height = "";
}
this.$el.children('.box-flyout')[0].style.height = height;
} }
this.$el.children('.box-flyout')[0].style.height = height;
}, },
setChatBoxWidth: function (width) { setChatBoxWidth: function (width) {
if (!this.model.get('minimized')) { if (width) {
if (width) { width = converse.applyDragResistance(width, this.model.get('default_width'))+'px';
width = converse.applyDragResistance(width, this.model.get('default_width'))+'px'; } else {
} else { width = "";
width = "";
}
this.$el[0].style.width = width;
this.$el.children('.box-flyout')[0].style.width = width;
} }
this.$el[0].style.width = width;
this.$el.children('.box-flyout')[0].style.width = width;
}, },
resizeChatBox: function (ev) { resizeChatBox: function (ev) {
...@@ -6246,14 +6302,6 @@ return parser; ...@@ -6246,14 +6302,6 @@ return parser;
}); });
}, },
onMinimizedChanged: function (item) {
if (item.get('minimized')) {
this.minimize();
} else {
this.maximize();
}
},
showStatusMessage: function (msg) { showStatusMessage: function (msg) {
msg = msg || this.model.get('status'); msg = msg || this.model.get('status');
if (typeof msg === "string") { if (typeof msg === "string") {
...@@ -6265,43 +6313,18 @@ return parser; ...@@ -6265,43 +6313,18 @@ return parser;
close: function (ev) { close: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
if (converse.connection.connected) { if (converse.connection.connected) {
// Immediately sending the chat state, because the
// model is going to be destroyed afterwards.
this.model.set('chat_state', converse.INACTIVE);
this.sendChatState();
this.model.destroy(); this.model.destroy();
this.setChatState(converse.INACTIVE);
} else {
this.hide();
} }
this.remove();
converse.emit('chatBoxClosed', this); converse.emit('chatBoxClosed', this);
return this; return this;
}, },
onMaximized: function () {
converse.chatboxviews.trimChats(this);
utils.refreshWebkit();
this.$content.scrollTop(this.model.get('scroll'));
this.setChatState(converse.ACTIVE).focus();
converse.emit('chatBoxMaximized', this);
},
onMinimized: function () {
utils.refreshWebkit();
converse.emit('chatBoxMinimized', this);
},
maximize: function () {
// Restore a minimized chat box
$('#conversejs').prepend(this.$el);
this.$el.show('fast', this.onMaximized.bind(this));
return this;
},
minimize: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
// save the scroll position to restore it on maximize
this.model.save({'scroll': this.$content.scrollTop()});
this.setChatState(converse.INACTIVE).model.minimize();
this.$el.hide('fast', this.onMinimized.bind(this));
},
renderToolbar: function (options) { renderToolbar: function (options) {
if (!converse.show_toolbar) { if (!converse.show_toolbar) {
return; return;
...@@ -6361,31 +6384,32 @@ return parser; ...@@ -6361,31 +6384,32 @@ return parser;
return this; return this;
}, },
afterShown: function () {
if (converse.connection.connected) {
// Without a connection, we haven't yet initialized
// localstorage
this.model.save();
}
this.setChatState(converse.ACTIVE);
this.scrollDown();
if (focus) {
this.focus();
}
},
show: function (focus) { show: function (focus) {
if (typeof this.debouncedShow === 'undefined') { if (typeof this.debouncedShow === 'undefined') {
/* We wrap the method in a debouncer and set it on the /* We wrap the method in a debouncer and set it on the
* instance, so that we have it debounced per instance. * instance, so that we have it debounced per instance.
* Debouncing it on the class-level is too broad. * Debouncing it on the class-level is too broad.
*/ */
this.debouncedShow = _.debounce(function (focus) { this.debouncedShow = _.debounce(function (focus) {
if (this.$el.is(':visible') && this.$el.css('opacity') === "1") { if (this.$el.is(':visible') && this.$el.css('opacity') === "1") {
if (focus) { this.focus(); } if (focus) { this.focus(); }
return; return;
} }
this.initDragResize().setDimensions(); this.initDragResize().setDimensions();
this.$el.fadeIn(function () { this.$el.fadeIn(this.afterShown.bind(this));
if (converse.connection.connected) {
// Without a connection, we haven't yet initialized
// localstorage
this.model.save();
}
converse.chatboxviews.trimChats(this);
this.setChatState(converse.ACTIVE);
this.scrollDown();
if (focus) {
this.focus();
}
}.bind(this));
}, 250, true); }, 250, true);
} }
this.debouncedShow.apply(this, arguments); this.debouncedShow.apply(this, arguments);
...@@ -6473,7 +6497,6 @@ return parser; ...@@ -6473,7 +6497,6 @@ return parser;
/* The initialize function gets called as soon as the plugin is /* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery. * loaded by converse.js's plugin machinery.
*/ */
this.updateSettings({ this.updateSettings({
archived_messages_page_size: '20', archived_messages_page_size: '20',
message_archiving: 'never', // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs) message_archiving: 'never', // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs)
...@@ -6482,22 +6505,22 @@ return parser; ...@@ -6482,22 +6505,22 @@ return parser;
converse.queryForArchivedMessages = function (options, callback, errback) { converse.queryForArchivedMessages = function (options, callback, errback) {
/* Do a MAM (XEP-0313) query for archived messages. /* Do a MAM (XEP-0313) query for archived messages.
* *
* Parameters: * Parameters:
* (Object) options - Query parameters, either MAM-specific or also for Result Set Management. * (Object) options - Query parameters, either MAM-specific or also for Result Set Management.
* (Function) callback - A function to call whenever we receive query-relevant stanza. * (Function) callback - A function to call whenever we receive query-relevant stanza.
* (Function) errback - A function to call when an error stanza is received. * (Function) errback - A function to call when an error stanza is received.
* *
* The options parameter can also be an instance of * The options parameter can also be an instance of
* Strophe.RSM to enable easy querying between results pages. * Strophe.RSM to enable easy querying between results pages.
* *
* The callback function may be called multiple times, first * The callback function may be called multiple times, first
* for the initial IQ result and then for each message * for the initial IQ result and then for each message
* returned. The last time the callback is called, a * returned. The last time the callback is called, a
* Strophe.RSM object is returned on which "next" or "previous" * Strophe.RSM object is returned on which "next" or "previous"
* can be called before passing it in again to this method, to * can be called before passing it in again to this method, to
* get the next or previous page in the result set. * get the next or previous page in the result set.
*/ */
var date, messages = []; var date, messages = [];
if (typeof options === "function") { if (typeof options === "function") {
callback = options; callback = options;
...@@ -6565,7 +6588,7 @@ return parser; ...@@ -6565,7 +6588,7 @@ return parser;
_.extend(converse_api, { _.extend(converse_api, {
/* Extend default converse.js API to add methods specific to MAM /* Extend default converse.js API to add methods specific to MAM
*/ */
'archive': { 'archive': {
'query': converse.queryForArchivedMessages.bind(converse) 'query': converse.queryForArchivedMessages.bind(converse)
} }
...@@ -6771,20 +6794,20 @@ return parser; ...@@ -6771,20 +6794,20 @@ return parser;
success: function (collection) { success: function (collection) {
if (collection.length === 0) { if (collection.length === 0) {
/* We don't have any roster contacts stored in sessionStorage, /* We don't have any roster contacts stored in sessionStorage,
* so lets fetch the roster from the XMPP server. We pass in * so lets fetch the roster from the XMPP server. We pass in
* 'sendPresence' as callback method, because after initially * 'sendPresence' as callback method, because after initially
* fetching the roster we are ready to receive presence * fetching the roster we are ready to receive presence
* updates from our contacts. * updates from our contacts.
*/ */
converse.roster.fetchFromServer(function () { converse.roster.fetchFromServer(function () {
converse.xmppstatus.sendPresence(); converse.xmppstatus.sendPresence();
}); });
} else if (converse.send_initial_presence) { } else if (converse.send_initial_presence) {
/* We're not going to fetch the roster again because we have /* We're not going to fetch the roster again because we have
* it already cached in sessionStorage, but we still need to * it already cached in sessionStorage, but we still need to
* send out a presence stanza because this is a new session. * send out a presence stanza because this is a new session.
* See: https://github.com/jcbrand/converse.js/issues/536 * See: https://github.com/jcbrand/converse.js/issues/536
*/ */
converse.xmppstatus.sendPresence(); converse.xmppstatus.sendPresence();
} }
} }
...@@ -6957,13 +6980,13 @@ return parser; ...@@ -6957,13 +6980,13 @@ return parser;
positionFetchedGroups: function (model, resp, options) { positionFetchedGroups: function (model, resp, options) {
/* Instead of throwing an add event for each group /* Instead of throwing an add event for each group
* fetched, we wait until they're all fetched and then * fetched, we wait until they're all fetched and then
* we position them. * we position them.
* Works around the problem of positionGroup not * Works around the problem of positionGroup not
* working when all groups besides the one being * working when all groups besides the one being
* positioned aren't already in inserted into the * positioned aren't already in inserted into the
* roster DOM element. * roster DOM element.
*/ */
model.sort(); model.sort();
model.each(function (group, idx) { model.each(function (group, idx) {
var view = this.get(group.get('name')); var view = this.get(group.get('name'));
...@@ -6981,8 +7004,8 @@ return parser; ...@@ -6981,8 +7004,8 @@ return parser;
positionGroup: function (view) { positionGroup: function (view) {
/* Place the group's DOM element in the correct alphabetical /* Place the group's DOM element in the correct alphabetical
* position amongst the other groups in the roster. * position amongst the other groups in the roster.
*/ */
var $groups = this.$roster.find('.roster-group'), var $groups = this.$roster.find('.roster-group'),
index = $groups.length ? this.model.indexOf(view.model) : 0; index = $groups.length ? this.model.indexOf(view.model) : 0;
if (index === 0) { if (index === 0) {
...@@ -6997,7 +7020,7 @@ return parser; ...@@ -6997,7 +7020,7 @@ return parser;
appendGroup: function (view) { appendGroup: function (view) {
/* Add the group at the bottom of the roster /* Add the group at the bottom of the roster
*/ */
var $last = this.$roster.find('.roster-group').last(); var $last = this.$roster.find('.roster-group').last();
var $siblings = $last.siblings('dd'); var $siblings = $last.siblings('dd');
if ($siblings.length > 0) { if ($siblings.length > 0) {
...@@ -7010,8 +7033,8 @@ return parser; ...@@ -7010,8 +7033,8 @@ return parser;
getGroup: function (name) { getGroup: function (name) {
/* Returns the group as specified by name. /* Returns the group as specified by name.
* Creates the group if it doesn't exist. * Creates the group if it doesn't exist.
*/ */
var view = this.get(name); var view = this.get(name);
if (view) { if (view) {
return view.model; return view.model;
...@@ -7097,16 +7120,16 @@ return parser; ...@@ -7097,16 +7120,16 @@ return parser;
if ((ask === 'subscribe') || (subscription === 'from')) { if ((ask === 'subscribe') || (subscription === 'from')) {
/* ask === 'subscribe' /* ask === 'subscribe'
* Means we have asked to subscribe to them. * Means we have asked to subscribe to them.
* *
* subscription === 'from' * subscription === 'from'
* They are subscribed to use, but not vice versa. * They are subscribed to use, but not vice versa.
* We assume that there is a pending subscription * We assume that there is a pending subscription
* from us to them (otherwise we're in a state not * from us to them (otherwise we're in a state not
* supported by converse.js). * supported by converse.js).
* *
* So in both cases the user is a "pending" contact. * So in both cases the user is a "pending" contact.
*/ */
this.$el.addClass('pending-xmpp-contact'); this.$el.addClass('pending-xmpp-contact');
this.$el.html(converse.templates.pending_contact( this.$el.html(converse.templates.pending_contact(
_.extend(item.toJSON(), { _.extend(item.toJSON(), {
...@@ -7249,8 +7272,8 @@ return parser; ...@@ -7249,8 +7272,8 @@ return parser;
positionContact: function (contact) { positionContact: function (contact) {
/* Place the contact's DOM element in the correct alphabetical /* Place the contact's DOM element in the correct alphabetical
* position amongst the other contacts in this group. * position amongst the other contacts in this group.
*/ */
var view = this.get(contact.get('id')); var view = this.get(contact.get('id'));
var index = this.model.contacts.indexOf(contact); var index = this.model.contacts.indexOf(contact);
view.$el.detach(); view.$el.detach();
...@@ -7279,10 +7302,10 @@ return parser; ...@@ -7279,10 +7302,10 @@ return parser;
filter: function (q) { filter: function (q) {
/* Filter the group's contacts based on the query "q". /* Filter the group's contacts based on the query "q".
* The query is matched against the contact's full name. * The query is matched against the contact's full name.
* If all contacts are filtered out (i.e. hidden), then the * If all contacts are filtered out (i.e. hidden), then the
* group must be filtered out as well. * group must be filtered out as well.
*/ */
var matches; var matches;
if (q.length === 0) { if (q.length === 0) {
if (this.model.get('state') === converse.OPENED) { if (this.model.get('state') === converse.OPENED) {
...@@ -7369,8 +7392,8 @@ return parser; ...@@ -7369,8 +7392,8 @@ return parser;
model: converse.RosterGroup, model: converse.RosterGroup,
comparator: function (a, b) { comparator: function (a, b) {
/* Groups are sorted alphabetically, ignoring case. /* Groups are sorted alphabetically, ignoring case.
* However, Ungrouped, Requesting Contacts and Pending Contacts * However, Ungrouped, Requesting Contacts and Pending Contacts
* appear last and in that order. */ * appear last and in that order. */
a = a.get('name'); a = a.get('name');
b = b.get('name'); b = b.get('name');
var special_groups = _.keys(HEADER_WEIGHTS); var special_groups = _.keys(HEADER_WEIGHTS);
...@@ -7481,9 +7504,9 @@ return parser; ...@@ -7481,9 +7504,9 @@ return parser;
var view = this.get(item.get('id')); var view = this.get(item.get('id'));
if (!view && item.get('box_id') === 'controlbox') { if (!view && item.get('box_id') === 'controlbox') {
view = new converse.ControlBoxView({model: item}); view = new converse.ControlBoxView({model: item});
this.add(item.get('id'), view); return this.add(item.get('id'), view);
} else { } else {
this._super.onChatBoxAdded.apply(this, arguments); return this._super.onChatBoxAdded.apply(this, arguments);
} }
}, },
...@@ -7640,8 +7663,8 @@ return parser; ...@@ -7640,8 +7663,8 @@ return parser;
initRoster: function () { initRoster: function () {
/* We initialize the roster, which will appear inside the /* We initialize the roster, which will appear inside the
* Contacts Panel. * Contacts Panel.
*/ */
var rostergroups = new converse.RosterGroups(); var rostergroups = new converse.RosterGroups();
rostergroups.browserStorage = new Backbone.BrowserStorage[converse.storage]( rostergroups.browserStorage = new Backbone.BrowserStorage[converse.storage](
b64_sha1('converse.roster.groups'+converse.bare_jid)); b64_sha1('converse.roster.groups'+converse.bare_jid));
...@@ -8135,7 +8158,7 @@ return parser; ...@@ -8135,7 +8158,7 @@ return parser;
// Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com> // Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2) // Licensed under the Mozilla Public License (MPLv2)
// //
/*global Backbone, define, window, setTimeout */ /*global Backbone, define, window */
/* This is a Converse.js plugin which add support for multi-user chat rooms, as /* This is a Converse.js plugin which add support for multi-user chat rooms, as
* specified in XEP-0045 Multi-user chat. * specified in XEP-0045 Multi-user chat.
...@@ -8145,8 +8168,8 @@ return parser; ...@@ -8145,8 +8168,8 @@ return parser;
"converse-core", "converse-core",
"converse-api", "converse-api",
"typeahead", "typeahead",
// TODO remove next two dependencies
"converse-chatview", "converse-chatview",
// XXX: should we remove this dependency?
"converse-controlbox" "converse-controlbox"
], factory); ], factory);
}(this, function (converse, converse_api) { }(this, function (converse, converse_api) {
...@@ -8228,6 +8251,8 @@ return parser; ...@@ -8228,6 +8251,8 @@ return parser;
}, },
onConnected: function () { onConnected: function () {
// TODO: This can probably be refactored to be an event
// handler (and therefore removed from overrides)
var converse = this._super.converse; var converse = this._super.converse;
this._super.onConnected.apply(this, arguments); this._super.onConnected.apply(this, arguments);
...@@ -8257,73 +8282,14 @@ return parser; ...@@ -8257,73 +8282,14 @@ return parser;
} }
}, },
ChatBoxes: {
registerMessageHandler: function () {
/* Override so that we can register a handler
* for chat room invites.
*/
this._super.registerMessageHandler.apply(this, arguments); // First call the original
this._super.converse.connection.addHandler(
function (message) {
this.onInvite(message);
return true;
}.bind(this), 'jabber:x:conference', 'message');
},
onInvite: function (message) {
/* An invitation to join a chat room has been received */
var converse = this._super.converse,
$message = $(message),
$x = $message.children('x[xmlns="jabber:x:conference"]'),
from = Strophe.getBareJidFromJid($message.attr('from')),
room_jid = $x.attr('jid'),
reason = $x.attr('reason'),
contact = converse.roster.get(from),
result;
if (converse.auto_join_on_invite) {
result = true;
} else {
contact = contact? contact.get('fullname'): Strophe.getNodeFromJid(from); // Invite request might come from someone not your roster list
if (!reason) {
result = confirm(
__(___("%1$s has invited you to join a chat room: %2$s"), contact, room_jid)
);
} else {
result = confirm(
__(___('%1$s has invited you to join a chat room: %2$s, and left the following reason: "%3$s"'), contact, room_jid, reason)
);
}
}
if (result === true) {
var chatroom = converse.chatboxviews.showChat({
'id': room_jid,
'jid': room_jid,
'name': Strophe.unescapeNode(Strophe.getNodeFromJid(room_jid)),
'nick': Strophe.unescapeNode(Strophe.getNodeFromJid(converse.connection.jid)),
'chatroom': true,
'box_id': b64_sha1(room_jid),
'password': $x.attr('password')
});
if (!_.contains(
[Strophe.Status.CONNECTING, Strophe.Status.CONNECTED],
chatroom.get('connection_status'))
) {
converse.chatboxviews.get(room_jid).join(null);
}
}
}
},
ChatBoxViews: { ChatBoxViews: {
onChatBoxAdded: function (item) { onChatBoxAdded: function (item) {
var view = this.get(item.get('id')); var view = this.get(item.get('id'));
if (!view && item.get('chatroom')) { if (!view && item.get('type') === 'chatroom') {
view = new converse.ChatRoomView({'model': item}); view = new converse.ChatRoomView({'model': item});
this.add(item.get('id'), view); return this.add(item.get('id'), view);
} else { } else {
this._super.onChatBoxAdded.apply(this, arguments); return this._super.onChatBoxAdded.apply(this, arguments);
} }
} }
} }
...@@ -8338,6 +8304,8 @@ return parser; ...@@ -8338,6 +8304,8 @@ return parser;
this.updateSettings({ this.updateSettings({
allow_muc: true, allow_muc: true,
auto_join_on_invite: false, // Auto-join chatroom on invite auto_join_on_invite: false, // Auto-join chatroom on invite
auto_join_rooms: [], // List of maps {'jid': 'room@example.org', 'nick': 'WizardKing69' },
// providing room jids and nicks or simply a list JIDs.
auto_list_rooms: false, auto_list_rooms: false,
hide_muc_server: false, hide_muc_server: false,
muc_history_max_stanzas: undefined, // Takes an integer, limits the amount of messages to fetch from chat room's history muc_history_max_stanzas: undefined, // Takes an integer, limits the amount of messages to fetch from chat room's history
...@@ -8347,11 +8315,12 @@ return parser; ...@@ -8347,11 +8315,12 @@ return parser;
converse.ChatRoomView = converse.ChatBoxView.extend({ converse.ChatRoomView = converse.ChatBoxView.extend({
/* Backbone View which renders a chat room, based upon the view /* Backbone View which renders a chat room, based upon the view
* for normal one-on-one chat boxes. * for normal one-on-one chat boxes.
*/ */
length: 300, length: 300,
tagName: 'div', tagName: 'div',
className: 'chatbox chatroom', className: 'chatbox chatroom',
is_chatroom: true,
events: { events: {
'click .close-chatbox-button': 'close', 'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'minimize', 'click .toggle-chatbox-button': 'minimize',
...@@ -8366,7 +8335,6 @@ return parser; ...@@ -8366,7 +8335,6 @@ return parser;
'mousedown .dragresize-left': 'onStartHorizontalResize', 'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize' 'mousedown .dragresize-topleft': 'onStartDiagonalResize'
}, },
is_chatroom: true,
initialize: function () { initialize: function () {
$(window).on('resize', _.debounce(this.setDimensions.bind(this), 100)); $(window).on('resize', _.debounce(this.setDimensions.bind(this), 100));
...@@ -8378,9 +8346,6 @@ return parser; ...@@ -8378,9 +8346,6 @@ return parser;
this.maximize(); this.maximize();
} }
}, this); }, this);
this.model.on('destroy', function () {
this.hide().leave();
}, this);
this.occupantsview = new converse.ChatRoomOccupantsView({ this.occupantsview = new converse.ChatRoomOccupantsView({
model: new converse.ChatRoomOccupants({nick: this.model.get('nick')}) model: new converse.ChatRoomOccupants({nick: this.model.get('nick')})
...@@ -8409,7 +8374,7 @@ return parser; ...@@ -8409,7 +8374,7 @@ return parser;
this.renderChatArea(); this.renderChatArea();
this.$content.on('scroll', _.debounce(this.onScroll.bind(this), 100)); this.$content.on('scroll', _.debounce(this.onScroll.bind(this), 100));
this.setWidth(); this.setWidth();
setTimeout(converse.refreshWebkit, 50); window.setTimeout(converse.refreshWebkit, 50);
return this; return this;
}, },
...@@ -8429,6 +8394,12 @@ return parser; ...@@ -8429,6 +8394,12 @@ return parser;
return this; return this;
}, },
close: function (ev) {
converse.connection.deleteHandler(this.handler);
this.leave();
converse.ChatBoxView.prototype.close.apply(this, arguments);
},
toggleOccupants: function (ev, preserve_state) { toggleOccupants: function (ev, preserve_state) {
if (ev) { if (ev) {
ev.preventDefault(); ev.preventDefault();
...@@ -8528,8 +8499,8 @@ return parser; ...@@ -8528,8 +8499,8 @@ return parser;
validateRoleChangeCommand: function (command, args) { validateRoleChangeCommand: function (command, args) {
/* Check that a command to change a chat room user's role or /* Check that a command to change a chat room user's role or
* affiliation has anough arguments. * affiliation has anough arguments.
*/ */
// TODO check if first argument is valid // TODO check if first argument is valid
if (args.length < 1 || args.length > 2) { if (args.length < 1 || args.length > 2) {
this.showStatusNotification( this.showStatusNotification(
...@@ -8552,11 +8523,11 @@ return parser; ...@@ -8552,11 +8523,11 @@ return parser;
onChatRoomMessageSubmitted: function (text) { onChatRoomMessageSubmitted: function (text) {
/* Gets called when the user presses enter to send off a /* Gets called when the user presses enter to send off a
* message in a chat room. * message in a chat room.
* *
* Parameters: * Parameters:
* (String) text - The message text. * (String) text - The message text.
*/ */
var match = text.replace(/^\s*/, "").match(/^\/(.*?)(?: (.*))?$/) || [false, '', ''], var match = text.replace(/^\s*/, "").match(/^\/(.*?)(?: (.*))?$/) || [false, '', ''],
args = match[2] && match[2].splitOnce(' ') || []; args = match[2] && match[2].splitOnce(' ') || [];
switch (match[1]) { switch (match[1]) {
...@@ -8731,7 +8702,9 @@ return parser; ...@@ -8731,7 +8702,9 @@ return parser;
presence.c("status", exit_msg); presence.c("status", exit_msg);
} }
converse.connection.addHandler( converse.connection.addHandler(
function () { this.model.set('connection_status', Strophe.Status.DISCONNECTED); }.bind(this), function () {
this.model.set('connection_status', Strophe.Status.DISCONNECTED);
}.bind(this),
null, "presence", null, presenceid); null, "presence", null, presenceid);
converse.connection.send(presence); converse.connection.send(presence);
}, },
...@@ -8854,27 +8827,27 @@ return parser; ...@@ -8854,27 +8827,27 @@ return parser;
}, },
/* http://xmpp.org/extensions/xep-0045.html /* http://xmpp.org/extensions/xep-0045.html
* ---------------------------------------- * ----------------------------------------
* 100 message Entering a room Inform user that any occupant is allowed to see the user's full JID * 100 message Entering a room Inform user that any occupant is allowed to see the user's full JID
* 101 message (out of band) Affiliation change Inform user that his or her affiliation changed while not in the room * 101 message (out of band) Affiliation change Inform user that his or her affiliation changed while not in the room
* 102 message Configuration change Inform occupants that room now shows unavailable members * 102 message Configuration change Inform occupants that room now shows unavailable members
* 103 message Configuration change Inform occupants that room now does not show unavailable members * 103 message Configuration change Inform occupants that room now does not show unavailable members
* 104 message Configuration change Inform occupants that a non-privacy-related room configuration change has occurred * 104 message Configuration change Inform occupants that a non-privacy-related room configuration change has occurred
* 110 presence Any room presence Inform user that presence refers to one of its own room occupants * 110 presence Any room presence Inform user that presence refers to one of its own room occupants
* 170 message or initial presence Configuration change Inform occupants that room logging is now enabled * 170 message or initial presence Configuration change Inform occupants that room logging is now enabled
* 171 message Configuration change Inform occupants that room logging is now disabled * 171 message Configuration change Inform occupants that room logging is now disabled
* 172 message Configuration change Inform occupants that the room is now non-anonymous * 172 message Configuration change Inform occupants that the room is now non-anonymous
* 173 message Configuration change Inform occupants that the room is now semi-anonymous * 173 message Configuration change Inform occupants that the room is now semi-anonymous
* 174 message Configuration change Inform occupants that the room is now fully-anonymous * 174 message Configuration change Inform occupants that the room is now fully-anonymous
* 201 presence Entering a room Inform user that a new room has been created * 201 presence Entering a room Inform user that a new room has been created
* 210 presence Entering a room Inform user that the service has assigned or modified the occupant's roomnick * 210 presence Entering a room Inform user that the service has assigned or modified the occupant's roomnick
* 301 presence Removal from room Inform user that he or she has been banned from the room * 301 presence Removal from room Inform user that he or she has been banned from the room
* 303 presence Exiting a room Inform all occupants of new room nickname * 303 presence Exiting a room Inform all occupants of new room nickname
* 307 presence Removal from room Inform user that he or she has been kicked from the room * 307 presence Removal from room Inform user that he or she has been kicked from the room
* 321 presence Removal from room Inform user that he or she is being removed from the room because of an affiliation change * 321 presence Removal from room Inform user that he or she is being removed from the room because of an affiliation change
* 322 presence Removal from room Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member * 322 presence Removal from room Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member
* 332 presence Removal from room Inform user that he or she is being removed from the room because of a system shutdown * 332 presence Removal from room Inform user that he or she is being removed from the room because of a system shutdown
*/ */
infoMessages: { infoMessages: {
100: __('This room is not anonymous'), 100: __('This room is not anonymous'),
102: __('This room now shows unavailable members'), 102: __('This room now shows unavailable members'),
...@@ -8898,15 +8871,15 @@ return parser; ...@@ -8898,15 +8871,15 @@ return parser;
actionInfoMessages: { actionInfoMessages: {
/* XXX: Note the triple underscore function and not double /* XXX: Note the triple underscore function and not double
* underscore. * underscore.
* *
* This is a hack. We can't pass the strings to __ because we * This is a hack. We can't pass the strings to __ because we
* don't yet know what the variable to interpolate is. * don't yet know what the variable to interpolate is.
* *
* Triple underscore will just return the string again, but we * Triple underscore will just return the string again, but we
* can then at least tell gettext to scan for it so that these * can then at least tell gettext to scan for it so that these
* strings are picked up by the translation machinery. * strings are picked up by the translation machinery.
*/ */
301: ___("<strong>%1$s</strong> has been banned"), 301: ___("<strong>%1$s</strong> has been banned"),
303: ___("<strong>%1$s</strong>'s nickname has changed"), 303: ___("<strong>%1$s</strong>'s nickname has changed"),
307: ___("<strong>%1$s</strong> has been kicked out"), 307: ___("<strong>%1$s</strong> has been kicked out"),
...@@ -8921,9 +8894,9 @@ return parser; ...@@ -8921,9 +8894,9 @@ return parser;
showStatusMessages: function (el, is_self) { showStatusMessages: function (el, is_self) {
/* Check for status codes and communicate their purpose to the user. /* Check for status codes and communicate their purpose to the user.
* Allow user to configure chat room if they are the owner. * Allow user to configure chat room if they are the owner.
* See: http://xmpp.org/registrar/mucstatus.html * See: http://xmpp.org/registrar/mucstatus.html
*/ */
var $el = $(el), var $el = $(el),
i, disconnect_msgs = [], msgs = [], reasons = []; i, disconnect_msgs = [], msgs = [], reasons = [];
...@@ -9066,10 +9039,10 @@ return parser; ...@@ -9066,10 +9039,10 @@ return parser;
fetchArchivedMessages: function (options) { fetchArchivedMessages: function (options) {
/* Fetch archived chat messages from the XMPP server. /* Fetch archived chat messages from the XMPP server.
* *
* Then, upon receiving them, call onChatRoomMessage * Then, upon receiving them, call onChatRoomMessage
* so that they are displayed inside it. * so that they are displayed inside it.
*/ */
if (!converse.features.findWhere({'var': Strophe.NS.MAM})) { if (!converse.features.findWhere({'var': Strophe.NS.MAM})) {
converse.log("Attempted to fetch archived messages but this user's server doesn't support XEP-0313"); converse.log("Attempted to fetch archived messages but this user's server doesn't support XEP-0313");
return; return;
...@@ -9243,11 +9216,11 @@ return parser; ...@@ -9243,11 +9216,11 @@ return parser;
converse.RoomsPanel = Backbone.View.extend({ converse.RoomsPanel = Backbone.View.extend({
/* Backbone View which renders the "Rooms" tab and accompanying /* Backbone View which renders the "Rooms" tab and accompanying
* panel in the control box. * panel in the control box.
* *
* In this panel, chat rooms can be listed, joined and new rooms * In this panel, chat rooms can be listed, joined and new rooms
* can be created. * can be created.
*/ */
tagName: 'div', tagName: 'div',
className: 'controlbox-pane', className: 'controlbox-pane',
id: 'chatrooms', id: 'chatrooms',
...@@ -9306,8 +9279,8 @@ return parser; ...@@ -9306,8 +9279,8 @@ return parser;
onRoomsFound: function (iq) { onRoomsFound: function (iq) {
/* Handle the IQ stanza returned from the server, containing /* Handle the IQ stanza returned from the server, containing
* all its public rooms. * all its public rooms.
*/ */
var name, jid, i, fragment, var name, jid, i, fragment,
$available_chatrooms = this.$el.find('#available-chatrooms'); $available_chatrooms = this.$el.find('#available-chatrooms');
this.rooms = $(iq).find('query').find('item'); this.rooms = $(iq).find('query').find('item');
...@@ -9338,7 +9311,7 @@ return parser; ...@@ -9338,7 +9311,7 @@ return parser;
updateRoomsList: function () { updateRoomsList: function () {
/* Send and IQ stanza to the server asking for all rooms /* Send and IQ stanza to the server asking for all rooms
*/ */
converse.connection.sendIQ( converse.connection.sendIQ(
$iq({ $iq({
to: this.model.get('muc_domain'), to: this.model.get('muc_domain'),
...@@ -9453,7 +9426,7 @@ return parser; ...@@ -9453,7 +9426,7 @@ return parser;
'jid': jid, 'jid': jid,
'name': name || Strophe.unescapeNode(Strophe.getNodeFromJid(jid)), 'name': name || Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
'nick': nick, 'nick': nick,
'chatroom': true, 'type': 'chatroom',
'box_id': b64_sha1(jid) 'box_id': b64_sha1(jid)
}); });
}, },
...@@ -9467,10 +9440,78 @@ return parser; ...@@ -9467,10 +9440,78 @@ return parser;
} }
}); });
/* Support for XEP-0249: Direct MUC invitations */
/* ------------------------------------------------------------ */
converse.onDirectMUCInvitation = function (message) {
/* A direct MUC invitation to join a room has been received */
var $message = $(message),
$x = $message.children('x[xmlns="jabber:x:conference"]'),
from = Strophe.getBareJidFromJid($message.attr('from')),
room_jid = $x.attr('jid'),
reason = $x.attr('reason'),
contact = converse.roster.get(from),
result;
if (converse.auto_join_on_invite) {
result = true;
} else {
// Invite request might come from someone not your roster list
contact = contact? contact.get('fullname'): Strophe.getNodeFromJid(from);
if (!reason) {
result = confirm(
__(___("%1$s has invited you to join a chat room: %2$s"),
contact, room_jid)
);
} else {
result = confirm(
__(___('%1$s has invited you to join a chat room: %2$s, and left the following reason: "%3$s"'),
contact, room_jid, reason)
);
}
}
if (result === true) {
var chatroom = converse.chatboxviews.showChat({
'id': room_jid,
'jid': room_jid,
'name': Strophe.unescapeNode(Strophe.getNodeFromJid(room_jid)),
'nick': Strophe.unescapeNode(Strophe.getNodeFromJid(converse.connection.jid)),
'type': 'chatroom',
'box_id': b64_sha1(room_jid),
'password': $x.attr('password')
});
if (!_.contains(
[Strophe.Status.CONNECTING, Strophe.Status.CONNECTED],
chatroom.get('connection_status'))
) {
converse.chatboxviews.get(room_jid).join(null);
}
}
};
var onConnected = function () {
converse.connection.addHandler(
function (message) {
converse.onDirectMUCInvitation(message);
return true;
}, 'jabber:x:conference', 'message');
_.each(converse.auto_join_rooms, function (room) {
if (typeof room === 'string') {
converse_api.rooms.open(room);
} else if (typeof room === 'object') {
converse_api.rooms.open(room.jid, room.nick);
} else {
converse.log('Invalid room criteria specified for "auto_join_rooms"', 'error');
}
});
};
converse.on('connected', onConnected);
converse.on('reconnected', onConnected);
/* ------------------------------------------------------------ */
/* We extend the default converse.js API to add methods specific to MUC
* chat rooms.
*/
_.extend(converse_api, { _.extend(converse_api, {
/* We extend the default converse.js API to add methods specific to MUC
* chat rooms.
*/
'rooms': { 'rooms': {
'open': function (jids, nick) { 'open': function (jids, nick) {
if (!nick) { if (!nick) {
...@@ -9489,7 +9530,7 @@ return parser; ...@@ -9489,7 +9530,7 @@ return parser;
'jid': jid, 'jid': jid,
'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)), 'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
'nick': nick, 'nick': nick,
'chatroom': true, 'type': 'chatroom',
'box_id': b64_sha1(jid) 'box_id': b64_sha1(jid)
}); });
} }
...@@ -9509,7 +9550,6 @@ return parser; ...@@ -9509,7 +9550,6 @@ return parser;
return converse.wrappedChatBox(converse.chatboxes.getChatBox(jids, true)); return converse.wrappedChatBox(converse.chatboxes.getChatBox(jids, true));
} }
return _.map(jids, _.partial(converse.wrappedChatBox, _.bind(converse.chatboxes.getChatBox, converse.chatboxes, _, true))); return _.map(jids, _.partial(converse.wrappedChatBox, _.bind(converse.chatboxes.getChatBox, converse.chatboxes, _, true)));
} }
} }
}); });
...@@ -9716,6 +9756,7 @@ return parser; ...@@ -9716,6 +9756,7 @@ return parser;
define("converse-minimize", [ define("converse-minimize", [
"converse-core", "converse-core",
"converse-api", "converse-api",
"converse-chatview"
], factory); ], factory);
}(this, function (converse, converse_api) { }(this, function (converse, converse_api) {
"use strict"; "use strict";
...@@ -9757,7 +9798,9 @@ return parser; ...@@ -9757,7 +9798,9 @@ return parser;
this._super.registerGlobalEventHandlers.apply(this, arguments); this._super.registerGlobalEventHandlers.apply(this, arguments);
$(window).on("resize", _.debounce(function (ev) { $(window).on("resize", _.debounce(function (ev) {
converse.chatboxviews.trimChats(); if (converse.connection.connected) {
converse.chatboxviews.trimChats();
}
}, 200)); }, 200));
}, },
...@@ -9799,15 +9842,86 @@ return parser; ...@@ -9799,15 +9842,86 @@ return parser;
}, },
}, },
ChatBoxView: {
events: {
'click .toggle-chatbox-button': 'minimize',
},
initialize: function () {
this.model.on('change:minimized', this.onMinimizedChanged, this);
return this._super.initialize.apply(this, arguments);
},
afterShown: function () {
this._super.afterShown.apply(this, arguments);
if (!this.model.get('minimized')) {
converse.chatboxviews.trimChats(this);
}
},
shouldShowOnTextMessage: function () {
return !this.model.get('minimized') &&
this._super.shouldShowOnTextMessage.apply(this, arguments);
},
setChatBoxHeight: function (height) {
if (!this.model.get('minimized')) {
return this._super.setChatBoxHeight.apply(this, arguments);
}
},
setChatBoxWidth: function (width) {
if (!this.model.get('minimized')) {
return this._super.setChatBoxWidth.apply(this, arguments);
}
},
onMinimizedChanged: function (item) {
if (item.get('minimized')) {
this.minimize();
} else {
this.maximize();
}
},
onMaximized: function () {
converse.chatboxviews.trimChats(this);
utils.refreshWebkit();
this.$content.scrollTop(this.model.get('scroll'));
this.setChatState(converse.ACTIVE).focus();
converse.emit('chatBoxMaximized', this);
},
onMinimized: function () {
utils.refreshWebkit();
converse.emit('chatBoxMinimized', this);
},
maximize: function () {
// Restore a minimized chat box
$('#conversejs').prepend(this.$el);
this.$el.show('fast', this.onMaximized.bind(this));
return this;
},
minimize: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
// save the scroll position to restore it on maximize
this.model.save({'scroll': this.$content.scrollTop()});
this.setChatState(converse.INACTIVE).model.minimize();
this.$el.hide('fast', this.onMinimized.bind(this));
},
},
ChatBoxes: { ChatBoxes: {
chatBoxShouldBeShown: function (chatbox) { chatBoxShouldBeShown: function (chatbox) {
return this._super.chatBoxShouldBeShown.apply(this, arguments) && return this._super.chatBoxShouldBeShown.apply(this, arguments) &&
!chatbox.get('minimized'); !chatbox.get('minimized');
}, },
}, },
ChatBoxViews: { ChatBoxViews: {
showChat: function (attrs) { showChat: function (attrs) {
/* Find the chat box and show it. If it doesn't exist, create it. /* Find the chat box and show it. If it doesn't exist, create it.
*/ */
...@@ -9831,16 +9945,16 @@ return parser; ...@@ -9831,16 +9945,16 @@ return parser;
trimChats: function (newchat) { trimChats: function (newchat) {
/* This method is called when a newly created chat box will /* This method is called when a newly created chat box will
* be shown. * be shown.
* *
* It checks whether there is enough space on the page to show * It checks whether there is enough space on the page to show
* another chat box. Otherwise it minimizes the oldest chat box * another chat box. Otherwise it minimizes the oldest chat box
* to create space. * to create space.
*/ */
if (converse.no_trimming || (this.model.length <= 1)) { if (converse.no_trimming || (this.model.length <= 1)) {
return; return;
} }
var oldest_chat, boxes_width, var oldest_chat, boxes_width, view,
$minimized = converse.minimized_chats.$el, $minimized = converse.minimized_chats.$el,
minimized_width = _.contains(this.model.pluck('minimized'), true) ? $minimized.outerWidth(true) : 0, minimized_width = _.contains(this.model.pluck('minimized'), true) ? $minimized.outerWidth(true) : 0,
new_id = newchat ? newchat.model.get('id') : null; new_id = newchat ? newchat.model.get('id') : null;
...@@ -9852,6 +9966,14 @@ return parser; ...@@ -9852,6 +9966,14 @@ return parser;
if ((minimized_width + boxes_width) > $('body').outerWidth(true)) { if ((minimized_width + boxes_width) > $('body').outerWidth(true)) {
oldest_chat = this.getOldestMaximizedChat([new_id]); oldest_chat = this.getOldestMaximizedChat([new_id]);
if (oldest_chat) { if (oldest_chat) {
// We hide the chat immediately, because waiting
// for the event to fire (and letting the
// ChatBoxView hide it then) causes race
// conditions.
view = this.get(oldest_chat.get('id'));
if (view) {
view.$el.hide();
}
oldest_chat.minimize(); oldest_chat.minimize();
} }
} }
...@@ -9906,7 +10028,7 @@ return parser; ...@@ -9906,7 +10028,7 @@ return parser;
this.model.toJSON(), this.model.toJSON(),
{ 'tooltip': __('Click to restore this chat') } { 'tooltip': __('Click to restore this chat') }
); );
if (this.model.get('chatroom')) { if (this.model.get('type') === 'chatroom') {
data.title = this.model.get('name'); data.title = this.model.get('name');
this.$el.addClass('chat-head-chatroom'); this.$el.addClass('chat-head-chatroom');
} else { } else {
...@@ -9929,8 +10051,15 @@ return parser; ...@@ -9929,8 +10051,15 @@ return parser;
close: function (ev) { close: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
this.remove(); this.remove();
this.model.destroy(); var view = converse.chatboxviews.get(this.model.get('id'));
converse.emit('chatBoxClosed', this); if (view) {
// This will call model.destroy(), removing it from the
// collection and will also emit 'chatBoxClosed'
view.close();
} else {
this.model.destroy();
converse.emit('chatBoxClosed', this);
}
return this; return this;
}, },
...@@ -10068,7 +10197,9 @@ return parser; ...@@ -10068,7 +10197,9 @@ return parser;
converse.on('controlBoxOpened', function (evt, chatbox) { converse.on('controlBoxOpened', function (evt, chatbox) {
// Wrapped in anon method because at scan time, chatboxviews // Wrapped in anon method because at scan time, chatboxviews
// attr not set yet. // attr not set yet.
converse.chatboxviews.trimChats(chatbox); if (converse.connection.connected) {
converse.chatboxviews.trimChats(chatbox);
}
}); });
} }
}); });
...@@ -10740,8 +10871,8 @@ return parser; ...@@ -10740,8 +10871,8 @@ return parser;
registerHooks: function () { registerHooks: function () {
/* Hook into Strophe's _connect_cb, so that we can send an IQ /* Hook into Strophe's _connect_cb, so that we can send an IQ
* requesting the registration fields. * requesting the registration fields.
*/ */
var conn = converse.connection; var conn = converse.connection;
var connect_cb = conn._connect_cb.bind(conn); var connect_cb = conn._connect_cb.bind(conn);
conn._connect_cb = function (req, callback, raw) { conn._connect_cb = function (req, callback, raw) {
...@@ -10757,11 +10888,11 @@ return parser; ...@@ -10757,11 +10888,11 @@ return parser;
getRegistrationFields: function (req, _callback, raw) { getRegistrationFields: function (req, _callback, raw) {
/* Send an IQ stanza to the XMPP server asking for the /* Send an IQ stanza to the XMPP server asking for the
* registration fields. * registration fields.
* Parameters: * Parameters:
* (Strophe.Request) req - The current request * (Strophe.Request) req - The current request
* (Function) callback * (Function) callback
*/ */
converse.log("sendQueryStanza was called"); converse.log("sendQueryStanza was called");
var conn = converse.connection; var conn = converse.connection;
conn.connected = true; conn.connected = true;
...@@ -10792,10 +10923,10 @@ return parser; ...@@ -10792,10 +10923,10 @@ return parser;
onRegistrationFields: function (stanza) { onRegistrationFields: function (stanza) {
/* Handler for Registration Fields Request. /* Handler for Registration Fields Request.
* *
* Parameters: * Parameters:
* (XMLElement) elem - The query stanza. * (XMLElement) elem - The query stanza.
*/ */
if (stanza.getElementsByTagName("query").length !== 1) { if (stanza.getElementsByTagName("query").length !== 1) {
converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, "unknown"); converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, "unknown");
return false; return false;
...@@ -10824,11 +10955,11 @@ return parser; ...@@ -10824,11 +10955,11 @@ return parser;
onProviderChosen: function (ev) { onProviderChosen: function (ev) {
/* Callback method that gets called when the user has chosen an /* Callback method that gets called when the user has chosen an
* XMPP provider. * XMPP provider.
* *
* Parameters: * Parameters:
* (Submit Event) ev - Form submission event. * (Submit Event) ev - Form submission event.
*/ */
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
var $form = $(ev.target), var $form = $(ev.target),
$domain_input = $form.find('input[name=domain]'), $domain_input = $form.find('input[name=domain]'),
...@@ -10907,11 +11038,11 @@ return parser; ...@@ -10907,11 +11038,11 @@ return parser;
renderRegistrationForm: function (stanza) { renderRegistrationForm: function (stanza) {
/* Renders the registration form based on the XForm fields /* Renders the registration form based on the XForm fields
* received from the XMPP server. * received from the XMPP server.
* *
* Parameters: * Parameters:
* (XMLElement) stanza - The IQ stanza received from the XMPP server. * (XMLElement) stanza - The IQ stanza received from the XMPP server.
*/ */
var $form= this.$('form'), var $form= this.$('form'),
$stanza = $(stanza), $stanza = $(stanza),
$fields, $input; $fields, $input;
...@@ -10964,12 +11095,12 @@ return parser; ...@@ -10964,12 +11095,12 @@ return parser;
reportErrors: function (stanza) { reportErrors: function (stanza) {
/* Report back to the user any error messages received from the /* Report back to the user any error messages received from the
* XMPP server after attempted registration. * XMPP server after attempted registration.
* *
* Parameters: * Parameters:
* (XMLElement) stanza - The IQ stanza received from the * (XMLElement) stanza - The IQ stanza received from the
* XMPP server. * XMPP server.
*/ */
var $form= this.$('form'), flash; var $form= this.$('form'), flash;
var $errmsgs = $(stanza).find('error text'); var $errmsgs = $(stanza).find('error text');
var $flash = $form.find('.form-errors'); var $flash = $form.find('.form-errors');
...@@ -10997,7 +11128,7 @@ return parser; ...@@ -10997,7 +11128,7 @@ return parser;
cancelRegistration: function (ev) { cancelRegistration: function (ev) {
/* Handler, when the user cancels the registration form. /* Handler, when the user cancels the registration form.
*/ */
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
converse.connection.reset(); converse.connection.reset();
this.render(); this.render();
...@@ -11005,12 +11136,12 @@ return parser; ...@@ -11005,12 +11136,12 @@ return parser;
submitRegistrationForm : function (ev) { submitRegistrationForm : function (ev) {
/* Handler, when the user submits the registration form. /* Handler, when the user submits the registration form.
* Provides form error feedback or starts the registration * Provides form error feedback or starts the registration
* process. * process.
* *
* Parameters: * Parameters:
* (Event) ev - the submit event. * (Event) ev - the submit event.
*/ */
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
var $empty_inputs = this.$('input.required:emptyVal'); var $empty_inputs = this.$('input.required:emptyVal');
if ($empty_inputs.length) { if ($empty_inputs.length) {
...@@ -11038,11 +11169,11 @@ return parser; ...@@ -11038,11 +11169,11 @@ return parser;
setFields: function (stanza) { setFields: function (stanza) {
/* Stores the values that will be sent to the XMPP server /* Stores the values that will be sent to the XMPP server
* during attempted registration. * during attempted registration.
* *
* Parameters: * Parameters:
* (XMLElement) stanza - the IQ stanza that will be sent to the XMPP server. * (XMLElement) stanza - the IQ stanza that will be sent to the XMPP server.
*/ */
var $query = $(stanza).find('query'), $xform; var $query = $(stanza).find('query'), $xform;
if ($query.length > 0) { if ($query.length > 0) {
$xform = $query.find('x[xmlns="'+Strophe.NS.XFORM+'"]'); $xform = $query.find('x[xmlns="'+Strophe.NS.XFORM+'"]');
...@@ -11090,12 +11221,12 @@ return parser; ...@@ -11090,12 +11221,12 @@ return parser;
_onRegisterIQ: function (stanza) { _onRegisterIQ: function (stanza) {
/* Callback method that gets called when a return IQ stanza /* Callback method that gets called when a return IQ stanza
* is received from the XMPP server, after attempting to * is received from the XMPP server, after attempting to
* register a new user. * register a new user.
* *
* Parameters: * Parameters:
* (XMLElement) stanza - The IQ stanza. * (XMLElement) stanza - The IQ stanza.
*/ */
var error = null, var error = null,
query = stanza.getElementsByTagName("query"); query = stanza.getElementsByTagName("query");
if (query.length > 0) { if (query.length > 0) {
...@@ -11158,27 +11289,6 @@ return parser; ...@@ -11158,27 +11289,6 @@ return parser;
converse_api.plugins.add('ping', { converse_api.plugins.add('ping', {
overrides: {
// Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
onConnected: function () {
var promise = this._super.onConnected();
promise.done(converse.registerPingHandler);
return promise;
},
onReconnected: function () {
// We need to re-register the ping event handler on the newly
// created connection.
var promise = this._super.onReconnected();
promise.done(converse.registerPingHandler);
return promise;
}
},
initialize: function () { initialize: function () {
/* The initialize function gets called as soon as the plugin is /* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery. * loaded by converse.js's plugin machinery.
...@@ -11252,6 +11362,13 @@ return parser; ...@@ -11252,6 +11362,13 @@ return parser;
converse.ping(jid); converse.ping(jid);
} }
}); });
var onConnected = function () {
// Wrapper so that we can spy on registerPingHandler in tests
converse.registerPingHandler();
};
converse.on('connected', onConnected);
converse.on('reconnected', onConnected);
} }
}); });
})); }));
...@@ -11326,15 +11443,18 @@ return parser; ...@@ -11326,15 +11443,18 @@ return parser;
return true; return true;
}; };
converse.shouldNotifyOfMessage = function ($message) { converse.shouldNotifyOfMessage = function (message) {
/* Is this a message worthy of notification? /* Is this a message worthy of notification?
*/ */
var $forwarded = $message.find('forwarded'); var $message = $(message),
$forwarded = $message.find('forwarded');
if ($forwarded.length) { if ($forwarded.length) {
return false; return false;
} } else if ($message.attr('type') === 'groupchat') {
if ($message.attr('type') === 'groupchat') {
return converse.shouldNotifyOfGroupMessage($message); return converse.shouldNotifyOfGroupMessage($message);
} else if (utils.isHeadlineMessage(message)) {
// We want to show notifications for headline messages.
return true;
} }
var is_me = Strophe.getBareJidFromJid($message.attr('from')) === converse.bare_jid; var is_me = Strophe.getBareJidFromJid($message.attr('from')) === converse.bare_jid;
return !converse.isOnlyChatStateNotification($message) && !is_me; return !converse.isOnlyChatStateNotification($message) && !is_me;
...@@ -11373,9 +11493,22 @@ return parser; ...@@ -11373,9 +11493,22 @@ return parser;
/* Shows an HTML5 Notification to indicate that a new chat /* Shows an HTML5 Notification to indicate that a new chat
* message was received. * message was received.
*/ */
var contact_jid = Strophe.getBareJidFromJid($message.attr('from')); var n, title, contact_jid, roster_item,
var roster_item = converse.roster.get(contact_jid); from_jid = $message.attr('from');
var n = new Notification(__(___("%1$s says"), roster_item.get('fullname')), { if ($message.attr('type') === 'headline' || from_jid.indexOf('@') === -1) {
// XXX: 2nd check is workaround for Prosody which doesn't
// give type "headline"
title = __(___("Notification from %1$s"), from_jid);
} else {
if (typeof converse.roster === 'undefined') {
converse.log("Could not send notification, because roster is undefined", "error");
return;
}
contact_jid = Strophe.getBareJidFromJid($message.attr('from'));
roster_item = converse.roster.get(contact_jid);
title = __(___("%1$s says"), roster_item.get('fullname'));
}
n = new Notification(title, {
body: $message.children('body').text(), body: $message.children('body').text(),
lang: converse.i18n.locale_data.converse[""].lang, lang: converse.i18n.locale_data.converse[""].lang,
icon: converse.notification_icon icon: converse.notification_icon
...@@ -11387,7 +11520,7 @@ return parser; ...@@ -11387,7 +11520,7 @@ return parser;
/* Creates an HTML5 Notification to inform of a change in a /* Creates an HTML5 Notification to inform of a change in a
* contact's chat state. * contact's chat state.
*/ */
if (_.contains(converse.chatstate_notification_blacklist, contact.get('jid'))) { if (_.contains(converse.chatstate_notification_blacklist, contact.jid)) {
// Don't notify if the user is being ignored. // Don't notify if the user is being ignored.
return; return;
} }
...@@ -11437,7 +11570,7 @@ return parser; ...@@ -11437,7 +11570,7 @@ return parser;
* to play sounds and show HTML5 notifications. * to play sounds and show HTML5 notifications.
*/ */
var $message = $(message); var $message = $(message);
if (!converse.shouldNotifyOfMessage($message)) { if (!converse.shouldNotifyOfMessage(message)) {
return false; return false;
} }
converse.playSoundNotification($message); converse.playSoundNotification($message);
...@@ -11474,25 +11607,115 @@ return parser; ...@@ -11474,25 +11607,115 @@ return parser;
// Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com> // Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2) // Licensed under the Mozilla Public License (MPLv2)
// //
/*global define */ /*global define, window */
(function (root, factory) { (function (root, factory) {
define("converse-headline", [ define("converse-headline", [
"converse-core", "converse-core",
"converse-api", "converse-api",
// TODO: remove this dependency
"converse-chatview" "converse-chatview"
], factory); ], factory);
}(this, function (converse, converse_api) { }(this, function (converse, converse_api) {
"use strict"; "use strict";
var $ = converse_api.env.jQuery,
_ = converse_api.env._,
utils = converse_api.env.utils,
__ = utils.__.bind(converse);
var onHeadlineMessage = function (message) {
/* Handler method for all incoming messages of type "headline".
*/
var $message = $(message),
from_jid = $message.attr('from');
if (utils.isHeadlineMessage(message)) {
converse.chatboxes.create({
'id': from_jid,
'jid': from_jid,
'fullname': from_jid,
'type': 'headline'
}).createMessage($message);
converse.emit('message', message);
}
return true;
};
converse_api.plugins.add('headline', { converse_api.plugins.add('headline', {
overrides: {
// Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
ChatBoxViews: {
onChatBoxAdded: function (item) {
var view = this.get(item.get('id'));
if (!view && item.get('type') === 'headline') {
view = new converse.HeadlinesBoxView({model: item});
this.add(item.get('id'), view);
return view;
} else {
return this._super.onChatBoxAdded.apply(this, arguments);
}
}
}
},
initialize: function () { initialize: function () {
/* The initialize function gets called as soon as the plugin is /* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery. * loaded by converse.js's plugin machinery.
*/ */
// TODO converse.HeadlinesBoxView = converse.ChatBoxView.extend({
className: 'chatbox headlines',
events: {
'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'minimize',
'keypress textarea.chat-textarea': 'keyPressed',
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize: function () {
$(window).on('resize', _.debounce(this.setDimensions.bind(this), 100));
this.model.messages.on('add', this.onMessageAdded, this);
this.model.on('show', this.show, this);
this.model.on('destroy', this.hide, this);
this.model.on('change:minimized', this.onMinimizedChanged, this);
this.render().fetchMessages().insertIntoPage().hide();
converse.emit('chatBoxInitialized', this);
},
render: function () {
this.$el.attr('id', this.model.get('box_id'))
.html(converse.templates.chatbox(
_.extend(this.model.toJSON(), {
show_toolbar: converse.show_toolbar,
show_textarea: false,
title: this.model.get('fullname'),
info_close: __('Close this box'),
info_minimize: __('Minimize this box'),
label_personal_message: ''
}
)
)
);
this.setWidth();
this.$content = this.$el.find('.chat-content');
converse.emit('chatBoxOpened', this);
window.setTimeout(utils.refreshWebkit, 50);
return this;
},
});
var registerHeadlineHandler = function () {
converse.connection.addHandler(
onHeadlineMessage, null, 'message');
};
converse.on('connected', registerHeadlineHandler);
converse.on('reconnected', registerHeadlineHandler);
} }
}); });
})); }));
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment