Commit 1c125da767ba502b800bd58896ed1aaac828a092
Committed by
Joenio Costa
1 parent
c9fb1096
Exists in
master
and in
29 other branches
chat fixes and new features
* try to reconnect if connection fails * fix ordering * reconnect if connection is lost but user status is not offline * remove some anchors * keep track of buddies order on the client * tweak notification to work on chromium * respect async request order * focus on window when notification clicked * deal with window visibility * add chat-buttons * fix recent_conversations * fix sorting and join room on opening * remove obsolete code * adjusting indentation (sorry but I needed too...) * fix load_conversation * return identifier as key on avatars action * fix typo * add timeout to notification * deal properly with offline messages * fix notification sound * clear unread after loading conversation on background * ignore message from room * add unread messages counter on chat-label * as for notification permission as soon as the chat connects * add profile_info_action to open chat * using jid to save message instead of identifier * add profile_info_action to open chat * as for notification permission as soon as the chat connects * add unread messages counter on chat-label Squashed and Signed-off-by: Joenio Costa <joenio@colivre.coop.br>
Showing
21 changed files
with
1050 additions
and
817 deletions
Show diff stats
app/controllers/public/chat_controller.rb
@@ -2,6 +2,7 @@ class ChatController < PublicController | @@ -2,6 +2,7 @@ class ChatController < PublicController | ||
2 | 2 | ||
3 | before_filter :login_required | 3 | before_filter :login_required |
4 | before_filter :check_environment_feature | 4 | before_filter :check_environment_feature |
5 | + before_filter :can_send_message, :only => :register_message | ||
5 | 6 | ||
6 | def start_session | 7 | def start_session |
7 | login = user.jid | 8 | login = user.jid |
@@ -54,6 +55,16 @@ class ChatController < PublicController | @@ -54,6 +55,16 @@ class ChatController < PublicController | ||
54 | end | 55 | end |
55 | end | 56 | end |
56 | 57 | ||
58 | + def avatars | ||
59 | + profiles = environment.profiles.where(:identifier => params[:profiles]) | ||
60 | + avatar_map = profiles.inject({}) do |result, profile| | ||
61 | + result[profile.identifier] = profile_icon(profile, :minor) | ||
62 | + result | ||
63 | + end | ||
64 | + | ||
65 | + render_json avatar_map | ||
66 | + end | ||
67 | + | ||
57 | def update_presence_status | 68 | def update_presence_status |
58 | if request.xhr? | 69 | if request.xhr? |
59 | current_user.update_attributes({:chat_status_at => DateTime.now}.merge(params[:status] || {})) | 70 | current_user.update_attributes({:chat_status_at => DateTime.now}.merge(params[:status] || {})) |
@@ -62,11 +73,17 @@ class ChatController < PublicController | @@ -62,11 +73,17 @@ class ChatController < PublicController | ||
62 | end | 73 | end |
63 | 74 | ||
64 | def save_message | 75 | def save_message |
65 | - to = environment.profiles.find_by_identifier(params[:to]) | ||
66 | - body = params[:body] | ||
67 | - | ||
68 | - ChatMessage.create!(:to => to, :from => user, :body => body) | ||
69 | - render :text => 'ok' | 76 | + if request.post? |
77 | + to = environment.profiles.where(:identifier => params[:to]).first | ||
78 | + body = params[:body] | ||
79 | + | ||
80 | + begin | ||
81 | + ChatMessage.create!(:to => to, :from => user, :body => body) | ||
82 | + return render_json({:status => 0}) | ||
83 | + rescue Exception => exception | ||
84 | + return render_json({:status => 3, :message => exception.to_s, :backtrace => exception.backtrace}) | ||
85 | + end | ||
86 | + end | ||
70 | end | 87 | end |
71 | 88 | ||
72 | def recent_messages | 89 | def recent_messages |
@@ -90,8 +107,9 @@ class ChatController < PublicController | @@ -90,8 +107,9 @@ class ChatController < PublicController | ||
90 | end | 107 | end |
91 | 108 | ||
92 | def recent_conversations | 109 | def recent_conversations |
93 | - conversations_order = ActiveRecord::Base.connection.execute("select profiles.identifier from profiles inner join (select distinct r.id as id, MAX(r.created_at) as created_at from (select from_id, to_id, created_at, (case when from_id=#{user.id} then to_id else from_id end) as id from chat_messages where from_id=#{user.id} or to_id=#{user.id}) as r group by id order by created_at desc, id) as t on profiles.id=t.id order by t.created_at desc").entries.map {|e| e['identifier']} | ||
94 | - render :json => {:order => conversations_order.reverse, :domain => environment.default_hostname.gsub('.','-')}.to_json | 110 | + profiles = Profile.find_by_sql("select profiles.* from profiles inner join (select distinct r.id as id, MAX(r.created_at) as created_at from (select from_id, to_id, created_at, (case when from_id=#{user.id} then to_id else from_id end) as id from chat_messages where from_id=#{user.id} or to_id=#{user.id}) as r group by id order by created_at desc, id) as t on profiles.id=t.id order by t.created_at desc") |
111 | + jids = profiles.map(&:jid).reverse | ||
112 | + render :json => jids.to_json | ||
95 | end | 113 | end |
96 | 114 | ||
97 | #TODO Ideally this is done through roster table on ejabberd. | 115 | #TODO Ideally this is done through roster table on ejabberd. |
@@ -108,4 +126,14 @@ class ChatController < PublicController | @@ -108,4 +126,14 @@ class ChatController < PublicController | ||
108 | end | 126 | end |
109 | end | 127 | end |
110 | 128 | ||
129 | + def can_send_message | ||
130 | + return render_json({:status => 1, :message => 'Missing parameters!'}) if params[:from].nil? || params[:to].nil? || params[:message].nil? | ||
131 | + return render_json({:status => 2, :message => 'You can not send message as another user!'}) if params[:from] != user.jid | ||
132 | + # TODO Maybe register the jid in a table someday to avoid this below | ||
133 | + return render_json({:status => 3, :messsage => 'You can not send messages to strangers!'}) if user.friends.where(:identifier => params[:to].split('@').first).blank? | ||
134 | + end | ||
135 | + | ||
136 | + def render_json(result) | ||
137 | + render :text => result.to_json | ||
138 | + end | ||
111 | end | 139 | end |
app/helpers/chat_helper.rb
@@ -9,12 +9,12 @@ module ChatHelper | @@ -9,12 +9,12 @@ module ChatHelper | ||
9 | avatar = profile_image(user, :portrait, :class => 'avatar') | 9 | avatar = profile_image(user, :portrait, :class => 'avatar') |
10 | content_tag('span', | 10 | content_tag('span', |
11 | link_to(avatar + content_tag('span', user.name) + ui_icon('ui-icon-triangle-1-s'), | 11 | link_to(avatar + content_tag('span', user.name) + ui_icon('ui-icon-triangle-1-s'), |
12 | - '#', | 12 | + '', |
13 | :onclick => 'toggleMenu(this); return false', | 13 | :onclick => 'toggleMenu(this); return false', |
14 | :class => icon_class + ' simplemenu-trigger' | 14 | :class => icon_class + ' simplemenu-trigger' |
15 | ) + | 15 | ) + |
16 | content_tag('ul', | 16 | content_tag('ul', |
17 | - links.map{|link| content_tag('li', link_to(link[1], '#', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"), | 17 | + links.map{|link| content_tag('li', link_to(link[1], '', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"), |
18 | :style => 'display: none; z-index: 100', | 18 | :style => 'display: none; z-index: 100', |
19 | :class => 'simplemenu-submenu' | 19 | :class => 'simplemenu-submenu' |
20 | ), | 20 | ), |
app/models/chat_message.rb
@@ -3,5 +3,4 @@ class ChatMessage < ActiveRecord::Base | @@ -3,5 +3,4 @@ class ChatMessage < ActiveRecord::Base | ||
3 | 3 | ||
4 | belongs_to :to, :class_name => 'Profile' | 4 | belongs_to :to, :class_name => 'Profile' |
5 | belongs_to :from, :class_name => 'Profile' | 5 | belongs_to :from, :class_name => 'Profile' |
6 | - | ||
7 | end | 6 | end |
app/views/blocks/profile_info_actions/_community.html.erb
@@ -13,8 +13,6 @@ | @@ -13,8 +13,6 @@ | ||
13 | </li> | 13 | </li> |
14 | <% end %> | 14 | <% end %> |
15 | 15 | ||
16 | - <li><%= report_abuse(profile, :button) %></li> | ||
17 | - | ||
18 | - <%= render_environment_features(:profile_actions) %> | 16 | + <%= render :partial => 'blocks/profile_info_actions/common' %> |
19 | <% end %> | 17 | <% end %> |
20 | </ul> | 18 | </ul> |
app/views/blocks/profile_info_actions/_enterprise.html.erb
@@ -8,5 +8,5 @@ | @@ -8,5 +8,5 @@ | ||
8 | <li><%= button(:'menu-mail', _('Send an e-mail'), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}, {:id => 'enterprise-contact-button'} ) %></li> | 8 | <li><%= button(:'menu-mail', _('Send an e-mail'), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}, {:id => 'enterprise-contact-button'} ) %></li> |
9 | <% end %> | 9 | <% end %> |
10 | 10 | ||
11 | - <li><%= report_abuse(profile, :button) %></li> | 11 | + <%= render :partial => 'blocks/profile_info_actions/common' %> |
12 | </ul> | 12 | </ul> |
app/views/blocks/profile_info_actions/_person.html.erb
@@ -11,6 +11,6 @@ | @@ -11,6 +11,6 @@ | ||
11 | <li><%= button(:back, _('Send an e-mail'), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}) %></li> | 11 | <li><%= button(:back, _('Send an e-mail'), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}) %></li> |
12 | <% end %> | 12 | <% end %> |
13 | 13 | ||
14 | - <li><%= report_abuse(profile, :button) %></li> | 14 | + <%= render :partial => 'blocks/profile_info_actions/common' %> |
15 | <% end %> | 15 | <% end %> |
16 | </ul> | 16 | </ul> |
app/views/chat/start_session_error.html.erb
1 | <p> | 1 | <p> |
2 | <%= ui_icon('ui-icon-alert') %> | 2 | <%= ui_icon('ui-icon-alert') %> |
3 | -<%= _('Could not connect to chat') %>, <a id='chat-retry' href='#' data-jid='<%= user.jid %>'><%= _('try again') %></a>. | 3 | +<%= _('Could not connect to chat') %>, <a id='chat-retry' href='' data-jid='<%= user.jid %>'><%= _('try again') %></a>. |
4 | </p> | 4 | </p> |
app/views/shared/logged_in/xmpp_chat.html.erb
@@ -7,13 +7,13 @@ | @@ -7,13 +7,13 @@ | ||
7 | var $own_name = '<%= user.name %>'; | 7 | var $own_name = '<%= user.name %>'; |
8 | var $muc_domain = '<%= "conference.#{environment.default_hostname}" %>'; | 8 | var $muc_domain = '<%= "conference.#{environment.default_hostname}" %>'; |
9 | var $bosh_service = '//<%= environment.default_hostname %>/http-bind'; | 9 | var $bosh_service = '//<%= environment.default_hostname %>/http-bind'; |
10 | - var $user_unavailable_error = '<%= _("<strong>ooops!</strong> The message could not be sent because the user is not online") %>'; | 10 | + var $user_unavailable_error = '<%= _("The user is not online now. He/She will receive these messages as soon as he/she gets online.") %>'; |
11 | var $update_presence_status_every = <%= User.expires_chat_status_every.minutes %>; | 11 | var $update_presence_status_every = <%= User.expires_chat_status_every.minutes %>; |
12 | var $presence = '<%= current_user.last_chat_status %>'; | 12 | var $presence = '<%= current_user.last_chat_status %>'; |
13 | </script> | 13 | </script> |
14 | 14 | ||
15 | - | ||
16 | <div id="chat-label"> | 15 | <div id="chat-label"> |
16 | + <span id="unread-messages"></span> | ||
17 | <span class="right-arrow">▶</span> | 17 | <span class="right-arrow">▶</span> |
18 | <span class="title"><%= _('Chat') %></span> | 18 | <span class="title"><%= _('Chat') %></span> |
19 | </div> | 19 | </div> |
@@ -98,10 +98,5 @@ | @@ -98,10 +98,5 @@ | ||
98 | </div> | 98 | </div> |
99 | </div> | 99 | </div> |
100 | </div> | 100 | </div> |
101 | - | ||
102 | - <div class="error-message"> | ||
103 | - <span class='error'>%{text}</span> | ||
104 | - </div> | ||
105 | - | ||
106 | </div> | 101 | </div> |
107 | </div> | 102 | </div> |
@@ -0,0 +1,8 @@ | @@ -0,0 +1,8 @@ | ||
1 | +<% label_name = profile.person? ? _('Open chat') : _('Join chat room') %> | ||
2 | +<% display = profile.person? ? profile.friends.include?(user) : profile.members.include?(user) %> | ||
3 | + | ||
4 | +<% if display %> | ||
5 | + <li> | ||
6 | + <%= button(:chat, label_name , {}, :class => 'open-conversation', 'data-jid' => profile.jid) %> | ||
7 | + </li> | ||
8 | +<% end %> |
db/migrate/20140820173129_create_chat_messages.rb
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +class CreateChatMessages < ActiveRecord::Migration | ||
2 | + def up | ||
3 | + create_table :chat_messages do |t| | ||
4 | + t.references :from | ||
5 | + t.references :to | ||
6 | + t.text :body | ||
7 | + t.timestamps | ||
8 | + end | ||
9 | + add_index :chat_messages, :from_id | ||
10 | + add_index :chat_messages, :to_id | ||
11 | + add_index :chat_messages, :created_at | ||
12 | + end | ||
13 | + | ||
14 | + def down | ||
15 | + drop_table :chat_messages | ||
16 | + remove_index :chat_messages, :from | ||
17 | + remove_index :chat_messages, :to | ||
18 | + remove_index :chat_messages, :created_at | ||
19 | + end | ||
20 | +end |
db/schema.rb
@@ -245,13 +245,17 @@ ActiveRecord::Schema.define(:version => 20150513213939) do | @@ -245,13 +245,17 @@ ActiveRecord::Schema.define(:version => 20150513213939) do | ||
245 | end | 245 | end |
246 | 246 | ||
247 | create_table "chat_messages", :force => true do |t| | 247 | create_table "chat_messages", :force => true do |t| |
248 | - t.integer "to_id" | ||
249 | t.integer "from_id" | 248 | t.integer "from_id" |
250 | - t.string "body" | 249 | + t.integer "to_id" |
250 | + t.text "body" | ||
251 | t.datetime "created_at", :null => false | 251 | t.datetime "created_at", :null => false |
252 | t.datetime "updated_at", :null => false | 252 | t.datetime "updated_at", :null => false |
253 | end | 253 | end |
254 | 254 | ||
255 | + add_index "chat_messages", ["created_at"], :name => "index_chat_messages_on_created_at" | ||
256 | + add_index "chat_messages", ["from_id"], :name => "index_chat_messages_on_from_id" | ||
257 | + add_index "chat_messages", ["to_id"], :name => "index_chat_messages_on_to_id" | ||
258 | + | ||
255 | create_table "comments", :force => true do |t| | 259 | create_table "comments", :force => true do |t| |
256 | t.string "title" | 260 | t.string "title" |
257 | t.text "body" | 261 | t.text "body" |
debian/noosfero.install
debian/update-noosfero-apache
@@ -18,9 +18,20 @@ if test -x /usr/share/noosfero/script/apacheconf; then | @@ -18,9 +18,20 @@ if test -x /usr/share/noosfero/script/apacheconf; then | ||
18 | fi | 18 | fi |
19 | 19 | ||
20 | apache_site='/etc/apache2/sites-available/noosfero' | 20 | apache_site='/etc/apache2/sites-available/noosfero' |
21 | + apache_site_configs='/etc/noosfero/apache.d' | ||
21 | if ! test -e "$apache_site"; then | 22 | if ! test -e "$apache_site"; then |
22 | echo "Generating apache virtual host ..." | 23 | echo "Generating apache virtual host ..." |
23 | cd /usr/share/noosfero && su noosfero -c "RAILS_ENV=production ./script/apacheconf virtualhosts" > "$apache_site" | 24 | cd /usr/share/noosfero && su noosfero -c "RAILS_ENV=production ./script/apacheconf virtualhosts" > "$apache_site" |
25 | + if ! test -d "$apache_site_configs"; then | ||
26 | + echo "Creating noosfero site config folder ..." | ||
27 | + mkdir $apache_site_configs | ||
28 | + fi | ||
29 | + else | ||
30 | + pattern="Include \/etc\/noosfero\/apache\/virtualhost.conf" | ||
31 | + include="Include \/etc\/noosfero\/apache.d\/*" | ||
32 | + if ! cat $apache_site | grep "^ *$include" > /dev/null ; then | ||
33 | + sed -i "s/.*$pattern.*/ $include\n&/" $apache_site | ||
34 | + fi | ||
24 | fi | 35 | fi |
25 | 36 | ||
26 | echo 'Noosfero Apache configuration updated.' | 37 | echo 'Noosfero Apache configuration updated.' |
public/javascripts/application.js
@@ -1140,10 +1140,20 @@ function notifyMe(title, options) { | @@ -1140,10 +1140,20 @@ function notifyMe(title, options) { | ||
1140 | 1140 | ||
1141 | // If the user is okay, let's create a notification | 1141 | // If the user is okay, let's create a notification |
1142 | if (permission === "granted") { | 1142 | if (permission === "granted") { |
1143 | - notification = new Notification(title, options); | 1143 | + notification = new Notification(title, options); |
1144 | } | 1144 | } |
1145 | }); | 1145 | }); |
1146 | } | 1146 | } |
1147 | + | ||
1148 | + setTimeout(function() {notification.close()}, 5000); | ||
1149 | + notification.onclick = function(){ | ||
1150 | + notification.close(); | ||
1151 | + // Chromium tweak | ||
1152 | + window.open().close(); | ||
1153 | + window.focus(); | ||
1154 | + this.cancel(); | ||
1155 | + }; | ||
1156 | + | ||
1147 | return notification; | 1157 | return notification; |
1148 | // At last, if the user already denied any notification, and you | 1158 | // At last, if the user already denied any notification, and you |
1149 | // want to be respectful there is no need to bother them any more. | 1159 | // want to be respectful there is no need to bother them any more. |
public/javascripts/chat.js
1 | /* XMPP/Jabber Noosfero's client | 1 | /* XMPP/Jabber Noosfero's client |
2 | 2 | ||
3 | - XMPP Core: | ||
4 | - http://xmpp.org/rfcs/rfc3920.html | 3 | +XMPP Core: |
4 | +http://xmpp.org/rfcs/rfc3920.html | ||
5 | 5 | ||
6 | - MUC support: | ||
7 | - http://xmpp.org/extensions/xep-0045.html | 6 | +MUC support: |
7 | +http://xmpp.org/extensions/xep-0045.html | ||
8 | 8 | ||
9 | - Messages and presence: | ||
10 | - http://xmpp.org/rfcs/rfc3921.html | 9 | +Messages and presence: |
10 | +http://xmpp.org/rfcs/rfc3921.html | ||
11 | */ | 11 | */ |
12 | 12 | ||
13 | jQuery(function($) { | 13 | jQuery(function($) { |
14 | - // extending the current namespaces in Strophe.NS | ||
15 | - Strophe.addNamespace('MUC_USER', 'http://jabber.org/protocol/muc#user'); | ||
16 | - Strophe.addNamespace('MUC_OWNER', 'http://jabber.org/protocol/muc#owner'); | ||
17 | - Strophe.addNamespace('CHAT_STATES', 'http://jabber.org/protocol/chatstates'); | ||
18 | - Strophe.addNamespace('DATA_FORMS', 'jabber:x:data'); | ||
19 | - | ||
20 | - var Jabber = { | ||
21 | - debug: true, | ||
22 | - connection: null, | ||
23 | - bosh_service: $bosh_service, | ||
24 | - muc_domain: $muc_domain, | ||
25 | - muc_supported: false, | ||
26 | - presence_status: '', | ||
27 | - conversation_prefix: 'conversation-', | ||
28 | - jids: {}, | ||
29 | - rooms: {}, | ||
30 | - no_more_messages: {}, | ||
31 | - | ||
32 | - template: function(selector) { | ||
33 | - return $('#chat #chat-templates '+selector).clone().html(); | ||
34 | - }, | ||
35 | - | ||
36 | - jid_to_id: function (jid) { | ||
37 | - return Strophe.getBareJidFromJid(jid).replace(/@/g, "-").replace(/\./g, "-"); | ||
38 | - }, | ||
39 | - | ||
40 | - jid_of: function(jid_id) { | ||
41 | - return Jabber.jids[jid_id].jid; | ||
42 | - }, | ||
43 | - name_of: function(jid_id) { | ||
44 | - return Jabber.jids[jid_id].name; | ||
45 | - }, | ||
46 | - type_of: function(jid_id) { | ||
47 | - return Jabber.jids[jid_id].type; | ||
48 | - }, | ||
49 | - unread_messages_of: function(jid_id, value) { | ||
50 | - Jabber.jids[jid_id].unread_messages = (value == undefined ? Jabber.jids[jid_id].unread_messages : value); | ||
51 | - return Jabber.jids[jid_id].unread_messages; | ||
52 | - }, | ||
53 | - | ||
54 | - insert_or_update_user: function (list, item, jid, name, presence, template, type, remove_on_offline) { | ||
55 | - var jid_id = Jabber.jid_to_id(jid); | ||
56 | - var identifier = Strophe.getNodeFromJid(jid); | ||
57 | - var html = template | ||
58 | - .replace('%{jid_id}', jid_id) | ||
59 | - .replace(/%{presence_status}/g, presence) | ||
60 | - .replace('%{avatar}', getAvatar(identifier)) | ||
61 | - .replace('%{name}', name); | ||
62 | - | ||
63 | - $(item).parent().remove(); | ||
64 | - if(presence != 'offline' || !remove_on_offline) | ||
65 | - $(list).append(html); | ||
66 | - Jabber.jids[jid_id] = {jid: jid, name: name, type: type, presence: presence}; | ||
67 | - }, | ||
68 | - insert_or_update_group: function (jid, presence) { | ||
69 | - var jid_id = Jabber.jid_to_id(jid); | ||
70 | - var list = $('#buddy-list .buddies ul.'+presence); | ||
71 | - var item = $('#' + jid_id); | ||
72 | - presence = presence || ($(item).length > 0 ? $(item).parent('li').attr('class') : 'offline'); | ||
73 | - log('adding or updating contact ' + jid + ' as ' + presence); | ||
74 | - Jabber.insert_or_update_user(list, item, jid, Jabber.name_of(jid_id), presence, Jabber.template('.buddy-item'), 'groupchat'); | ||
75 | - $("#chat-window .tab a[href='#"+ Jabber.conversation_prefix + jid_id +"']") | ||
76 | - .removeClass() | ||
77 | - .addClass('icon-menu-' + presence + '-11'); | ||
78 | - }, | ||
79 | - insert_or_update_contact: function (jid, name, presence) { | ||
80 | - var jid_id = Jabber.jid_to_id(jid); | ||
81 | - var item = $('#' + jid_id); | ||
82 | - presence = presence || ($(item).length > 0 ? $(item).parent('li').attr('class') : 'offline'); | ||
83 | - var list = $('#buddy-list .buddies ul' + (presence=='offline' ? '.offline' : '.online')); | ||
84 | - | ||
85 | - log('adding or updating contact ' + jid + ' as ' + presence); | ||
86 | - Jabber.insert_or_update_user(list, item, jid, name, presence, Jabber.template('.buddy-item'), 'chat'); | ||
87 | - $("#chat-window .tab a[href='#"+ Jabber.conversation_prefix + jid_id +"']") | ||
88 | - .removeClass() | ||
89 | - .addClass('icon-menu-' + presence + '-11'); | ||
90 | - }, | ||
91 | - insert_or_update_occupant: function (jid, name, presence, room_jid) { | ||
92 | - log('adding or updating occupant ' + jid + ' as ' + presence); | ||
93 | - var jid_id = Jabber.jid_to_id(jid); | ||
94 | - var room_jid_id = Jabber.jid_to_id(room_jid); | ||
95 | - var list = $('#' + Jabber.conversation_prefix + room_jid_id + ' .occupants ul'); | ||
96 | - var item = $(list).find('a[data-id='+ jid_id +']'); | ||
97 | - Jabber.insert_or_update_user(list, item, jid, name, presence, Jabber.template('.occupant-item'), 'chat', true); | ||
98 | - if (Jabber.rooms[room_jid_id] === undefined) | ||
99 | - Jabber.rooms[room_jid_id] = {}; | ||
100 | - | ||
101 | - var room = Jabber.rooms[room_jid_id]; | ||
102 | - if(presence == 'offline') { | ||
103 | - delete Jabber.rooms[room_jid_id][name]; | ||
104 | - } | ||
105 | - else { | ||
106 | - Jabber.rooms[room_jid_id][name] = jid; | ||
107 | - } | 14 | + // extending the current namespaces in Strophe.NS |
15 | + Strophe.addNamespace('MUC_USER', 'http://jabber.org/protocol/muc#user'); | ||
16 | + Strophe.addNamespace('MUC_OWNER', 'http://jabber.org/protocol/muc#owner'); | ||
17 | + Strophe.addNamespace('CHAT_STATES', 'http://jabber.org/protocol/chatstates'); | ||
18 | + Strophe.addNamespace('DATA_FORMS', 'jabber:x:data'); | ||
19 | + | ||
20 | + var Jabber = { | ||
21 | + debug: true, | ||
22 | + connection: null, | ||
23 | + bosh_service: $bosh_service, | ||
24 | + muc_domain: $muc_domain, | ||
25 | + muc_supported: false, | ||
26 | + presence_status: '', | ||
27 | + conversation_prefix: 'conversation-', | ||
28 | + conversations_order: null, | ||
29 | + notification_sound: new Audio('/sounds/receive.wav'), | ||
30 | + window_visibility: null, | ||
31 | + jids: {}, | ||
32 | + rooms: {}, | ||
33 | + no_more_messages: {}, | ||
34 | + avatars: {}, | ||
35 | + | ||
36 | + template: function(selector) { | ||
37 | + return $('#chat #chat-templates '+selector).clone().html(); | ||
38 | + }, | ||
39 | + | ||
40 | + jid_to_id: function (jid) { | ||
41 | + return Strophe.getBareJidFromJid(jid).replace(/@/g, "-").replace(/\./g, "-"); | ||
42 | + }, | ||
43 | + | ||
44 | + jid_of: function(jid_id) { | ||
45 | + return Jabber.jids[jid_id].jid; | ||
46 | + }, | ||
47 | + name_of: function(jid_id) { | ||
48 | + return Jabber.jids[jid_id].name; | ||
49 | + }, | ||
50 | + type_of: function(jid_id) { | ||
51 | + return Jabber.jids[jid_id].type; | ||
52 | + }, | ||
53 | + presence_of: function(jid_id) { | ||
54 | + return Jabber.jids[jid_id].presence; | ||
55 | + }, | ||
56 | + unread_messages_of: function(jid_id, value) { | ||
57 | + Jabber.jids[jid_id].unread_messages = (value == undefined ? Jabber.jids[jid_id].unread_messages : value); | ||
58 | + return Jabber.jids[jid_id].unread_messages; | ||
59 | + }, | ||
60 | + | ||
61 | + insert_or_update_user: function (list, item, jid, name, presence, template, type, remove_on_offline) { | ||
62 | + var jid_id = Jabber.jid_to_id(jid); | ||
63 | + var identifier = Strophe.getNodeFromJid(jid); | ||
64 | + var html = template | ||
65 | + .replace('%{jid_id}', jid_id) | ||
66 | + .replace(/%{presence_status}/g, presence) | ||
67 | + .replace('%{avatar}', getAvatar(identifier)) | ||
68 | + .replace('%{name}', name); | ||
69 | + | ||
70 | + $(item).parent().remove(); | ||
71 | + if(presence != 'offline' || !remove_on_offline){ | ||
72 | + $(list).append(html); | ||
73 | + sort_conversations(); | ||
74 | + } | ||
75 | + Jabber.jids[jid_id] = {jid: jid, name: name, type: type, presence: presence}; | ||
76 | + }, | ||
77 | + insert_or_update_group: function (jid, presence) { | ||
78 | + var jid_id = Jabber.jid_to_id(jid); | ||
79 | + var list = $('#buddy-list .buddies ul.'+presence); | ||
80 | + var item = $('#' + jid_id); | ||
81 | + presence = presence || ($(item).length > 0 ? $(item).parent('li').attr('class') : 'offline'); | ||
82 | + log('adding or updating contact ' + jid + ' as ' + presence); | ||
83 | + Jabber.insert_or_update_user(list, item, jid, Jabber.name_of(jid_id), presence, Jabber.template('.buddy-item'), 'groupchat'); | ||
84 | + $("#chat-window .tab a[href='#"+ Jabber.conversation_prefix + jid_id +"']") | ||
85 | + .removeClass() | ||
86 | + .addClass('icon-menu-' + presence + '-11'); | ||
87 | + }, | ||
88 | + insert_or_update_contact: function (jid, name, presence) { | ||
89 | + var jid_id = Jabber.jid_to_id(jid); | ||
90 | + var item = $('#' + jid_id); | ||
91 | + presence = presence || ($(item).length > 0 ? $(item).parent('li').attr('class') : 'offline'); | ||
92 | + var list = $('#buddy-list .buddies ul' + (presence=='offline' ? '.offline' : '.online')); | ||
93 | + | ||
94 | + log('adding or updating contact ' + jid + ' as ' + presence); | ||
95 | + Jabber.insert_or_update_user(list, item, jid, name, presence, Jabber.template('.buddy-item'), 'chat'); | ||
96 | + $("#chat-window .tab a[href='#"+ Jabber.conversation_prefix + jid_id +"']") | ||
97 | + .removeClass() | ||
98 | + .addClass('icon-menu-' + presence + '-11'); | ||
99 | + }, | ||
100 | + insert_or_update_occupant: function (jid, name, presence, room_jid) { | ||
101 | + log('adding or updating occupant ' + jid + ' as ' + presence); | ||
102 | + var jid_id = Jabber.jid_to_id(jid); | ||
103 | + var room_jid_id = Jabber.jid_to_id(room_jid); | ||
104 | + var list = $('#' + Jabber.conversation_prefix + room_jid_id + ' .occupants ul'); | ||
105 | + var item = $(list).find('a[data-id='+ jid_id +']'); | ||
106 | + Jabber.insert_or_update_user(list, item, jid, name, presence, Jabber.template('.occupant-item'), 'chat', true); | ||
107 | + if (Jabber.rooms[room_jid_id] === undefined) | ||
108 | + Jabber.rooms[room_jid_id] = {}; | ||
109 | + | ||
110 | + var room = Jabber.rooms[room_jid_id]; | ||
111 | + if(presence == 'offline') { | ||
112 | + delete Jabber.rooms[room_jid_id][name]; | ||
113 | + } | ||
114 | + else { | ||
115 | + Jabber.rooms[room_jid_id][name] = jid; | ||
116 | + } | ||
108 | 117 | ||
109 | - list.parents('.occupants').find('.occupants-online').text(Object.keys(Jabber.rooms[room_jid_id]).length); | ||
110 | - }, | ||
111 | - | ||
112 | - remove_contact: function(jid) { | ||
113 | - var jid_id = Jabber.jid_to_id(jid) | ||
114 | - log('Removing contact ' + jid); | ||
115 | - $('#' + jid_id).parent('li').remove(); | ||
116 | - }, | ||
117 | - | ||
118 | - render_body_message: function(body) { | ||
119 | - body = body.replace(/\r?\n/g, '<br>'); | ||
120 | - body = $().emoticon(body); | ||
121 | - body = linkify(body, { | ||
122 | - callback: function(text, href) { | ||
123 | - return href ? '<a href="' + href + '" title="' + href + '" target="_blank">' + text + '</a>' : text; | ||
124 | - } | ||
125 | - }); | ||
126 | - return body; | ||
127 | - }, | 118 | + list.parents('.occupants').find('.occupants-online').text(Object.keys(Jabber.rooms[room_jid_id]).length); |
119 | + }, | ||
120 | + | ||
121 | + remove_contact: function(jid) { | ||
122 | + var jid_id = Jabber.jid_to_id(jid) | ||
123 | + log('Removing contact ' + jid); | ||
124 | + $('#' + jid_id).parent('li').remove(); | ||
125 | + }, | ||
126 | + | ||
127 | + render_body_message: function(body) { | ||
128 | + body = body.replace(/\r?\n/g, '<br>'); | ||
129 | + body = $().emoticon(body); | ||
130 | + body = linkify(body, { | ||
131 | + callback: function(text, href) { | ||
132 | + return href ? '<a href="' + href + '" title="' + href + '" target="_blank">' + text + '</a>' : text; | ||
133 | + } | ||
134 | + }); | ||
135 | + return body; | ||
136 | + }, | ||
128 | 137 | ||
129 | - show_message: function (jid, name, body, who, identifier, time, offset) { | ||
130 | - if(!offset) offset = 0; | ||
131 | - if (body) { | ||
132 | - body = Jabber.render_body_message(body); | ||
133 | - var jid_id = Jabber.jid_to_id(jid); | ||
134 | - var tab_id = '#' + Jabber.conversation_prefix + jid_id; | ||
135 | - var history = $(tab_id).find('.history'); | 138 | + show_message: function (jid, name, body, who, identifier, time, offset) { |
139 | + if(!offset) offset = 0; | ||
140 | + if (body) { | ||
141 | + body = Jabber.render_body_message(body); | ||
142 | + var jid_id = Jabber.jid_to_id(jid); | ||
143 | + var tab_id = '#' + Jabber.conversation_prefix + jid_id; | ||
144 | + var history = $(tab_id).find('.history'); | ||
136 | 145 | ||
137 | - var offset_container = history.find('.chat-offset-container-'+offset); | ||
138 | - if(offset_container.length == 0) | ||
139 | - offset_container = $('<div class="chat-offset-container-'+offset+'"></div>').prependTo(history); | 146 | + var offset_container = history.find('.chat-offset-container-'+offset); |
147 | + if(offset_container.length == 0) | ||
148 | + offset_container = $('<div class="chat-offset-container-'+offset+'"></div>').prependTo(history); | ||
140 | 149 | ||
141 | - if (offset_container.find('.message:last').attr('data-who') == who) { | ||
142 | - offset_container.find('.message:last .content').append('<p>' + body + '</p>'); | ||
143 | - } | ||
144 | - else { | ||
145 | - if (time==undefined) { | ||
146 | - time = new Date().toISOString(); | ||
147 | - } | ||
148 | - var message_html = Jabber.template('.message') | ||
149 | - .replace('%{message}', body) | ||
150 | - .replace(/%{who}/g, who) | ||
151 | - .replace('%{time}', time) | ||
152 | - .replace('%{name}', name) | ||
153 | - .replace('%{avatar}', getAvatar(identifier)); | ||
154 | - offset_container.append(message_html); | ||
155 | - $(".message span.time").timeago(); | ||
156 | - } | ||
157 | - if(offset == 0) history.scrollTo({top:'100%', left:'0%'}); | ||
158 | - else history.scrollTo(offset_container.height()); | ||
159 | - if (who != "self") { | ||
160 | - if ($(tab_id).find('.history:visible').length == 0) { | ||
161 | - count_unread_messages(jid_id); | ||
162 | - } | ||
163 | - document.alert_title = name; | ||
164 | - } | ||
165 | - } | ||
166 | - }, | ||
167 | - | ||
168 | - show_status: function(presence) { | ||
169 | - log('changing my status to ' + presence); | ||
170 | - $('#buddy-list .user-status .simplemenu-trigger') | ||
171 | - .removeClass('icon-menu-chat') | ||
172 | - .removeClass('icon-menu-offline') | ||
173 | - .removeClass('icon-menu-dnd') | ||
174 | - .addClass('icon-menu-' + (presence || 'offline')); | ||
175 | - $('#buddy-list #user-status img.avatar').replaceWith(getMyAvatar()); | ||
176 | - $.get('/chat/update_presence_status', { status: {chat_status: presence, last_chat_status: presence} }); | ||
177 | - }, | ||
178 | - | ||
179 | - send_availability_status: function(presence) { | ||
180 | - log('send availability status ' + presence); | ||
181 | - Jabber.connection.send($pres().c('show').t(presence).up()); | ||
182 | - Jabber.show_status(presence); | ||
183 | - }, | ||
184 | - | ||
185 | - enter_room: function(jid, push) { | ||
186 | - if(push == undefined) | ||
187 | - push = true | ||
188 | - var jid_id = Jabber.jid_to_id(jid); | ||
189 | - var conversation_id = Jabber.conversation_prefix + jid_id; | ||
190 | - var button = $('#' + conversation_id + ' .join'); | ||
191 | - button.hide(); | ||
192 | - button.siblings('.leave').show(); | ||
193 | - Jabber.connection.send( | ||
194 | - $pres({to: jid + '/' + $own_name}).c('x', {xmlns: Strophe.NS.MUC}).c('history', {maxchars: 0}) | ||
195 | - ); | ||
196 | - Jabber.insert_or_update_group(jid, 'online'); | 150 | + if (offset_container.find('.message:last').attr('data-who') == who) { |
151 | + offset_container.find('.message:last .content').append('<p>' + body + '</p>'); | ||
152 | + } | ||
153 | + else { | ||
154 | + if (time==undefined) { | ||
155 | + time = new Date().toISOString(); | ||
156 | + } | ||
157 | + var message_html = Jabber.template('.message') | ||
158 | + .replace('%{message}', body) | ||
159 | + .replace(/%{who}/g, who) | ||
160 | + .replace('%{time}', time) | ||
161 | + .replace('%{name}', name) | ||
162 | + .replace('%{avatar}', getAvatar(identifier)); | ||
163 | + offset_container.append(message_html); | ||
164 | + $(".message span.time").timeago(); | ||
165 | + } | ||
166 | + if(offset == 0) history.scrollTo({top:'100%', left:'0%'}); | ||
167 | + else history.scrollTo(offset_container.height()); | ||
168 | + if (who != "self") { | ||
169 | + if ($(tab_id).find('.history:visible').length == 0) { | ||
170 | + count_unread_messages(jid_id); | ||
171 | + } | ||
172 | + document.alert_title = name; | ||
173 | + } | ||
174 | + } | ||
175 | + }, | ||
176 | + | ||
177 | + show_status: function(presence) { | ||
178 | + log('changing my status to ' + presence); | ||
179 | + $('#buddy-list .user-status .simplemenu-trigger') | ||
180 | + .removeClass('icon-menu-chat') | ||
181 | + .removeClass('icon-menu-offline') | ||
182 | + .removeClass('icon-menu-dnd') | ||
183 | + .addClass('icon-menu-' + (presence || 'offline')); | ||
184 | + $('#buddy-list #user-status img.avatar').replaceWith(getMyAvatar()); | ||
185 | + $.get('/chat/update_presence_status', { status: {chat_status: presence, last_chat_status: presence} }); | ||
186 | + }, | ||
187 | + | ||
188 | + send_availability_status: function(presence) { | ||
189 | + log('send availability status ' + presence); | ||
190 | + Jabber.connection.send($pres().c('show').t(presence).up()); | ||
191 | + Jabber.show_status(presence); | ||
192 | + }, | ||
193 | + | ||
194 | + enter_room: function(jid, push) { | ||
195 | + if(push == undefined) | ||
196 | + push = true | ||
197 | + var jid_id = Jabber.jid_to_id(jid); | ||
198 | + var conversation_id = Jabber.conversation_prefix + jid_id; | ||
199 | + var button = $('#' + conversation_id + ' .join'); | ||
200 | + button.hide(); | ||
201 | + button.siblings('.leave').show(); | ||
202 | + Jabber.connection.send( | ||
203 | + $pres({to: jid + '/' + $own_name}).c('x', {xmlns: Strophe.NS.MUC}).c('history', {maxchars: 0}) | ||
204 | + ); | ||
205 | + Jabber.insert_or_update_group(jid, 'online'); | ||
206 | + Jabber.update_chat_title(); | ||
207 | + sort_conversations(); | ||
208 | + if(push) | ||
209 | + $.post('/chat/join', {room_id: jid}); | ||
210 | + }, | ||
211 | + | ||
212 | + leave_room: function(jid, push) { | ||
213 | + if(push == undefined) | ||
214 | + push = true | ||
215 | + var jid_id = Jabber.jid_to_id(jid); | ||
216 | + var conversation_id = Jabber.conversation_prefix + jid_id; | ||
217 | + var button = $('#' + conversation_id + ' .leave'); | ||
218 | + button.hide(); | ||
219 | + button.siblings('.join').show(); | ||
220 | + Jabber.connection.send($pres({from: Jabber.connection.jid, to: jid + '/' + $own_name, type: 'unavailable'})) | ||
221 | + Jabber.insert_or_update_group(jid, 'offline'); | ||
222 | + sort_conversations(); | ||
223 | + if(push) | ||
224 | + $.post('/chat/leave', {room_id: jid}); | ||
225 | + }, | ||
226 | + | ||
227 | + update_chat_title: function () { | ||
228 | + var friends_online = $('#buddy-list #friends .buddy-list.online li').length; | ||
229 | + $('#friends-online').text(friends_online); | ||
230 | + var friends_offline = $('#buddy-list #friends .buddy-list.offline li').length; | ||
231 | + $('#friends-offline').text(friends_offline); | ||
232 | + var groups_online = $('#buddy-list #rooms .buddy-list li').length; | ||
233 | + $('#groups-online').text(groups_online); | ||
234 | + }, | ||
235 | + | ||
236 | + on_connect: function (status) { | ||
237 | + switch (status) { | ||
238 | + case Strophe.Status.CONNECTING: | ||
239 | + log('connecting...'); | ||
240 | + break; | ||
241 | + case Strophe.Status.CONNFAIL: | ||
242 | + log('failed to connect'); | ||
243 | + setTimeout(function(){Jabber.connect()}, 10000); | ||
244 | + break; | ||
245 | + case Strophe.Status.DISCONNECTING: | ||
246 | + log('disconnecting...'); | ||
247 | + $('#buddy-list .toolbar').addClass('small-loading-dark'); | ||
248 | + break; | ||
249 | + case Strophe.Status.DISCONNECTED: | ||
250 | + log('disconnected'); | ||
251 | + $('#buddy-list ul.buddy-list, .occupants ul.occupant-list').html(''); | ||
197 | Jabber.update_chat_title(); | 252 | Jabber.update_chat_title(); |
198 | - sort_conversations(); | ||
199 | - if(push) | ||
200 | - $.post('/chat/join', {room_id: jid}); | ||
201 | - }, | ||
202 | - | ||
203 | - leave_room: function(jid, push) { | ||
204 | - if(push == undefined) | ||
205 | - push = true | 253 | + $('#buddy-list .toolbar').removeClass('small-loading-dark'); |
254 | + $('textarea').prop('disabled', 'disabled'); | ||
255 | + if(Jabber.presence_status != 'offline') | ||
256 | + Jabber.connect(); | ||
257 | + break; | ||
258 | + case Strophe.Status.CONNECTED: | ||
259 | + log('connected'); | ||
260 | + case Strophe.Status.ATTACHED: | ||
261 | + log('XMPP/BOSH session attached'); | ||
262 | + $('#buddy-list .toolbar').removeClass('small-loading-dark'); | ||
263 | + $('textarea').prop('disabled', ''); | ||
264 | + break; | ||
265 | + } | ||
266 | + }, | ||
267 | + | ||
268 | + on_roster: function (iq) { | ||
269 | + log('receiving roster'); | ||
270 | + var profiles = []; | ||
271 | + var contacts_to_insert = {}; | ||
272 | + var groups_to_insert = []; | ||
273 | + | ||
274 | + $(iq).find('item').each(function () { | ||
275 | + var jid = $(this).attr('jid'); | ||
276 | + profiles.push(getIdentifier(jid)); | ||
277 | + var name = $(this).attr('name') || jid; | ||
206 | var jid_id = Jabber.jid_to_id(jid); | 278 | var jid_id = Jabber.jid_to_id(jid); |
207 | - var conversation_id = Jabber.conversation_prefix + jid_id; | ||
208 | - var button = $('#' + conversation_id + ' .leave'); | ||
209 | - button.hide(); | ||
210 | - button.siblings('.join').show(); | ||
211 | - Jabber.connection.send($pres({from: Jabber.connection.jid, to: jid + '/' + $own_name, type: 'unavailable'})) | ||
212 | - Jabber.insert_or_update_group(jid, 'offline'); | ||
213 | - sort_conversations(); | ||
214 | - if(push) | ||
215 | - $.post('/chat/leave', {room_id: jid}); | ||
216 | - }, | ||
217 | - | ||
218 | - update_chat_title: function () { | ||
219 | - var friends_online = $('#buddy-list #friends .buddy-list.online li').length; | ||
220 | - $('#friends-online').text(friends_online); | ||
221 | - var friends_offline = $('#buddy-list #friends .buddy-list.offline li').length; | ||
222 | - $('#friends-offline').text(friends_offline); | ||
223 | - var groups_online = $('#buddy-list #rooms .buddy-list li').length; | ||
224 | - $('#groups-online').text(groups_online); | ||
225 | - }, | ||
226 | - | ||
227 | - on_connect: function (status) { | ||
228 | - switch (status) { | ||
229 | - case Strophe.Status.CONNECTING: | ||
230 | - log('connecting...'); | ||
231 | - break; | ||
232 | - case Strophe.Status.CONNFAIL: | ||
233 | - log('failed to connect'); | ||
234 | - break; | ||
235 | - case Strophe.Status.DISCONNECTING: | ||
236 | - log('disconnecting...'); | ||
237 | - $('#buddy-list .toolbar').addClass('small-loading-dark'); | ||
238 | - break; | ||
239 | - case Strophe.Status.DISCONNECTED: | ||
240 | - log('disconnected'); | ||
241 | - $('#buddy-list ul.buddy-list, .occupants ul.occupant-list').html(''); | ||
242 | - Jabber.update_chat_title(); | ||
243 | - $('#buddy-list .toolbar').removeClass('small-loading-dark'); | ||
244 | - $('textarea').prop('disabled', 'disabled'); | ||
245 | - break; | ||
246 | - case Strophe.Status.CONNECTED: | ||
247 | - log('connected'); | ||
248 | - case Strophe.Status.ATTACHED: | ||
249 | - log('XMPP/BOSH session attached'); | ||
250 | - $('#buddy-list .toolbar').removeClass('small-loading-dark'); | ||
251 | - $('textarea').prop('disabled', ''); | ||
252 | - break; | ||
253 | - } | ||
254 | - }, | ||
255 | - | ||
256 | - on_roster: function (iq) { | ||
257 | - log('receiving roster'); | ||
258 | - $(iq).find('item').each(function () { | ||
259 | - var jid = $(this).attr('jid'); | ||
260 | - var name = $(this).attr('name') || jid; | ||
261 | - var jid_id = Jabber.jid_to_id(jid); | ||
262 | - Jabber.insert_or_update_contact(jid, name); | ||
263 | - }); | ||
264 | - //TODO Add groups through roster too... | ||
265 | - $.ajax({ | ||
266 | - url: '/chat/roster_groups', | ||
267 | - dataType: 'json', | ||
268 | - success: function(data){ | ||
269 | - data.each(function(room){ | ||
270 | - var jid_id = Jabber.jid_to_id(room.jid); | ||
271 | - Jabber.jids[jid_id] = {jid: room.jid, name: room.name, type: 'groupchat'}; | ||
272 | - //FIXME This must check on session if the user is inside the room... | ||
273 | - Jabber.insert_or_update_group(room.jid, 'offline'); | 279 | + contacts_to_insert[jid] = name; |
280 | + }); | ||
281 | + | ||
282 | + //TODO Add groups through roster too... | ||
283 | + $.ajax({ | ||
284 | + url: '/chat/roster_groups', | ||
285 | + dataType: 'json', | ||
286 | + success: function(data){ | ||
287 | + $(data).each(function(index, room){ | ||
288 | + profiles.push(getIdentifier(room.jid)); | ||
289 | + var jid_id = Jabber.jid_to_id(room.jid); | ||
290 | + Jabber.jids[jid_id] = {jid: room.jid, name: room.name, type: 'groupchat'}; | ||
291 | + //FIXME This must check on session if the user is inside the room... | ||
292 | + groups_to_insert.push(room.jid); | ||
293 | + | ||
294 | + }); | ||
295 | + $.getJSON('/chat/avatars', {profiles: profiles}, function(data) { | ||
296 | + for(identifier in data) | ||
297 | + Jabber.avatars[identifier] = data[identifier]; | ||
298 | + | ||
299 | + // Insert contacts | ||
300 | + for(contact_jid in contacts_to_insert) | ||
301 | + Jabber.insert_or_update_contact(contact_jid, contacts_to_insert[contact_jid]); | ||
302 | + | ||
303 | + // Insert groups | ||
304 | + for (var i = 0; i < groups_to_insert.length; i++) | ||
305 | + Jabber.insert_or_update_group(groups_to_insert[i], 'offline'); | ||
306 | + | ||
307 | + $.getJSON('/chat/recent_conversations', {}, function(data) { | ||
308 | + Jabber.conversations_order = data; | ||
309 | + sort_conversations(); | ||
274 | }); | 310 | }); |
275 | - }, | ||
276 | - error: function(data, textStatus, jqXHR){ | ||
277 | - console.log(data); | ||
278 | - }, | ||
279 | - }); | ||
280 | - sort_conversations(); | ||
281 | - // set up presence handler and send initial presence | ||
282 | - Jabber.connection.addHandler(Jabber.on_presence, null, "presence"); | ||
283 | - Jabber.send_availability_status(Jabber.presence_status); | ||
284 | - load_defaults(); | ||
285 | - }, | ||
286 | - | ||
287 | - // NOTE: cause Noosfero store's rosters in database based on friendship relation between people | ||
288 | - // these event never occurs cause jabber service (ejabberd) didn't know when a roster was changed | ||
289 | - on_roster_changed: function (iq) { | ||
290 | - log('roster changed'); | ||
291 | - $(iq).find('item').each(function () { | ||
292 | - var sub = $(this).attr('subscription'); | ||
293 | - var jid = $(this).attr('jid'); | ||
294 | - var name = $(this).attr('name') || jid; | ||
295 | - if (sub == 'remove') { | ||
296 | - // contact is being removed | ||
297 | - Jabber.remove_contact(jid); | ||
298 | - } else { | ||
299 | - // contact is being added or modified | ||
300 | - Jabber.insert_or_update_contact(jid, name); | ||
301 | - } | ||
302 | - }); | ||
303 | - return true; | ||
304 | - }, | ||
305 | - | ||
306 | - parse: function (stanza) { | ||
307 | - var result = {}; | ||
308 | - if (Strophe.isTagEqual(stanza, 'presence')) { | ||
309 | - result.from = $(stanza).attr('from'); | ||
310 | - result.type = $(stanza).attr('type'); | ||
311 | - if (result.type == 'unavailable') { | ||
312 | - result.show = 'offline'; | ||
313 | - } else { | ||
314 | - var show = $(stanza).find("show").text(); | ||
315 | - if (show === "" || show == "chat") { | ||
316 | - result.show = 'chat'; | ||
317 | - } | ||
318 | - else if (show == "dnd" || show == "xa") { | ||
319 | - result.show = 'dnd'; | ||
320 | - } | ||
321 | - else { | ||
322 | - result.show = 'away'; | ||
323 | - } | ||
324 | - } | ||
325 | - if ($(stanza).find('x[xmlns="'+ Strophe.NS.MUC_USER +'"]').length > 0) { | ||
326 | - result.is_from_room = true; | ||
327 | - result.from_user = $(stanza).find('x item').attr('jid'); | ||
328 | - if ($(stanza).find('x item').attr('affiliation') == 'owner') { | ||
329 | - result.awaiting_configuration = ($(stanza).find('x status').attr('code') == '201'); | ||
330 | - } | ||
331 | - } | 311 | + |
312 | + // set up presence handler and send initial presence | ||
313 | + Jabber.connection.addHandler(Jabber.on_presence, null, "presence"); | ||
314 | + Jabber.send_availability_status(Jabber.presence_status); | ||
315 | + load_defaults(); | ||
316 | + }); | ||
317 | + }, | ||
318 | + error: function(data, textStatus, jqXHR){ | ||
319 | + console.log(data); | ||
320 | + }, | ||
321 | + }); | ||
322 | + | ||
323 | + }, | ||
324 | + | ||
325 | + // NOTE: cause Noosfero store's rosters in database based on friendship relation between people | ||
326 | + // these event never occurs cause jabber service (ejabberd) didn't know when a roster was changed | ||
327 | + on_roster_changed: function (iq) { | ||
328 | + log('roster changed'); | ||
329 | + $(iq).find('item').each(function () { | ||
330 | + var sub = $(this).attr('subscription'); | ||
331 | + var jid = $(this).attr('jid'); | ||
332 | + var name = $(this).attr('name') || jid; | ||
333 | + if (sub == 'remove') { | ||
334 | + // contact is being removed | ||
335 | + Jabber.remove_contact(jid); | ||
336 | + } else { | ||
337 | + // contact is being added or modified | ||
338 | + Jabber.insert_or_update_contact(jid, name); | ||
332 | } | 339 | } |
333 | - else if (Strophe.isTagEqual(stanza, 'message')) { | ||
334 | - result.from = $(stanza).attr('from'); | ||
335 | - result.body = $(stanza).find('body').text(); | ||
336 | - if ($(stanza).find('error').length > 0) { | ||
337 | - result.error = $(stanza).find('error text').text(); | ||
338 | - if (!result.error && $(stanza).find('error').find('service-unavailable').length > 0) { | ||
339 | - result.error = $user_unavailable_error; | ||
340 | - } | ||
341 | - } | 340 | + }); |
341 | + return true; | ||
342 | + }, | ||
343 | + | ||
344 | + parse: function (stanza) { | ||
345 | + var result = {}; | ||
346 | + if (Strophe.isTagEqual(stanza, 'presence')) { | ||
347 | + result.from = $(stanza).attr('from'); | ||
348 | + result.type = $(stanza).attr('type'); | ||
349 | + if (result.type == 'unavailable') { | ||
350 | + result.show = 'offline'; | ||
351 | + } else { | ||
352 | + var show = $(stanza).find("show").text(); | ||
353 | + if (show === "" || show == "chat") { | ||
354 | + result.show = 'chat'; | ||
355 | + } | ||
356 | + else if (show == "dnd" || show == "xa") { | ||
357 | + result.show = 'dnd'; | ||
358 | + } | ||
359 | + else { | ||
360 | + result.show = 'away'; | ||
361 | + } | ||
342 | } | 362 | } |
343 | - return result; | ||
344 | - }, | ||
345 | - | ||
346 | - on_presence: function (presence) { | ||
347 | - presence = Jabber.parse(presence); | ||
348 | - if (presence.type != 'error') { | ||
349 | - if (presence.is_from_room) { | ||
350 | - log('receiving room presence from ' + presence.from + ' as ' + presence.show); | ||
351 | - var name = Strophe.getResourceFromJid(presence.from); | ||
352 | - if (presence.from_user) { | ||
353 | - Jabber.insert_or_update_occupant(presence.from_user, name, presence.show, presence.from); | ||
354 | - } | ||
355 | - else { | ||
356 | - log('ooops! user jid not found in presence stanza'); | ||
357 | - } | ||
358 | - if (presence.awaiting_configuration) { | ||
359 | - log('sending instant room configuration to ' + Strophe.getBareJidFromJid(presence.from)); | ||
360 | - Jabber.connection.sendIQ( | ||
361 | - $iq({type: 'set', to: Strophe.getBareJidFromJid(presence.from)}) | ||
362 | - .c('query', {xmlns: Strophe.NS.MUC_OWNER}) | ||
363 | - .c('x', {xmlns: Strophe.NS.DATA_FORMS, type: 'submit'}) | ||
364 | - ); | ||
365 | - } | ||
366 | - } | ||
367 | - else { | ||
368 | - log('receiving contact presence from ' + presence.from + ' as ' + presence.show); | ||
369 | - var jid = Strophe.getBareJidFromJid(presence.from); | ||
370 | - if (jid != Jabber.connection.jid) { | ||
371 | - var name = Jabber.name_of(Jabber.jid_to_id(jid)); | ||
372 | - Jabber.insert_or_update_contact(jid, name, presence.show); | ||
373 | - Jabber.update_chat_title(); | ||
374 | - } | ||
375 | - else { | ||
376 | - // why server sends presence from myself to me? | ||
377 | - log('ignoring presence from myself'); | ||
378 | - if(presence.show=='offline') { | ||
379 | - console.log(Jabber.presence_status); | ||
380 | - Jabber.send_availability_status(Jabber.presence_status); | ||
381 | - } | ||
382 | - } | ||
383 | - } | 363 | + if ($(stanza).find('x[xmlns="'+ Strophe.NS.MUC_USER +'"]').length > 0) { |
364 | + result.is_from_room = true; | ||
365 | + result.from_user = $(stanza).find('x item').attr('jid'); | ||
366 | + if ($(stanza).find('x item').attr('affiliation') == 'owner') { | ||
367 | + result.awaiting_configuration = ($(stanza).find('x status').attr('code') == '201'); | ||
368 | + } | ||
384 | } | 369 | } |
385 | - return true; | ||
386 | - }, | ||
387 | - | ||
388 | - on_private_message: function (message) { | ||
389 | - message = Jabber.parse(message); | ||
390 | - log('receiving message from ' + message.from); | ||
391 | - var jid = Strophe.getBareJidFromJid(message.from); | ||
392 | - var jid_id = Jabber.jid_to_id(jid); | ||
393 | - var name = Jabber.name_of(jid_id); | ||
394 | - create_conversation_tab(name, jid_id); | ||
395 | - Jabber.show_message(jid, name, escape_html(message.body), 'other', Strophe.getNodeFromJid(jid)); | ||
396 | - notifyMessage(message); | ||
397 | - return true; | ||
398 | - }, | ||
399 | - | ||
400 | - on_public_message: function (message) { | ||
401 | - message = Jabber.parse(message); | ||
402 | - log('receiving message from ' + message.from); | ||
403 | - var name = Strophe.getResourceFromJid(message.from); | ||
404 | - // is a message from the room itself | ||
405 | - if (! name) { | ||
406 | - Jabber.show_notice(Jabber.jid_to_id(message.from), message.body); | ||
407 | - } | ||
408 | - // is a message from another user, not mine | ||
409 | - else if ($own_name != name) { | ||
410 | - var jid = Jabber.rooms[Jabber.jid_to_id(message.from)][name]; | ||
411 | - Jabber.show_message(message.from, name, escape_html(message.body), name, Strophe.getNodeFromJid(jid)); | 370 | + } |
371 | + else if (Strophe.isTagEqual(stanza, 'message')) { | ||
372 | + result.from = $(stanza).attr('from'); | ||
373 | + result.body = $(stanza).find('body').text(); | ||
374 | + if ($(stanza).find('error').length > 0) { | ||
375 | + result.error = $(stanza).find('error text').text(); | ||
376 | + if (!result.error && $(stanza).find('error').find('service-unavailable').length > 0) { | ||
377 | + result.error = $user_unavailable_error; | ||
378 | + } | ||
412 | } | 379 | } |
413 | - notifyMessage(message); | ||
414 | - return true; | ||
415 | - }, | ||
416 | - | ||
417 | - on_message_error: function (message) { | ||
418 | - message = Jabber.parse(message) | ||
419 | - var jid = Strophe.getBareJidFromJid(message.from); | ||
420 | - log('Receiving error message from ' + jid); | ||
421 | - var body = Jabber.template('.error-message').replace('%{text}', message.error); | ||
422 | - Jabber.show_message(jid, Jabber.name_of(Jabber.jid_to_id(jid)), body, 'other', Strophe.getNodeFromJid(jid)); | ||
423 | - return true; | ||
424 | - }, | ||
425 | - | ||
426 | - on_muc_support: function(iq) { | ||
427 | - if ($(iq).find('identity[category=conference]').length > 0 && $(iq).find('feature[var="'+ Strophe.NS.MUC +'"]').length > 0) { | ||
428 | - var name = $(iq).find('identity[category=conference]').attr('name'); | ||
429 | - log('muc support found with identity '+ name); | ||
430 | - Jabber.muc_supported = true; | 380 | + } |
381 | + return result; | ||
382 | + }, | ||
383 | + | ||
384 | + on_presence: function (presence) { | ||
385 | + presence = Jabber.parse(presence); | ||
386 | + if (presence.type != 'error') { | ||
387 | + if (presence.is_from_room) { | ||
388 | + log('receiving room presence from ' + presence.from + ' as ' + presence.show); | ||
389 | + var name = Strophe.getResourceFromJid(presence.from); | ||
390 | + if (presence.from_user) { | ||
391 | + Jabber.insert_or_update_occupant(presence.from_user, name, presence.show, presence.from); | ||
392 | + } | ||
393 | + else { | ||
394 | + log('ooops! user jid not found in presence stanza'); | ||
395 | + } | ||
396 | + if (presence.awaiting_configuration) { | ||
397 | + log('sending instant room configuration to ' + Strophe.getBareJidFromJid(presence.from)); | ||
398 | + Jabber.connection.sendIQ( | ||
399 | + $iq({type: 'set', to: Strophe.getBareJidFromJid(presence.from)}) | ||
400 | + .c('query', {xmlns: Strophe.NS.MUC_OWNER}) | ||
401 | + .c('x', {xmlns: Strophe.NS.DATA_FORMS, type: 'submit'}) | ||
402 | + ); | ||
403 | + } | ||
431 | } | 404 | } |
432 | else { | 405 | else { |
433 | - log('muc support not found'); | 406 | + log('receiving contact presence from ' + presence.from + ' as ' + presence.show); |
407 | + var jid = Strophe.getBareJidFromJid(presence.from); | ||
408 | + if (jid != Jabber.connection.jid) { | ||
409 | + var jid_id = Jabber.jid_to_id(jid); | ||
410 | + var name = Jabber.name_of(jid_id); | ||
411 | + if(presence.show == 'chat') | ||
412 | + Jabber.remove_notice(jid_id); | ||
413 | + Jabber.insert_or_update_contact(jid, name, presence.show); | ||
414 | + Jabber.update_chat_title(); | ||
415 | + } | ||
416 | + else { | ||
417 | + // why server sends presence from myself to me? | ||
418 | + log('ignoring presence from myself'); | ||
419 | + if(presence.show=='offline') { | ||
420 | + Jabber.send_availability_status(Jabber.presence_status); | ||
421 | + } | ||
422 | + } | ||
434 | } | 423 | } |
435 | - }, | 424 | + } |
425 | + return true; | ||
426 | + }, | ||
436 | 427 | ||
437 | - attach_connection: function(data) { | ||
438 | - // create the connection and attach it | ||
439 | - Jabber.connection = new Strophe.Connection(Jabber.bosh_service); | ||
440 | - Jabber.connection.attach(data.jid, data.sid, data.rid, Jabber.on_connect); | 428 | + on_private_message: function (message) { |
429 | + message = Jabber.parse(message); | ||
430 | + log('receiving message from ' + message.from); | ||
431 | + var jid = Strophe.getBareJidFromJid(message.from); | ||
432 | + var jid_id = Jabber.jid_to_id(jid); | ||
433 | + var name = Jabber.name_of(jid_id); | ||
434 | + create_conversation_tab(name, jid_id); | ||
435 | + Jabber.show_message(jid, name, escape_html(message.body), 'other', Strophe.getNodeFromJid(jid)); | ||
436 | + renew_conversation_order(jid); | ||
437 | + notifyMessage(message); | ||
438 | + return true; | ||
439 | + }, | ||
440 | + | ||
441 | + on_public_message: function (message) { | ||
442 | + message = Jabber.parse(message); | ||
443 | + log('receiving message from ' + message.from); | ||
444 | + var name = Strophe.getResourceFromJid(message.from); | ||
445 | + // is a message from the room itself | ||
446 | + if (! name) { | ||
447 | + // FIXME Ignoring message from room for now. | ||
448 | + // Jabber.show_notice(Jabber.jid_to_id(message.from), message.body); | ||
449 | + } | ||
450 | + // is a message from another user, not mine | ||
451 | + else if ($own_name != name) { | ||
452 | + var jid = Jabber.rooms[Jabber.jid_to_id(message.from)][name]; | ||
453 | + Jabber.show_message(message.from, name, escape_html(message.body), name, Strophe.getNodeFromJid(jid)); | ||
454 | + renew_conversation_order(jid); | ||
455 | + notifyMessage(message); | ||
456 | + } | ||
457 | + return true; | ||
458 | + }, | ||
441 | 459 | ||
442 | - // handle get roster list (buddy list) | ||
443 | - Jabber.connection.sendIQ($iq({type: 'get'}).c('query', {xmlns: Strophe.NS.ROSTER}), Jabber.on_roster); | 460 | + on_message_error: function (message) { |
461 | + }, | ||
444 | 462 | ||
445 | - // handle presence updates in roster list | ||
446 | - Jabber.connection.addHandler(Jabber.on_roster_changed, 'jabber:iq:roster', 'iq', 'set'); | 463 | + on_muc_support: function(iq) { |
464 | + if ($(iq).find('identity[category=conference]').length > 0 && $(iq).find('feature[var="'+ Strophe.NS.MUC +'"]').length > 0) { | ||
465 | + var name = $(iq).find('identity[category=conference]').attr('name'); | ||
466 | + log('muc support found with identity '+ name); | ||
467 | + Jabber.muc_supported = true; | ||
468 | + } | ||
469 | + else { | ||
470 | + log('muc support not found'); | ||
471 | + } | ||
472 | + }, | ||
447 | 473 | ||
448 | - // Handle messages | ||
449 | - Jabber.connection.addHandler(Jabber.on_private_message, null, "message", "chat"); | 474 | + attach_connection: function(data) { |
475 | + // create the connection and attach it | ||
476 | + Jabber.connection = new Strophe.Connection(Jabber.bosh_service); | ||
477 | + Jabber.connection.attach(data.jid, data.sid, data.rid, Jabber.on_connect); | ||
450 | 478 | ||
451 | - // Handle conference messages | ||
452 | - Jabber.connection.addHandler(Jabber.on_public_message, null, "message", "groupchat"); | 479 | + // handle get roster list (buddy list) |
480 | + Jabber.connection.sendIQ($iq({type: 'get'}).c('query', {xmlns: Strophe.NS.ROSTER}), Jabber.on_roster); | ||
453 | 481 | ||
454 | - // Handle message errors | ||
455 | - Jabber.connection.addHandler(Jabber.on_message_error, null, "message", "error"); | 482 | + // handle presence updates in roster list |
483 | + Jabber.connection.addHandler(Jabber.on_roster_changed, 'jabber:iq:roster', 'iq', 'set'); | ||
456 | 484 | ||
457 | - // discovering MUC support | ||
458 | - Jabber.connection.sendIQ( | ||
459 | - $iq({type: 'get', from: Jabber.connection.jid, to: Jabber.muc_domain}) | ||
460 | - .c('query', {xmlns: Strophe.NS.DISCO_INFO}), | ||
461 | - Jabber.on_muc_support | ||
462 | - ); | 485 | + // Handle messages |
486 | + Jabber.connection.addHandler(Jabber.on_private_message, null, "message", "chat"); | ||
463 | 487 | ||
464 | - // uncomment for extra debugging | ||
465 | - //Strophe.log = function (lvl, msg) { log(msg); }; | ||
466 | - }, | 488 | + // Handle conference messages |
489 | + Jabber.connection.addHandler(Jabber.on_public_message, null, "message", "groupchat"); | ||
467 | 490 | ||
468 | - connect: function() { | ||
469 | - if (Jabber.connection && Jabber.connection.connected) { | ||
470 | - Jabber.send_availability_status(Jabber.presence_status); | ||
471 | - } | ||
472 | - else { | ||
473 | - log('starting XMPP/BOSH session...'); | ||
474 | - $('#buddy-list .toolbar').removeClass('small-loading-dark').addClass('small-loading-dark'); | ||
475 | - $('.dialog-error').hide(); | ||
476 | - $.ajax({ | ||
477 | - url: '/chat/start_session', | ||
478 | - dataType: 'json', | ||
479 | - success: function(data) { | ||
480 | - Jabber.attach_connection(data) | ||
481 | - }, | ||
482 | - error: function(error) { | ||
483 | - $('#buddy-list .toolbar').removeClass('small-loading-dark'); | ||
484 | - $('#buddy-list .dialog-error') | ||
485 | - .html(error.responseText) | ||
486 | - .show('highlight') | ||
487 | - .unbind('click') | ||
488 | - .click(function() { $(this).hide('highlight'); }); | ||
489 | - } | ||
490 | - }); | ||
491 | - } | ||
492 | - }, | ||
493 | - | ||
494 | - deliver_message: function(jid, body) { | ||
495 | - var type = Jabber.type_of(Jabber.jid_to_id(jid)); | ||
496 | - var message = $msg({to: jid, from: Jabber.connection.jid, "type": type}) | ||
497 | - .c('body').t(body).up() | ||
498 | - .c('active', {xmlns: Strophe.NS.CHAT_STATES}); | ||
499 | - Jabber.connection.send(message); | ||
500 | - Jabber.show_message(jid, $own_name, escape_html(body), 'self', Strophe.getNodeFromJid(Jabber.connection.jid)); | ||
501 | - move_conversation_to_the_top(jid); | ||
502 | - }, | ||
503 | - | ||
504 | - is_a_room: function(jid_id) { | ||
505 | - return Jabber.type_of(jid_id) == 'groupchat'; | ||
506 | - }, | ||
507 | - | ||
508 | - show_notice: function(jid_id, msg) { | ||
509 | - var tab_id = '#' + Jabber.conversation_prefix + jid_id; | ||
510 | - var notice = $(tab_id).find('.history .notice'); | ||
511 | - if (notice.length > 0) | ||
512 | - notice.html(msg) | ||
513 | - else | ||
514 | - $(tab_id).find('.history').append("<span class='notice'>" + msg + "</span>"); | ||
515 | - } | ||
516 | - }; | ||
517 | - | ||
518 | - $('#chat-connect').live('click', function() { | ||
519 | - Jabber.presence_status = 'chat'; | ||
520 | - Jabber.connect(); | ||
521 | - }); | ||
522 | - | ||
523 | - $('#chat-disconnect').click(function() { | ||
524 | - disconnect(); | ||
525 | - }); | ||
526 | - | ||
527 | - $('#chat-busy').click(function() { | ||
528 | - Jabber.presence_status = 'dnd'; | ||
529 | - Jabber.connect(); | ||
530 | - }); | ||
531 | - | ||
532 | - $('#chat-retry').live('click', function() { | ||
533 | - Jabber.presence_status = Jabber.presence_status || 'chat'; | ||
534 | - Jabber.connect(); | ||
535 | - }); | ||
536 | - | ||
537 | - $('.conversation textarea').live('keydown', function(e) { | ||
538 | - if (e.keyCode == 13) { | ||
539 | - var jid = $(this).attr('data-to'); | ||
540 | - var body = $(this).val(); | ||
541 | - body = body.stripScripts(); | ||
542 | - save_message(jid, body); | ||
543 | - Jabber.deliver_message(jid, body); | ||
544 | - $(this).val(''); | ||
545 | - return false; | ||
546 | - } | ||
547 | - }); | ||
548 | - | ||
549 | - function save_message(jid, body) { | ||
550 | - $.post('/chat/save_message', { | ||
551 | - to: getIdentifier(jid), | ||
552 | - body: body | ||
553 | - }); | ||
554 | - } | 491 | + // Handle message errors |
492 | + Jabber.connection.addHandler(Jabber.on_message_error, null, "message", "error"); | ||
555 | 493 | ||
556 | - // open new conversation or change to already opened tab | ||
557 | - $('#buddy-list .buddies li a').live('click', function() { | ||
558 | - var jid_id = $(this).attr('id'); | ||
559 | - var name = Jabber.name_of(jid_id); | ||
560 | - var conversation = create_conversation_tab(name, jid_id); | ||
561 | - | ||
562 | - $('.conversation').hide(); | ||
563 | - conversation.show(); | ||
564 | - count_unread_messages(jid_id, true); | ||
565 | - if(conversation.find('.chat-offset-container-0').length == 0) | ||
566 | - recent_messages(Jabber.jid_of(jid_id)); | ||
567 | - conversation.find('.conversation .input-div textarea.input').focus(); | ||
568 | - $.post('/chat/tab', {tab_id: jid_id}); | ||
569 | - }); | ||
570 | - | ||
571 | - // put name into text area when click in one occupant | ||
572 | - $('.occupants .occupant-list li a').live('click', function() { | ||
573 | - var jid_id = $(this).attr('data-id'); | ||
574 | - var name = Jabber.name_of(jid_id); | ||
575 | - var val = $('.conversation textarea:visible').val(); | ||
576 | - $('.conversation textarea:visible').focus().val(val + name + ', '); | ||
577 | - }); | 494 | + // discovering MUC support |
495 | + Jabber.connection.sendIQ( | ||
496 | + $iq({type: 'get', from: Jabber.connection.jid, to: Jabber.muc_domain}) | ||
497 | + .c('query', {xmlns: Strophe.NS.DISCO_INFO}), | ||
498 | + Jabber.on_muc_support | ||
499 | + ); | ||
578 | 500 | ||
579 | - $('#chat .conversation .history').live('click', function() { | ||
580 | - $('.conversation textarea:visible').focus(); | ||
581 | - }); | 501 | + // uncomment for extra debugging |
502 | + //Strophe.log = function (lvl, msg) { log(msg); }; | ||
503 | + }, | ||
582 | 504 | ||
583 | - function toggle_chat_window() { | ||
584 | - if(jQuery('#conversations .conversation').length == 0) jQuery('.buddies a').first().click(); | ||
585 | - jQuery('#chat').toggleClass('opened'); | ||
586 | - jQuery('#chat-label').toggleClass('opened'); | ||
587 | - } | 505 | + connect: function() { |
506 | + if (Notification.permission !== "granted" && Notification.permission !== "denied") { | ||
507 | + Notification.requestPermission(function (permission) { | ||
508 | + if (!('permission' in Notification)) { | ||
509 | + Notification.permission = permission; | ||
510 | + } | ||
511 | + }); | ||
512 | + } | ||
588 | 513 | ||
589 | - function load_conversation(jid) { | ||
590 | - var jid_id = Jabber.jid_to_id(jid); | ||
591 | - var name = Jabber.name_of(jid_id); | ||
592 | - if (jid) { | ||
593 | - if (Strophe.getDomainFromJid(jid) == Jabber.muc_domain) { | ||
594 | - if (Jabber.muc_supported) { | ||
595 | - log('opening groupchat with ' + jid); | ||
596 | - Jabber.jids[jid_id] = {jid: jid, name: name, type: 'groupchat'}; | ||
597 | - var conversation = create_conversation_tab(name, jid_id); | ||
598 | - Jabber.enter_room(jid); | ||
599 | - recent_messages(jid); | ||
600 | - return conversation; | ||
601 | - } | ||
602 | - } | ||
603 | - else { | ||
604 | - log('opening chat with ' + jid); | ||
605 | - Jabber.jids[jid_id] = {jid: jid, name: name, type: 'friendchat'}; | ||
606 | - var conversation = create_conversation_tab(name, jid_id); | ||
607 | - recent_messages(jid); | ||
608 | - return conversation; | ||
609 | - } | 514 | + if (Jabber.connection && Jabber.connection.connected) { |
515 | + Jabber.send_availability_status(Jabber.presence_status); | ||
610 | } | 516 | } |
611 | - } | ||
612 | - | ||
613 | - function open_conversation(jid) { | ||
614 | - var conversation = load_conversation(jid); | ||
615 | - var jid_id = $(this).attr('id'); | ||
616 | - | ||
617 | - $('.conversation').hide(); | ||
618 | - conversation.show(); | ||
619 | - count_unread_messages(jid_id, true); | ||
620 | - if(conversation.find('.chat-offset-container-0').length == 0) | ||
621 | - recent_messages(Jabber.jid_of(jid_id)); | ||
622 | - conversation.find('.input').focus(); | ||
623 | - $('#chat').addClass('opened'); | ||
624 | - $('#chat-label').addClass('opened'); | ||
625 | - $.post('/chat/tab', {tab_id: jid_id}); | ||
626 | - } | ||
627 | - | ||
628 | - function create_conversation_tab(title, jid_id) { | ||
629 | - var conversation_id = Jabber.conversation_prefix + jid_id; | ||
630 | - var conversation = $('#' + conversation_id); | ||
631 | - if (conversation.length > 0) { | ||
632 | - return conversation; | 517 | + else { |
518 | + log('starting XMPP/BOSH session...'); | ||
519 | + $('#buddy-list .toolbar').removeClass('small-loading-dark').addClass('small-loading-dark'); | ||
520 | + $('.dialog-error').hide(); | ||
521 | + $.ajax({ | ||
522 | + url: '/chat/start_session', | ||
523 | + dataType: 'json', | ||
524 | + success: function(data) { | ||
525 | + Jabber.attach_connection(data) | ||
526 | + }, | ||
527 | + error: function(error) { | ||
528 | + $('#buddy-list .toolbar').removeClass('small-loading-dark'); | ||
529 | + $('#buddy-list .dialog-error') | ||
530 | + .html(error.responseText) | ||
531 | + .show('highlight') | ||
532 | + .unbind('click') | ||
533 | + .click(function() { $(this).hide('highlight'); }); | ||
534 | + } | ||
535 | + }); | ||
633 | } | 536 | } |
537 | + }, | ||
634 | 538 | ||
635 | - var jid = Jabber.jid_of(jid_id); | ||
636 | - var identifier = getIdentifier(jid); | 539 | + deliver_message: function(jid, body) { |
540 | + var jid_id = Jabber.jid_to_id(jid); | ||
541 | + var type = Jabber.type_of(jid_id); | ||
542 | + var presence = Jabber.presence_of(jid_id); | ||
543 | + var message = $msg({to: jid, from: Jabber.connection.jid, "type": type}) | ||
544 | + .c('body').t(body).up() | ||
545 | + .c('active', {xmlns: Strophe.NS.CHAT_STATES}); | ||
546 | + Jabber.connection.send(message); | ||
547 | + Jabber.show_message(jid, $own_name, escape_html(body), 'self', Strophe.getNodeFromJid(Jabber.connection.jid)); | ||
548 | + save_message(jid, body); | ||
549 | + renew_conversation_order(jid); | ||
550 | + move_conversation_to_the_top(jid); | ||
551 | + if (presence == 'offline') | ||
552 | + Jabber.show_notice(jid_id, $user_unavailable_error); | ||
553 | + }, | ||
554 | + | ||
555 | + is_a_room: function(jid_id) { | ||
556 | + return Jabber.type_of(jid_id) == 'groupchat'; | ||
557 | + }, | ||
558 | + | ||
559 | + show_notice: function(jid_id, msg) { | ||
560 | + var tab_id = '#' + Jabber.conversation_prefix + jid_id; | ||
561 | + var history = $(tab_id).find('.history'); | ||
562 | + var notice = $(tab_id).find('.history .notice'); | ||
563 | + if (notice.length > 0) | ||
564 | + notice.html(msg) | ||
565 | + else | ||
566 | + $(tab_id).find('.history').append("<span class='notice'>" + msg + "</span>"); | ||
567 | + history.scrollTo({top:'100%', left:'0%'}); | ||
568 | + }, | ||
569 | + | ||
570 | + remove_notice: function(jid_id) { | ||
571 | + var tab_id = '#' + Jabber.conversation_prefix + jid_id; | ||
572 | + var notice = $(tab_id).find('.history .notice').remove(); | ||
573 | + }, | ||
574 | + }; | ||
637 | 575 | ||
638 | - var panel = $('#chat-templates .conversation').clone().appendTo($conversations).attr('id', conversation_id); | ||
639 | - panel.find('.chat-target .avatar').replaceWith(getAvatar(identifier)); | ||
640 | - panel.find('.chat-target .other-name').html(title); | ||
641 | - $('#chat .history').perfectScrollbar(); | 576 | + $('#chat-connect').live('click', function() { |
577 | + Jabber.presence_status = 'chat'; | ||
578 | + Jabber.connect(); | ||
579 | + $('#chat .simplemenu-submenu').hide(); | ||
580 | + return false; | ||
581 | + }); | ||
642 | 582 | ||
643 | - panel.find('.history').scroll(function(){ | ||
644 | - if($(this).scrollTop() == 0){ | ||
645 | - var offset = panel.find('.message p').size(); | ||
646 | - recent_messages(jid, offset); | ||
647 | - } | ||
648 | - }); | 583 | + $('#chat-disconnect').click(function() { |
584 | + disconnect(); | ||
585 | + $('#chat .simplemenu-submenu').hide(); | ||
586 | + return false; | ||
587 | + }); | ||
588 | + | ||
589 | + $('#chat-busy').click(function() { | ||
590 | + Jabber.presence_status = 'dnd'; | ||
591 | + Jabber.connect(); | ||
592 | + $('#chat .simplemenu-submenu').hide(); | ||
593 | + return false; | ||
594 | + }); | ||
649 | 595 | ||
650 | - var textarea = panel.find('textarea'); | ||
651 | - textarea.attr('name', panel.id); | 596 | + $('#chat-retry').live('click', function() { |
597 | + Jabber.presence_status = Jabber.presence_status || 'chat'; | ||
598 | + Jabber.connect(); | ||
599 | + return false; | ||
600 | + }); | ||
601 | + | ||
602 | + $('.conversation textarea').live('keydown', function(e) { | ||
603 | + if (e.keyCode == 13) { | ||
604 | + var jid = $(this).attr('data-to'); | ||
605 | + var body = $(this).val(); | ||
606 | + body = $('<div>'+ body +'</div>').find('script,noscript,style').remove().end().html(); | ||
607 | + Jabber.deliver_message(jid, body); | ||
608 | + $(this).val(''); | ||
609 | + return false; | ||
610 | + } | ||
611 | + }); | ||
652 | 612 | ||
653 | - if (Jabber.is_a_room(jid_id)) { | ||
654 | - panel.append(Jabber.template('.occupant-list-template')); | ||
655 | - panel.find('.history').addClass('room'); | ||
656 | - var room_actions = $('#chat-templates .room-action').clone(); | ||
657 | - room_actions.data('jid', jid); | ||
658 | - panel.find('.history').after(room_actions); | ||
659 | - $('#chat .occupants .occupant-list').perfectScrollbar(); | 613 | + function save_message(jid, body) { |
614 | + $.post('/chat/save_message', { | ||
615 | + to: getIdentifier(jid), | ||
616 | + body: body | ||
617 | + }, function(data){ | ||
618 | + if(data.status > 0){ | ||
619 | + console.log(data.message); | ||
620 | + if(data.backtrace) console.log(data.backtrace); | ||
660 | } | 621 | } |
661 | - textarea.attr('data-to', jid); | ||
662 | - | ||
663 | - return panel; | ||
664 | - } | ||
665 | - | ||
666 | - function ensure_scroll(jid, offset) { | ||
667 | - var jid_id = Jabber.jid_to_id(jid); | ||
668 | - var history = jQuery('#conversation-'+jid_id+' .history'); | ||
669 | - // Load more messages if was not enough to show the scroll | ||
670 | - if(history.prop('scrollHeight') - history.prop('clientHeight') <= 0){ | ||
671 | - var offset = history.find('.message p').size(); | ||
672 | - recent_messages(jid, offset); | ||
673 | - } | ||
674 | - } | ||
675 | - | ||
676 | - function recent_messages(jid, offset) { | ||
677 | - if (Jabber.no_more_messages[jid]) return; | ||
678 | - | ||
679 | - if(!offset) offset = 0; | ||
680 | - start_fetching('.history'); | ||
681 | - $.getJSON('/chat/recent_messages', {identifier: getIdentifier(jid), offset: offset}, function(data) { | ||
682 | - // Register if no more messages returned and stop trying to load | ||
683 | - // more messages in the future. | ||
684 | - if(data.length == 0) | ||
685 | - Jabber.no_more_messages[jid] = true; | ||
686 | - | ||
687 | - $.each(data, function(i, message) { | ||
688 | - var body = message['body']; | ||
689 | - var from = message['from']; | ||
690 | - var to = message['to']; | ||
691 | - var date = message['created_at']; | ||
692 | - var who = from['id']==getCurrentIdentifier() ? 'self' : from['id'] | ||
693 | - | ||
694 | - Jabber.show_message(jid, from['name'], body, who, from['id'], date, offset); | ||
695 | - }); | ||
696 | - stop_fetching('.history'); | ||
697 | - ensure_scroll(jid, offset); | ||
698 | - }); | ||
699 | - } | ||
700 | - | ||
701 | - function move_conversation_to_the_top(jid) { | ||
702 | - id = Jabber.jid_to_id(jid); | ||
703 | - var link = $('#'+id); | ||
704 | - var li = link.closest('li'); | ||
705 | - var ul = link.closest('ul'); | ||
706 | - ul.prepend(li); | ||
707 | - } | ||
708 | - | ||
709 | - function sort_conversations() { | ||
710 | - $.getJSON('/chat/recent_conversations', {}, function(data) { | ||
711 | - $.each(data['order'], function(i, identifier) { | ||
712 | - move_conversation_to_the_top(identifier+'-'+data['domain']); | ||
713 | - }) | ||
714 | - }) | ||
715 | - } | ||
716 | - | ||
717 | - function load_defaults() { | ||
718 | - $.getJSON('/chat/my_session', {}, function(data) { | ||
719 | - $.each(data.rooms, function(i, room_jid) { | ||
720 | - $('#chat').trigger('opengroup', room_jid); | ||
721 | - }) | ||
722 | - | ||
723 | - $('#'+data.tab_id).click(); | ||
724 | - | ||
725 | - if(data.status == 'opened') | ||
726 | - toggle_chat_window(); | ||
727 | - }) | ||
728 | - } | ||
729 | - | ||
730 | - function count_unread_messages(jid_id, hide) { | ||
731 | - var unread = $('.buddies #'+jid_id+ ' .unread-messages'); | ||
732 | - if (hide) { | ||
733 | - unread.hide(); | ||
734 | - unread.siblings('img').show(); | ||
735 | - Jabber.unread_messages_of(jid_id, 0); | ||
736 | - unread.text(''); | 622 | + }).fail(function(){ |
623 | + console.log('500 - Internal server error.') | ||
624 | + }); | ||
625 | + } | ||
626 | + | ||
627 | + // open new conversation or change to already opened tab | ||
628 | + $('#buddy-list .buddies li a').live('click', function() { | ||
629 | + var jid = Jabber.jid_of($(this).attr('id')); | ||
630 | + open_conversation(jid); | ||
631 | + return false; | ||
632 | + }); | ||
633 | + | ||
634 | + // put name into text area when click in one occupant | ||
635 | + $('.occupants .occupant-list li a').live('click', function() { | ||
636 | + var jid_id = $(this).attr('data-id'); | ||
637 | + var name = Jabber.name_of(jid_id); | ||
638 | + var val = $('.conversation textarea:visible').val(); | ||
639 | + $('.conversation textarea:visible').focus().val(val + name + ', '); | ||
640 | + return false; | ||
641 | + }); | ||
642 | + | ||
643 | + $('#chat .conversation .history').live('click', function() { | ||
644 | + $('.conversation textarea:visible').focus(); | ||
645 | + }); | ||
646 | + | ||
647 | + function toggle_chat_window() { | ||
648 | + if(jQuery('#conversations .conversation').length == 0) jQuery('.buddies a').first().click(); | ||
649 | + jQuery('#chat').toggleClass('opened'); | ||
650 | + jQuery('#chat-label').toggleClass('opened'); | ||
651 | + } | ||
652 | + | ||
653 | + function load_conversation(jid) { | ||
654 | + var jid_id = Jabber.jid_to_id(jid); | ||
655 | + var name = Jabber.name_of(jid_id); | ||
656 | + if (jid) { | ||
657 | + if (Strophe.getDomainFromJid(jid) == Jabber.muc_domain) { | ||
658 | + if (Jabber.muc_supported) { | ||
659 | + log('opening groupchat with ' + jid); | ||
660 | + Jabber.jids[jid_id] = {jid: jid, name: name, type: 'groupchat'}; | ||
661 | + var conversation = create_conversation_tab(name, jid_id); | ||
662 | + Jabber.enter_room(jid); | ||
663 | + recent_messages(jid, 0, true); | ||
664 | + return conversation; | ||
665 | + } | ||
737 | } | 666 | } |
738 | else { | 667 | else { |
739 | - unread.siblings('img').hide(); | ||
740 | - unread.css('display', 'inline-block'); | ||
741 | - var unread_messages = Jabber.unread_messages_of(jid_id) || 0; | ||
742 | - Jabber.unread_messages_of(jid_id, ++unread_messages); | ||
743 | - unread.text(unread_messages); | ||
744 | - } | ||
745 | - update_total_unread_messages(); | ||
746 | - } | ||
747 | - | ||
748 | - function update_total_unread_messages() { | ||
749 | - var total_unread = $('#openchat .unread-messages'); | ||
750 | - var sum = 0; | ||
751 | - $('.buddies .unread-messages').each(function() { | ||
752 | - sum += Number($(this).text()); | ||
753 | - }); | ||
754 | - if(sum>0) { | ||
755 | - total_unread.text(sum); | ||
756 | - } else { | ||
757 | - total_unread.text(''); | 668 | + log('opening chat with ' + jid); |
669 | + Jabber.jids[jid_id] = {jid: jid, name: name, type: 'chat'}; | ||
670 | + var conversation = create_conversation_tab(name, jid_id); | ||
671 | + recent_messages(jid, 0, true); | ||
672 | + return conversation; | ||
758 | } | 673 | } |
759 | - } | 674 | + } |
675 | + } | ||
676 | + | ||
677 | + function open_conversation(jid) { | ||
678 | + var conversation = load_conversation(jid); | ||
679 | + var jid_id = Jabber.jid_to_id(jid); | ||
680 | + | ||
681 | + $('.conversation').hide(); | ||
682 | + conversation.show(); | ||
683 | + conversation.find('.input').focus(); | ||
684 | + $('#chat').addClass('opened'); | ||
685 | + $('#chat-label').addClass('opened'); | ||
686 | + $.post('/chat/tab', {tab_id: jid_id}); | ||
687 | + } | ||
688 | + | ||
689 | + function create_conversation_tab(title, jid_id) { | ||
690 | + var conversation_id = Jabber.conversation_prefix + jid_id; | ||
691 | + var conversation = $('#' + conversation_id); | ||
692 | + if (conversation.length > 0) { | ||
693 | + return conversation; | ||
694 | + } | ||
695 | + | ||
696 | + var jid = Jabber.jid_of(jid_id); | ||
697 | + var identifier = getIdentifier(jid); | ||
760 | 698 | ||
761 | - var $conversations = $('#chat-window #conversations'); | 699 | + var panel = $('#chat-templates .conversation').clone().appendTo($conversations).attr('id', conversation_id); |
700 | + panel.find('.chat-target .avatar').replaceWith(getAvatar(identifier)); | ||
701 | + panel.find('.chat-target .other-name').html(title); | ||
702 | + $('#chat .history').perfectScrollbar(); | ||
762 | 703 | ||
763 | - function log(msg) { | ||
764 | - if(Jabber.debug && window.console && window.console.log) { | ||
765 | - var time = new Date(); | ||
766 | - window.console.log('['+ time.toTimeString() +'] ' + msg); | 704 | + panel.find('.history').scroll(function(){ |
705 | + if($(this).scrollTop() == 0){ | ||
706 | + var offset = panel.find('.message p').size(); | ||
707 | + recent_messages(jid, offset); | ||
767 | } | 708 | } |
768 | - } | 709 | + }); |
710 | + | ||
711 | + var textarea = panel.find('textarea'); | ||
712 | + textarea.attr('name', panel.id); | ||
713 | + | ||
714 | + if (Jabber.is_a_room(jid_id)) { | ||
715 | + panel.append(Jabber.template('.occupant-list-template')); | ||
716 | + panel.find('.history').addClass('room'); | ||
717 | + var room_actions = $('#chat-templates .room-action').clone(); | ||
718 | + room_actions.data('jid', jid); | ||
719 | + panel.find('.history').after(room_actions); | ||
720 | + $('#chat .occupants .occupant-list').perfectScrollbar(); | ||
721 | + } | ||
722 | + textarea.attr('data-to', jid); | ||
723 | + | ||
724 | + return panel; | ||
725 | + } | ||
726 | + | ||
727 | + function ensure_scroll(jid, offset) { | ||
728 | + var jid_id = Jabber.jid_to_id(jid); | ||
729 | + var history = jQuery('#conversation-'+jid_id+' .history'); | ||
730 | + // Load more messages if was not enough to show the scroll | ||
731 | + if(history.prop('scrollHeight') - history.prop('clientHeight') <= 0){ | ||
732 | + var offset = history.find('.message p').size(); | ||
733 | + recent_messages(jid, offset); | ||
734 | + } | ||
735 | + } | ||
736 | + | ||
737 | + function recent_messages(jid, offset, clear_unread) { | ||
738 | + if (Jabber.no_more_messages[jid]) return; | ||
739 | + | ||
740 | + if(!offset) offset = 0; | ||
741 | + start_fetching('.history'); | ||
742 | + $.getJSON('/chat/recent_messages', {identifier: getIdentifier(jid), offset: offset}, function(data) { | ||
743 | + // Register if no more messages returned and stop trying to load | ||
744 | + // more messages in the future. | ||
745 | + if(data.length == 0) | ||
746 | + Jabber.no_more_messages[jid] = true; | ||
747 | + | ||
748 | + $.each(data, function(i, message) { | ||
749 | + var body = message['body']; | ||
750 | + var from = message['from']; | ||
751 | + var to = message['to']; | ||
752 | + var date = message['created_at']; | ||
753 | + var who = from['id']==getCurrentIdentifier() ? 'self' : from['id'] | ||
754 | + | ||
755 | + Jabber.show_message(jid, from['name'], body, who, from['id'], date, offset); | ||
756 | + }); | ||
757 | + stop_fetching('.history'); | ||
758 | + ensure_scroll(jid, offset); | ||
769 | 759 | ||
770 | - function escape_html(body) { | ||
771 | - return body | ||
772 | - .replace(/&/g, '&') | ||
773 | - .replace(/</g, '<') | ||
774 | - .replace(/>/g, '>'); | ||
775 | - } | 760 | + if(clear_unread){ |
761 | + var jid_id = Jabber.jid_to_id(jid); | ||
762 | + count_unread_messages(jid_id, true); | ||
763 | + } | ||
764 | + }); | ||
765 | + } | ||
766 | + | ||
767 | + function move_conversation_to_the_top(jid) { | ||
768 | + id = Jabber.jid_to_id(jid); | ||
769 | + var link = $('#'+id); | ||
770 | + var li = link.closest('li'); | ||
771 | + var ul = link.closest('ul'); | ||
772 | + ul.prepend(li); | ||
773 | + } | ||
774 | + | ||
775 | + function renew_conversation_order(jid){ | ||
776 | + var i = Jabber.conversations_order.indexOf(jid); | ||
777 | + // Remove element from the list | ||
778 | + if(i >= 0) { | ||
779 | + var elem = Jabber.conversations_order[i]; | ||
780 | + var a = Jabber.conversations_order.slice(0,i); | ||
781 | + var b = Jabber.conversations_order.slice(i+1, Jabber.conversations_order.length); | ||
782 | + Jabber.conversations_order = a.concat(b); | ||
783 | + } else | ||
784 | + var elem = jid; | ||
785 | + | ||
786 | + Jabber.conversations_order = Jabber.conversations_order.concat(elem); | ||
787 | + } | ||
788 | + | ||
789 | + function sort_conversations() { | ||
790 | + if(Jabber.conversations_order){ | ||
791 | + for (var i = 0; i < Jabber.conversations_order.length; i++) | ||
792 | + move_conversation_to_the_top(Jabber.conversations_order[i]); | ||
793 | + } | ||
794 | + } | ||
795 | + | ||
796 | + function load_defaults() { | ||
797 | + $.getJSON('/chat/my_session', {}, function(data) { | ||
798 | + $.each(data.rooms, function(i, room_jid) { | ||
799 | + load_conversation(room_jid); | ||
800 | + }) | ||
801 | + | ||
802 | + $('#'+data.tab_id).click(); | ||
803 | + | ||
804 | + if(data.status == 'opened') | ||
805 | + toggle_chat_window(); | ||
806 | + }) | ||
807 | + } | ||
808 | + | ||
809 | + function count_unread_messages(jid_id, hide) { | ||
810 | + var unread = $('.buddies #'+jid_id+ ' .unread-messages'); | ||
811 | + if (hide) { | ||
812 | + unread.hide(); | ||
813 | + unread.siblings('img').show(); | ||
814 | + Jabber.unread_messages_of(jid_id, 0); | ||
815 | + unread.text(''); | ||
816 | + } | ||
817 | + else { | ||
818 | + unread.siblings('img').hide(); | ||
819 | + unread.css('display', 'inline-block'); | ||
820 | + var unread_messages = Jabber.unread_messages_of(jid_id) || 0; | ||
821 | + Jabber.unread_messages_of(jid_id, ++unread_messages); | ||
822 | + unread.text(unread_messages); | ||
823 | + } | ||
824 | + update_total_unread_messages(); | ||
825 | + } | ||
826 | + | ||
827 | + function update_total_unread_messages() { | ||
828 | + var total_unread = $('#unread-messages'); | ||
829 | + var sum = 0; | ||
830 | + $('#chat .unread-messages').each(function() { | ||
831 | + sum += Number($(this).text()); | ||
832 | + }); | ||
833 | + if(sum>0) { | ||
834 | + total_unread.text(sum); | ||
835 | + } else { | ||
836 | + total_unread.text(''); | ||
837 | + } | ||
838 | + } | ||
776 | 839 | ||
777 | - function getCurrentIdentifier() { | ||
778 | - return getIdentifier(Jabber.connection.jid); | ||
779 | - } | 840 | + var $conversations = $('#chat-window #conversations'); |
780 | 841 | ||
781 | - function getIdentifier(jid) { | ||
782 | - return Strophe.getNodeFromJid(jid); | ||
783 | - } | 842 | + function log(msg) { |
843 | + if(Jabber.debug && window.console && window.console.log) { | ||
844 | + var time = new Date(); | ||
845 | + window.console.log('['+ time.toTimeString() +'] ' + msg); | ||
846 | + } | ||
847 | + } | ||
848 | + | ||
849 | + function escape_html(body) { | ||
850 | + return body | ||
851 | + .replace(/&/g, '&') | ||
852 | + .replace(/</g, '<') | ||
853 | + .replace(/>/g, '>'); | ||
854 | + } | ||
855 | + | ||
856 | + function getCurrentIdentifier() { | ||
857 | + return getIdentifier(Jabber.connection.jid); | ||
858 | + } | ||
859 | + | ||
860 | + function getIdentifier(jid) { | ||
861 | + return Strophe.getNodeFromJid(jid); | ||
862 | + } | ||
863 | + | ||
864 | + function getMyAvatar() { | ||
865 | + return getAvatar(getCurrentIdentifier()); | ||
866 | + } | ||
867 | + | ||
868 | + function getAvatar(identifier) { | ||
869 | + if(Jabber.avatars[identifier]) | ||
870 | + var src = Jabber.avatars[identifier]; | ||
871 | + else | ||
872 | + var src = "/chat/avatar/"+identifier; | ||
873 | + | ||
874 | + return '<img class="avatar" src="' + src + '">'; | ||
875 | + } | ||
876 | + | ||
877 | + function disconnect() { | ||
878 | + log('disconnect'); | ||
879 | + if (Jabber.connection && Jabber.connection.connected) { | ||
880 | + Jabber.connection.disconnect(); | ||
881 | + } | ||
882 | + Jabber.presence_status = 'offline'; | ||
883 | + Jabber.show_status('offline'); | ||
884 | + } | ||
885 | + | ||
886 | + function notifyMessage(message) { | ||
887 | + var jid = Strophe.getBareJidFromJid(message.from); | ||
888 | + var jid_id = Jabber.jid_to_id(jid); | ||
889 | + var name = Jabber.name_of(jid_id); | ||
890 | + var identifier = Strophe.getNodeFromJid(jid); | ||
891 | + var avatar = "/chat/avatar/"+identifier | ||
892 | + if(!$('#chat').hasClass('opened') || window.isHidden() || !Jabber.window_visibility) { | ||
893 | + var options = {body: message.body, icon: avatar, tag: jid_id}; | ||
894 | + $(notifyMe(name, options)).on('click', function(){ | ||
895 | + open_conversation(jid); | ||
896 | + }); | ||
897 | + Jabber.notification_sound.play(); | ||
898 | + } | ||
899 | + } | ||
784 | 900 | ||
785 | - function getMyAvatar() { | ||
786 | - return getAvatar(getCurrentIdentifier()); | ||
787 | - } | 901 | + $('.title-bar a').click(function() { |
902 | + $(this).parents('.status-group').find('.buddies').toggle('fast'); | ||
903 | + return false; | ||
904 | + }); | ||
905 | + $('#chat').on('click', '.occupants a', function() { | ||
906 | + $(this).siblings('.occupant-list').toggle('fast'); | ||
907 | + $(this).toggleClass('up'); | ||
908 | + return false; | ||
909 | + }); | ||
788 | 910 | ||
789 | - function getAvatar(identifier) { | ||
790 | - return '<img class="avatar" src="/chat/avatar/' + identifier + '">'; | ||
791 | - } | 911 | + //restore connection if user was connected |
912 | + if($presence=='' || $presence == 'chat') { | ||
913 | + $('#chat-connect').trigger('click'); | ||
914 | + } else if($presence == 'dnd') { | ||
915 | + $('#chat-busy').trigger('click'); | ||
916 | + } | ||
792 | 917 | ||
793 | - function disconnect() { | ||
794 | - log('disconnect'); | ||
795 | - if (Jabber.connection && Jabber.connection.connected) { | ||
796 | - Jabber.connection.disconnect(); | ||
797 | - } | ||
798 | - Jabber.presence_status = 'offline'; | ||
799 | - Jabber.show_status('offline'); | ||
800 | - } | ||
801 | - | ||
802 | - function notifyMessage(message) { | ||
803 | - var jid = Strophe.getBareJidFromJid(message.from); | ||
804 | - var jid_id = Jabber.jid_to_id(jid); | ||
805 | - var name = Jabber.name_of(jid_id); | ||
806 | - var identifier = Strophe.getNodeFromJid(jid); | ||
807 | - var avatar = "/chat/avatar/"+identifier | ||
808 | - if(!$('#chat').hasClass('opened') || window.isHidden()) { | ||
809 | - var options = {body: message.body, icon: avatar, tag: jid_id}; | ||
810 | - console.log('Notify '+name); | ||
811 | - $(notifyMe(name, options)).on('click', function(){ | ||
812 | - open_conversation(jid); | ||
813 | - }); | ||
814 | - $.sound.play('/sounds/receive.wav'); | ||
815 | - } | ||
816 | - } | ||
817 | - | ||
818 | - $('.title-bar a').click(function() { | ||
819 | - $(this).parents('.status-group').find('.buddies').toggle('fast'); | ||
820 | - }); | ||
821 | - $('#chat').on('click', '.occupants a', function() { | ||
822 | - $(this).siblings('.occupant-list').toggle('fast'); | ||
823 | - $(this).toggleClass('up'); | ||
824 | - }); | ||
825 | - | ||
826 | - //restore connection if user was connected | ||
827 | - if($presence=='' || $presence == 'chat') { | ||
828 | - $('#chat-connect').trigger('click'); | ||
829 | - } else if($presence == 'dnd') { | ||
830 | - $('#chat-busy').trigger('click'); | ||
831 | - } | ||
832 | - | ||
833 | - $('#chat #buddy-list .buddies').perfectScrollbar(); | 918 | + $('#chat #buddy-list .buddies').perfectScrollbar(); |
834 | 919 | ||
835 | // custom css expression for a case-insensitive contains() | 920 | // custom css expression for a case-insensitive contains() |
836 | jQuery.expr[':'].Contains = function(a,i,m){ | 921 | jQuery.expr[':'].Contains = function(a,i,m){ |
837 | - return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0; | 922 | + return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0; |
838 | }; | 923 | }; |
839 | 924 | ||
840 | $('#chat .search').change( function () { | 925 | $('#chat .search').change( function () { |
@@ -856,6 +941,7 @@ jQuery(function($) { | @@ -856,6 +941,7 @@ jQuery(function($) { | ||
856 | 941 | ||
857 | $('#chat .buddies a').live('click', function(){ | 942 | $('#chat .buddies a').live('click', function(){ |
858 | $('#chat .search').val('').change(); | 943 | $('#chat .search').val('').change(); |
944 | + return false; | ||
859 | }); | 945 | }); |
860 | 946 | ||
861 | $('#chat-label').click(function(){ | 947 | $('#chat-label').click(function(){ |
@@ -871,6 +957,14 @@ jQuery(function($) { | @@ -871,6 +957,14 @@ jQuery(function($) { | ||
871 | $('.room-action.leave').live('click', function(){ | 957 | $('.room-action.leave').live('click', function(){ |
872 | var jid = $(this).data('jid'); | 958 | var jid = $(this).data('jid'); |
873 | Jabber.leave_room(jid); | 959 | Jabber.leave_room(jid); |
960 | + return false; | ||
961 | + }); | ||
962 | + | ||
963 | + $('.open-conversation').live('click', function(){ | ||
964 | + open_conversation($(this).data('jid')); | ||
965 | + return false; | ||
874 | }); | 966 | }); |
875 | 967 | ||
968 | + window.onfocus = function() {Jabber.window_visibility = true}; | ||
969 | + window.onblur = function() {Jabber.window_visibility = false}; | ||
876 | }); | 970 | }); |
public/stylesheets/chat.css
@@ -201,6 +201,21 @@ | @@ -201,6 +201,21 @@ | ||
201 | bottom: 132px; | 201 | bottom: 132px; |
202 | } | 202 | } |
203 | 203 | ||
204 | +#chat-label.opened #unread-messages, | ||
205 | +#unread-messages:empty { | ||
206 | + display: none; | ||
207 | +} | ||
208 | + | ||
209 | +#unread-messages { | ||
210 | + padding: 3px 5px; | ||
211 | + background-color: #F57900; | ||
212 | + border-radius: 5px; | ||
213 | + margin-top: -10px; | ||
214 | + margin-left: -30px; | ||
215 | + position: absolute; | ||
216 | + z-index: 1; | ||
217 | +} | ||
218 | + | ||
204 | #chat .unread-messages { | 219 | #chat .unread-messages { |
205 | height: 32px; | 220 | height: 32px; |
206 | line-height: 32px; | 221 | line-height: 32px; |
test/functional/chat_controller_test.rb
@@ -95,6 +95,54 @@ class ChatControllerTest < ActionController::TestCase | @@ -95,6 +95,54 @@ class ChatControllerTest < ActionController::TestCase | ||
95 | assert_not_equal chat_status_at, @person.user.chat_status_at | 95 | assert_not_equal chat_status_at, @person.user.chat_status_at |
96 | end | 96 | end |
97 | 97 | ||
98 | + should 'forbid to register a message without to' do | ||
99 | + @controller.stubs(:current_user).returns(@person.user) | ||
100 | + @request.stubs(:xhr?).returns(true) | ||
101 | + | ||
102 | + post :save_message, {:body =>'Hello!'} | ||
103 | + assert ActiveSupport::JSON.decode(@response.body)['status'] == 1 | ||
104 | + end | ||
105 | + | ||
106 | + should 'forbid to register a message without body' do | ||
107 | + @controller.stubs(:current_user).returns(@person.user) | ||
108 | + @request.stubs(:xhr?).returns(true) | ||
109 | + | ||
110 | + post :save_message, {:to =>'mary'} | ||
111 | + assert ActiveSupport::JSON.decode(@response.body)['status'] == 1 | ||
112 | + end | ||
113 | + | ||
114 | + should 'forbid user to register a message to a stranger' do | ||
115 | + @controller.stubs(:current_user).returns(@person.user) | ||
116 | + @request.stubs(:xhr?).returns(true) | ||
117 | + | ||
118 | + post :save_message, {:to =>'random', :body => 'Hello, stranger!'} | ||
119 | + assert ActiveSupport::JSON.decode(@response.body)['status'] == 2 | ||
120 | + end | ||
121 | + | ||
122 | + should 'register a message to a friend' do | ||
123 | + @controller.stubs(:current_user).returns(@person.user) | ||
124 | + friend = create_user('friend').person | ||
125 | + @person.add_friend friend | ||
126 | + @request.stubs(:xhr?).returns(true) | ||
127 | + | ||
128 | + assert_difference 'ChatMessage.count', 1 do | ||
129 | + post :save_message, {:to => friend.identifier, :body => 'Hey! How is it going?'} | ||
130 | + assert ActiveSupport::JSON.decode(@response.body)['status'] == 0 | ||
131 | + end | ||
132 | + end | ||
133 | + | ||
134 | + should 'register a message to a group' do | ||
135 | + @controller.stubs(:current_user).returns(@person.user) | ||
136 | + group = fast_create(Organization) | ||
137 | + group.add_member(@person) | ||
138 | + @request.stubs(:xhr?).returns(true) | ||
139 | + | ||
140 | + assert_difference 'ChatMessage.count', 1 do | ||
141 | + post :save_message, {:to => group.identifier, :body => 'Hey! How is it going?'} | ||
142 | + assert ActiveSupport::JSON.decode(@response.body)['status'] == 0 | ||
143 | + end | ||
144 | + end | ||
145 | + | ||
98 | should 'toggle chat status' do | 146 | should 'toggle chat status' do |
99 | login_as 'testuser' | 147 | login_as 'testuser' |
100 | 148 |
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | +require 'test_helper' | ||
2 | + | ||
3 | +class ChatMessageTest < ActiveSupport::TestCase | ||
4 | + should 'create message' do | ||
5 | + assert_difference 'ChatMessage.count', 1 do | ||
6 | + ChatMessage.create!(:from => fast_create(Person), :to => fast_create(Person), :body => 'Hey! How are you?' ) | ||
7 | + end | ||
8 | + end | ||
9 | +end |
-
mentioned in commit 0725f74d54984d10514911602276c4401f54cae4