Commit 5a1b308e authored by JC Brand's avatar JC Brand

Render images from URLs

parent c738d085
...@@ -1347,6 +1347,9 @@ ...@@ -1347,6 +1347,9 @@
#conversejs .chatbox .chat-body .chat-info.chat-date { #conversejs .chatbox .chat-body .chat-info.chat-date {
display: inline-block; display: inline-block;
margin-top: 1em; } margin-top: 1em; }
#conversejs .chatbox .chat-body .chat-image {
max-width: 100%;
max-height: 100%; }
#conversejs .chatbox .chat-body .chat-message { #conversejs .chatbox .chat-body .chat-message {
margin: 0.3em; } margin: 0.3em; }
#conversejs .chatbox .chat-body .chat-message span { #conversejs .chatbox .chat-body .chat-message span {
......
...@@ -154,6 +154,10 @@ ...@@ -154,6 +154,10 @@
margin-top: 1em; margin-top: 1em;
} }
} }
.chat-image {
max-width: 100%;
max-height: 100%;
}
.chat-message { .chat-message {
margin: 0.3em; margin: 0.3em;
span { span {
......
...@@ -819,19 +819,6 @@ ...@@ -819,19 +819,6 @@
expect(msg.html()).toEqual('<p>This message contains <em>some</em> <b>markup</b></p>'); expect(msg.html()).toEqual('<p>This message contains <em>some</em> <b>markup</b></p>');
}.bind(converse)); }.bind(converse));
it("can contain hyperlinks, which will be clickable", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = 'This message contains a hyperlink: www.opkode.com';
spyOn(view, 'sendMessage').andCallThrough();
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('This message contains a hyperlink: <a target="_blank" rel="noopener" href="http://www.opkode.com">www.opkode.com</a>');
}.bind(converse));
it("should display emoticons correctly", function () { it("should display emoticons correctly", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid); test_utils.openChatBoxFor(contact_jid);
...@@ -856,21 +843,51 @@ ...@@ -856,21 +843,51 @@
} }
}.bind(converse)); }.bind(converse));
it("will have properly escaped URLs", function () { it("can contain hyperlinks, which will be clickable", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid); test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid); var view = converse.chatboxviews.get(contact_jid);
var message = 'This message contains a hyperlink: www.opkode.com';
spyOn(view, 'sendMessage').andCallThrough(); spyOn(view, 'sendMessage').andCallThrough();
runs(function () {
var message = "http://www.opkode.com/'onmouseover='alert(1)'whatever";
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
});
waits(500);
runs(function () {
expect(view.sendMessage).toHaveBeenCalled(); expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content'); var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message); expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('This message contains a hyperlink: <a target="_blank" rel="noopener" href="http://www.opkode.com">www.opkode.com</a>');
});
});
it("will have properly escaped URLs", function () {
if (/PhantomJS/.test(window.navigator.userAgent)) {
// Flaky under PhantomJS due to timeouts
return;
}
// TODO: make these local urls
var message, msg;
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = converse.chatboxviews.get(contact_jid);
spyOn(view, 'sendMessage').andCallThrough();
runs(function () {
message = "http://www.opkode.com/'onmouseover='alert(1)'whatever";
test_utils.sendMessage(view, message);
});
waits(500);
runs(function () {
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="http://www.opkode.com/%27onmouseover=%27alert%281%29%27whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>'); expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="http://www.opkode.com/%27onmouseover=%27alert%281%29%27whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>');
message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever'; message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever';
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
});
waits(500);
runs(function () {
expect(view.sendMessage).toHaveBeenCalled(); expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content'); msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message); expect(msg.text()).toEqual(message);
...@@ -878,6 +895,9 @@ ...@@ -878,6 +895,9 @@
message = "https://en.wikipedia.org/wiki/Ender's_Game"; message = "https://en.wikipedia.org/wiki/Ender's_Game";
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
});
waits(500);
runs(function () {
expect(view.sendMessage).toHaveBeenCalled(); expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content'); msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message); expect(msg.text()).toEqual(message);
...@@ -885,11 +905,37 @@ ...@@ -885,11 +905,37 @@
message = "https://en.wikipedia.org/wiki/Ender%27s_Game"; message = "https://en.wikipedia.org/wiki/Ender%27s_Game";
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
});
waits(500);
runs(function () {
expect(view.sendMessage).toHaveBeenCalled(); expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content'); msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message); expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender%27s_Game</a>'); expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender%27s_Game</a>');
}.bind(converse)); });
});
it("will render images from their URLs", function () {
if (/PhantomJS/.test(window.navigator.userAgent)) {
// Doesn't work when running tests in PhantomJS, since
// the page is loaded via file:///
return;
}
var message = document.URL.split(window.location.pathname)[0] + "/logo/conversejs.svg";
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = converse.chatboxviews.get(contact_jid);
spyOn(view, 'sendMessage').andCallThrough();
runs(function () {
test_utils.sendMessage(view, message);
});
waits(500);
runs(function () {
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.html()).toEqual('<img src="'+message+'" class="chat-image">');
});
});
}.bind(converse)); }.bind(converse));
......
...@@ -20,6 +20,16 @@ ...@@ -20,6 +20,16 @@
'list-multi': 'dropdown' 'list-multi': 'dropdown'
}; };
var isImage = function (url) {
var deferred = new $.Deferred();
$("<img>", {
src: url,
error: deferred.reject,
load: deferred.resolve
});
return deferred.promise();
};
$.expr[':'].emptyVal = function(obj){ $.expr[':'].emptyVal = function(obj){
return obj.value === ''; return obj.value === '';
}; };
...@@ -34,19 +44,27 @@ ...@@ -34,19 +44,27 @@
return false; return false;
}; };
$.fn.throttledHTML = _.throttle($.fn.html, 500);
$.fn.addHyperlinks = function () { $.fn.addHyperlinks = function () {
if (this.length > 0) { if (this.length > 0) {
this.each(function (i, obj) { this.each(function (i, obj) {
var x = $(obj).html(); var $obj = $(obj);
var list = x.match(/\b(https?:\/\/|www\.|https?:\/\/www\.)[^\s<]{2,200}\b/g ); var x = $obj.html();
if (list) { _.each(x.match(/\b(https?:\/\/|www\.|https?:\/\/www\.)[^\s<]{2,200}\b/g), function (url) {
for (i=0; i<list.length; i++) { isImage(url)
var prot = list[i].indexOf('http://') === 0 || list[i].indexOf('https://') === 0 ? '' : 'http://'; .then(function () {
var escaped_url = encodeURI(decodeURI(list[i])).replace(/[!'()]/g, escape).replace(/\*/g, "%2A"); event.target.className = 'chat-image';
x = x.replace(list[i], '<a target="_blank" rel="noopener" href="' + prot + escaped_url + '">'+ list[i] + '</a>' ); x = x.replace(url, event.target.outerHTML);
} $obj.throttledHTML(x);
} })
$(obj).html(x); .fail(function () {
var prot = url.indexOf('http://') === 0 || url.indexOf('https://') === 0 ? '' : 'http://';
var escaped_url = encodeURI(decodeURI(url)).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
x = x.replace(url, '<a target="_blank" rel="noopener" href="' + prot + escaped_url + '">'+ url + '</a>' );
$obj.throttledHTML(x);
});
});
}); });
} }
return this; return this;
......
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