Move methods from chatbox view to message view
Specifically the methods related to requesting an upload slot and uploading a file. Also show a progress indicator while a file is being uploaded. Updates #161
This commit is contained in:
parent
db790183d8
commit
c01e9f8265
@ -1214,17 +1214,6 @@ the operating system or browser (which might not support emoji).
|
|||||||
|
|
||||||
See also `emojione_image_path`_.
|
See also `emojione_image_path`_.
|
||||||
|
|
||||||
|
|
||||||
show_message_load_animation
|
|
||||||
---------------------------
|
|
||||||
* Default: ``false``
|
|
||||||
|
|
||||||
Determines whether a CSS3 background-color fade-out animation is shown when messages
|
|
||||||
appear in chats.
|
|
||||||
|
|
||||||
Set to ``false`` by default since this option causes performance issues on Firefox.
|
|
||||||
|
|
||||||
|
|
||||||
show_only_online_users
|
show_only_online_users
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@
|
|||||||
expect(chatbox.messages.length).toEqual(1);
|
expect(chatbox.messages.length).toEqual(1);
|
||||||
var msg_obj = chatbox.messages.models[0];
|
var msg_obj = chatbox.messages.models[0];
|
||||||
expect(msg_obj.get('message')).toEqual(message);
|
expect(msg_obj.get('message')).toEqual(message);
|
||||||
expect(msg_obj.get('fullname')).toEqual(sender_jid);
|
expect(msg_obj.get('fullname')).toEqual(undefined);
|
||||||
expect(msg_obj.get('sender')).toEqual('them');
|
expect(msg_obj.get('sender')).toEqual('them');
|
||||||
expect(msg_obj.get('delayed')).toEqual(false);
|
expect(msg_obj.get('delayed')).toEqual(false);
|
||||||
// Now check that the message appears inside the chatbox in the DOM
|
// Now check that the message appears inside the chatbox in the DOM
|
||||||
@ -714,6 +714,7 @@
|
|||||||
expect(msg_txt).toEqual(message);
|
expect(msg_txt).toEqual(message);
|
||||||
var sender_txt = $chat_content.find('span.chat-msg-them').text();
|
var sender_txt = $chat_content.find('span.chat-msg-them').text();
|
||||||
expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
|
expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
|
||||||
|
expect(sender_txt.indexOf('max.frankfurter@localhost')).not.toBe(-1);
|
||||||
done();
|
done();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -862,10 +862,10 @@
|
|||||||
var message = '/me is tired';
|
var message = '/me is tired';
|
||||||
var nick = mock.chatroom_names[0],
|
var nick = mock.chatroom_names[0],
|
||||||
msg = $msg({
|
msg = $msg({
|
||||||
from: 'lounge@localhost/'+nick,
|
'from': 'lounge@localhost/'+nick,
|
||||||
id: (new Date()).getTime(),
|
'id': (new Date()).getTime(),
|
||||||
to: 'dummy@localhost',
|
'to': 'dummy@localhost',
|
||||||
type: 'groupchat'
|
'type': 'groupchat'
|
||||||
}).c('body').t(message).tree();
|
}).c('body').t(message).tree();
|
||||||
view.model.onMessage(msg);
|
view.model.onMessage(msg);
|
||||||
expect(_.includes($(view.el).find('.chat-msg-author').text(), '**Dyon van de Wege')).toBeTruthy();
|
expect(_.includes($(view.el).find('.chat-msg-author').text(), '**Dyon van de Wege')).toBeTruthy();
|
||||||
@ -3306,6 +3306,7 @@
|
|||||||
to: 'dummy@localhost',
|
to: 'dummy@localhost',
|
||||||
type: 'groupchat'
|
type: 'groupchat'
|
||||||
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||||
|
|
||||||
view.model.onMessage(msg);
|
view.model.onMessage(msg);
|
||||||
|
|
||||||
// Check that the notification appears inside the chatbox in the DOM
|
// Check that the notification appears inside the chatbox in the DOM
|
||||||
|
@ -241,6 +241,7 @@
|
|||||||
xhr.onload();
|
xhr.onload();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const XMLHttpRequestBackup = window.XMLHttpRequest;
|
||||||
window.XMLHttpRequest = jasmine.createSpy('XMLHttpRequest');
|
window.XMLHttpRequest = jasmine.createSpy('XMLHttpRequest');
|
||||||
XMLHttpRequest.and.callFake(function () {
|
XMLHttpRequest.and.callFake(function () {
|
||||||
return xhr;
|
return xhr;
|
||||||
@ -288,6 +289,7 @@
|
|||||||
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
|
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
|
||||||
"<query xmlns='jabber:iq:roster'><item jid='marty@mcfly.net' name='Marty McFly'/></query>"+
|
"<query xmlns='jabber:iq:roster'><item jid='marty@mcfly.net' name='Marty McFly'/></query>"+
|
||||||
"</iq>");
|
"</iq>");
|
||||||
|
window.XMLHttpRequest = XMLHttpRequestBackup;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -204,12 +204,14 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe("when clicked", function () {
|
describe("when clicked", function () {
|
||||||
|
|
||||||
it("a file upload slot is requested", mock.initConverseWithAsync(function (done, _converse) {
|
it("a file upload slot is requested", mock.initConverseWithAsync(function (done, _converse) {
|
||||||
test_utils.waitUntilDiscoConfirmed(
|
test_utils.waitUntilDiscoConfirmed(
|
||||||
_converse, _converse.domain,
|
_converse, _converse.domain,
|
||||||
[{'category': 'server', 'type':'IM'}],
|
[{'category': 'server', 'type':'IM'}],
|
||||||
['http://jabber.org/protocol/disco#items'], [], 'info').then(function () {
|
['http://jabber.org/protocol/disco#items'], [], 'info').then(function () {
|
||||||
|
|
||||||
|
var send_backup = XMLHttpRequest.prototype.send;
|
||||||
var IQ_stanzas = _converse.connection.IQ_stanzas;
|
var IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||||
|
|
||||||
test_utils.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.tld'], 'items').then(function () {
|
test_utils.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.tld'], 'items').then(function () {
|
||||||
@ -224,11 +226,11 @@
|
|||||||
'lastModifiedDate': "",
|
'lastModifiedDate': "",
|
||||||
'name': "my-juliet.jpg"
|
'name': "my-juliet.jpg"
|
||||||
};
|
};
|
||||||
view.model.sendFile(file);
|
view.model.sendFiles([file]);
|
||||||
return test_utils.waitUntil(function () {
|
return test_utils.waitUntil(function () {
|
||||||
return _.filter(IQ_stanzas, function (iq) {
|
return _.filter(IQ_stanzas, function (iq) {
|
||||||
return iq.nodeTree.querySelector('iq[to="upload.montague.tld"] request');
|
return iq.nodeTree.querySelector('iq[to="upload.montague.tld"] request');
|
||||||
});
|
}).length > 0;
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
var iq = IQ_stanzas.pop();
|
var iq = IQ_stanzas.pop();
|
||||||
expect(iq.toLocaleString()).toBe(
|
expect(iq.toLocaleString()).toBe(
|
||||||
@ -243,6 +245,9 @@
|
|||||||
"content-type='image/jpeg'/>"+
|
"content-type='image/jpeg'/>"+
|
||||||
"</iq>");
|
"</iq>");
|
||||||
|
|
||||||
|
var base_url = document.URL.split(window.location.pathname)[0];
|
||||||
|
var message = base_url+"/logo/conversejs-filled.svg";
|
||||||
|
|
||||||
var stanza = Strophe.xmlHtmlNode(
|
var stanza = Strophe.xmlHtmlNode(
|
||||||
"<iq from='upload.montague.tld'"+
|
"<iq from='upload.montague.tld'"+
|
||||||
" id='"+iq.nodeTree.getAttribute('id')+"'"+
|
" id='"+iq.nodeTree.getAttribute('id')+"'"+
|
||||||
@ -253,11 +258,21 @@
|
|||||||
" <header name='Authorization'>Basic Base64String==</header>"+
|
" <header name='Authorization'>Basic Base64String==</header>"+
|
||||||
" <header name='Cookie'>foo=bar; user=romeo</header>"+
|
" <header name='Cookie'>foo=bar; user=romeo</header>"+
|
||||||
" </put>"+
|
" </put>"+
|
||||||
" <get url='https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg' />"+
|
" <get url='"+message+"' />"+
|
||||||
"</slot>"+
|
"</slot>"+
|
||||||
"</iq>").firstElementChild;
|
"</iq>").firstElementChild;
|
||||||
spyOn(view.model, 'uploadFile').and.callFake(function () {
|
|
||||||
return new window.Promise((resolve, reject) => { resolve(); });
|
spyOn(XMLHttpRequest.prototype, 'send').and.callFake(function () {
|
||||||
|
const message = view.model.messages.at(0);
|
||||||
|
expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('0');
|
||||||
|
message.set('progress', 0.5);
|
||||||
|
expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('0.5');
|
||||||
|
message.set('progress', 1);
|
||||||
|
expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('1');
|
||||||
|
message.save({
|
||||||
|
'upload': _converse.SUCCESS,
|
||||||
|
'message': message.get('get')
|
||||||
|
});
|
||||||
});
|
});
|
||||||
var sent_stanza;
|
var sent_stanza;
|
||||||
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
|
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
|
||||||
@ -267,19 +282,27 @@
|
|||||||
|
|
||||||
return test_utils.waitUntil(function () {
|
return test_utils.waitUntil(function () {
|
||||||
return sent_stanza;
|
return sent_stanza;
|
||||||
}).then(function () {
|
}, 1000).then(function () {
|
||||||
expect(view.model.uploadFile).toHaveBeenCalled();
|
|
||||||
expect(sent_stanza.toLocaleString()).toBe(
|
expect(sent_stanza.toLocaleString()).toBe(
|
||||||
"<message from='dummy@localhost/resource' "+
|
"<message from='dummy@localhost/resource' "+
|
||||||
"to='irini.vlastuin@localhost' "+
|
"to='irini.vlastuin@localhost' "+
|
||||||
"type='chat' "+
|
"type='chat' "+
|
||||||
"id='"+sent_stanza.nodeTree.getAttribute('id')+"' xmlns='jabber:client'>"+
|
"id='"+sent_stanza.nodeTree.getAttribute('id')+"' xmlns='jabber:client'>"+
|
||||||
"<body>https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg</body>"+
|
"<body>"+message+"</body>"+
|
||||||
"<active xmlns='http://jabber.org/protocol/chatstates'/>"+
|
"<active xmlns='http://jabber.org/protocol/chatstates'/>"+
|
||||||
"<x xmlns='jabber:x:oob'>"+
|
"<x xmlns='jabber:x:oob'>"+
|
||||||
"<url>https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg</url>"+
|
"<url>"+message+"</url>"+
|
||||||
"</x>"+
|
"</x>"+
|
||||||
"</message>");
|
"</message>");
|
||||||
|
return test_utils.waitUntil(function () {
|
||||||
|
return view.el.querySelector('.chat-image');
|
||||||
|
}, 1000);
|
||||||
|
}).then(function () {
|
||||||
|
// Check that the image renders
|
||||||
|
expect(view.el.querySelector('.chat-message .chat-msg-content').innerHTML).toEqual(
|
||||||
|
'<a target="_blank" rel="noopener" href="http://localhost:8000/logo/conversejs-filled.svg">'+
|
||||||
|
'<img class="chat-image" src="http://localhost:8000/logo/conversejs-filled.svg"></a>')
|
||||||
|
XMLHttpRequest.prototype.send = send_backup;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,15 +7,19 @@
|
|||||||
(function (root, factory) {
|
(function (root, factory) {
|
||||||
define([
|
define([
|
||||||
"converse-core",
|
"converse-core",
|
||||||
|
"emojione",
|
||||||
"tpl!chatboxes",
|
"tpl!chatboxes",
|
||||||
"backbone.overview"
|
"backbone.overview"
|
||||||
], factory);
|
], factory);
|
||||||
}(this, function (converse, tpl_chatboxes) {
|
}(this, function (converse, emojione, tpl_chatboxes) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { $msg, Backbone, Promise, Strophe, b64_sha1, moment, utils, _ } = converse.env;
|
const { $msg, Backbone, Promise, Strophe, b64_sha1, moment, utils, _ } = converse.env;
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob');
|
Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob');
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-chatboxes', {
|
converse.plugins.add('converse-chatboxes', {
|
||||||
|
|
||||||
overrides: {
|
overrides: {
|
||||||
@ -74,10 +78,100 @@
|
|||||||
|
|
||||||
|
|
||||||
_converse.Message = Backbone.Model.extend({
|
_converse.Message = Backbone.Model.extend({
|
||||||
defaults(){
|
|
||||||
|
defaults () {
|
||||||
return {
|
return {
|
||||||
msgid: _converse.connection.getUniqueId()
|
'msgid': _converse.connection.getUniqueId(),
|
||||||
|
'time': moment().format()
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
if (this.get('file')) {
|
||||||
|
this.on('change:put', this.uploadFile, this);
|
||||||
|
|
||||||
|
if (!_.includes([_converse.SUCCESS, _converse.FAILURE], this.get('upload'))) {
|
||||||
|
this.getRequestSlotURL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sendSlotRequestStanza () {
|
||||||
|
/* Send out an IQ stanza to request a file upload slot.
|
||||||
|
*
|
||||||
|
* https://xmpp.org/extensions/xep-0363.html#request
|
||||||
|
*/
|
||||||
|
const file = this.get('file');
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const iq = converse.env.$iq({
|
||||||
|
'from': _converse.jid,
|
||||||
|
'to': this.get('slot_request_url'),
|
||||||
|
'type': 'get'
|
||||||
|
}).c('request', {
|
||||||
|
'xmlns': Strophe.NS.HTTPUPLOAD,
|
||||||
|
'filename': file.name,
|
||||||
|
'size': file.size,
|
||||||
|
'content-type': file.type
|
||||||
|
})
|
||||||
|
_converse.connection.sendIQ(iq, resolve, reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getRequestSlotURL () {
|
||||||
|
this.sendSlotRequestStanza().then((stanza) => {
|
||||||
|
const slot = stanza.querySelector('slot');
|
||||||
|
if (slot) {
|
||||||
|
this.save({
|
||||||
|
'get': slot.querySelector('get').getAttribute('url'),
|
||||||
|
'put': slot.querySelector('put').getAttribute('url'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.save({
|
||||||
|
'type': 'error',
|
||||||
|
'message': __("Sorry, could not determine upload URL.")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch((e) => {
|
||||||
|
_converse.log(e, Strophe.LogLevel.ERROR);
|
||||||
|
return this.save({
|
||||||
|
'type': 'error',
|
||||||
|
'message': __("Sorry, could not determine upload URL.")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadFile () {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.onreadystatechange = () => {
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
_converse.log("Status: " + xhr.status, Strophe.LogLevel.INFO);
|
||||||
|
if (xhr.status === 200 || xhr.status === 201) {
|
||||||
|
this.save({
|
||||||
|
'upload': _converse.SUCCESS,
|
||||||
|
'message': this.get('get')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.save({
|
||||||
|
'upload': _converse.FAILURE,
|
||||||
|
'message': __('Sorry, could not succesfully upload your file')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.upload.addEventListener("progress", (evt) => {
|
||||||
|
if (evt.lengthComputable) {
|
||||||
|
this.set('progress', evt.loaded / evt.total);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
xhr.onerror = () => {
|
||||||
|
this.save({
|
||||||
|
'upload': _converse.FAILURE,
|
||||||
|
'message': __('Sorry, could not succesfully upload your file')
|
||||||
|
});
|
||||||
|
};
|
||||||
|
xhr.open('PUT', this.get('put'), true);
|
||||||
|
xhr.setRequestHeader("Content-type", 'application/octet-stream');
|
||||||
|
xhr.send(this.get('file'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -97,6 +191,7 @@
|
|||||||
'num_unread': 0,
|
'num_unread': 0,
|
||||||
'show_avatar': true,
|
'show_avatar': true,
|
||||||
'type': 'chatbox',
|
'type': 'chatbox',
|
||||||
|
'message_type': 'chat',
|
||||||
'url': ''
|
'url': ''
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -106,6 +201,12 @@
|
|||||||
b64_sha1(`converse.messages${this.get('jid')}${_converse.bare_jid}`));
|
b64_sha1(`converse.messages${this.get('jid')}${_converse.bare_jid}`));
|
||||||
this.messages.chatbox = this;
|
this.messages.chatbox = this;
|
||||||
|
|
||||||
|
this.messages.on('change:upload', (message) => {
|
||||||
|
if (message.get('upload') === _converse.SUCCESS) {
|
||||||
|
this.sendMessageStanza(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.save({
|
this.save({
|
||||||
// The chat_state will be set to ACTIVE once the chat box is opened
|
// 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.
|
// and we listen for change:chat_state, so shouldn't set it to ACTIVE here.
|
||||||
@ -125,7 +226,7 @@
|
|||||||
const stanza = $msg({
|
const stanza = $msg({
|
||||||
'from': _converse.connection.jid,
|
'from': _converse.connection.jid,
|
||||||
'to': this.get('jid'),
|
'to': this.get('jid'),
|
||||||
'type': 'chat',
|
'type': this.get('message_type'),
|
||||||
'id': message.get('msgid')
|
'id': message.get('msgid')
|
||||||
}).c('body').t(message.get('message')).up()
|
}).c('body').t(message.get('message')).up()
|
||||||
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
|
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
|
||||||
@ -149,17 +250,34 @@
|
|||||||
if (_converse.forward_messages) {
|
if (_converse.forward_messages) {
|
||||||
// Forward the message, so that other connected resources are also aware of it.
|
// Forward the message, so that other connected resources are also aware of it.
|
||||||
_converse.connection.send(
|
_converse.connection.send(
|
||||||
$msg({ to: _converse.bare_jid, type: 'chat', id: message.get('msgid') })
|
$msg({
|
||||||
.c('forwarded', {'xmlns': Strophe.NS.FORWARD})
|
'to': _converse.bare_jid,
|
||||||
.c('delay', {
|
'type': this.get('message_type'),
|
||||||
'xmns': Strophe.NS.DELAY,
|
'id': message.get('msgid')
|
||||||
'stamp': moment().format()
|
}).c('forwarded', {'xmlns': Strophe.NS.FORWARD})
|
||||||
}).up()
|
.c('delay', {
|
||||||
.cnode(messageStanza.tree())
|
'xmns': Strophe.NS.DELAY,
|
||||||
|
'stamp': moment().format()
|
||||||
|
}).up()
|
||||||
|
.cnode(messageStanza.tree())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getOutgoingMessageAttributes (text, spoiler_hint) {
|
||||||
|
const fullname = _converse.xmppstatus.get('fullname'),
|
||||||
|
is_spoiler = this.get('composing_spoiler');
|
||||||
|
|
||||||
|
return {
|
||||||
|
'fullname': _.isEmpty(fullname) ? _converse.bare_jid : fullname,
|
||||||
|
'sender': 'me',
|
||||||
|
'time': moment().format(),
|
||||||
|
'message': text ? u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse) : undefined,
|
||||||
|
'is_spoiler': is_spoiler,
|
||||||
|
'spoiler_hint': is_spoiler ? spoiler_hint : undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
sendMessage (attrs) {
|
sendMessage (attrs) {
|
||||||
/* Responsible for sending off a text message.
|
/* Responsible for sending off a text message.
|
||||||
*
|
*
|
||||||
@ -169,83 +287,25 @@
|
|||||||
this.sendMessageStanza(this.messages.create(attrs));
|
this.sendMessageStanza(this.messages.create(attrs));
|
||||||
},
|
},
|
||||||
|
|
||||||
notifyUploadFailure (err_msg, error) {
|
|
||||||
err_msg = err_msg || __("Sorry, failed to upload the file");
|
|
||||||
this.trigger('showHelpMessages', [err_msg], 'error');
|
|
||||||
if (error instanceof Error) {
|
|
||||||
_converse.log(error, Strophe.LogLevel.ERROR);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
sendFile (file) {
|
|
||||||
_converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then((result) => {
|
|
||||||
if (!result.length) {
|
|
||||||
this.notifyUploadFailure(__("Sorry, file upload is not supported by your server."));
|
|
||||||
}
|
|
||||||
const request_slot_url = result[0].id;
|
|
||||||
if (!request_slot_url) {
|
|
||||||
return this.notifyUploadFailure(__("Could not determine request slot URL for file upload"));
|
|
||||||
}
|
|
||||||
this.trigger('showHelpMessages', [__('The file upload starts now')], 'info');
|
|
||||||
this.requestSlot(file, request_slot_url).then((stanza) => {
|
|
||||||
const slot = stanza.querySelector('slot');
|
|
||||||
if (slot) {
|
|
||||||
const put = slot.querySelector('put').getAttribute('url');
|
|
||||||
const get = slot.querySelector('get').getAttribute('url');
|
|
||||||
this.uploadFile(put, file)
|
|
||||||
.then(_.bind(this.sendMessage, this, {'message': get, 'file': true}))
|
|
||||||
.catch(this.notifyUploadFailure.bind(this, null));
|
|
||||||
} else {
|
|
||||||
this.notifyUploadFailure();
|
|
||||||
}
|
|
||||||
}).catch(this.notifyUploadFailure.bind(this, null));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
sendFiles (files) {
|
sendFiles (files) {
|
||||||
_.each(files, this.sendFile.bind(this));
|
_converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then((result) => {
|
||||||
},
|
const slot_request_url = _.get(result.pop(), 'id');
|
||||||
|
if (!slot_request_url) {
|
||||||
requestSlot (file, request_slot_url) {
|
const err_msg = __("Sorry, looks like file upload is not supported by your server.");
|
||||||
/* Send out an IQ stanza to request a file upload slot.
|
return this.trigger('showHelpMessages', [err_msg], 'error');
|
||||||
*
|
}
|
||||||
* https://xmpp.org/extensions/xep-0363.html#request
|
_.each(files, (file) => {
|
||||||
*/
|
this.messages.create(
|
||||||
return new Promise((resolve, reject) => {
|
_.extend(
|
||||||
const iq = converse.env.$iq({
|
this.getOutgoingMessageAttributes(), {
|
||||||
'from': _converse.jid,
|
'file': file,
|
||||||
'to': request_slot_url,
|
'progress': 0,
|
||||||
'type': 'get'
|
'slot_request_url': slot_request_url,
|
||||||
}).c('request', {
|
'type': this.get('message_type'),
|
||||||
'xmlns': Strophe.NS.HTTPUPLOAD,
|
})
|
||||||
'filename': file.name,
|
);
|
||||||
'size': file.size,
|
});
|
||||||
'content-type': file.type
|
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
})
|
|
||||||
_converse.connection.sendIQ(iq, resolve, reject);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadFile (url, file) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.onreadystatechange = function () {
|
|
||||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
||||||
_converse.log("Status: " + xhr.status, Strophe.LogLevel.INFO);
|
|
||||||
if (xhr.status === 200 || xhr.status === 201) {
|
|
||||||
resolve(url, file);
|
|
||||||
} else {
|
|
||||||
xhr.onerror();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr.onerror = function () {
|
|
||||||
reject(xhr.responseText);
|
|
||||||
};
|
|
||||||
xhr.open('PUT', url, true);
|
|
||||||
xhr.setRequestHeader("Content-type", 'application/octet-stream');
|
|
||||||
xhr.send(file);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getMessageBody (message) {
|
getMessageBody (message) {
|
||||||
@ -255,7 +315,7 @@
|
|||||||
_.propertyOf(message.querySelector('body'))('textContent');
|
_.propertyOf(message.querySelector('body'))('textContent');
|
||||||
},
|
},
|
||||||
|
|
||||||
getMessageAttributes (message, delay, original_stanza) {
|
getMessageAttributesFromStanza (message, delay, original_stanza) {
|
||||||
/* Parses a passed in message stanza and returns an object
|
/* Parses a passed in message stanza and returns an object
|
||||||
* of attributes.
|
* of attributes.
|
||||||
*
|
*
|
||||||
@ -292,10 +352,10 @@
|
|||||||
let sender, fullname;
|
let sender, fullname;
|
||||||
if ((is_groupchat && from === this.get('nick')) || (!is_groupchat && from === _converse.bare_jid)) {
|
if ((is_groupchat && from === this.get('nick')) || (!is_groupchat && from === _converse.bare_jid)) {
|
||||||
sender = 'me';
|
sender = 'me';
|
||||||
fullname = _converse.xmppstatus.get('fullname') || from;
|
fullname = _converse.xmppstatus.get('fullname');
|
||||||
} else {
|
} else {
|
||||||
sender = 'them';
|
sender = 'them';
|
||||||
fullname = this.get('fullname') || from;
|
fullname = this.get('fullname');
|
||||||
}
|
}
|
||||||
const spoiler = message.querySelector(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`);
|
const spoiler = message.querySelector(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`);
|
||||||
const attrs = {
|
const attrs = {
|
||||||
@ -320,7 +380,7 @@
|
|||||||
/* Create a Backbone.Message object inside this chat box
|
/* Create a Backbone.Message object inside this chat box
|
||||||
* based on the identified message stanza.
|
* based on the identified message stanza.
|
||||||
*/
|
*/
|
||||||
return this.messages.create(this.getMessageAttributes.apply(this, arguments));
|
return this.messages.create(this.getMessageAttributesFromStanza.apply(this, arguments));
|
||||||
},
|
},
|
||||||
|
|
||||||
newMessageWillBeHidden () {
|
newMessageWillBeHidden () {
|
||||||
|
@ -105,7 +105,6 @@
|
|||||||
'chatview_avatar_height': 32,
|
'chatview_avatar_height': 32,
|
||||||
'chatview_avatar_width': 32,
|
'chatview_avatar_width': 32,
|
||||||
'show_toolbar': true,
|
'show_toolbar': true,
|
||||||
'show_message_load_animation': false,
|
|
||||||
'time_format': 'HH:mm',
|
'time_format': 'HH:mm',
|
||||||
'visible_toolbar_buttons': {
|
'visible_toolbar_buttons': {
|
||||||
'call': false,
|
'call': false,
|
||||||
@ -613,24 +612,25 @@
|
|||||||
showChatStateNotification (message) {
|
showChatStateNotification (message) {
|
||||||
/* Support for XEP-0085, Chat State Notifications */
|
/* Support for XEP-0085, Chat State Notifications */
|
||||||
let text;
|
let text;
|
||||||
const from = message.get('from');
|
const from = message.get('from'),
|
||||||
const data = `data-csn=${from}`;
|
username = message.get('fullname') || from,
|
||||||
|
data = `data-csn=${from}`;
|
||||||
this.clearChatStateNotification(from);
|
this.clearChatStateNotification(from);
|
||||||
|
|
||||||
if (message.get('chat_state') === _converse.COMPOSING) {
|
if (message.get('chat_state') === _converse.COMPOSING) {
|
||||||
if (message.get('sender') === 'me') {
|
if (message.get('sender') === 'me') {
|
||||||
text = __('Typing from another device');
|
text = __('Typing from another device');
|
||||||
} else {
|
} else {
|
||||||
text = message.get('fullname')+' '+__('is typing');
|
text = username +' '+__('is typing');
|
||||||
}
|
}
|
||||||
} else if (message.get('chat_state') === _converse.PAUSED) {
|
} else if (message.get('chat_state') === _converse.PAUSED) {
|
||||||
if (message.get('sender') === 'me') {
|
if (message.get('sender') === 'me') {
|
||||||
text = __('Stopped typing on the other device');
|
text = __('Stopped typing on the other device');
|
||||||
} else {
|
} else {
|
||||||
text = message.get('fullname')+' '+__('has stopped typing');
|
text = username +' '+__('has stopped typing');
|
||||||
}
|
}
|
||||||
} else if (message.get('chat_state') === _converse.GONE) {
|
} else if (message.get('chat_state') === _converse.GONE) {
|
||||||
text = message.get('fullname')+' '+__('has gone away');
|
text = username +' '+__('has gone away');
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -707,7 +707,7 @@
|
|||||||
if (message.get('chat_state')) {
|
if (message.get('chat_state')) {
|
||||||
this.showChatStateNotification(message);
|
this.showChatStateNotification(message);
|
||||||
}
|
}
|
||||||
if (message.get('message')) {
|
if (message.get('file') || message.get('message')) {
|
||||||
this.handleTextMessage(message);
|
this.handleTextMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,29 +755,10 @@
|
|||||||
if (this.parseMessageForCommands(text)) {
|
if (this.parseMessageForCommands(text)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
|
const attrs = this.model.getOutgoingMessageAttributes(text, spoiler_hint);
|
||||||
this.model.sendMessage(attrs);
|
this.model.sendMessage(attrs);
|
||||||
},
|
},
|
||||||
|
|
||||||
getOutgoingMessageAttributes (text, spoiler_hint) {
|
|
||||||
/* Overridable method which returns the attributes to be
|
|
||||||
* passed to Backbone.Message's constructor.
|
|
||||||
*/
|
|
||||||
const fullname = _converse.xmppstatus.get('fullname'),
|
|
||||||
is_spoiler = this.model.get('composing_spoiler'),
|
|
||||||
attrs = {
|
|
||||||
'fullname': _.isEmpty(fullname) ? _converse.bare_jid : fullname,
|
|
||||||
'sender': 'me',
|
|
||||||
'time': moment().format(),
|
|
||||||
'message': u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse),
|
|
||||||
'is_spoiler': is_spoiler
|
|
||||||
};
|
|
||||||
if (is_spoiler) {
|
|
||||||
attrs.spoiler_hint = spoiler_hint;
|
|
||||||
}
|
|
||||||
return attrs;
|
|
||||||
},
|
|
||||||
|
|
||||||
sendChatState () {
|
sendChatState () {
|
||||||
/* 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.
|
||||||
|
@ -142,6 +142,9 @@
|
|||||||
10: 'RECONNECTING',
|
10: 'RECONNECTING',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_converse.SUCCESS = 'success';
|
||||||
|
_converse.FAILURE = 'failure';
|
||||||
|
|
||||||
_converse.DEFAULT_IMAGE_TYPE = 'image/png';
|
_converse.DEFAULT_IMAGE_TYPE = 'image/png';
|
||||||
_converse.DEFAULT_IMAGE = "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";
|
_converse.DEFAULT_IMAGE = "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";
|
||||||
|
|
||||||
|
@ -128,8 +128,8 @@
|
|||||||
//
|
//
|
||||||
// New functions which don't exist yet can also be added.
|
// New functions which don't exist yet can also be added.
|
||||||
ChatBox: {
|
ChatBox: {
|
||||||
getMessageAttributes (message, delay, original_stanza) {
|
getMessageAttributesFromStanza (message, delay, original_stanza) {
|
||||||
const attrs = this.__super__.getMessageAttributes.apply(this, arguments);
|
const attrs = this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
|
||||||
const archive_id = getMessageArchiveID(original_stanza);
|
const archive_id = getMessageArchiveID(original_stanza);
|
||||||
if (archive_id) {
|
if (archive_id) {
|
||||||
attrs.archive_id = archive_id;
|
attrs.archive_id = archive_id;
|
||||||
|
@ -10,16 +10,18 @@
|
|||||||
"xss",
|
"xss",
|
||||||
"emojione",
|
"emojione",
|
||||||
"tpl!action",
|
"tpl!action",
|
||||||
|
"tpl!file",
|
||||||
"tpl!message",
|
"tpl!message",
|
||||||
"tpl!spoiler_message"
|
"tpl!spoiler_message"
|
||||||
], factory);
|
], factory);
|
||||||
}(this, function (
|
}(this, function (
|
||||||
converse,
|
converse,
|
||||||
xss,
|
xss,
|
||||||
emojione,
|
emojione,
|
||||||
tpl_action,
|
tpl_action,
|
||||||
tpl_message,
|
tpl_file,
|
||||||
tpl_spoiler_message
|
tpl_message,
|
||||||
|
tpl_spoiler_message
|
||||||
) {
|
) {
|
||||||
"use strict";
|
"use strict";
|
||||||
const { Backbone, _, moment } = converse.env;
|
const { Backbone, _, moment } = converse.env;
|
||||||
@ -38,60 +40,57 @@
|
|||||||
_converse.MessageView = Backbone.NativeView.extend({
|
_converse.MessageView = Backbone.NativeView.extend({
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
this.model.collection.chatbox.on('change:fullname', this.render, this);
|
const chatbox = this.model.collection.chatbox;
|
||||||
|
chatbox.on('change:fullname', (chatbox) => this.model.save('fullname', chatbox.get('fullname')));
|
||||||
|
|
||||||
|
this.model.on('change:fullname', this.render, this);
|
||||||
|
this.model.on('change:progress', this.renderFileUploadProgresBar, this);
|
||||||
|
this.model.on('change:type', this.render, this);
|
||||||
|
this.model.on('change:upload', this.render, this);
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const chatbox = this.model.collection.chatbox;
|
if (this.model.get('file') && !this.model.get('message')) {
|
||||||
|
return this.renderFileUploadProgresBar();
|
||||||
|
}
|
||||||
|
let template, username,
|
||||||
|
text = this.model.get('message');
|
||||||
|
|
||||||
let text = this.model.get('message'),
|
// TODO: store proper username on the message itself
|
||||||
fullname = chatbox.get('fullname') || chatbox.get('jid'),
|
if (this.isMeCommand()) {
|
||||||
template, username;
|
const arr = this.getValuesForMeCommand();
|
||||||
|
template = arr[0];
|
||||||
const match = text.match(/^\/(.*?)(?: (.*))?$/);
|
username = arr[1];
|
||||||
if ((match) && (match[1] === 'me')) {
|
text = arr[2];
|
||||||
text = text.replace(/^\/me/, '');
|
|
||||||
template = tpl_action;
|
|
||||||
if (this.model.get('sender') === 'me') {
|
|
||||||
fullname = _converse.xmppstatus.get('fullname') || this.model.get('fullname');
|
|
||||||
username = _.isNil(fullname)? _converse.bare_jid: fullname;
|
|
||||||
} else {
|
|
||||||
username = this.model.get('fullname');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
username = this.model.get('sender') === 'me' && __('me') || fullname;
|
const fullname = _converse.xmppstatus.get('fullname') || this.model.get('fullname');
|
||||||
|
username = this.model.get('sender') === 'me' && __('me') || fullname || this.model.get('from');
|
||||||
template = this.model.get('is_spoiler') ? tpl_spoiler_message : tpl_message;
|
template = this.model.get('is_spoiler') ? tpl_spoiler_message : tpl_message;
|
||||||
}
|
}
|
||||||
text = u.geoUriToHttp(text, _converse);
|
const moment_time = moment(this.model.get('time'));
|
||||||
|
|
||||||
const msg_time = moment(this.model.get('time')) || moment;
|
|
||||||
const msg = u.stringToElement(template(
|
const msg = u.stringToElement(template(
|
||||||
_.extend(this.model.toJSON(), {
|
_.extend(this.model.toJSON(), {
|
||||||
'time': msg_time.format(_converse.time_format),
|
'pretty_time': moment_time.format(_converse.time_format),
|
||||||
'isodate': msg_time.format(),
|
'time': moment_time.format(),
|
||||||
'username': username,
|
'username': username,
|
||||||
'extra_classes': this.getExtraMessageClasses(),
|
'extra_classes': this.getExtraMessageClasses(),
|
||||||
'label_show': __('Show hidden message')
|
'label_show': __('Show hidden message')
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
if (_converse.show_message_load_animation) {
|
|
||||||
window.setTimeout(_.partial(u.removeClass, 'onload', msg), 2000);
|
|
||||||
}
|
|
||||||
const msg_content = msg.querySelector('.chat-msg-content');
|
const msg_content = msg.querySelector('.chat-msg-content');
|
||||||
msg_content.innerHTML = u.addEmoji(
|
text = xss.filterXSS(text, {'whiteList': {}});
|
||||||
_converse, emojione, u.addHyperlinks(xss.filterXSS(text, {'whiteList': {}}))
|
msg_content.innerHTML = _.flow(
|
||||||
);
|
_.partial(u.geoUriToHttp, _, _converse.geouri_replacement),
|
||||||
|
_.partial(u.addHyperlinks, _),
|
||||||
|
_.partial(u.addEmoji, _converse, emojione, _),
|
||||||
|
u.renderMovieURLs,
|
||||||
|
u.renderAudioURLs
|
||||||
|
)(text);
|
||||||
|
|
||||||
if (msg_content.textContent.endsWith('mp4')) {
|
u.renderImageURLs(msg_content).then(() => {
|
||||||
msg_content.innerHTML = u.renderMovieURLs(msg_content);
|
this.model.collection.trigger('rendered');
|
||||||
} else if (msg_content.textContent.endsWith('mp3')) {
|
});
|
||||||
msg_content.innerHTML = u.renderAudioURLs(msg_content);
|
|
||||||
} else {
|
|
||||||
u.renderImageURLs(msg_content).then(() => {
|
|
||||||
this.model.collection.trigger('rendered');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!_.isNil(this.el.parentElement)) {
|
if (!_.isNil(this.el.parentElement)) {
|
||||||
this.el.parentElement.replaceChild(msg, this.el);
|
this.el.parentElement.replaceChild(msg, this.el);
|
||||||
}
|
}
|
||||||
@ -99,13 +98,42 @@
|
|||||||
return this.el;
|
return this.el;
|
||||||
},
|
},
|
||||||
|
|
||||||
getExtraMessageClasses () {
|
renderFileUploadProgresBar () {
|
||||||
let extra_classes;
|
const msg = u.stringToElement(tpl_file(this.model.toJSON()));
|
||||||
if (_converse.show_message_load_animation) {
|
if (!_.isNil(this.el.parentElement)) {
|
||||||
extra_classes = 'onload ' + (this.model.get('delayed') && 'delayed' || '');
|
this.el.parentElement.replaceChild(msg, this.el);
|
||||||
} else {
|
|
||||||
extra_classes = this.model.get('delayed') && 'delayed' || '';
|
|
||||||
}
|
}
|
||||||
|
this.setElement(msg);
|
||||||
|
return this.el;
|
||||||
|
},
|
||||||
|
|
||||||
|
isMeCommand () {
|
||||||
|
const match = this.model.get('message').match(/^\/(.*?)(?: (.*))?$/);
|
||||||
|
return match && match[1] === 'me';
|
||||||
|
},
|
||||||
|
|
||||||
|
getValuesForMeCommand() {
|
||||||
|
let username, text;
|
||||||
|
const match = this.model.get('message').match(/^\/(.*?)(?: (.*))?$/);
|
||||||
|
if (match && match[1] === 'me') {
|
||||||
|
text = this.model.get('message').replace(/^\/me/, '');
|
||||||
|
}
|
||||||
|
if (this.model.get('sender') === 'me') {
|
||||||
|
const fullname = _converse.xmppstatus.get('fullname') || this.model.get('fullname');
|
||||||
|
username = _.isNil(fullname) ? _converse.bare_jid : fullname;
|
||||||
|
} else {
|
||||||
|
username = this.model.get('fullname') || this.model.get('from');
|
||||||
|
}
|
||||||
|
return [tpl_action, username, text]
|
||||||
|
},
|
||||||
|
|
||||||
|
processMessageText () {
|
||||||
|
var text = this.get('message');
|
||||||
|
text = u.geoUriToHttp(text, _converse.geouri_replacement);
|
||||||
|
},
|
||||||
|
|
||||||
|
getExtraMessageClasses () {
|
||||||
|
let extra_classes = this.model.get('delayed') && 'delayed' || '';
|
||||||
if (this.model.get('type') === 'groupchat' && this.model.get('sender') === 'them') {
|
if (this.model.get('type') === 'groupchat' && this.model.get('sender') === 'them') {
|
||||||
if (this.model.collection.chatbox.isUserMentioned(this.model.get('message'))) {
|
if (this.model.collection.chatbox.isUserMentioned(this.model.get('message'))) {
|
||||||
// Add special class to mark groupchat messages
|
// Add special class to mark groupchat messages
|
||||||
|
@ -182,6 +182,7 @@
|
|||||||
'features_fetched': false,
|
'features_fetched': false,
|
||||||
'roomconfig': {},
|
'roomconfig': {},
|
||||||
'type': converse.CHATROOMS_TYPE,
|
'type': converse.CHATROOMS_TYPE,
|
||||||
|
'message_type': 'groupchat'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="message chat-message {{{o.extra_classes}}}" data-isodate="{{{o.isodate}}}">
|
<div class="message chat-message chat-action {{{o.extra_classes}}}" data-isodate="{{{o.time}}}">
|
||||||
<span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.time}}} **{{{o.username}}} </span>
|
<span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} **{{{o.username}}}</span>
|
||||||
<span class="chat-msg-content chat-action"><!-- message gets added here via renderMessage --></span>
|
<span class="chat-msg-content"><!-- message gets added here via renderMessage --></span>
|
||||||
</div>
|
</div>
|
||||||
|
3
src/templates/file.html
Normal file
3
src/templates/file.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div class="message" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
|
||||||
|
<progress value="{{{o.progress}}}"/>
|
||||||
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
<div class="message chat-message {{{o.extra_classes}}}" data-isodate="{{{o.isodate}}}" data-msgid="{{{o.msgid}}}">
|
<div class="message chat-message {{{o.extra_classes}}}" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
|
||||||
<span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.time}}} {{{o.username}}}: </span>
|
<span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} {{{o.username}}}: </span>
|
||||||
<span class="chat-msg-content"><!-- message gets added here via renderMessage --></span>
|
<span class="chat-msg-content"><!-- message gets added here via renderMessage --></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="message chat-message {{{o.extra_classes}}}" data-isodate="{{{o.isodate}}}" data-msgid="{{{o.msgid}}}">
|
<div class="message chat-message {{{o.extra_classes}}}" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
|
||||||
<span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.time}}} {{{o.username}}}: </span>
|
<span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} {{{o.username}}}: </span>
|
||||||
<div class="spoiler-hint">{{{o.spoiler_hint}}}</div>
|
<div class="spoiler-hint">{{{o.spoiler_hint}}}</div>
|
||||||
<a class="icon-eye toggle-spoiler" data-toggle-state="closed" href="#">{{{o.label_show}}}</a>
|
<a class="icon-eye toggle-spoiler" data-toggle-state="closed" href="#">{{{o.label_show}}}</a>
|
||||||
<div class="chat-msg-content spoiler collapsed"><!-- message gets added here via renderMessage --></div>
|
<div class="chat-msg-content spoiler collapsed"><!-- message gets added here via renderMessage --></div>
|
||||||
|
@ -213,12 +213,18 @@
|
|||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
u.renderMovieURLs = function (obj) {
|
u.renderMovieURLs = function (text) {
|
||||||
return "<video controls><source src=\"" + obj.textContent + "\" type=\"video/mp4\"></video>";
|
if (text.endsWith('mp4')) {
|
||||||
|
return "<video controls><source src=\"" + text + "\" type=\"video/mp4\"></video>";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
u.renderAudioURLs = function (obj) {
|
u.renderAudioURLs = function (text) {
|
||||||
return "<audio controls><source src=\"" + obj.textContent + "\" type=\"audio/mpeg\"></audio>";
|
if (text.endsWith('mp3')) {
|
||||||
|
return "<audio controls><source src=\"" + text+ "\" type=\"audio/mpeg\"></audio>";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
u.slideInAllElements = function (elements, duration=300) {
|
u.slideInAllElements = function (elements, duration=300) {
|
||||||
@ -714,9 +720,9 @@
|
|||||||
el.dispatchEvent(evt);
|
el.dispatchEvent(evt);
|
||||||
};
|
};
|
||||||
|
|
||||||
u.geoUriToHttp = function(text, _converse) {
|
u.geoUriToHttp = function(text, geouri_replacement) {
|
||||||
const regex = /geo:([\-0-9.]+),([\-0-9.]+)(?:,([\-0-9.]+))?(?:\?(.*))?/g;
|
const regex = /geo:([\-0-9.]+),([\-0-9.]+)(?:,([\-0-9.]+))?(?:\?(.*))?/g;
|
||||||
return text.replace(regex, _converse.geouri_replacement);
|
return text.replace(regex, geouri_replacement);
|
||||||
};
|
};
|
||||||
|
|
||||||
u.httpToGeoUri = function(text, _converse) {
|
u.httpToGeoUri = function(text, _converse) {
|
||||||
|
Loading…
Reference in New Issue
Block a user