Commit a9714abce37feed0e829b0c5428df8fadb690524

Authored by Joenio Costa
Committed by Antonio Terceiro
1 parent 0cc4c43f

Some improvements/fixes in chat messages

- render URLs
- more emotes
- title to emoticons images

(ActionItem1635)
app/views/layouts/chat.rhtml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
6 <meta name="description" content="<%= @environment.name %>" /> 6 <meta name="description" content="<%= @environment.name %>" />
7 <link rel="shortcut icon" href="<%= image_path(theme_favicon) %>" type="image/x-icon" /> 7 <link rel="shortcut icon" href="<%= image_path(theme_favicon) %>" type="image/x-icon" />
8 - <%= javascript_include_tag 'jquery-latest', 'jquery.noconflict', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.scrollabletab', 'strophejs-1.0.1/strophe', 'application', 'chat', 'jquery.emoticon', '/designs/icons/pidgin/emoticons.js', :cache => 'cache-chat' %> 8 + <%= javascript_include_tag 'jquery-latest', 'jquery.noconflict', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.scrollabletab', 'strophejs-1.0.1/strophe', 'jquery.emoticon', '/designs/icons/pidgin/emoticons.js', 'ba-linkify', 'application', 'chat', :cache => 'cache-chat' %>
9 <%= stylesheet_link_tag noosfero_stylesheets, :cache => 'cache' %> 9 <%= stylesheet_link_tag noosfero_stylesheets, :cache => 'cache' %>
10 <%= stylesheet_link_tag icon_theme_stylesheet_path %> 10 <%= stylesheet_link_tag icon_theme_stylesheet_path %>
11 <%= stylesheet_link_tag theme_stylesheet_path %> 11 <%= stylesheet_link_tag theme_stylesheet_path %>
public/designs/icons/pidgin/emoticons.js
1 /* Copyright (c) 2010 Colivre - www.colivre.coop.br 1 /* Copyright (c) 2010 Colivre - www.colivre.coop.br
2 -/* Copyright (c) 2009 Marak Squires - www.maraksquires.com 2 + Copyright (c) 2009 Marak Squires - www.maraksquires.com
3 3
4 Permission is hereby granted, free of charge, to any person 4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation 5 obtaining a copy of this software and associated documentation
@@ -24,93 +24,128 @@ OTHER DEALINGS IN THE SOFTWARE. @@ -24,93 +24,128 @@ OTHER DEALINGS IN THE SOFTWARE.
24 */ 24 */
25 25
26 var emoticons = { 26 var emoticons = {
27 - "image_path": '/designs/icons/pidgin/pidgin/emotes/default/',  
28 - "emoticon": {  
29 - "::smile": {  
30 - "image": "mean.png",  
31 - "emotes": {  
32 - ":-)": "",  
33 - ":)": "",  
34 - ":]": "",  
35 - "=]": "",  
36 - "=)": ""  
37 - }  
38 - },  
39 - "::bigSmile": {  
40 - "image": "excited.png",  
41 - "emotes": {  
42 - ":D": "",  
43 - "=D": "",  
44 - ":-D": "",  
45 - "XD": "",  
46 - "BD": ""  
47 - }  
48 - },  
49 - "::shock": {  
50 - "image": "shocked.png",  
51 - "emotes": {  
52 - ":O": "",  
53 - ":0": "",  
54 - "=O": "",  
55 - ":-0": "",  
56 - ":-O": ""  
57 -  
58 - }  
59 - },  
60 - "::frown": {  
61 - "image": "sad.png",  
62 - "emotes": {  
63 - ":-(": "",  
64 - "=(": "",  
65 - ":[": "",  
66 - ":<": "",  
67 - "=[": "",  
68 - ":(": "",  
69 - ":-\\": ""  
70 - }  
71 - },  
72 - "::tongue": {  
73 - "image": "tongue.png",  
74 - "emotes": {  
75 - ":P": "",  
76 - "=P": "",  
77 - "XP": "",  
78 - }  
79 - },  
80 - "::bored": {  
81 - "image": "thinking.png",  
82 - "emotes": {  
83 - "=I": "",  
84 - ":/": "",  
85 - ":-\\": "",  
86 - ":|": ""  
87 - }  
88 - },  
89 - "::wink": {  
90 - "image": "wink.png",  
91 - "emotes": {  
92 - ";-)": "",  
93 - ";)": "",  
94 - ";]": ""  
95 - }  
96 - },  
97 - "::love": {  
98 - "image": "rose.png",  
99 - "emotes": {  
100 - "<3": "",  
101 - "<3": "",  
102 - "S2": "",  
103 - ":3": ""  
104 - }  
105 - }  
106 - ,  
107 - "::confused": {  
108 - "image": "confused.png",  
109 - "emotes": {  
110 - ":S": "",  
111 - "=S": "",  
112 - ":\?": ""  
113 - }  
114 - }  
115 - } 27 + "image_path": '/designs/icons/pidgin/pidgin/emotes/default/',
  28 + "emoticon": {
  29 + "::smile": {
  30 + "image": "mean.png",
  31 + "emotes": {
  32 + ":-)": "",
  33 + ":)": "",
  34 + ":]": "",
  35 + "=]": "",
  36 + "=)": ""
  37 + }
  38 + },
  39 + "::bigSmile": {
  40 + "image": "excited.png",
  41 + "emotes": {
  42 + ":D": "",
  43 + "=D": "",
  44 + ":-D": ""
  45 + }
  46 + },
  47 + "::shock": {
  48 + "image": "shocked.png",
  49 + "emotes": {
  50 + ":O": "",
  51 + ":0": "",
  52 + "=O": "",
  53 + ":-0": "",
  54 + ":-O": ""
  55 + }
  56 + },
  57 + "::frown": {
  58 + "image": "sad.png",
  59 + "emotes": {
  60 + ":-(": "",
  61 + "=(": "",
  62 + ":[": "",
  63 + ":<": "",
  64 + "=[": "",
  65 + ":(": "",
  66 + ":-\\": ""
  67 + }
  68 + },
  69 + "::tongue": {
  70 + "image": "tongue.png",
  71 + "emotes": {
  72 + ":P": "",
  73 + ":-P": "",
  74 + "=P": ""
  75 + }
  76 + },
  77 + "::bored": {
  78 + "image": "thinking.png",
  79 + "emotes": {
  80 + "=I": "",
  81 + ":/": "",
  82 + ":-\\": "",
  83 + ":|": ""
  84 + }
  85 + },
  86 + "::wink": {
  87 + "image": "wink.png",
  88 + "emotes": {
  89 + ";-)": "",
  90 + ";)": "",
  91 + ";]": ""
  92 + }
  93 + },
  94 + "::love": {
  95 + "image": "in_love.png",
  96 + "emotes": {
  97 + "<3": "",
  98 + "<333": ""
  99 + }
  100 + },
  101 + "::confused": {
  102 + "image": "confused.png",
  103 + "emotes": {
  104 + ":S": "",
  105 + "=S": "",
  106 + ":\?": ""
  107 + }
  108 + },
  109 + "::crying": {
  110 + "image": "crying.png",
  111 + "emotes": {
  112 + ":'(": "",
  113 + ";*(": ""
  114 + }
  115 + },
  116 + "::kiss": {
  117 + "image": "kiss.png",
  118 + "emotes": {
  119 + ":-*": "",
  120 + ":*": ""
  121 + }
  122 + },
  123 + "::evil": {
  124 + "image": "devil.png",
  125 + "emotes": {
  126 + ">:)": "",
  127 + ">;)": "",
  128 + ">:-)": ""
  129 + }
  130 + },
  131 + "::nolove": {
  132 + "image": "love-over.png",
  133 + "emotes": {
  134 + "</3": ""
  135 + }
  136 + },
  137 + "::praise": {
  138 + "image": "victory.png",
  139 + "emotes": {
  140 + "\\o/": ""
  141 + }
  142 + },
  143 + "::angel": {
  144 + "image": "angel.png",
  145 + "emotes": {
  146 + "O:-)": "",
  147 + "O:)": ""
  148 + }
  149 + }
  150 + }
116 }; 151 };
public/javascripts/ba-linkify.js 0 → 100644
@@ -0,0 +1,214 @@ @@ -0,0 +1,214 @@
  1 +/*!
  2 + * JavaScript Linkify - v0.3 - 6/27/2009
  3 + * http://benalman.com/projects/javascript-linkify/
  4 + *
  5 + * Copyright (c) 2009 "Cowboy" Ben Alman
  6 + * Dual licensed under the MIT and GPL licenses.
  7 + * http://benalman.com/about/license/
  8 + *
  9 + * Some regexps adapted from http://userscripts.org/scripts/review/7122
  10 + */
  11 +
  12 +// Script: JavaScript Linkify: Process links in text!
  13 +//
  14 +// *Version: 0.3, Last updated: 6/27/2009*
  15 +//
  16 +// Project Home - http://benalman.com/projects/javascript-linkify/
  17 +// GitHub - http://github.com/cowboy/javascript-linkify/
  18 +// Source - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.js
  19 +// (Minified) - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.min.js (2.8kb)
  20 +//
  21 +// About: License
  22 +//
  23 +// Copyright (c) 2009 "Cowboy" Ben Alman,
  24 +// Dual licensed under the MIT and GPL licenses.
  25 +// http://benalman.com/about/license/
  26 +//
  27 +// About: Examples
  28 +//
  29 +// This working example, complete with fully commented code, illustrates one way
  30 +// in which this code can be used.
  31 +//
  32 +// Linkify - http://benalman.com/code/projects/javascript-linkify/examples/linkify/
  33 +//
  34 +// About: Support and Testing
  35 +//
  36 +// Information about what browsers this code has been tested in.
  37 +//
  38 +// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.
  39 +//
  40 +// About: Release History
  41 +//
  42 +// 0.3 - (6/27/2009) Initial release
  43 +
  44 +// Function: linkify
  45 +//
  46 +// Turn text into linkified html.
  47 +//
  48 +// Usage:
  49 +//
  50 +// > var html = linkify( text [, options ] );
  51 +//
  52 +// Arguments:
  53 +//
  54 +// text - (String) Non-HTML text containing links to be parsed.
  55 +// options - (Object) An optional object containing linkify parse options.
  56 +//
  57 +// Options:
  58 +//
  59 +// callback (Function) - If specified, this will be called once for each link-
  60 +// or non-link-chunk with two arguments, text and href. If the chunk is
  61 +// non-link, href will be omitted. If unspecified, the default linkification
  62 +// callback is used.
  63 +// punct_regexp (RegExp) - A RegExp that will be used to trim trailing
  64 +// punctuation from links, instead of the default. If set to null, trailing
  65 +// punctuation will not be trimmed.
  66 +//
  67 +// Returns:
  68 +//
  69 +// (String) An HTML string containing links.
  70 +
  71 +window.linkify = (function(){
  72 + var
  73 + SCHEME = "[a-z\\d.-]+://",
  74 + IPV4 = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
  75 + HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
  76 + TLD = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
  77 + HOST_OR_IP = "(?:" + HOSTNAME + TLD + "|" + IPV4 + ")",
  78 + PATH = "(?:[;/][^#?<>\\s]*)?",
  79 + QUERY_FRAG = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",
  80 + URI1 = "\\b" + SCHEME + "[^<>\\s]+",
  81 + URI2 = "\\b" + HOST_OR_IP + PATH + QUERY_FRAG + "(?!\\w)",
  82 +
  83 + MAILTO = "mailto:",
  84 + EMAIL = "(?:" + MAILTO + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + HOST_OR_IP + QUERY_FRAG + "(?!\\w)",
  85 +
  86 + URI_RE = new RegExp( "(?:" + URI1 + "|" + URI2 + "|" + EMAIL + ")", "ig" ),
  87 + SCHEME_RE = new RegExp( "^" + SCHEME, "i" ),
  88 +
  89 + quotes = {
  90 + "'": "`",
  91 + '>': '<',
  92 + ')': '(',
  93 + ']': '[',
  94 + '}': '{',
  95 + '»': '«',
  96 + '›': '‹'
  97 + },
  98 +
  99 + default_options = {
  100 + callback: function( text, href ) {
  101 + return href ? '<a href="' + href + '" title="' + href + '">' + text + '</a>' : text;
  102 + },
  103 + punct_regexp: /(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
  104 + };
  105 +
  106 + return function( txt, options ) {
  107 + options = options || {};
  108 +
  109 + // Temp variables.
  110 + var arr,
  111 + i,
  112 + link,
  113 + href,
  114 +
  115 + // Output HTML.
  116 + html = '',
  117 +
  118 + // Store text / link parts, in order, for re-combination.
  119 + parts = [],
  120 +
  121 + // Used for keeping track of indices in the text.
  122 + idx_prev,
  123 + idx_last,
  124 + idx,
  125 + link_last,
  126 +
  127 + // Used for trimming trailing punctuation and quotes from links.
  128 + matches_begin,
  129 + matches_end,
  130 + quote_begin,
  131 + quote_end;
  132 +
  133 + // Initialize options.
  134 + for ( i in default_options ) {
  135 + if ( options[ i ] === undefined ) {
  136 + options[ i ] = default_options[ i ];
  137 + }
  138 + }
  139 +
  140 + // Find links.
  141 + while ( arr = URI_RE.exec( txt ) ) {
  142 +
  143 + link = arr[0];
  144 + idx_last = URI_RE.lastIndex;
  145 + idx = idx_last - link.length;
  146 +
  147 + // Not a link if preceded by certain characters.
  148 + if ( /[\/:]/.test( txt.charAt( idx - 1 ) ) ) {
  149 + continue;
  150 + }
  151 +
  152 + // Trim trailing punctuation.
  153 + do {
  154 + // If no changes are made, we don't want to loop forever!
  155 + link_last = link;
  156 +
  157 + quote_end = link.substr( -1 )
  158 + quote_begin = quotes[ quote_end ];
  159 +
  160 + // Ending quote character?
  161 + if ( quote_begin ) {
  162 + matches_begin = link.match( new RegExp( '\\' + quote_begin + '(?!$)', 'g' ) );
  163 + matches_end = link.match( new RegExp( '\\' + quote_end, 'g' ) );
  164 +
  165 + // If quotes are unbalanced, remove trailing quote character.
  166 + if ( ( matches_begin ? matches_begin.length : 0 ) < ( matches_end ? matches_end.length : 0 ) ) {
  167 + link = link.substr( 0, link.length - 1 );
  168 + idx_last--;
  169 + }
  170 + }
  171 +
  172 + // Ending non-quote punctuation character?
  173 + if ( options.punct_regexp ) {
  174 + link = link.replace( options.punct_regexp, function(a){
  175 + idx_last -= a.length;
  176 + return '';
  177 + });
  178 + }
  179 + } while ( link.length && link !== link_last );
  180 +
  181 + href = link;
  182 +
  183 + // Add appropriate protocol to naked links.
  184 + if ( !SCHEME_RE.test( href ) ) {
  185 + href = ( href.indexOf( '@' ) !== -1 ? ( !href.indexOf( MAILTO ) ? '' : MAILTO )
  186 + : !href.indexOf( 'irc.' ) ? 'irc://'
  187 + : !href.indexOf( 'ftp.' ) ? 'ftp://'
  188 + : 'http://' )
  189 + + href;
  190 + }
  191 +
  192 + // Push preceding non-link text onto the array.
  193 + if ( idx_prev != idx ) {
  194 + parts.push([ txt.slice( idx_prev, idx ) ]);
  195 + idx_prev = idx_last;
  196 + }
  197 +
  198 + // Push massaged link onto the array
  199 + parts.push([ link, href ]);
  200 + };
  201 +
  202 + // Push remaining non-link text onto the array.
  203 + parts.push([ txt.substr( idx_prev ) ]);
  204 +
  205 + // Process the array items.
  206 + for ( i = 0; i < parts.length; i++ ) {
  207 + html += options.callback.apply( window, parts[i] );
  208 + }
  209 +
  210 + // In case of catastrophic failure, return the original text;
  211 + return html || txt;
  212 + };
  213 +
  214 +})();
0 \ No newline at end of file 215 \ No newline at end of file
public/javascripts/chat.js
@@ -50,11 +50,21 @@ jQuery(function($) { @@ -50,11 +50,21 @@ jQuery(function($) {
50 $('#' + jid_id).parent('li').remove(); 50 $('#' + jid_id).parent('li').remove();
51 }, 51 },
52 52
  53 + render_body_message: function(body) {
  54 + body = $().emoticon(body);
  55 + body = linkify(body, {
  56 + callback: function(text, href) {
  57 + return href ? '<a href="' + href + '" title="' + href + '" target="_blank">' + text + '</a>' : text;
  58 + }
  59 + });
  60 + return body;
  61 + },
  62 +
53 show_message: function (jid, body, who) { 63 show_message: function (jid, body, who) {
54 jid_id = Jabber.jid_to_id(jid); 64 jid_id = Jabber.jid_to_id(jid);
55 if (body) { 65 if (body) {
56 var tab_id = '#' + Jabber.tab_prefix + jid_id; 66 var tab_id = '#' + Jabber.tab_prefix + jid_id;
57 - body = $().emoticon(body); 67 + body = Jabber.render_body_message(body);
58 if ($(tab_id).find('.message').length > 0 && $(tab_id).find('.message:last').hasClass(who)) { 68 if ($(tab_id).find('.message').length > 0 && $(tab_id).find('.message:last').hasClass(who)) {
59 $(tab_id).find('.history').find('.message:last').append('<p>' + body + '</p>'); 69 $(tab_id).find('.history').find('.message:last').append('<p>' + body + '</p>');
60 } 70 }
@@ -75,7 +85,7 @@ jQuery(function($) { @@ -75,7 +85,7 @@ jQuery(function($) {
75 .replace('%{avatar_url}', '/chat/avatar/' + identifier); 85 .replace('%{avatar_url}', '/chat/avatar/' + identifier);
76 $('#' + Jabber.tab_prefix + jid_id).find('.history').append(message_html); 86 $('#' + Jabber.tab_prefix + jid_id).find('.history').append(message_html);
77 } 87 }
78 - $(tab_id).find('.history').scrollTo('100%'); 88 + $(tab_id).find('.history').scrollTo({top:'100%', left:'0%'});
79 if (who === "other" && $(tab_id).find('.history:visible').length == 0) { 89 if (who === "other" && $(tab_id).find('.history:visible').length == 0) {
80 count_unread_messages(jid_id); 90 count_unread_messages(jid_id);
81 } 91 }
@@ -361,7 +371,7 @@ jQuery(function($) { @@ -361,7 +371,7 @@ jQuery(function($) {
361 // TODO notify window close 371 // TODO notify window close
362 }, 372 },
363 show: function(event, ui) { 373 show: function(event, ui) {
364 - $(ui.panel).find('.history').scrollTo('100%'); 374 + $(ui.panel).find('.history').scrollTo({top:'100%', left:'0%'});
365 $(ui.panel).find('textarea').focus(); 375 $(ui.panel).find('textarea').focus();
366 var jid_id = ui.panel.id.replace('conversation-', ''); 376 var jid_id = ui.panel.id.replace('conversation-', '');
367 count_unread_messages(jid_id, true); 377 count_unread_messages(jid_id, true);
public/javascripts/jquery.emoticon.js
@@ -44,8 +44,8 @@ RegExp.escape = function(text) { @@ -44,8 +44,8 @@ RegExp.escape = function(text) {
44 for( var a in emoticons.emoticon ) { 44 for( var a in emoticons.emoticon ) {
45 emoticon = emoticons.emoticon[a]; 45 emoticon = emoticons.emoticon[a];
46 for( var emote in emoticon.emotes ) { 46 for( var emote in emoticon.emotes ) {
47 - emote = RegExp.escape(emote);  
48 - newText = newText.replace( new RegExp( emote, 'gi' ), '<img src="'+emoticons.image_path + emoticon.image + '" />'); 47 + emote_pattern = RegExp.escape(emote) + "([^/]|$)";
  48 + newText = newText.replace( new RegExp( emote_pattern, 'gi' ), '<img src="'+emoticons.image_path + emoticon.image + '" alt="'+ emote +'" title="'+ emote +'" />$1');
49 } 49 }
50 } 50 }
51 return newText; 51 return newText;
public/stylesheets/application.css
@@ -4536,6 +4536,9 @@ h1#agenda-title { @@ -4536,6 +4536,9 @@ h1#agenda-title {
4536 overflow-y: scroll; 4536 overflow-y: scroll;
4537 padding-top: 5px; 4537 padding-top: 5px;
4538 } 4538 }
  4539 +.msie7 #chat-window .conversation .history {
  4540 + overflow-x: hidden;
  4541 +}
4539 #chat .unread-messages { 4542 #chat .unread-messages {
4540 background: red; 4543 background: red;
4541 position: absolute; 4544 position: absolute;