2016-02-16 08:46:47 +01:00
// Converse.js (A browser based XMPP chat client)
// http://conversejs.org
//
// Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
//
2016-02-20 16:06:12 +01:00
/*global Backbone, define, window, document, locales */
2016-02-16 08:46:47 +01:00
( function ( root , factory ) {
2016-02-23 08:13:30 +01:00
define ( "converse-core" , [
"jquery" ,
"underscore" ,
"polyfill" ,
"utils" ,
"moment_with_locales" ,
"strophe" ,
2016-08-11 16:05:25 +02:00
"pluggable" ,
2016-02-23 08:13:30 +01:00
"strophe.disco" ,
"backbone.browserStorage" ,
"backbone.overview" ,
] , factory ) ;
2016-11-22 17:36:39 +01:00
} ( this , function ( $ , _ , dummy , utils , moment , Strophe , pluggable ) {
2016-03-09 11:00:11 +01:00
/ *
2016-02-16 08:46:47 +01:00
* Cannot use this due to Safari bug .
* See https : //github.com/jcbrand/converse.js/issues/196
* /
2016-02-23 08:13:30 +01:00
// "use strict";
// Strophe globals
var $build = Strophe . $build ;
var $iq = Strophe . $iq ;
var $pres = Strophe . $pres ;
var b64 _sha1 = Strophe . SHA1 . b64 _sha1 ;
Strophe = Strophe . Strophe ;
2016-02-16 08:46:47 +01:00
// Use Mustache style syntax for variable interpolation
/ * C o n f i g u r a t i o n o f u n d e r s c o r e t e m p l a t e s ( t h i s c o n f i g i s d i s t i n c t t o t h e
* config of requirejs - tpl in main . js ) . This one is for normal inline templates .
* /
_ . templateSettings = {
evaluate : /\{\[([\s\S]+?)\]\}/g ,
interpolate : /\{\{([\s\S]+?)\}\}/g
} ;
2016-03-01 23:26:36 +01:00
// We create an object to act as the "this" context for event handlers (as
// defined below and accessible via converse_api.listen).
// We don't want the inner converse object to be the context, since it
// contains sensitive information, and we don't want it to be something in
// the DOM or window, because then anyone can trigger converse events.
var event _context = { } ;
2016-02-16 08:46:47 +01:00
var converse = {
2016-11-22 17:36:39 +01:00
templates : { } ,
2016-06-09 11:01:54 +02:00
2016-02-16 08:46:47 +01:00
emit : function ( evt , data ) {
2016-03-01 23:26:36 +01:00
$ ( event _context ) . trigger ( evt , data ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-06-09 11:01:54 +02:00
2016-09-21 13:12:42 +02:00
once : function ( evt , handler , context ) {
if ( context ) {
handler = handler . bind ( context ) ;
}
2016-03-01 23:26:36 +01:00
$ ( event _context ) . one ( evt , handler ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-06-09 11:01:54 +02:00
2016-09-21 13:12:42 +02:00
on : function ( evt , handler , context ) {
2016-05-23 11:48:26 +02:00
if ( _ . contains ( [ 'ready' , 'initialized' ] , evt ) ) {
converse . log ( 'Warning: The "' + evt + '" event has been deprecated and will be removed, please use "connected".' ) ;
}
2016-09-21 13:12:42 +02:00
if ( context ) {
handler = handler . bind ( context ) ;
}
2016-03-01 23:26:36 +01:00
$ ( event _context ) . bind ( evt , handler ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-06-09 11:01:54 +02:00
2016-02-16 08:46:47 +01:00
off : function ( evt , handler ) {
2016-03-01 23:26:36 +01:00
$ ( event _context ) . unbind ( evt , handler ) ;
2016-02-16 08:46:47 +01:00
}
} ;
2016-06-09 11:01:54 +02:00
// Make converse pluggable
2016-08-31 12:06:17 +02:00
pluggable . enable ( converse , 'converse' , 'pluggable' ) ;
2016-06-09 11:01:54 +02:00
2016-02-20 16:06:12 +01:00
// Module-level constants
converse . STATUS _WEIGHTS = {
2016-02-16 08:46:47 +01:00
'offline' : 6 ,
'unavailable' : 5 ,
'xa' : 4 ,
'away' : 3 ,
'dnd' : 2 ,
'chat' : 1 , // We currently don't differentiate between "chat" and "online"
'online' : 1
} ;
2016-09-16 13:06:52 +02:00
converse . ANONYMOUS = "anonymous" ;
converse . CLOSED = 'closed' ;
converse . EXTERNAL = "external" ;
2016-02-28 20:24:06 +01:00
converse . LOGIN = "login" ;
2016-08-18 21:43:18 +02:00
converse . LOGOUT = "logout" ;
2016-02-28 20:24:06 +01:00
converse . OPENED = 'opened' ;
2016-09-16 13:06:52 +02:00
converse . PREBIND = "prebind" ;
2016-02-16 08:46:47 +01:00
2016-02-29 21:54:34 +01:00
var PRETTY _CONNECTION _STATUS = {
0 : 'ERROR' ,
1 : 'CONNECTING' ,
2 : 'CONNFAIL' ,
3 : 'AUTHENTICATING' ,
4 : 'AUTHFAIL' ,
5 : 'CONNECTED' ,
6 : 'DISCONNECTED' ,
7 : 'DISCONNECTING' ,
8 : 'ATTACHED' ,
9 : 'REDIRECT'
} ;
converse . log = function ( txt , level ) {
var logger ;
if ( typeof console === "undefined" || typeof console . log === "undefined" ) {
logger = { log : function ( ) { } , error : function ( ) { } } ;
} else {
logger = console ;
}
if ( converse . debug ) {
if ( level === 'error' ) {
logger . log ( 'ERROR: ' + txt ) ;
} else {
logger . log ( txt ) ;
}
}
} ;
2016-02-16 08:46:47 +01:00
converse . initialize = function ( settings , callback ) {
"use strict" ;
2016-09-30 10:42:15 +02:00
settings = typeof settings !== "undefined" ? settings : { } ;
2016-06-09 23:22:34 +02:00
var init _deferred = new $ . Deferred ( ) ;
2016-02-16 08:46:47 +01:00
var converse = this ;
var unloadevent ;
if ( 'onpagehide' in window ) {
// Pagehide gets thrown in more cases than unload. Specifically it
// gets thrown when the page is cached and not just
// closed/destroyed. It's the only viable event on mobile Safari.
// https://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/
unloadevent = 'pagehide' ;
} else if ( 'onbeforeunload' in window ) {
unloadevent = 'beforeunload' ;
} else if ( 'onunload' in window ) {
unloadevent = 'unload' ;
}
// Logging
Strophe . log = function ( level , msg ) { converse . log ( level + ' ' + msg , level ) ; } ;
Strophe . error = function ( msg ) { converse . log ( msg , 'error' ) ; } ;
// Add Strophe Namespaces
Strophe . addNamespace ( 'CARBONS' , 'urn:xmpp:carbons:2' ) ;
Strophe . addNamespace ( 'CHATSTATES' , 'http://jabber.org/protocol/chatstates' ) ;
Strophe . addNamespace ( 'CSI' , 'urn:xmpp:csi:0' ) ;
Strophe . addNamespace ( 'ROSTERX' , 'http://jabber.org/protocol/rosterx' ) ;
Strophe . addNamespace ( 'XFORM' , 'jabber:x:data' ) ;
2016-03-16 12:16:32 +01:00
Strophe . addNamespace ( 'NICK' , 'http://jabber.org/protocol/nick' ) ;
2016-05-30 18:20:23 +02:00
Strophe . addNamespace ( 'HINTS' , 'urn:xmpp:hints' ) ;
2016-06-27 23:50:38 +02:00
Strophe . addNamespace ( 'PUBSUB' , 'http://jabber.org/protocol/pubsub' ) ;
2016-02-16 08:46:47 +01:00
2016-03-01 09:58:36 +01:00
// Instance level constants
2016-02-16 08:46:47 +01:00
this . TIMEOUTS = { // Set as module attr so that we can override in tests.
2016-08-18 11:28:48 +02:00
'PAUSED' : 10000 ,
2016-02-16 08:46:47 +01:00
'INACTIVE' : 90000
} ;
2016-03-01 09:58:36 +01:00
// XEP-0085 Chat states
// http://xmpp.org/extensions/xep-0085.html
this . INACTIVE = 'inactive' ;
this . ACTIVE = 'active' ;
this . COMPOSING = 'composing' ;
this . PAUSED = 'paused' ;
this . GONE = 'gone' ;
2016-02-16 08:46:47 +01:00
// Detect support for the user's locale
// ------------------------------------
this . isConverseLocale = function ( locale ) { return typeof locales [ locale ] !== "undefined" ; } ;
this . isMomentLocale = function ( locale ) { return moment . locale ( ) !== moment . locale ( locale ) ; } ;
if ( ! moment . locale ) { //moment.lang is deprecated after 2.8.1, use moment.locale instead
moment . locale = moment . lang ;
}
2016-07-18 13:41:31 +02:00
moment . locale ( utils . detectLocale ( this . isMomentLocale ) ) ;
this . i18n = settings . i18n ? settings . i18n : locales [ utils . detectLocale ( this . isConverseLocale ) ] || { } ;
2016-02-16 08:46:47 +01:00
// Translation machinery
// ---------------------
var _ _ = utils . _ _ . bind ( this ) ;
2016-06-21 17:06:51 +02:00
var DESC _GROUP _TOGGLE = _ _ ( 'Click to hide these contacts' ) ;
2016-02-16 08:46:47 +01:00
// Default configuration values
// ----------------------------
this . default _settings = {
allow _contact _requests : true ,
animate : true ,
2016-09-16 13:06:52 +02:00
authentication : 'login' , // Available values are "login", "prebind", "anonymous" and "external".
2016-02-16 08:46:47 +01:00
auto _away : 0 , // Seconds after which user status is set to 'away'
auto _login : false , // Currently only used in connection with anonymous login
auto _reconnect : false ,
auto _subscribe : false ,
auto _xa : 0 , // Seconds after which user status is set to 'xa'
bosh _service _url : undefined , // The BOSH connection manager URL.
2016-10-12 13:55:47 +02:00
connection _options : { } ,
2016-03-31 10:37:28 +02:00
credentials _url : null , // URL from where login credentials can be fetched
2016-02-16 08:46:47 +01:00
csi _waiting _time : 0 , // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out.
debug : false ,
2016-05-31 10:03:06 +02:00
default _state : 'online' ,
2016-02-16 08:46:47 +01:00
expose _rid _and _sid : false ,
2016-05-28 08:35:16 +02:00
filter _by _resource : false ,
2016-02-16 08:46:47 +01:00
forward _messages : false ,
hide _offline _users : false ,
include _offline _state : false ,
jid : undefined ,
keepalive : false ,
locked _domain : undefined ,
message _carbons : false , // Support for XEP-280
password : undefined ,
prebind : false , // XXX: Deprecated, use "authentication" instead.
prebind _url : null ,
rid : undefined ,
roster _groups : false ,
show _only _online _users : false ,
sid : undefined ,
storage : 'session' ,
2016-08-21 12:49:54 +02:00
message _storage : 'session' ,
2016-03-14 18:03:48 +01:00
strict _plugin _dependencies : false ,
2016-02-18 10:17:00 +01:00
synchronize _availability : true , // Set to false to not sync with other clients or with resource name of the particular client that it should synchronize with
2016-02-16 08:46:47 +01:00
websocket _url : undefined ,
xhr _custom _status : false ,
xhr _custom _status _url : '' ,
} ;
_ . extend ( this , this . default _settings ) ;
// Allow only whitelisted configuration attributes to be overwritten
_ . extend ( this , _ . pick ( settings , Object . keys ( this . default _settings ) ) ) ;
// BBB
2016-02-28 20:24:06 +01:00
if ( this . prebind === true ) { this . authentication = converse . PREBIND ; }
2016-02-16 08:46:47 +01:00
2016-02-28 20:24:06 +01:00
if ( this . authentication === converse . ANONYMOUS ) {
2016-07-24 10:54:57 +02:00
if ( this . auto _login && ! this . jid ) {
throw new Error ( "Config Error: you need to provide the server's " +
"domain via the 'jid' option when using anonymous " +
"authentication with auto_login." ) ;
2016-02-16 08:46:47 +01:00
}
}
$ . fx . off = ! this . animate ;
// Module-level variables
// ----------------------
this . callback = callback || function ( ) { } ;
/ * W h e n r e l o a d i n g t h e p a g e :
* For new sessions , we need to send out a presence stanza to notify
* the server / network that we ' re online .
* When re - attaching to an existing session ( e . g . via the keepalive
* option ) , we don ' t need to again send out a presence stanza , because
* it ' s as if "we never left" ( see onConnectStatusChanged ) .
* https : //github.com/jcbrand/converse.js/issues/521
* /
this . send _initial _presence = true ;
this . msg _counter = 0 ;
2016-10-26 12:50:00 +02:00
this . user _settings = settings ; // Save the user settings so that they can be used by plugins
2016-02-16 08:46:47 +01:00
// Module-level functions
// ----------------------
2016-10-26 12:50:00 +02:00
this . wrappedChatBox = function ( chatbox ) {
/ * W r a p a c h a t b o x f o r o u t s i d e c o n s u m p t i o n ( i . e . s o t h a t i t c a n b e
* returned via the API .
* /
if ( ! chatbox ) { return ; }
var view = converse . chatboxviews . get ( chatbox . get ( 'id' ) ) ;
return {
'close' : view . close . bind ( view ) ,
'focus' : view . focus . bind ( view ) ,
'get' : chatbox . get . bind ( chatbox ) ,
'open' : view . show . bind ( view ) ,
'set' : chatbox . set . bind ( chatbox )
} ;
} ;
2016-02-16 08:46:47 +01:00
2016-02-20 16:06:12 +01:00
this . generateResource = function ( ) {
2016-02-16 14:31:46 +01:00
return '/converse.js-' + Math . floor ( Math . random ( ) * 139749825 ) . toString ( ) ;
} ;
2016-02-16 08:46:47 +01:00
this . sendCSI = function ( stat ) {
/* Send out a Chat Status Notification (XEP-0352) */
if ( converse . features [ Strophe . NS . CSI ] || true ) {
converse . connection . send ( $build ( stat , { xmlns : Strophe . NS . CSI } ) ) ;
2016-04-08 10:29:30 +02:00
converse . inactive = ( stat === converse . INACTIVE ) ? true : false ;
2016-02-16 08:46:47 +01:00
}
} ;
this . onUserActivity = function ( ) {
/* Resets counters and flags relating to CSI and auto_away/auto_xa */
2016-04-08 10:29:30 +02:00
if ( converse . idle _seconds > 0 ) {
converse . idle _seconds = 0 ;
2016-02-16 08:46:47 +01:00
}
if ( ! converse . connection . authenticated ) {
// We can't send out any stanzas when there's no authenticated connection.
2016-04-08 10:29:30 +02:00
// converse can happen when the connection reconnects.
2016-02-16 08:46:47 +01:00
return ;
}
2016-04-08 10:29:30 +02:00
if ( converse . inactive ) {
converse . sendCSI ( converse . ACTIVE ) ;
2016-02-16 08:46:47 +01:00
}
2016-04-08 10:29:30 +02:00
if ( converse . auto _changed _status === true ) {
converse . auto _changed _status = false ;
2016-06-01 18:01:09 +02:00
// XXX: we should really remember the original state here, and
// then set it back to that...
2016-05-31 10:03:06 +02:00
converse . xmppstatus . setStatus ( converse . default _state ) ;
2016-02-16 08:46:47 +01:00
}
} ;
this . onEverySecond = function ( ) {
/ * A n i n t e r v a l h a n d l e r r u n n i n g e v e r y s e c o n d .
2016-04-08 10:29:30 +02:00
* Used for CSI and the auto _away and auto _xa features .
2016-02-16 08:46:47 +01:00
* /
if ( ! converse . connection . authenticated ) {
// We can't send out any stanzas when there's no authenticated connection.
// This can happen when the connection reconnects.
return ;
}
2016-04-08 10:29:30 +02:00
var stat = converse . xmppstatus . getStatus ( ) ;
converse . idle _seconds ++ ;
if ( converse . csi _waiting _time > 0 &&
converse . idle _seconds > converse . csi _waiting _time &&
! converse . inactive ) {
converse . sendCSI ( converse . INACTIVE ) ;
2016-02-16 08:46:47 +01:00
}
2016-04-08 10:29:30 +02:00
if ( converse . auto _away > 0 &&
converse . idle _seconds > converse . auto _away &&
stat !== 'away' && stat !== 'xa' ) {
converse . auto _changed _status = true ;
converse . xmppstatus . setStatus ( 'away' ) ;
} else if ( converse . auto _xa > 0 &&
converse . idle _seconds > converse . auto _xa && stat !== 'xa' ) {
converse . auto _changed _status = true ;
converse . xmppstatus . setStatus ( 'xa' ) ;
2016-02-16 08:46:47 +01:00
}
} ;
this . registerIntervalHandler = function ( ) {
/ * S e t a n i n t e r v a l o f o n e s e c o n d a n d r e g i s t e r a h a n d l e r f o r i t .
* Required for the auto _away , auto _xa and csi _waiting _time features .
* /
2016-04-08 10:29:30 +02:00
if ( converse . auto _away < 1 && converse . auto _xa < 1 && converse . csi _waiting _time < 1 ) {
2016-02-16 08:46:47 +01:00
// Waiting time of less then one second means features aren't used.
return ;
}
2016-04-08 10:29:30 +02:00
converse . idle _seconds = 0 ;
converse . auto _changed _status = false ; // Was the user's status changed by converse.js?
$ ( window ) . on ( 'click mousemove keypress focus' + unloadevent , converse . onUserActivity ) ;
converse . everySecondTrigger = window . setInterval ( converse . onEverySecond , 1000 ) ;
2016-02-16 08:46:47 +01:00
} ;
2016-02-29 21:54:34 +01:00
2016-08-23 10:00:41 +02:00
this . giveFeedback = function ( subject , klass , message ) {
2016-02-16 08:46:47 +01:00
$ ( '.conn-feedback' ) . each ( function ( idx , el ) {
var $el = $ ( el ) ;
2016-08-23 10:00:41 +02:00
$el . addClass ( 'conn-feedback' ) . text ( subject ) ;
2016-02-16 08:46:47 +01:00
if ( klass ) {
$el . addClass ( klass ) ;
} else {
$el . removeClass ( 'error' ) ;
}
} ) ;
2016-08-23 10:00:41 +02:00
converse . emit ( 'feedback' , {
'klass' : klass ,
'message' : message ,
'subject' : subject
} ) ;
2016-02-16 08:46:47 +01:00
} ;
this . rejectPresenceSubscription = function ( jid , message ) {
/ * R e j e c t o r c a n c e l a n o t h e r u s e r ' s s u b s c r i p t i o n t o o u r p r e s e n c e u p d a t e s .
* Parameters :
* ( String ) jid - The Jabber ID of the user whose subscription
* is being canceled .
* ( String ) message - An optional message to the user
* /
var pres = $pres ( { to : jid , type : "unsubscribed" } ) ;
if ( message && message !== "" ) { pres . c ( "status" ) . t ( message ) ; }
converse . connection . send ( pres ) ;
} ;
2016-04-08 10:29:30 +02:00
this . reconnect = _ . debounce ( function ( condition ) {
2016-08-21 13:17:06 +02:00
converse . log ( 'The connection has dropped, attempting to reconnect.' ) ;
converse . giveFeedback (
2016-11-02 13:44:56 +01:00
_ _ ( "Reconnecting" ) ,
'warn' ,
_ _ ( 'The connection has dropped, attempting to reconnect.' )
2016-08-23 10:00:41 +02:00
) ;
2016-11-02 13:44:56 +01:00
converse . connection . reconnecting = true ;
converse . connection . disconnect ( 're-connecting' ) ;
converse . connection . reset ( ) ;
2016-04-08 10:29:30 +02:00
converse . _tearDown ( ) ;
2016-11-23 12:53:06 +01:00
converse . logIn ( null , true ) ;
2016-04-08 10:29:30 +02:00
} , 1000 ) ;
2016-02-16 08:46:47 +01:00
2016-12-04 16:00:46 +01:00
this . disconnect = function ( ) {
delete converse . connection . reconnecting ;
converse . _tearDown ( ) ;
converse . chatboxviews . closeAllChatBoxes ( ) ;
converse . emit ( 'disconnected' ) ;
converse . log ( 'DISCONNECTED' ) ;
return 'disconnected' ;
} ;
2016-03-31 14:23:45 +02:00
this . onDisconnected = function ( condition ) {
2016-11-23 15:50:28 +01:00
if ( converse . disconnection _cause !== converse . LOGOUT && converse . auto _reconnect ) {
if ( converse . disconnection _cause === Strophe . Status . CONNFAIL ) {
2016-08-18 21:43:18 +02:00
converse . reconnect ( condition ) ;
converse . log ( 'RECONNECTING' ) ;
2016-12-04 16:00:46 +01:00
} else if ( converse . disconnection _cause === Strophe . Status . DISCONNECTED ) {
2016-08-19 16:11:37 +02:00
window . setTimeout ( _ . partial ( converse . reconnect , condition ) , 3000 ) ;
converse . log ( 'RECONNECTING IN 3 SECONDS' ) ;
2016-08-18 21:43:18 +02:00
}
2016-11-23 15:50:28 +01:00
converse . emit ( 'reconnecting' ) ;
return 'reconnecting' ;
2016-08-18 21:43:18 +02:00
}
2016-12-04 16:00:46 +01:00
return this . disconnect ( ) ;
2016-08-18 21:43:18 +02:00
} ;
this . setDisconnectionCause = function ( connection _status ) {
if ( typeof converse . disconnection _cause === "undefined" ) {
converse . disconnection _cause = connection _status ;
2016-03-31 14:23:45 +02:00
}
} ;
2016-08-18 21:43:18 +02:00
this . onConnectStatusChanged = function ( status , condition ) {
2016-02-16 08:46:47 +01:00
converse . log ( "Status changed to: " + PRETTY _CONNECTION _STATUS [ status ] ) ;
if ( status === Strophe . Status . CONNECTED || status === Strophe . Status . ATTACHED ) {
// By default we always want to send out an initial presence stanza.
converse . send _initial _presence = true ;
delete converse . disconnection _cause ;
2016-08-18 21:43:18 +02:00
if ( converse . connection . reconnecting ) {
2016-02-16 08:46:47 +01:00
converse . log ( status === Strophe . Status . CONNECTED ? 'Reconnected' : 'Reattached' ) ;
2016-11-23 12:53:06 +01:00
converse . onConnected ( true ) ;
2016-02-16 08:46:47 +01:00
} else {
converse . log ( status === Strophe . Status . CONNECTED ? 'Connected' : 'Attached' ) ;
if ( converse . connection . restored ) {
2016-08-18 21:43:18 +02:00
// No need to send an initial presence stanza when
// we're restoring an existing session.
converse . send _initial _presence = false ;
2016-02-16 08:46:47 +01:00
}
converse . onConnected ( ) ;
}
} else if ( status === Strophe . Status . DISCONNECTED ) {
2016-08-18 21:43:18 +02:00
converse . setDisconnectionCause ( status ) ;
2016-03-31 14:23:45 +02:00
converse . onDisconnected ( condition ) ;
2016-02-16 08:46:47 +01:00
} else if ( status === Strophe . Status . ERROR ) {
2016-08-23 10:00:41 +02:00
converse . giveFeedback (
_ _ ( 'Connection error' ) , 'error' ,
_ _ ( 'An error occurred while connecting to the chat server.' )
) ;
2016-02-16 08:46:47 +01:00
} else if ( status === Strophe . Status . CONNECTING ) {
converse . giveFeedback ( _ _ ( 'Connecting' ) ) ;
} else if ( status === Strophe . Status . AUTHENTICATING ) {
converse . giveFeedback ( _ _ ( 'Authenticating' ) ) ;
} else if ( status === Strophe . Status . AUTHFAIL ) {
2016-08-23 10:00:41 +02:00
converse . giveFeedback ( _ _ ( 'Authentication failed.' ) , 'error' ) ;
2016-02-16 08:46:47 +01:00
converse . connection . disconnect ( _ _ ( 'Authentication Failed' ) ) ;
converse . disconnection _cause = Strophe . Status . AUTHFAIL ;
2016-12-04 15:14:40 +01:00
} else if ( status === Strophe . Status . CONNFAIL ) {
converse . setDisconnectionCause ( status ) ;
} else if ( status === Strophe . Status . DISCONNECTING ) {
2016-08-18 21:43:18 +02:00
converse . setDisconnectionCause ( status ) ;
2016-12-04 16:00:46 +01:00
if ( condition ) {
converse . giveFeedback (
_ _ ( "Disconnected" ) , 'warn' ,
_ _ ( "The connection to the chat server has dropped" )
) ;
}
2016-02-16 08:46:47 +01:00
}
} ;
this . updateMsgCounter = function ( ) {
if ( this . msg _counter > 0 ) {
if ( document . title . search ( /^Messages \(\d+\) / ) === - 1 ) {
document . title = "Messages (" + this . msg _counter + ") " + document . title ;
} else {
document . title = document . title . replace ( /^Messages \(\d+\) / , "Messages (" + this . msg _counter + ") " ) ;
}
} else if ( document . title . search ( /^Messages \(\d+\) / ) !== - 1 ) {
document . title = document . title . replace ( /^Messages \(\d+\) / , "" ) ;
}
} ;
this . incrementMsgCounter = function ( ) {
this . msg _counter += 1 ;
this . updateMsgCounter ( ) ;
} ;
this . clearMsgCounter = function ( ) {
this . msg _counter = 0 ;
this . updateMsgCounter ( ) ;
} ;
2016-03-16 12:16:32 +01:00
this . initStatus = function ( ) {
var deferred = new $ . Deferred ( ) ;
2016-02-16 08:46:47 +01:00
this . xmppstatus = new this . XMPPStatus ( ) ;
var id = b64 _sha1 ( 'converse.xmppstatus-' + converse . bare _jid ) ;
this . xmppstatus . id = id ; // Appears to be necessary for backbone.browserStorage
this . xmppstatus . browserStorage = new Backbone . BrowserStorage [ converse . storage ] ( id ) ;
2016-03-16 12:16:32 +01:00
this . xmppstatus . fetch ( {
success : deferred . resolve ,
error : deferred . resolve
} ) ;
converse . emit ( 'statusInitialized' ) ;
return deferred . promise ( ) ;
2016-02-16 08:46:47 +01:00
} ;
this . initSession = function ( ) {
this . session = new this . Session ( ) ;
var id = b64 _sha1 ( 'converse.bosh-session' ) ;
this . session . id = id ; // Appears to be necessary for backbone.browserStorage
this . session . browserStorage = new Backbone . BrowserStorage [ converse . storage ] ( id ) ;
this . session . fetch ( ) ;
} ;
this . clearSession = function ( ) {
2016-10-17 10:34:09 +02:00
if ( ! _ . isUndefined ( this . roster ) ) {
2016-02-16 08:46:47 +01:00
this . roster . browserStorage . _clear ( ) ;
}
this . session . browserStorage . _clear ( ) ;
} ;
this . logOut = function ( ) {
2016-11-22 10:12:39 +01:00
converse . chatboxviews . closeAllChatBoxes ( ) ;
2016-08-18 21:43:18 +02:00
converse . disconnection _cause = converse . LOGOUT ;
2016-04-13 11:15:15 +02:00
if ( typeof converse . connection !== 'undefined' ) {
converse . connection . disconnect ( ) ;
converse . connection . reset ( ) ;
}
2016-11-03 10:48:59 +01:00
converse . clearSession ( ) ;
converse . _tearDown ( ) ;
2016-11-02 08:42:24 +01:00
converse . emit ( 'logout' ) ;
2016-02-16 08:46:47 +01:00
} ;
2016-06-17 11:30:47 +02:00
this . saveWindowState = function ( ev , hidden ) {
// XXX: eventually we should be able to just use
// document.visibilityState (when we drop support for older
// browsers).
2016-06-16 18:14:22 +02:00
var state ;
var v = "visible" , h = "hidden" ,
event _map = {
'focus' : v ,
'focusin' : v ,
'pageshow' : v ,
'blur' : h ,
'focusout' : h ,
'pagehide' : h
} ;
2016-06-17 11:30:47 +02:00
ev = ev || document . createEvent ( 'Events' ) ;
2016-06-16 18:14:22 +02:00
if ( ev . type in event _map ) {
state = event _map [ ev . type ] ;
} else {
state = document [ hidden ] ? "hidden" : "visible" ;
}
if ( state === 'visible' ) {
converse . clearMsgCounter ( ) ;
}
converse . windowState = state ;
} ;
2016-02-16 08:46:47 +01:00
this . registerGlobalEventHandlers = function ( ) {
2016-06-16 18:14:22 +02:00
// Taken from:
// http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active
var hidden = "hidden" ;
// Standards:
if ( hidden in document ) {
2016-06-17 11:30:47 +02:00
document . addEventListener ( "visibilitychange" , _ . partial ( converse . saveWindowState , _ , hidden ) ) ;
2016-06-16 18:14:22 +02:00
} else if ( ( hidden = "mozHidden" ) in document ) {
2016-06-17 11:30:47 +02:00
document . addEventListener ( "mozvisibilitychange" , _ . partial ( converse . saveWindowState , _ , hidden ) ) ;
2016-06-16 18:14:22 +02:00
} else if ( ( hidden = "webkitHidden" ) in document ) {
2016-06-17 11:30:47 +02:00
document . addEventListener ( "webkitvisibilitychange" , _ . partial ( converse . saveWindowState , _ , hidden ) ) ;
2016-06-16 18:14:22 +02:00
} else if ( ( hidden = "msHidden" ) in document ) {
2016-06-17 11:30:47 +02:00
document . addEventListener ( "msvisibilitychange" , _ . partial ( converse . saveWindowState , _ , hidden ) ) ;
2016-06-16 18:14:22 +02:00
} else if ( "onfocusin" in document ) {
// IE 9 and lower:
2016-06-17 11:30:47 +02:00
document . onfocusin = document . onfocusout = _ . partial ( converse . saveWindowState , _ , hidden ) ;
2016-06-16 18:14:22 +02:00
} else {
// All others:
2016-06-17 11:30:47 +02:00
window . onpageshow = window . onpagehide = window . onfocus = window . onblur = _ . partial ( converse . saveWindowState , _ , hidden ) ;
2016-06-16 18:14:22 +02:00
}
// set the initial state (but only if browser supports the Page Visibility API)
if ( document [ hidden ] !== undefined ) {
2016-06-17 11:30:47 +02:00
_ . partial ( converse . saveWindowState , _ , hidden ) ( { type : document [ hidden ] ? "blur" : "focus" } ) ;
2016-06-16 18:14:22 +02:00
}
2016-02-16 08:46:47 +01:00
} ;
this . enableCarbons = function ( ) {
/ * A s k t h e X M P P s e r v e r t o e n a b l e M e s s a g e C a r b o n s
* See XEP - 0280 https : //xmpp.org/extensions/xep-0280.html#enabling
* /
if ( ! this . message _carbons || this . session . get ( 'carbons_enabled' ) ) {
return ;
}
var carbons _iq = new Strophe . Builder ( 'iq' , {
from : this . connection . jid ,
id : 'enablecarbons' ,
type : 'set'
} )
. c ( 'enable' , { xmlns : Strophe . NS . CARBONS } ) ;
this . connection . addHandler ( function ( iq ) {
if ( $ ( iq ) . find ( 'error' ) . length > 0 ) {
converse . log ( 'ERROR: An error occured while trying to enable message carbons.' ) ;
} else {
this . session . save ( { carbons _enabled : true } ) ;
converse . log ( 'Message carbons have been enabled.' ) ;
}
} . bind ( this ) , null , "iq" , null , "enablecarbons" ) ;
this . connection . send ( carbons _iq ) ;
} ;
2016-06-22 12:21:56 +02:00
this . initRoster = function ( ) {
2016-11-02 14:13:49 +01:00
/ * I n i t i a l i z e t h e B a k c b o n e c o l l e c t i o n s t h a t r e p r e s e n t t h e c o n t a t s
* roster and the roster groups .
* /
2016-09-21 11:48:53 +02:00
converse . roster = new converse . RosterContacts ( ) ;
converse . roster . browserStorage = new Backbone . BrowserStorage . session (
b64 _sha1 ( 'converse.contacts-' + converse . bare _jid ) ) ;
converse . rostergroups = new converse . RosterGroups ( ) ;
converse . rostergroups . browserStorage = new Backbone . BrowserStorage . session (
2016-06-22 12:21:56 +02:00
b64 _sha1 ( 'converse.roster.groups' + converse . bare _jid ) ) ;
2016-11-02 14:13:49 +01:00
converse . emit ( 'rosterInitialized' ) ;
2016-06-22 12:21:56 +02:00
} ;
2016-09-21 15:00:09 +02:00
this . populateRoster = function ( ) {
/ * F e t c h a l l t h e r o s t e r g r o u p s , a n d t h e n t h e r o s t e r c o n t a c t s .
* Emit an event after fetching is done in each case .
* /
converse . rostergroups . fetchRosterGroups ( ) . then ( function ( ) {
converse . emit ( 'rosterGroupsFetched' ) ;
converse . roster . fetchRosterContacts ( ) . then ( function ( ) {
converse . emit ( 'rosterContactsFetched' ) ;
converse . sendInitialPresence ( ) ;
} ) ;
} ) ;
} ;
2016-06-22 12:21:56 +02:00
this . unregisterPresenceHandler = function ( ) {
2016-09-21 11:48:53 +02:00
if ( typeof converse . presence _ref !== 'undefined' ) {
converse . connection . deleteHandler ( converse . presence _ref ) ;
delete converse . presence _ref ;
2016-06-22 12:21:56 +02:00
}
} ;
this . registerPresenceHandler = function ( ) {
2016-09-21 11:48:53 +02:00
converse . unregisterPresenceHandler ( ) ;
converse . presence _ref = converse . connection . addHandler (
2016-06-22 12:21:56 +02:00
function ( presence ) {
converse . roster . presenceHandler ( presence ) ;
return true ;
} , null , 'presence' , null ) ;
} ;
2016-09-20 12:10:28 +02:00
this . sendInitialPresence = function ( ) {
if ( converse . send _initial _presence ) {
converse . xmppstatus . sendPresence ( ) ;
}
} ;
2016-11-23 12:53:06 +01:00
this . onStatusInitialized = function ( reconnecting ) {
/ * C o n t i n u e w i t h s e s s i o n e s t a b l i s h m e n t ( e . g . f e t c h i n g c h a t b o x e s ,
* populating the roster etc . ) necessary once the connection has
* been established .
* /
if ( reconnecting ) {
// No need to recreate the roster, otherwise we lose our
// cached data. However we still emit an event, to give
// event handlers a chance to register views for the
// roster and its groups, before we start populating.
converse . emit ( 'rosterReadyAfterReconnection' ) ;
} else {
converse . registerIntervalHandler ( ) ;
converse . initRoster ( ) ;
}
// First set up chat boxes, before populating the roster, so that
// the controlbox is properly set up and ready for the rosterview.
2016-11-02 13:44:56 +01:00
converse . chatboxes . onConnected ( ) ;
2016-11-22 10:35:36 +01:00
converse . populateRoster ( ) ;
2016-11-02 13:44:56 +01:00
converse . registerPresenceHandler ( ) ;
converse . giveFeedback ( _ _ ( 'Contacts' ) ) ;
2016-11-23 12:53:06 +01:00
if ( reconnecting ) {
converse . xmppstatus . sendPresence ( ) ;
} else {
init _deferred . resolve ( ) ;
converse . emit ( 'initialized' ) ;
2016-03-16 12:16:32 +01:00
}
} ;
2016-11-02 13:44:56 +01:00
this . setUserJid = function ( ) {
converse . jid = converse . connection . jid ;
converse . bare _jid = Strophe . getBareJidFromJid ( converse . connection . jid ) ;
converse . resource = Strophe . getResourceFromJid ( converse . connection . jid ) ;
converse . domain = Strophe . getDomainFromJid ( converse . connection . jid ) ;
} ;
2016-11-23 12:53:06 +01:00
this . onConnected = function ( reconnecting ) {
2016-11-02 13:44:56 +01:00
/ * C a l l e d a s s o o n a s a n e w c o n n e c t i o n h a s b e e n e s t a b l i s h e d , e i t h e r
* by logging in or by attaching to an existing BOSH session .
* /
2016-11-23 12:53:06 +01:00
// Solves problem of returned PubSub BOSH response not received
// by browser.
2016-05-11 11:10:27 +02:00
converse . connection . flush ( ) ;
2016-11-23 12:53:06 +01:00
2016-11-02 13:44:56 +01:00
converse . setUserJid ( ) ;
converse . enableCarbons ( ) ;
2016-02-16 08:46:47 +01:00
2016-11-23 12:53:06 +01:00
// If there's no xmppstatus obj, then we were never connected to
// begin with, so we set reconnecting to false.
reconnecting = _ . isUndefined ( converse . xmppstatus ) ? false : reconnecting ;
if ( reconnecting ) {
converse . onStatusInitialized ( true ) ;
converse . emit ( 'reconnected' ) ;
2016-11-23 11:24:33 +01:00
} else {
2016-11-23 12:53:06 +01:00
// There might be some open chat boxes. We don't
// know whether these boxes are of the same account or not, so we
// close them now.
converse . chatboxviews . closeAllChatBoxes ( ) ;
converse . features = new converse . Features ( ) ;
converse . initStatus ( ) . done ( _ . partial ( converse . onStatusInitialized , false ) ) ;
converse . emit ( 'connected' ) ;
2016-11-23 11:24:33 +01:00
}
2016-11-02 14:13:49 +01:00
} ;
2016-02-29 22:18:28 +01:00
this . RosterContact = Backbone . Model . extend ( {
2016-02-29 22:59:56 +01:00
2016-02-29 22:18:28 +01:00
initialize : function ( attributes , options ) {
var jid = attributes . jid ;
var bare _jid = Strophe . getBareJidFromJid ( jid ) ;
var resource = Strophe . getResourceFromJid ( jid ) ;
attributes . jid = bare _jid ;
this . set ( _ . extend ( {
'id' : bare _jid ,
'jid' : bare _jid ,
'fullname' : bare _jid ,
'chat_status' : 'offline' ,
'user_id' : Strophe . getNodeFromJid ( jid ) ,
'resources' : resource ? [ resource ] : [ ] ,
'groups' : [ ] ,
'image_type' : 'image/png' ,
'image' : "iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAIAAABt+uBvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwHCy455JBsggAABkJJREFUeNrtnM1PE1sUwHvvTD8otWLHST/Gimi1CEgr6M6FEWuIBo2pujDVsNDEP8GN/4MbN7oxrlipG2OCgZgYlxAbkRYw1KqkIDRCSkM7nXvvW8x7vjyNeQ9m7p1p3z1LQk/v/Dhz7vkEXL161cHl9wI5Ag6IA+KAOCAOiAPigDggLhwQB2S+iNZ+PcYY/SWEEP2HAAAIoSAIoihCCP+ngDDGtVotGAz29/cfOXJEUZSOjg6n06lp2sbGRqlUWlhYyGazS0tLbrdbEASrzgksyeYJId3d3el0uqenRxRFAAAA4KdfIIRgjD9+/Pj8+fOpqSndslofEIQwHA6Pjo4mEon//qmFhYXHjx8vLi4ihBgDEnp7e9l8E0Jo165dQ0NDd+/eDYVC2/qsJElDQ0OEkKWlpa2tLZamxAhQo9EIBoOjo6MXL17csZLe3l5FUT59+lQul5l5JRaAVFWNRqN37tw5ceKEQVWRSOTw4cOFQuHbt2+iKLYCIISQLMu3b99OJpOmKAwEAgcPHszn8+vr6wzsiG6UQQhxuVyXLl0aGBgwUW0sFstkMl6v90fo1KyAMMYDAwPnzp0zXfPg4GAqlWo0Gk0MiBAiy/L58+edTqf5Aa4onj59OhaLYYybFRCEMBaL0fNxBw4cSCQStN0QRUBut3t4eJjq6U+dOiVJElVPRBFQIBDo6+ujCqirqyscDlONGykC2lYyYSR6pBoQQapHZwAoHo/TuARYAOrs7GQASFEUqn6aIiBJkhgA6ujooFpUo6iaTa7koFwnaoWadLNe81tbWwzoaJrWrICWl5cZAFpbW6OabVAEtLi4yABQsVjUNK0pAWWzWQaAcrlcswKanZ1VVZUqHYRQEwOq1Wpv3ryhCmh6erpcLjdrNl+v1ycnJ+l5UELI27dvv3//3qxxEADgy5cvExMT9Mznw4cPtFtAdAPFarU6Pj5eKpVM17yxsfHy5cvV1VXazXu62gVBKBQKT58+rdVqJqrFGL948eLdu3dU8/g/H4FBUaJYLAqC0NPTY9brMD4+PjY25mDSracOCABACJmZmXE6nUePHjWu8NWrV48ePSKEsGlAs7Agfd5nenq6Wq0mk0kjDzY2NvbkyRMIIbP2PLvhBUEQ8vl8NpuNx+M+n29bzhVjvLKycv/+/YmJCcazQuwA6YzW1tYmJyf1SY+2trZ/rRk1Go1SqfT69esHDx4UCgVmNaa/zZ/9ABUhRFXVYDB48uTJeDweiUQkSfL7/T9MA2NcqVTK5fLy8vL8/PzU1FSxWHS5XJaM4wGr9sUwxqqqer3eUCgkSZJuUBBCfTRvc3OzXC6vrKxUKhWn02nhCJ5lM4oQQo/HgxD6+vXr58+fHf8sDOp+HQDg8XgclorFU676dKLlo6yWRdItIBwQB8QBcUCtfosRQjRNQwhhjPUC4w46WXryBSHU1zgEQWBz99EFhDGu1+t+v//48ePxeFxRlD179ng8nh0Efgiher2+vr6ur3HMzMysrq7uTJVdACGEurq6Ll++nEgkPB7Pj9jPoDHqOxyqqubz+WfPnuVyuV9XPeyeagAAAoHArVu3BgcHab8CuVzu4cOHpVKJUnfA5GweY+xyuc6cOXPv3r1IJMLAR8iyPDw8XK/Xi8Wiqqqmm5KZgBBC7e3tN27cuHbtGuPVpf7+/lAoNDs7W61WzfVKpgHSSzw3b95MpVKW3MfRaDQSiczNzVUqFRMZmQOIEOL1eq9fv3727FlL1t50URRFluX5+flqtWpWEGAOIFEUU6nUlStXLKSjy759+xwOx9zcnKZpphzGHMzhcDiTydgk9r1w4YIp7RPTAAmCkMlk2FeLf/tIEKbTab/fbwtAhJBoNGrutpNx6e7uPnTokC1eMU3T0um0DZPMkZER6wERQnw+n/FFSxpy7Nix3bt3WwwIIcRgIWnHkkwmjecfRgGx7DtuV/r6+iwGhDHev3+/bQF1dnYaH6E2CkiWZdsC2rt3r8WAHA5HW1ubbQGZcjajgOwTH/4qNko1Wlg4IA6IA+KAOKBWBUQIsfNojyliKIoRRfH9+/dut9umf3wzpoUNNQ4BAJubmwz+ic+OxefzWWlBhJD29nbug7iT5sIBcUAcEAfEAXFAHBAHxOVn+QMrmWpuPZx12gAAAABJRU5ErkJggg==" ,
'status' : ''
} , attributes ) ) ;
this . on ( 'destroy' , function ( ) { this . removeFromRoster ( ) ; } . bind ( this ) ) ;
2016-03-08 11:42:52 +01:00
this . on ( 'change:chat_status' , function ( item ) {
converse . emit ( 'contactStatusChanged' , item . attributes ) ;
} ) ;
2016-02-29 22:18:28 +01:00
} ,
2016-02-29 22:59:56 +01:00
subscribe : function ( message ) {
2016-02-29 22:18:28 +01:00
/ * S e n d a p r e s e n c e s u b s c r i p t i o n r e q u e s t t o t h i s r o s t e r c o n t a c t
2016-03-18 09:49:32 +01:00
*
* Parameters :
* ( String ) message - An optional message to explain the
* reason for the subscription request .
* /
2016-02-29 22:18:28 +01:00
this . save ( 'ask' , "subscribe" ) ; // ask === 'subscribe' Means we have ask to subscribe to them.
var pres = $pres ( { to : this . get ( 'jid' ) , type : "subscribe" } ) ;
if ( message && message !== "" ) {
pres . c ( "status" ) . t ( message ) . up ( ) ;
}
var nick = converse . xmppstatus . get ( 'fullname' ) ;
if ( nick && nick !== "" ) {
pres . c ( 'nick' , { 'xmlns' : Strophe . NS . NICK } ) . t ( nick ) . up ( ) ;
}
converse . connection . send ( pres ) ;
return this ;
} ,
ackSubscribe : function ( ) {
/ * U p o n r e c e i v i n g t h e p r e s e n c e s t a n z a o f t y p e " s u b s c r i b e d " ,
2016-03-18 09:49:32 +01:00
* the user SHOULD acknowledge receipt of that subscription
* state notification by sending a presence stanza of type
* "subscribe" to the contact
* /
2016-02-29 22:18:28 +01:00
converse . connection . send ( $pres ( {
'type' : 'subscribe' ,
'to' : this . get ( 'jid' )
} ) ) ;
} ,
ackUnsubscribe : function ( jid ) {
/ * U p o n r e c e i v i n g t h e p r e s e n c e s t a n z a o f t y p e " u n s u b s c r i b e d " ,
2016-03-18 09:49:32 +01:00
* the user SHOULD acknowledge receipt of that subscription state
* notification by sending a presence stanza of type "unsubscribe"
* this step lets the user ' s server know that it MUST no longer
* send notification of the subscription state change to the user .
* Parameters :
* ( String ) jid - The Jabber ID of the user who is unsubscribing
* /
2016-02-29 22:18:28 +01:00
converse . connection . send ( $pres ( { 'type' : 'unsubscribe' , 'to' : this . get ( 'jid' ) } ) ) ;
this . destroy ( ) ; // Will cause removeFromRoster to be called.
} ,
unauthorize : function ( message ) {
/ * U n a u t h o r i z e t h i s c o n t a c t ' s p r e s e n c e s u b s c r i p t i o n
2016-03-18 09:49:32 +01:00
* Parameters :
* ( String ) message - Optional message to send to the person being unauthorized
* /
2016-02-29 22:18:28 +01:00
converse . rejectPresenceSubscription ( this . get ( 'jid' ) , message ) ;
return this ;
} ,
authorize : function ( message ) {
/ * A u t h o r i z e p r e s e n c e s u b s c r i p t i o n
2016-03-18 09:49:32 +01:00
* Parameters :
* ( String ) message - Optional message to send to the person being authorized
* /
2016-02-29 22:18:28 +01:00
var pres = $pres ( { to : this . get ( 'jid' ) , type : "subscribed" } ) ;
if ( message && message !== "" ) {
pres . c ( "status" ) . t ( message ) ;
}
converse . connection . send ( pres ) ;
return this ;
} ,
removeResource : function ( resource ) {
var resources = this . get ( 'resources' ) , idx ;
if ( resource ) {
idx = _ . indexOf ( resources , resource ) ;
if ( idx !== - 1 ) {
resources . splice ( idx , 1 ) ;
this . save ( { 'resources' : resources } ) ;
}
}
else {
// if there is no resource (resource is null), it probably
// means that the user is now completely offline. To make sure
// that there isn't any "ghost" resources left, we empty the array
this . save ( { 'resources' : [ ] } ) ;
return 0 ;
}
return resources . length ;
} ,
removeFromRoster : function ( callback ) {
/ * I n s t r u c t t h e X M P P s e r v e r t o r e m o v e t h i s c o n t a c t f r o m o u r r o s t e r
2016-03-18 09:49:32 +01:00
* Parameters :
* ( Function ) callback
* /
2016-02-29 22:18:28 +01:00
var iq = $iq ( { type : 'set' } )
. c ( 'query' , { xmlns : Strophe . NS . ROSTER } )
. c ( 'item' , { jid : this . get ( 'jid' ) , subscription : "remove" } ) ;
converse . connection . sendIQ ( iq , callback , callback ) ;
return this ;
}
} ) ;
this . RosterContacts = Backbone . Collection . extend ( {
model : converse . RosterContact ,
2016-02-29 22:59:56 +01:00
2016-02-29 22:18:28 +01:00
comparator : function ( contact1 , contact2 ) {
var name1 , name2 ;
var status1 = contact1 . get ( 'chat_status' ) || 'offline' ;
var status2 = contact2 . get ( 'chat_status' ) || 'offline' ;
if ( converse . STATUS _WEIGHTS [ status1 ] === converse . STATUS _WEIGHTS [ status2 ] ) {
name1 = contact1 . get ( 'fullname' ) . toLowerCase ( ) ;
name2 = contact2 . get ( 'fullname' ) . toLowerCase ( ) ;
return name1 < name2 ? - 1 : ( name1 > name2 ? 1 : 0 ) ;
} else {
return converse . STATUS _WEIGHTS [ status1 ] < converse . STATUS _WEIGHTS [ status2 ] ? - 1 : 1 ;
}
} ,
2016-09-20 12:10:28 +02:00
fetchRosterContacts : function ( ) {
/ * F e t c h e s t h e r o s t e r c o n t a c t s , f i r s t b y t r y i n g t h e
* sessionStorage cache , and if that ' s empty , then by querying
* the XMPP server .
*
* Returns a promise which resolves once the contacts have been
* fetched .
* /
var deferred = new $ . Deferred ( ) ;
this . fetch ( {
add : true ,
success : function ( collection ) {
if ( collection . length === 0 ) {
/ * W e d o n ' t h a v e a n y r o s t e r c o n t a c t s s t o r e d i n s e s s i o n S t o r a g e ,
* so lets fetch the roster from the XMPP server . We pass in
* 'sendPresence' as callback method , because after initially
* fetching the roster we are ready to receive presence
* updates from our contacts .
* /
converse . send _initial _presence = true ;
converse . roster . fetchFromServer ( deferred . resolve ) ;
} else {
converse . emit ( 'cachedRoster' , collection ) ;
deferred . resolve ( ) ;
}
}
} ) ;
return deferred . promise ( ) ;
} ,
2016-02-29 22:18:28 +01:00
subscribeToSuggestedItems : function ( msg ) {
$ ( msg ) . find ( 'item' ) . each ( function ( i , items ) {
if ( this . getAttribute ( 'action' ) === 'add' ) {
converse . roster . addAndSubscribe (
this . getAttribute ( 'jid' ) , null , converse . xmppstatus . get ( 'fullname' ) ) ;
}
} ) ;
return true ;
} ,
isSelf : function ( jid ) {
return ( Strophe . getBareJidFromJid ( jid ) === Strophe . getBareJidFromJid ( converse . connection . jid ) ) ;
} ,
addAndSubscribe : function ( jid , name , groups , message , attributes ) {
/ * A d d a r o s t e r c o n t a c t a n d t h e n o n c e w e h a v e c o n f i r m a t i o n f r o m
2016-03-18 09:49:32 +01:00
* the XMPP server we subscribe to that contact ' s presence updates .
* Parameters :
* ( String ) jid - The Jabber ID of the user being added and subscribed to .
* ( String ) name - The name of that user
* ( Array of Strings ) groups - Any roster groups the user might belong to
* ( String ) message - An optional message to explain the
* reason for the subscription request .
* ( Object ) attributes - Any additional attributes to be stored on the user ' s model .
* /
2016-02-29 22:18:28 +01:00
this . addContact ( jid , name , groups , attributes ) . done ( function ( contact ) {
if ( contact instanceof converse . RosterContact ) {
contact . subscribe ( message ) ;
}
} ) ;
} ,
sendContactAddIQ : function ( jid , name , groups , callback , errback ) {
/ * S e n d a n I Q s t a n z a t o t h e X M P P s e r v e r t o a d d a n e w r o s t e r c o n t a c t .
2016-03-01 22:57:27 +01:00
*
* Parameters :
* ( String ) jid - The Jabber ID of the user being added
* ( String ) name - The name of that user
* ( Array of Strings ) groups - Any roster groups the user might belong to
2016-03-16 12:16:32 +01:00
* ( Function ) callback - A function to call once the IQ is returned
2016-03-01 22:57:27 +01:00
* ( Function ) errback - A function to call if an error occured
* /
2016-02-29 22:18:28 +01:00
name = _ . isEmpty ( name ) ? jid : name ;
var iq = $iq ( { type : 'set' } )
. c ( 'query' , { xmlns : Strophe . NS . ROSTER } )
. c ( 'item' , { jid : jid , name : name } ) ;
_ . map ( groups , function ( group ) { iq . c ( 'group' ) . t ( group ) . up ( ) ; } ) ;
converse . connection . sendIQ ( iq , callback , errback ) ;
} ,
addContact : function ( jid , name , groups , attributes ) {
/ * A d d s a R o s t e r C o n t a c t i n s t a n c e t o c o n v e r s e . r o s t e r a n d
2016-03-01 22:57:27 +01:00
* registers the contact on the XMPP server .
* Returns a promise which is resolved once the XMPP server has
* responded .
*
* Parameters :
* ( String ) jid - The Jabber ID of the user being added and subscribed to .
* ( String ) name - The name of that user
* ( Array of Strings ) groups - Any roster groups the user might belong to
* ( Object ) attributes - Any additional attributes to be stored on the user ' s model .
* /
2016-02-29 22:18:28 +01:00
var deferred = new $ . Deferred ( ) ;
groups = groups || [ ] ;
name = _ . isEmpty ( name ) ? jid : name ;
this . sendContactAddIQ ( jid , name , groups ,
function ( iq ) {
var contact = this . create ( _ . extend ( {
ask : undefined ,
fullname : name ,
groups : groups ,
jid : jid ,
requesting : false ,
subscription : 'none'
} , attributes ) , { sort : false } ) ;
deferred . resolve ( contact ) ;
} . bind ( this ) ,
function ( err ) {
alert ( _ _ ( "Sorry, there was an error while trying to add " + name + " as a contact." ) ) ;
converse . log ( err ) ;
deferred . resolve ( err ) ;
}
) ;
return deferred . promise ( ) ;
} ,
addResource : function ( bare _jid , resource ) {
var item = this . get ( bare _jid ) ,
resources ;
if ( item ) {
resources = item . get ( 'resources' ) ;
if ( resources ) {
if ( _ . indexOf ( resources , resource ) === - 1 ) {
resources . push ( resource ) ;
item . set ( { 'resources' : resources } ) ;
}
} else {
item . set ( { 'resources' : [ resource ] } ) ;
}
}
} ,
subscribeBack : function ( bare _jid ) {
var contact = this . get ( bare _jid ) ;
if ( contact instanceof converse . RosterContact ) {
contact . authorize ( ) . subscribe ( ) ;
} else {
// Can happen when a subscription is retried or roster was deleted
this . addContact ( bare _jid , '' , [ ] , { 'subscription' : 'from' } ) . done ( function ( contact ) {
if ( contact instanceof converse . RosterContact ) {
contact . authorize ( ) . subscribe ( ) ;
}
} ) ;
}
} ,
getNumOnlineContacts : function ( ) {
var count = 0 ,
ignored = [ 'offline' , 'unavailable' ] ,
models = this . models ,
models _length = models . length ,
i ;
if ( converse . show _only _online _users ) {
ignored = _ . union ( ignored , [ 'dnd' , 'xa' , 'away' ] ) ;
}
for ( i = 0 ; i < models _length ; i ++ ) {
if ( _ . indexOf ( ignored , models [ i ] . get ( 'chat_status' ) ) === - 1 ) {
count ++ ;
}
}
return count ;
} ,
onRosterPush : function ( iq ) {
/ * H a n d l e r o s t e r u p d a t e s f r o m t h e X M P P s e r v e r .
2016-03-18 09:49:32 +01:00
* See : https : //xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
*
* Parameters :
* ( XMLElement ) IQ - The IQ stanza received from the XMPP server .
* /
2016-02-29 22:18:28 +01:00
var id = iq . getAttribute ( 'id' ) ;
var from = iq . getAttribute ( 'from' ) ;
if ( from && from !== "" && Strophe . getBareJidFromJid ( from ) !== converse . bare _jid ) {
// Receiving client MUST ignore stanza unless it has no from or from = user's bare JID.
// XXX: Some naughty servers apparently send from a full
// JID so we need to explicitly compare bare jids here.
// https://github.com/jcbrand/converse.js/issues/493
converse . connection . send (
$iq ( { type : 'error' , id : id , from : converse . connection . jid } )
. c ( 'error' , { 'type' : 'cancel' } )
. c ( 'service-unavailable' , { 'xmlns' : Strophe . NS . ROSTER } )
) ;
return true ;
}
converse . connection . send ( $iq ( { type : 'result' , id : id , from : converse . connection . jid } ) ) ;
$ ( iq ) . children ( 'query' ) . find ( 'item' ) . each ( function ( idx , item ) {
this . updateContact ( item ) ;
} . bind ( this ) ) ;
converse . emit ( 'rosterPush' , iq ) ;
return true ;
} ,
fetchFromServer : function ( callback ) {
/* Get the roster from the XMPP server */
var iq = $iq ( { type : 'get' , 'id' : converse . connection . getUniqueId ( 'roster' ) } )
. c ( 'query' , { xmlns : Strophe . NS . ROSTER } ) ;
return converse . connection . sendIQ ( iq , function ( ) {
this . onReceivedFromServer . apply ( this , arguments ) ;
callback . apply ( this , arguments ) ;
} . bind ( this ) ) ;
} ,
onReceivedFromServer : function ( iq ) {
/ * A n I Q s t a n z a c o n t a i n i n g t h e r o s t e r h a s b e e n r e c e i v e d f r o m
2016-03-18 09:49:32 +01:00
* the XMPP server .
* /
2016-02-29 22:18:28 +01:00
$ ( iq ) . children ( 'query' ) . find ( 'item' ) . each ( function ( idx , item ) {
this . updateContact ( item ) ;
} . bind ( this ) ) ;
2016-08-31 11:59:54 +02:00
converse . emit ( 'roster' , iq ) ;
2016-02-29 22:18:28 +01:00
} ,
updateContact : function ( item ) {
/ * U p d a t e o r c r e a t e R o s t e r C o n t a c t m o d e l s b a s e d o n i t e m s
2016-03-18 09:49:32 +01:00
* received in the IQ from the server .
* /
2016-02-29 22:18:28 +01:00
var jid = item . getAttribute ( 'jid' ) ;
if ( this . isSelf ( jid ) ) { return ; }
var groups = [ ] ,
contact = this . get ( jid ) ,
ask = item . getAttribute ( "ask" ) ,
subscription = item . getAttribute ( "subscription" ) ;
$ . map ( item . getElementsByTagName ( 'group' ) , function ( group ) {
groups . push ( Strophe . getText ( group ) ) ;
} ) ;
if ( ! contact ) {
if ( ( subscription === "none" && ask === null ) || ( subscription === "remove" ) ) {
return ; // We're lazy when adding contacts.
}
this . create ( {
ask : ask ,
fullname : item . getAttribute ( "name" ) || jid ,
groups : groups ,
jid : jid ,
subscription : subscription
} , { sort : false } ) ;
} else {
if ( subscription === "remove" ) {
return contact . destroy ( ) ; // will trigger removeFromRoster
}
// We only find out about requesting contacts via the
// presence handler, so if we receive a contact
// here, we know they aren't requesting anymore.
// see docs/DEVELOPER.rst
contact . save ( {
subscription : subscription ,
ask : ask ,
requesting : null ,
groups : groups
} ) ;
}
} ,
2016-03-16 12:16:32 +01:00
createRequestingContact : function ( presence ) {
/ * C r e a t e s a R e q u e s t i n g C o n t a c t .
*
* Note : this method gets completely overridden by converse - vcard . js
2016-03-01 22:57:27 +01:00
* /
2016-03-16 12:16:32 +01:00
var bare _jid = Strophe . getBareJidFromJid ( presence . getAttribute ( 'from' ) ) ;
var nick = $ ( presence ) . children ( 'nick[xmlns=' + Strophe . NS . NICK + ']' ) . text ( ) ;
2016-03-01 22:57:27 +01:00
var user _data = {
2016-02-29 22:18:28 +01:00
jid : bare _jid ,
subscription : 'none' ,
ask : null ,
requesting : true ,
2016-03-16 12:16:32 +01:00
fullname : nick || bare _jid ,
2016-03-01 22:57:27 +01:00
} ;
this . create ( user _data ) ;
converse . emit ( 'contactRequest' , user _data ) ;
2016-02-29 22:18:28 +01:00
} ,
2016-03-16 12:16:32 +01:00
handleIncomingSubscription : function ( presence ) {
var jid = presence . getAttribute ( 'from' ) ;
2016-02-29 22:18:28 +01:00
var bare _jid = Strophe . getBareJidFromJid ( jid ) ;
var contact = this . get ( bare _jid ) ;
if ( ! converse . allow _contact _requests ) {
2016-03-16 12:16:32 +01:00
converse . rejectPresenceSubscription (
jid ,
_ _ ( "This client does not allow presence subscriptions" )
) ;
2016-02-29 22:18:28 +01:00
}
if ( converse . auto _subscribe ) {
if ( ( ! contact ) || ( contact . get ( 'subscription' ) !== 'to' ) ) {
this . subscribeBack ( bare _jid ) ;
} else {
contact . authorize ( ) ;
}
} else {
if ( contact ) {
if ( contact . get ( 'subscription' ) !== 'none' ) {
contact . authorize ( ) ;
} else if ( contact . get ( 'ask' ) === "subscribe" ) {
contact . authorize ( ) ;
}
} else if ( ! contact ) {
2016-03-16 12:16:32 +01:00
this . createRequestingContact ( presence ) ;
2016-02-29 22:18:28 +01:00
}
}
} ,
presenceHandler : function ( presence ) {
var $presence = $ ( presence ) ,
presence _type = presence . getAttribute ( 'type' ) ;
if ( presence _type === 'error' ) { return true ; }
var jid = presence . getAttribute ( 'from' ) ,
bare _jid = Strophe . getBareJidFromJid ( jid ) ,
resource = Strophe . getResourceFromJid ( jid ) ,
chat _status = $presence . find ( 'show' ) . text ( ) || 'online' ,
status _message = $presence . find ( 'status' ) ,
contact = this . get ( bare _jid ) ;
2016-12-05 11:50:01 +01:00
2016-02-29 22:18:28 +01:00
if ( this . isSelf ( bare _jid ) ) {
2016-03-16 16:49:44 +01:00
if ( ( converse . connection . jid !== jid ) &&
( presence _type !== 'unavailable' ) &&
( converse . synchronize _availability === true ||
converse . synchronize _availability === resource ) ) {
// Another resource has changed its status and
// synchronize_availability option set to update,
// we'll update ours as well.
2016-02-29 22:18:28 +01:00
converse . xmppstatus . save ( { 'status' : chat _status } ) ;
2016-03-16 16:49:44 +01:00
if ( status _message . length ) {
converse . xmppstatus . save ( {
'status_message' : status _message . text ( )
} ) ;
}
2016-02-29 22:18:28 +01:00
}
return ;
} else if ( ( $presence . find ( 'x' ) . attr ( 'xmlns' ) || '' ) . indexOf ( Strophe . NS . MUC ) === 0 ) {
return ; // Ignore MUC
}
if ( contact && ( status _message . text ( ) !== contact . get ( 'status' ) ) ) {
contact . save ( { 'status' : status _message . text ( ) } ) ;
}
if ( presence _type === 'subscribed' && contact ) {
contact . ackSubscribe ( ) ;
} else if ( presence _type === 'unsubscribed' && contact ) {
contact . ackUnsubscribe ( ) ;
} else if ( presence _type === 'unsubscribe' ) {
return ;
} else if ( presence _type === 'subscribe' ) {
2016-03-16 12:16:32 +01:00
this . handleIncomingSubscription ( presence ) ;
2016-02-29 22:18:28 +01:00
} else if ( presence _type === 'unavailable' && contact ) {
// Only set the user to offline if there aren't any
// other resources still available.
if ( contact . removeResource ( resource ) === 0 ) {
contact . save ( { 'chat_status' : "offline" } ) ;
}
} else if ( contact ) { // presence_type is undefined
this . addResource ( bare _jid , resource ) ;
contact . save ( { 'chat_status' : chat _status } ) ;
}
}
} ) ;
2016-06-21 17:06:51 +02:00
this . RosterGroup = Backbone . Model . extend ( {
initialize : function ( attributes , options ) {
this . set ( _ . extend ( {
description : DESC _GROUP _TOGGLE ,
state : converse . OPENED
} , attributes ) ) ;
// Collection of contacts belonging to this group.
this . contacts = new converse . RosterContacts ( ) ;
}
} ) ;
this . RosterGroups = Backbone . Collection . extend ( {
model : converse . RosterGroup ,
2016-09-20 12:10:28 +02:00
fetchRosterGroups : function ( ) {
/ * F e t c h e s a l l t h e r o s t e r g r o u p s f r o m s e s s i o n S t o r a g e .
*
* Returns a promise which resolves once the groups have been
* returned .
* /
var deferred = new $ . Deferred ( ) ;
this . fetch ( {
silent : true , // We need to first have all groups before
// we can start positioning them, so we set
// 'silent' to true.
success : deferred . resolve
} ) ;
return deferred . promise ( ) ;
}
2016-06-21 17:06:51 +02:00
} ) ;
2016-02-16 08:46:47 +01:00
this . Message = Backbone . Model . extend ( {
defaults : function ( ) {
return {
msgid : converse . connection . getUniqueId ( )
} ;
}
} ) ;
2016-02-29 22:18:28 +01:00
2016-02-16 08:46:47 +01:00
this . Messages = Backbone . Collection . extend ( {
model : converse . Message ,
comparator : 'time'
} ) ;
2016-02-29 22:18:28 +01:00
2016-02-16 08:46:47 +01:00
this . ChatBox = Backbone . Model . extend ( {
initialize : function ( ) {
2016-02-28 20:24:06 +01:00
this . messages = new converse . Messages ( ) ;
2016-08-21 12:49:54 +02:00
this . messages . browserStorage = new Backbone . BrowserStorage [ converse . message _storage ] (
2016-02-28 20:24:06 +01:00
b64 _sha1 ( 'converse.messages' + this . get ( 'jid' ) + converse . bare _jid ) ) ;
2016-04-01 14:46:19 +02:00
this . save ( {
2016-02-28 20:24:06 +01:00
// The chat_state will be set to ACTIVE once the chat box is opened
// and we listen for change:chat_state, so shouldn't set it to ACTIVE here.
'box_id' : b64 _sha1 ( this . get ( 'jid' ) ) ,
2016-04-01 14:46:19 +02:00
'chat_state' : undefined ,
'num_unread' : this . get ( 'num_unread' ) || 0 ,
2016-02-28 20:24:06 +01:00
'time_opened' : this . get ( 'time_opened' ) || moment ( ) . valueOf ( ) ,
'url' : '' ,
'user_id' : Strophe . getNodeFromJid ( this . get ( 'jid' ) )
2016-04-01 14:46:19 +02:00
} ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-05-30 19:07:59 +02:00
getMessageAttributes : function ( $message , $delay , original _stanza ) {
2016-02-16 08:46:47 +01:00
$delay = $delay || $message . find ( 'delay' ) ;
2016-06-22 18:47:07 +02:00
var type = $message . attr ( 'type' ) ,
body , stamp , time , sender , from ;
if ( type === 'error' ) {
body = $message . find ( 'error' ) . children ( 'text' ) . text ( ) ;
} else {
body = $message . children ( 'body' ) . text ( ) ;
}
var delayed = $delay . length > 0 ,
2016-02-16 08:46:47 +01:00
fullname = this . get ( 'fullname' ) ,
2016-06-22 18:47:07 +02:00
is _groupchat = type === 'groupchat' ,
2016-03-01 09:58:36 +01:00
chat _state = $message . find ( converse . COMPOSING ) . length && converse . COMPOSING ||
$message . find ( converse . PAUSED ) . length && converse . PAUSED ||
$message . find ( converse . INACTIVE ) . length && converse . INACTIVE ||
$message . find ( converse . ACTIVE ) . length && converse . ACTIVE ||
2016-06-22 18:47:07 +02:00
$message . find ( converse . GONE ) . length && converse . GONE ;
2016-02-16 08:46:47 +01:00
if ( is _groupchat ) {
from = Strophe . unescapeNode ( Strophe . getResourceFromJid ( $message . attr ( 'from' ) ) ) ;
} else {
from = Strophe . getBareJidFromJid ( $message . attr ( 'from' ) ) ;
}
2016-03-20 01:47:51 +01:00
if ( _ . isEmpty ( fullname ) ) {
fullname = from ;
}
2016-02-16 08:46:47 +01:00
if ( delayed ) {
stamp = $delay . attr ( 'stamp' ) ;
time = stamp ;
} else {
time = moment ( ) . format ( ) ;
}
if ( ( is _groupchat && from === this . get ( 'nick' ) ) || ( ! is _groupchat && from === converse . bare _jid ) ) {
sender = 'me' ;
} else {
sender = 'them' ;
}
2016-05-30 19:07:59 +02:00
return {
2016-06-22 18:47:07 +02:00
'type' : type ,
'chat_state' : chat _state ,
'delayed' : delayed ,
'fullname' : fullname ,
'message' : body || undefined ,
'msgid' : $message . attr ( 'id' ) ,
'sender' : sender ,
'time' : time
2016-05-30 19:07:59 +02:00
} ;
} ,
createMessage : function ( $message , $delay , original _stanza ) {
return this . messages . create ( this . getMessageAttributes . apply ( this , arguments ) ) ;
2016-02-16 08:46:47 +01:00
}
} ) ;
2016-05-25 09:52:57 +02:00
2016-02-16 08:46:47 +01:00
this . ChatBoxes = Backbone . Collection . extend ( {
model : converse . ChatBox ,
comparator : 'time_opened' ,
registerMessageHandler : function ( ) {
2016-06-22 18:47:07 +02:00
converse . connection . addHandler ( this . onMessage . bind ( this ) , null , 'message' , 'chat' ) ;
converse . connection . addHandler ( this . onErrorMessage . bind ( this ) , null , 'message' , 'error' ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-04-05 13:23:16 +02:00
chatBoxMayBeShown : function ( chatbox ) {
2016-03-14 17:04:27 +01:00
return true ;
} ,
onChatBoxesFetched : function ( collection ) {
2016-02-28 20:24:06 +01:00
/ * S h o w c h a t b o x e s u p o n r e c e i v i n g t h e m f r o m s e s s i o n S t o r a g e
*
* This method gets overridden entirely in src / converse - controlbox . js
* if the controlbox plugin is active .
* /
2016-11-02 13:44:56 +01:00
var that = this ;
2016-02-28 20:24:06 +01:00
collection . each ( function ( chatbox ) {
2016-11-02 13:44:56 +01:00
if ( that . chatBoxMayBeShown ( chatbox ) ) {
2016-02-28 20:24:06 +01:00
chatbox . trigger ( 'show' ) ;
}
2016-11-02 13:44:56 +01:00
} ) ;
2016-04-05 13:23:16 +02:00
converse . emit ( 'chatBoxesFetched' ) ;
2016-02-28 20:24:06 +01:00
} ,
2016-02-16 08:46:47 +01:00
onConnected : function ( ) {
this . browserStorage = new Backbone . BrowserStorage [ converse . storage ] (
b64 _sha1 ( 'converse.chatboxes-' + converse . bare _jid ) ) ;
this . registerMessageHandler ( ) ;
this . fetch ( {
add : true ,
2016-03-09 11:54:50 +01:00
success : this . onChatBoxesFetched . bind ( this )
2016-02-16 08:46:47 +01:00
} ) ;
} ,
2016-06-22 18:47:07 +02:00
onErrorMessage : function ( message ) {
/ * H a n d l e r m e t h o d f o r a l l i n c o m i n g e r r o r m e s s a g e s t a n z a s
* /
// TODO: we can likely just reuse "onMessage" below
var $message = $ ( message ) ,
from _jid = Strophe . getBareJidFromJid ( $message . attr ( 'from' ) ) ;
if ( from _jid === converse . bare _jid ) {
return true ;
}
// Get chat box, but only create a new one when the message has a body.
var chatbox = this . getChatBox ( from _jid ) ;
if ( ! chatbox ) {
return true ;
}
chatbox . createMessage ( $message , null , message ) ;
return true ;
} ,
2016-02-16 08:46:47 +01:00
onMessage : function ( message ) {
2016-03-28 12:49:52 +02:00
/ * H a n d l e r m e t h o d f o r a l l i n c o m i n g s i n g l e - u s e r c h a t " m e s s a g e "
* stanzas .
2016-02-16 08:46:47 +01:00
* /
var $message = $ ( message ) ,
2016-03-28 12:49:52 +02:00
contact _jid , $forwarded , $delay , from _bare _jid ,
from _resource , is _me , msgid ,
2016-02-16 08:46:47 +01:00
chatbox , resource ,
from _jid = $message . attr ( 'from' ) ,
to _jid = $message . attr ( 'to' ) ,
2016-03-13 18:41:16 +01:00
to _resource = Strophe . getResourceFromJid ( to _jid ) ;
2016-02-16 08:46:47 +01:00
2016-05-28 08:35:16 +02:00
if ( converse . filter _by _resource && ( to _resource && to _resource !== converse . resource ) ) {
2016-03-28 12:49:52 +02:00
converse . log (
'onMessage: Ignoring incoming message intended for a different resource: ' + to _jid ,
'info'
) ;
2016-02-16 08:46:47 +01:00
return true ;
2016-03-28 12:49:52 +02:00
} 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'
) ;
2016-02-16 08:46:47 +01:00
return true ;
}
$forwarded = $message . find ( 'forwarded' ) ;
if ( $forwarded . length ) {
$message = $forwarded . children ( 'message' ) ;
$delay = $forwarded . children ( 'delay' ) ;
from _jid = $message . attr ( 'from' ) ;
to _jid = $message . attr ( 'to' ) ;
}
from _bare _jid = Strophe . getBareJidFromJid ( from _jid ) ;
from _resource = Strophe . getResourceFromJid ( from _jid ) ;
is _me = from _bare _jid === converse . bare _jid ;
msgid = $message . attr ( 'id' ) ;
if ( is _me ) {
// I am the sender, so this must be a forwarded message...
contact _jid = Strophe . getBareJidFromJid ( to _jid ) ;
resource = Strophe . getResourceFromJid ( to _jid ) ;
} else {
contact _jid = from _bare _jid ;
resource = from _resource ;
}
2016-08-12 22:38:39 +02:00
converse . emit ( 'message' , message ) ;
2016-02-16 08:46:47 +01:00
// Get chat box, but only create a new one when the message has a body.
chatbox = this . getChatBox ( contact _jid , $message . find ( 'body' ) . length > 0 ) ;
if ( ! chatbox ) {
return true ;
}
if ( msgid && chatbox . messages . findWhere ( { msgid : msgid } ) ) {
return true ; // We already have this message stored.
}
2016-05-30 18:17:26 +02:00
chatbox . createMessage ( $message , $delay , message ) ;
2016-02-16 08:46:47 +01:00
return true ;
} ,
2016-08-19 17:16:36 +02:00
getChatBox : function ( jid , create , attrs ) {
2016-02-16 08:46:47 +01:00
/ * R e t u r n s a c h a t b o x o r o p t i o n a l l y r e t u r n a n e w l y
* created one if one doesn ' t exist .
*
* Parameters :
* ( String ) jid - The JID of the user whose chat box we want
* ( Boolean ) create - Should a new chat box be created if none exists ?
* /
jid = jid . toLowerCase ( ) ;
var bare _jid = Strophe . getBareJidFromJid ( jid ) ;
var chatbox = this . get ( bare _jid ) ;
if ( ! chatbox && create ) {
var roster _item = converse . roster . get ( bare _jid ) ;
if ( roster _item === undefined ) {
converse . log ( 'Could not get roster item for JID ' + bare _jid , 'error' ) ;
return ;
}
2016-08-19 17:16:36 +02:00
chatbox = this . create ( _ . extend ( {
2016-02-16 08:46:47 +01:00
'id' : bare _jid ,
'jid' : bare _jid ,
'fullname' : _ . isEmpty ( roster _item . get ( 'fullname' ) ) ? jid : roster _item . get ( 'fullname' ) ,
'image_type' : roster _item . get ( 'image_type' ) ,
'image' : roster _item . get ( 'image' ) ,
'url' : roster _item . get ( 'url' )
2016-08-19 17:16:36 +02:00
} , attrs || { } ) ) ;
2016-02-16 08:46:47 +01:00
}
return chatbox ;
}
} ) ;
this . ChatBoxViews = Backbone . Overview . extend ( {
initialize : function ( ) {
this . model . on ( "add" , this . onChatBoxAdded , this ) ;
2016-03-28 13:42:33 +02:00
this . model . on ( "destroy" , this . removeChat , this ) ;
2016-02-16 08:46:47 +01:00
} ,
_ensureElement : function ( ) {
/ * O v e r r i d e m e t h o d f r o m b a c k b o n e . j s
* If the # conversejs element doesn ' t exist , create it .
* /
if ( ! this . el ) {
var $el = $ ( '#conversejs' ) ;
if ( ! $el . length ) {
$el = $ ( '<div id="conversejs">' ) ;
$ ( 'body' ) . append ( $el ) ;
}
2016-11-22 17:36:39 +01:00
$el . html ( '' ) ;
2016-02-16 08:46:47 +01:00
this . setElement ( $el , false ) ;
} else {
this . setElement ( _ . result ( this , 'el' ) , false ) ;
}
} ,
onChatBoxAdded : function ( item ) {
2016-03-29 13:50:50 +02:00
// Views aren't created here, since the core code doesn't
2016-03-28 13:42:33 +02:00
// contain any views. Instead, they're created in overrides in
2016-03-29 13:50:50 +02:00
// plugins, such as in converse-chatview.js and converse-muc.js
return this . get ( item . get ( 'id' ) ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-03-28 13:42:33 +02:00
removeChat : function ( item ) {
this . remove ( item . get ( 'id' ) ) ;
} ,
2016-02-28 20:24:06 +01:00
closeAllChatBoxes : function ( ) {
/ * T h i s m e t h o d g e t s o v e r r i d d e n i n s r c / c o n v e r s e - c o n t r o l b o x . j s i f
* the controlbox plugin is active .
* /
this . each ( function ( view ) { view . close ( ) ; } ) ;
2016-02-16 08:46:47 +01:00
return this ;
} ,
2016-04-05 13:23:16 +02:00
chatBoxMayBeShown : function ( chatbox ) {
return this . model . chatBoxMayBeShown ( chatbox ) ;
} ,
2016-06-17 10:37:40 +02:00
getChatBox : function ( attrs , create ) {
2016-02-16 08:46:47 +01:00
var chatbox = this . model . get ( attrs . jid ) ;
2016-06-17 10:37:40 +02:00
if ( ! chatbox && create ) {
2016-02-16 08:46:47 +01:00
chatbox = this . model . create ( attrs , {
'error' : function ( model , response ) {
converse . log ( response . responseText ) ;
}
} ) ;
}
2016-06-06 09:49:44 +02:00
return chatbox ;
} ,
showChat : function ( attrs ) {
/ * F i n d t h e c h a t b o x a n d s h o w i t ( i f i t m a y b e s h o w n ) .
* If it doesn ' t exist , create it .
* /
2016-06-17 10:37:40 +02:00
var chatbox = this . getChatBox ( attrs , true ) ;
2016-04-05 13:23:16 +02:00
if ( this . chatBoxMayBeShown ( chatbox ) ) {
chatbox . trigger ( 'show' , true ) ;
}
2016-02-16 08:46:47 +01:00
return chatbox ;
}
} ) ;
2016-02-28 20:24:06 +01:00
this . XMPPStatus = Backbone . Model . extend ( {
initialize : function ( ) {
this . set ( {
'status' : this . getStatus ( )
} ) ;
this . on ( 'change' , function ( item ) {
if ( _ . has ( item . changed , 'status' ) ) {
converse . emit ( 'statusChanged' , this . get ( 'status' ) ) ;
}
if ( _ . has ( item . changed , 'status_message' ) ) {
converse . emit ( 'statusMessageChanged' , this . get ( 'status_message' ) ) ;
}
} . bind ( this ) ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-02-28 20:24:06 +01:00
constructPresence : function ( type , status _message ) {
var presence ;
2016-05-31 11:39:14 +02:00
type = typeof type === 'string' ? type : ( this . get ( 'status' ) || converse . default _state ) ;
status _message = typeof status _message === 'string' ? status _message : undefined ;
2016-02-28 20:24:06 +01:00
// Most of these presence types are actually not explicitly sent,
2016-05-31 11:39:14 +02:00
// but I add all of them here for reference and future proofing.
2016-02-28 20:24:06 +01:00
if ( ( type === 'unavailable' ) ||
( type === 'probe' ) ||
( type === 'error' ) ||
( type === 'unsubscribe' ) ||
( type === 'unsubscribed' ) ||
( type === 'subscribe' ) ||
( type === 'subscribed' ) ) {
presence = $pres ( { 'type' : type } ) ;
} else if ( type === 'offline' ) {
presence = $pres ( { 'type' : 'unavailable' } ) ;
2016-05-31 11:39:14 +02:00
} else if ( type === 'online' ) {
presence = $pres ( ) ;
2016-02-28 20:24:06 +01:00
} else {
2016-05-31 11:39:14 +02:00
presence = $pres ( ) . c ( 'show' ) . t ( type ) . up ( ) ;
}
if ( status _message ) {
presence . c ( 'status' ) . t ( status _message ) ;
2016-02-28 20:24:06 +01:00
}
return presence ;
2016-02-16 08:46:47 +01:00
} ,
2016-02-28 20:24:06 +01:00
sendPresence : function ( type , status _message ) {
converse . connection . send ( this . constructPresence ( type , status _message ) ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-02-28 20:24:06 +01:00
setStatus : function ( value ) {
this . sendPresence ( value ) ;
this . save ( { 'status' : value } ) ;
2016-02-16 08:46:47 +01:00
} ,
2016-02-28 20:24:06 +01:00
getStatus : function ( ) {
2016-05-31 10:03:06 +02:00
return this . get ( 'status' ) || converse . default _state ;
2016-02-16 08:46:47 +01:00
} ,
setStatusMessage : function ( status _message ) {
this . sendPresence ( this . getStatus ( ) , status _message ) ;
var prev _status = this . get ( 'status_message' ) ;
this . save ( { 'status_message' : status _message } ) ;
if ( this . xhr _custom _status ) {
$ . ajax ( {
url : this . xhr _custom _status _url ,
type : 'POST' ,
data : { 'msg' : status _message }
} ) ;
}
if ( prev _status === status _message ) {
this . trigger ( "update-status-ui" , this ) ;
}
}
} ) ;
this . Session = Backbone . Model ; // General session settings to be saved to sessionStorage.
this . Feature = Backbone . Model ;
this . Features = Backbone . Collection . extend ( {
/ * S e r v i c e D i s c o v e r y
2016-03-18 09:49:32 +01:00
* -- -- -- -- -- -- -- -- -
* This collection stores Feature Models , representing features
* provided by available XMPP entities ( e . g . servers )
* See XEP - 0030 for more details : http : //xmpp.org/extensions/xep-0030.html
* All features are shown here : http : //xmpp.org/registrar/disco-features.html
* /
2016-02-16 08:46:47 +01:00
model : converse . Feature ,
initialize : function ( ) {
this . addClientIdentities ( ) . addClientFeatures ( ) ;
this . browserStorage = new Backbone . BrowserStorage [ converse . storage ] (
2016-09-22 14:03:42 +02:00
b64 _sha1 ( 'converse.features' + converse . bare _jid )
) ;
2016-02-16 08:46:47 +01:00
this . on ( 'add' , this . onFeatureAdded , this ) ;
if ( this . browserStorage . records . length === 0 ) {
// browserStorage is empty, so we've likely never queried this
// domain for features yet
converse . connection . disco . info ( converse . domain , null , this . onInfo . bind ( this ) ) ;
converse . connection . disco . items ( converse . domain , null , this . onItems . bind ( this ) ) ;
} else {
this . fetch ( { add : true } ) ;
}
} ,
onFeatureAdded : function ( feature ) {
converse . emit ( 'serviceDiscovered' , feature ) ;
} ,
addClientIdentities : function ( ) {
/* See http:/ / xmpp . org / registrar / disco - categories . html
* /
converse . connection . disco . addIdentity ( 'client' , 'web' , 'Converse.js' ) ;
return this ;
} ,
addClientFeatures : function ( ) {
/ * T h e s t r o p h e . d i s c o . j s p l u g i n k e e p s a l i s t o f f e a t u r e s w h i c h
* it will advertise to any # info queries made to it .
*
* See : http : //xmpp.org/extensions/xep-0030.html#info
* /
converse . connection . disco . addFeature ( Strophe . NS . BOSH ) ;
converse . connection . disco . addFeature ( Strophe . NS . CHATSTATES ) ;
converse . connection . disco . addFeature ( Strophe . NS . DISCO _INFO ) ;
converse . connection . disco . addFeature ( Strophe . NS . ROSTERX ) ; // Limited support
if ( converse . message _carbons ) {
converse . connection . disco . addFeature ( Strophe . NS . CARBONS ) ;
}
return this ;
} ,
onItems : function ( stanza ) {
$ ( stanza ) . find ( 'query item' ) . each ( function ( idx , item ) {
converse . connection . disco . info (
$ ( item ) . attr ( 'jid' ) ,
null ,
this . onInfo . bind ( this ) ) ;
} . bind ( this ) ) ;
} ,
onInfo : function ( stanza ) {
var $stanza = $ ( stanza ) ;
if ( ( $stanza . find ( 'identity[category=server][type=im]' ) . length === 0 ) &&
( $stanza . find ( 'identity[category=conference][type=text]' ) . length === 0 ) ) {
// This isn't an IM server component
return ;
}
$stanza . find ( 'feature' ) . each ( function ( idx , feature ) {
var namespace = $ ( feature ) . attr ( 'var' ) ;
this [ namespace ] = true ;
this . create ( {
'var' : namespace ,
'from' : $stanza . attr ( 'from' )
} ) ;
} . bind ( this ) ) ;
}
} ) ;
this . setUpXMLLogging = function ( ) {
2016-05-28 10:55:03 +02:00
Strophe . log = function ( level , msg ) {
converse . log ( msg , level ) ;
} ;
2016-02-16 08:46:47 +01:00
if ( this . debug ) {
2016-04-28 16:52:27 +02:00
this . connection . xmlInput = function ( body ) { converse . log ( body . outerHTML ) ; } ;
this . connection . xmlOutput = function ( body ) { converse . log ( body . outerHTML ) ; } ;
2016-02-16 08:46:47 +01:00
}
} ;
2016-03-31 10:37:28 +02:00
this . fetchLoginCredentials = function ( ) {
var deferred = new $ . Deferred ( ) ;
$ . ajax ( {
url : converse . credentials _url ,
type : 'GET' ,
dataType : "json" ,
success : function ( response ) {
deferred . resolve ( {
'jid' : response . jid ,
'password' : response . password
} ) ;
} ,
error : function ( response ) {
delete converse . connection ;
converse . emit ( 'noResumeableSession' ) ;
deferred . reject ( response ) ;
}
} ) ;
return deferred . promise ( ) ;
} ;
2016-02-16 08:46:47 +01:00
this . startNewBOSHSession = function ( ) {
2016-11-07 18:30:10 +01:00
var that = this ;
2016-02-16 08:46:47 +01:00
$ . ajax ( {
url : this . prebind _url ,
type : 'GET' ,
2016-03-16 13:56:11 +01:00
dataType : "json" ,
2016-02-16 08:46:47 +01:00
success : function ( response ) {
2016-11-07 18:30:10 +01:00
that . connection . attach (
2016-02-16 08:46:47 +01:00
response . jid ,
response . sid ,
response . rid ,
2016-11-07 18:30:10 +01:00
that . onConnectStatusChanged
2016-02-16 08:46:47 +01:00
) ;
2016-11-07 18:30:10 +01:00
} ,
2016-02-16 08:46:47 +01:00
error : function ( response ) {
2016-11-07 18:30:10 +01:00
delete that . connection ;
that . emit ( 'noResumeableSession' ) ;
}
2016-02-16 08:46:47 +01:00
} ) ;
} ;
2016-11-23 12:53:06 +01:00
this . attemptPreboundSession = function ( reconnecting ) {
2016-02-16 08:46:47 +01:00
/ * H a n d l e s e s s i o n r e s u m p t i o n o r i n i t i a l i z a t i o n w h e n p r e b i n d i s b e i n g u s e d .
* /
2016-11-23 12:53:06 +01:00
if ( ! reconnecting && this . keepalive ) {
2016-02-16 08:46:47 +01:00
if ( ! this . jid ) {
2016-04-13 17:11:04 +02:00
throw new Error ( "attemptPreboundSession: when using 'keepalive' with 'prebind, " +
"you must supply the JID of the current user." ) ;
2016-02-16 08:46:47 +01:00
}
try {
return this . connection . restore ( this . jid , this . onConnectStatusChanged ) ;
} catch ( e ) {
this . log ( "Could not restore session for jid: " + this . jid + " Error message: " + e . message ) ;
this . clearSession ( ) ; // If there's a roster, we want to clear it (see #555)
}
}
2016-11-07 18:30:10 +01:00
// No keepalive, or session resumption has failed.
2016-11-23 12:53:06 +01:00
if ( ! reconnecting && this . jid && this . sid && this . rid ) {
2016-11-07 18:30:10 +01:00
return this . connection . attach ( this . jid , this . sid , this . rid , this . onConnectStatusChanged ) ;
} else if ( this . prebind _url ) {
return this . startNewBOSHSession ( ) ;
2016-02-16 08:46:47 +01:00
} else {
2016-11-07 18:30:10 +01:00
throw new Error ( "attemptPreboundSession: If you use prebind and not keepalive, " +
"then you MUST supply JID, RID and SID values or a prebind_url." ) ;
2016-02-16 08:46:47 +01:00
}
} ;
2016-03-31 10:37:28 +02:00
this . autoLogin = function ( credentials ) {
if ( credentials ) {
2016-04-13 13:59:09 +02:00
// If passed in, then they come from credentials_url, so we
2016-03-31 10:37:28 +02:00
// set them on the converse object.
this . jid = credentials . jid ;
this . password = credentials . password ;
}
if ( this . authentication === converse . ANONYMOUS ) {
2016-07-24 10:54:57 +02:00
if ( ! this . jid ) {
throw new Error ( "Config Error: when using anonymous login " +
"you need to provide the server's domain via the 'jid' option. " +
"Either when calling converse.initialize, or when calling " +
"converse.user.login." ) ;
}
2016-03-31 10:37:28 +02:00
this . connection . connect ( this . jid . toLowerCase ( ) , null , this . onConnectStatusChanged ) ;
} else if ( this . authentication === converse . LOGIN ) {
2016-08-18 21:43:18 +02:00
var password = converse . connection . pass || this . password ;
if ( ! password ) {
2016-08-23 20:10:08 +02:00
if ( this . auto _login && ! this . password ) {
throw new Error ( "initConnection: If you use auto_login and " +
"authentication='login' then you also need to provide a password." ) ;
}
converse . disconnection _cause = Strophe . Status . AUTHFAIL ;
2016-12-04 16:00:46 +01:00
converse . disconnect ( ) ;
2016-10-27 12:58:51 +02:00
return ;
2016-03-31 10:37:28 +02:00
}
var resource = Strophe . getResourceFromJid ( this . jid ) ;
if ( ! resource ) {
this . jid = this . jid . toLowerCase ( ) + converse . generateResource ( ) ;
} else {
this . jid = Strophe . getBareJidFromJid ( this . jid ) . toLowerCase ( ) + '/' + resource ;
}
2016-08-18 21:43:18 +02:00
this . connection . connect ( this . jid , password , this . onConnectStatusChanged ) ;
2016-03-31 10:37:28 +02:00
}
} ;
2016-11-23 12:53:06 +01:00
this . attemptNonPreboundSession = function ( credentials , reconnecting ) {
2016-02-16 08:46:47 +01:00
/ * H a n d l e s e s s i o n r e s u m p t i o n o r i n i t i a l i z a t i o n w h e n p r e b i n d i s n o t b e i n g u s e d .
*
* Two potential options exist and are handled in this method :
* 1. keepalive
* 2. auto _login
* /
2016-11-23 12:53:06 +01:00
if ( this . keepalive && ! reconnecting ) {
2016-02-16 08:46:47 +01:00
try {
2016-05-31 12:24:36 +02:00
return this . connection . restore ( this . jid , this . onConnectStatusChanged ) ;
2016-02-16 08:46:47 +01:00
} catch ( e ) {
this . log ( "Could not restore session. Error message: " + e . message ) ;
this . clearSession ( ) ; // If there's a roster, we want to clear it (see #555)
}
}
if ( this . auto _login ) {
2016-11-07 18:30:10 +01:00
if ( credentials ) {
// When credentials are passed in, they override prebinding
// or credentials fetching via HTTP
this . autoLogin ( credentials ) ;
} else if ( this . credentials _url ) {
2016-03-31 10:37:28 +02:00
this . fetchLoginCredentials ( ) . done ( this . autoLogin . bind ( this ) ) ;
} else if ( ! this . jid ) {
throw new Error (
"initConnection: If you use auto_login, you also need" +
"to give either a jid value (and if applicable a " +
"password) or you need to pass in a URL from where the " +
"username and password can be fetched (via credentials_url)."
) ;
} else {
2016-11-07 18:30:10 +01:00
// Probably ANONYMOUS login
2016-03-31 10:37:28 +02:00
this . autoLogin ( ) ;
2016-02-16 08:46:47 +01:00
}
2016-12-04 15:14:40 +01:00
} else if ( reconnecting ) {
this . autoLogin ( ) ;
2016-02-16 08:46:47 +01:00
}
} ;
2016-11-23 12:53:06 +01:00
this . logIn = function ( credentials , reconnecting ) {
2016-11-07 18:30:10 +01:00
// We now try to resume or automatically set up a new session.
// Otherwise the user will be shown a login form.
if ( this . authentication === converse . PREBIND ) {
2016-11-23 12:53:06 +01:00
this . attemptPreboundSession ( reconnecting ) ;
2016-04-13 13:52:28 +02:00
} else {
2016-11-23 12:53:06 +01:00
this . attemptNonPreboundSession ( credentials , reconnecting ) ;
2016-04-13 13:52:28 +02:00
}
} ;
2016-02-16 08:46:47 +01:00
this . initConnection = function ( ) {
2016-04-13 17:11:04 +02:00
if ( this . connection ) {
return ;
}
if ( ! this . bosh _service _url && ! this . websocket _url ) {
throw new Error ( "initConnection: you must supply a value for either the bosh_service_url or websocket_url or both." ) ;
}
if ( ( 'WebSocket' in window || 'MozWebSocket' in window ) && this . websocket _url ) {
2016-10-12 13:55:47 +02:00
this . connection = new Strophe . Connection ( this . websocket _url , this . connection _options ) ;
2016-04-13 17:11:04 +02:00
} else if ( this . bosh _service _url ) {
2016-10-12 13:55:47 +02:00
this . connection = new Strophe . Connection (
this . bosh _service _url ,
_ . extend ( this . connection _options , { 'keepalive' : this . keepalive } )
) ;
2016-02-16 08:46:47 +01:00
} else {
2016-04-13 17:11:04 +02:00
throw new Error ( "initConnection: this browser does not support websockets and bosh_service_url wasn't specified." ) ;
2016-02-16 08:46:47 +01:00
}
} ;
this . _tearDown = function ( ) {
/ * R e m o v e t h o s e v i e w s w h i c h a r e o n l y a l l o w e d w i t h a v a l i d
* connection .
* /
2016-06-22 12:21:56 +02:00
this . unregisterPresenceHandler ( ) ;
2016-02-16 08:46:47 +01:00
if ( this . roster ) {
this . roster . off ( ) . reset ( ) ; // Removes roster contacts
}
this . chatboxes . remove ( ) ; // Don't call off(), events won't get re-registered upon reconnect.
if ( this . features ) {
this . features . reset ( ) ;
}
2016-04-08 10:29:30 +02:00
$ ( window ) . off ( 'click mousemove keypress focus' + unloadevent , converse . onUserActivity ) ;
window . clearInterval ( converse . everySecondTrigger ) ;
2016-02-16 08:46:47 +01:00
return this ;
} ;
2016-11-22 17:36:39 +01:00
this . initChatBoxes = function ( ) {
2016-02-16 08:46:47 +01:00
this . chatboxes = new this . ChatBoxes ( ) ;
this . chatboxviews = new this . ChatBoxViews ( { model : this . chatboxes } ) ;
2016-11-22 17:36:39 +01:00
} ;
this . _initialize = function ( ) {
this . initChatBoxes ( ) ;
2016-02-16 08:46:47 +01:00
this . initSession ( ) ;
this . initConnection ( ) ;
2016-04-13 17:11:04 +02:00
this . setUpXMLLogging ( ) ;
this . logIn ( ) ;
2016-02-16 08:46:47 +01:00
return this ;
} ;
// Initialization
// --------------
// This is the end of the initialize method.
if ( settings . connection ) {
this . connection = settings . connection ;
}
2016-06-09 11:01:54 +02:00
var updateSettings = function ( settings ) {
/ * H e l p e r m e t h o d w h i c h g e t s p u t o n t h e p l u g i n a n d a l l o w s i t t o
* add more user - facing config settings to converse . js .
* /
2016-10-27 13:30:58 +02:00
utils . merge ( converse . default _settings , settings ) ;
utils . merge ( converse , settings ) ;
2016-10-26 12:50:00 +02:00
utils . applyUserSettings ( converse , settings , converse . user _settings ) ;
2016-06-09 11:01:54 +02:00
} ;
2016-11-02 21:19:17 +01:00
// If initialize gets called a second time (e.g. during tests), then we
// need to re-apply all plugins (for a new converse instance), and we
// therefore need to clear this array that prevents plugins from being
// initialized twice.
// If initialize is called for the first time, then this array is empty
// in any case.
converse . pluggable . initialized _plugins = [ ] ;
2016-06-09 11:01:54 +02:00
converse . pluggable . initializePlugins ( {
'updateSettings' : updateSettings ,
'converse' : converse
} ) ;
2016-06-16 17:21:11 +02:00
converse . emit ( 'pluginsInitialized' ) ;
2016-06-10 00:05:55 +02:00
converse . _initialize ( ) ;
converse . registerGlobalEventHandlers ( ) ;
2016-11-02 23:08:20 +01:00
if ( ! _ . isUndefined ( converse . connection ) &&
converse . connection . service === 'jasmine tests' ) {
return converse ;
} else {
return init _deferred . promise ( ) ;
}
2016-02-16 08:46:47 +01:00
} ;
2016-02-20 16:06:12 +01:00
return converse ;
2016-02-16 08:46:47 +01:00
} ) ) ;