Commit c01e9f82 authored by JC Brand's avatar JC Brand

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
parent db790183
...@@ -1214,17 +1214,6 @@ the operating system or browser (which might not support emoji). ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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();
}); });
}); });
......
This diff is collapsed.
...@@ -105,7 +105,6 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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,66 @@ ...@@ -38,60 +40,66 @@
_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 text = this.model.get('message'), }
fullname = chatbox.get('fullname') || chatbox.get('jid'), let template, username,
template, username; text = this.model.get('message');
const match = text.match(/^\/(.*?)(?: (.*))?$/); // TODO: store proper username on the message itself
if ((match) && (match[1] === 'me')) { if (this.isMeCommand()) {
text = text.replace(/^\/me/, ''); const arr = this.getValuesForMeCommand();
template = tpl_action; template = arr[0];
if (this.model.get('sender') === 'me') { username = arr[1];
fullname = _converse.xmppstatus.get('fullname') || this.model.get('fullname'); text = arr[2];
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); if (!_.isNil(this.el.parentElement)) {
} else { this.el.parentElement.replaceChild(msg, this.el);
u.renderImageURLs(msg_content).then(() => {
this.model.collection.trigger('rendered');
});
} }
this.setElement(msg);
return this.el;
},
renderFileUploadProgresBar () {
const msg = u.stringToElement(tpl_file(this.model.toJSON()));
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 +107,33 @@ ...@@ -99,13 +107,33 @@
return this.el; return this.el;
}, },
getExtraMessageClasses () { isMeCommand () {
let extra_classes; const match = this.model.get('message').match(/^\/(.*?)(?: (.*))?$/);
if (_converse.show_message_load_animation) { return match && match[1] === 'me';
extra_classes = 'onload ' + (this.model.get('delayed') && 'delayed' || ''); },
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 { } else {
extra_classes = this.model.get('delayed') && 'delayed' || ''; 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 @@ ...@@ -182,6 +182,7 @@
'features_fetched': false, 'features_fetched': false,
'roomconfig': {}, 'roomconfig': {},
'type': converse.CHATROOMS_TYPE, 'type': converse.CHATROOMS_TYPE,
'message_type': 'groupchat'
} }
); );
}, },
......
<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}}}&nbsp;</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>
<div class="message" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
<progress value="{{{o.progress}}}"/>
</div>
<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}}}:&nbsp;</span> <span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} {{{o.username}}}:&nbsp;</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>
<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}}}:&nbsp;</span> <span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} {{{o.username}}}:&nbsp;</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 @@ ...@@ -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 @@ ...@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment