diff --git a/css/converse.css b/css/converse.css index f9752d0c8..9477ca8a6 100644 --- a/css/converse.css +++ b/css/converse.css @@ -1347,6 +1347,9 @@ #conversejs .chatbox .chat-body .chat-info.chat-date { display: inline-block; margin-top: 1em; } + #conversejs .chatbox .chat-body .chat-image { + max-width: 100%; + max-height: 100%; } #conversejs .chatbox .chat-body .chat-message { margin: 0.3em; } #conversejs .chatbox .chat-body .chat-message span { diff --git a/sass/_chatbox.scss b/sass/_chatbox.scss index 423f35eee..31151f7cb 100644 --- a/sass/_chatbox.scss +++ b/sass/_chatbox.scss @@ -154,6 +154,10 @@ margin-top: 1em; } } + .chat-image { + max-width: 100%; + max-height: 100%; + } .chat-message { margin: 0.3em; span { diff --git a/spec/chatbox.js b/spec/chatbox.js index 4625e7205..a7b8864b3 100644 --- a/spec/chatbox.js +++ b/spec/chatbox.js @@ -819,19 +819,6 @@ expect(msg.html()).toEqual('<p>This message contains <em>some</em> <b>markup</b></p>'); }.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: www.opkode.com'); - }.bind(converse)); - it("should display emoticons correctly", function () { var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; test_utils.openChatBoxFor(contact_jid); @@ -856,40 +843,99 @@ } }.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'; 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(); + 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.text()).toEqual(message); + expect(msg.html()).toEqual('This message contains a hyperlink: www.opkode.com'); + }); + }); - var message = "http://www.opkode.com/'onmouseover='alert(1)'whatever"; - 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('http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever'); + 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('http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever'); - message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever'; - test_utils.sendMessage(view, message); - 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('http://www.opkode.com/"onmouseover="alert(1)"whatever'); + 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('http://www.opkode.com/"onmouseover="alert(1)"whatever'); - message = "https://en.wikipedia.org/wiki/Ender's_Game"; - test_utils.sendMessage(view, message); - 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('https://en.wikipedia.org/wiki/Ender\'s_Game'); + message = "https://en.wikipedia.org/wiki/Ender's_Game"; + 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('https://en.wikipedia.org/wiki/Ender\'s_Game'); - message = "https://en.wikipedia.org/wiki/Ender%27s_Game"; - test_utils.sendMessage(view, message); - 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('https://en.wikipedia.org/wiki/Ender%27s_Game'); - }.bind(converse)); + message = "https://en.wikipedia.org/wiki/Ender%27s_Game"; + 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('https://en.wikipedia.org/wiki/Ender%27s_Game'); + }); + }); + + 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(''); + }); + }); }.bind(converse)); diff --git a/src/utils.js b/src/utils.js index 227bfd8ef..0da69a171 100755 --- a/src/utils.js +++ b/src/utils.js @@ -20,6 +20,16 @@ 'list-multi': 'dropdown' }; + var isImage = function (url) { + var deferred = new $.Deferred(); + $("", { + src: url, + error: deferred.reject, + load: deferred.resolve + }); + return deferred.promise(); + }; + $.expr[':'].emptyVal = function(obj){ return obj.value === ''; }; @@ -34,19 +44,27 @@ return false; }; + $.fn.throttledHTML = _.throttle($.fn.html, 500); + $.fn.addHyperlinks = function () { if (this.length > 0) { this.each(function (i, obj) { - var x = $(obj).html(); - var list = x.match(/\b(https?:\/\/|www\.|https?:\/\/www\.)[^\s<]{2,200}\b/g ); - if (list) { - for (i=0; i'+ list[i] + '' ); - } - } - $(obj).html(x); + var $obj = $(obj); + var x = $obj.html(); + _.each(x.match(/\b(https?:\/\/|www\.|https?:\/\/www\.)[^\s<]{2,200}\b/g), function (url) { + isImage(url) + .then(function () { + event.target.className = 'chat-image'; + x = x.replace(url, event.target.outerHTML); + $obj.throttledHTML(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, ''+ url + '' ); + $obj.throttledHTML(x); + }); + }); }); } return this;