summaryrefslogtreecommitdiff
path: root/addons/im/static/src
diff options
context:
space:
mode:
authorVidhin Mehta <vme@tinyerp.com>2013-08-23 10:36:02 +0530
committerVidhin Mehta <vme@tinyerp.com>2013-08-23 10:36:02 +0530
commit75ddd5ccbec73c4a671b6a74efaa3d41dc2ea2db (patch)
tree50ed3620df15e8533760f5d47e754a1d39c1e856 /addons/im/static/src
parent42f27a15275effd9c43dfe99e32c395ca6b3fd8a (diff)
parenta7f4b6aaa0359d12046e81183417c24f093cd7c3 (diff)
[MERGE]Trunk.
bzr revid: vme@tinyerp.com-20130823050602-33bqv07e7g8dhv7x
Diffstat (limited to 'addons/im/static/src')
-rw-r--r--addons/im/static/src/css/im.css157
-rw-r--r--addons/im/static/src/css/im_common.css190
-rw-r--r--addons/im/static/src/img/icon.pngbin16237 -> 0 bytes
-rw-r--r--addons/im/static/src/js/im.js339
-rw-r--r--addons/im/static/src/js/im_common.js408
-rw-r--r--addons/im/static/src/xml/im.xml33
-rw-r--r--addons/im/static/src/xml/im_common.xml36
7 files changed, 648 insertions, 515 deletions
diff --git a/addons/im/static/src/css/im.css b/addons/im/static/src/css/im.css
index 269d411d416..03a20bb0510 100644
--- a/addons/im/static/src/css/im.css
+++ b/addons/im/static/src/css/im.css
@@ -105,160 +105,3 @@
vertical-align: middle;
border: 0;
}
-
-/* conversations */
-
-.openerp .oe_im_chatview {
- position: fixed;
- overflow: hidden;
- bottom: 6px;
- margin-right: 6px;
- background: rgba(60, 60, 60, 0.8);
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 0 3px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.3);
- -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3);
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3);
- width: 240px;
-}
-.openerp .oe_im_chatview .oe_im_chatview_disconnected {
- display:none;
- z-index: 100;
- width: 100%;
- background: #E8EBEF;
- padding: 5px;
- font-size: 11px;
- color: #999;
- line-height: 14px;
- height: 28px;
- overflow: hidden;
-}
-.openerp .oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_disconnected {
- display: block;
-}
-.openerp .oe_im_chatview .oe_im_chatview_header {
- padding: 3px 6px 2px;
- background: #DEDEDE;
- background: -moz-linear-gradient(#FCFCFC, #DEDEDE);
- background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE));
- -moz-border-radius: 3px 3px 0 0;
- -webkit-border-radius: 3px 3px 0 0;
- border-radius: 3px 3px 0 0;
- border-bottom: 1px solid #AEB9BD;
- cursor: pointer;
-}
-.openerp .oe_im_chatview .oe_im_chatview_close {
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- -webkit-appearance: none;
- font-size: 18px;
- line-height: 16px;
- float: right;
- font-weight: bold;
- color: black;
- text-shadow: 0 1px 0 white;
- opacity: 0.2;
-}
-.openerp .oe_im_chatview .oe_im_chatview_content {
- overflow: auto;
- height: 287px;
-}
-.openerp .oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_content {
- height: 249px;
-}
-.openerp .oe_im_chatview .oe_im_chatview_footer {
- position: relative;
- padding: 3px;
- border-top: 1px solid #AEB9BD;
- background: #DEDEDE;
- background: -moz-linear-gradient(#FCFCFC, #DEDEDE);
- background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE));
- -moz-border-radius: 0 0 3px 3px;
- -webkit-border-radius: 0 0 3px 3px;
- border-radius: 0 0 3px 3px;
-}
-.openerp .oe_im_chatview .oe_im_chatview_input {
- width: 222px;
- font-family: Lato, Helvetica, sans-serif;
- font-size: 13px;
- color: #333;
- padding: 1px 5px;
- border: 1px solid #AEB9BD;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: inset 0 1px 4px rgba(0,0,0,0.2);
- -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2);
- box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_im_chatview .oe_im_chatview_bubble {
- background: white;
- position: relative;
- padding: 3px;
- margin: 3px;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
-}
-.openerp .oe_im_chatview .oe_im_chatview_clip {
- position: relative;
- float: left;
- width: 26px;
- height: 26px;
- margin-right: 4px;
- -moz-box-shadow: 0 0 2px 1px rgba(0,0,0,0.25);
- -webkit-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25);
- box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25);
-}
-.openerp .oe_im_chatview .oe_im_chatview_avatar {
- float: left;
- width: 26px;
- height: auto;
- clip: rect(0, 26px, 26px, 0);
- max-width: 100%;
- width: auto 9;
- height: auto;
- vertical-align: middle;
- border: 0;
- -ms-interpolation-mode: bicubic;
-}
-.openerp .oe_im_chatview .oe_im_chatview_time {
- position: absolute;
- right: 0px;
- top: 0px;
- margin: 3px;
- text-align: right;
- line-height: 13px;
- font-size: 11px;
- color: #999;
- width: 60px;
- overflow: hidden;
-}
-.openerp .oe_im_chatview .oe_im_chatview_from {
- margin: 0 0 2px 0;
- line-height: 14px;
- font-weight: bold;
- font-size: 12px;
- width: 140px;
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
- color: #3A87AD;
-}
-.openerp .oe_im_chatview .oe_im_chatview_bubble_list {
-}
-.openerp .oe_im_chatview .oe_im_chatview_bubble_item {
- margin: 0 0 2px 30px;
- line-height: 14px;
- word-wrap: break-word;
-}
-
-.openerp .oe_im_chatview_online {
- display: none;
- margin-top: -4px;
- width: 11px;
- height: 11px;
-}
diff --git a/addons/im/static/src/css/im_common.css b/addons/im/static/src/css/im_common.css
new file mode 100644
index 00000000000..f3877af1686
--- /dev/null
+++ b/addons/im/static/src/css/im_common.css
@@ -0,0 +1,190 @@
+
+
+
+.openerp_style { /* base style of openerp */
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
+ color: #4c4c4c;
+ font-size: 13px;
+ background: white;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
+}
+
+/* button */
+
+.oe_chat_button {
+ position: fixed;
+ bottom: 0px;
+ right: 6px;
+ display: inline-block;
+ min-width: 100px;
+ background-color: rgba(60, 60, 60, 0.6);
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, Verdana, sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ padding: 10px;
+ color: white;
+ text-shadow: rgb(59, 76, 88) 1px 1px 0px;
+ border: 1px solid rgb(80, 80, 80);
+ border-bottom: 0px;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ cursor: pointer;
+}
+
+/* conversations */
+
+.oe_im_chatview {
+ position: fixed;
+ overflow: hidden;
+ margin-bottom: 5px;
+ margin-right: 6px;
+ background: rgba(60, 60, 60, 0.8);
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 0 3px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.3);
+ -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3);
+ width: 240px;
+}
+.oe_im_chatview .oe_im_chatview_disconnected {
+ display:none;
+ z-index: 100;
+ width: 100%;
+ background: #E8EBEF;
+ padding: 5px;
+ font-size: 11px;
+ color: #999;
+ line-height: 14px;
+ height: 28px;
+ overflow: hidden;
+}
+.oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_disconnected {
+ display: block;
+}
+.oe_im_chatview .oe_im_chatview_header {
+ padding: 3px 6px 2px;
+ background: #DEDEDE;
+ background: -moz-linear-gradient(#FCFCFC, #DEDEDE);
+ background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE));
+ -moz-border-radius: 3px 3px 0 0;
+ -webkit-border-radius: 3px 3px 0 0;
+ border-radius: 3px 3px 0 0;
+ border-bottom: 1px solid #AEB9BD;
+ cursor: pointer;
+}
+.oe_im_chatview .oe_im_chatview_close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ font-size: 18px;
+ line-height: 16px;
+ float: right;
+ font-weight: bold;
+ color: black;
+ text-shadow: 0 1px 0 white;
+ opacity: 0.2;
+}
+.oe_im_chatview .oe_im_chatview_content {
+ overflow: auto;
+ height: 287px;
+ width: 240px;
+}
+.oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_content {
+ height: 249px;
+}
+.oe_im_chatview .oe_im_chatview_footer {
+ position: relative;
+ padding: 3px;
+ border-top: 1px solid #AEB9BD;
+ background: #DEDEDE;
+ background: -moz-linear-gradient(#FCFCFC, #DEDEDE);
+ background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE));
+ -moz-border-radius: 0 0 3px 3px;
+ -webkit-border-radius: 0 0 3px 3px;
+ border-radius: 0 0 3px 3px;
+}
+.oe_im_chatview .oe_im_chatview_input {
+ width: 222px;
+ font-family: Lato, Helvetica, sans-serif;
+ font-size: 13px;
+ color: #333;
+ padding: 1px 5px;
+ border: 1px solid #AEB9BD;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: inset 0 1px 4px rgba(0,0,0,0.2);
+ -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2);
+ box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2);
+}
+.oe_im_chatview .oe_im_chatview_bubble {
+ background: white;
+ position: relative;
+ padding: 3px;
+ margin: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+}
+.oe_im_chatview .oe_im_chatview_clip {
+ position: relative;
+ float: left;
+ width: 26px;
+ height: 26px;
+ margin-right: 4px;
+ -moz-box-shadow: 0 0 2px 1px rgba(0,0,0,0.25);
+ -webkit-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25);
+ box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25);
+}
+.oe_im_chatview .oe_im_chatview_avatar {
+ float: left;
+ width: 26px;
+ height: auto;
+ clip: rect(0, 26px, 26px, 0);
+ max-width: 100%;
+ width: auto 9;
+ height: auto;
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+.oe_im_chatview .oe_im_chatview_time {
+ position: absolute;
+ right: 0px;
+ top: 0px;
+ margin: 3px;
+ text-align: right;
+ line-height: 13px;
+ font-size: 11px;
+ color: #999;
+ width: 60px;
+ overflow: hidden;
+}
+.oe_im_chatview .oe_im_chatview_from {
+ margin: 0 0 2px 0;
+ line-height: 14px;
+ font-weight: bold;
+ font-size: 12px;
+ width: 140px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ color: #3A87AD;
+}
+.oe_im_chatview .oe_im_chatview_bubble_list {
+}
+.oe_im_chatview .oe_im_chatview_bubble_item {
+ margin: 0 0 2px 30px;
+ line-height: 14px;
+ word-wrap: break-word;
+}
+
+.oe_im_chatview_online {
+ display: none;
+ margin-top: -4px;
+ width: 11px;
+ height: 11px;
+}
diff --git a/addons/im/static/src/img/icon.png b/addons/im/static/src/img/icon.png
deleted file mode 100644
index 07d2503ce4e..00000000000
--- a/addons/im/static/src/img/icon.png
+++ /dev/null
Binary files differ
diff --git a/addons/im/static/src/js/im.js b/addons/im/static/src/js/im.js
index a271c9eccf0..9af04bf4f43 100644
--- a/addons/im/static/src/js/im.js
+++ b/addons/im/static/src/js/im.js
@@ -1,17 +1,25 @@
-openerp.im = function(instance) {
+(function() {
+ "use strict";
+
+ var instance = openerp;
+
+ openerp.im = {};
var USERS_LIMIT = 20;
- var ERROR_DELAY = 5000;
- var _t = instance.web._t,
- _lt = instance.web._lt;
+ var _t = instance.web._t;
var QWeb = instance.web.qweb;
instance.web.UserMenu.include({
do_update: function(){
var self = this;
this.update_promise.then(function() {
+ im_common.notification = function(message) {
+ instance.client.do_warn(message);
+ };
+ im_common.connection = openerp.session;
+
var im = new instance.im.InstantMessaging(self);
im.appendTo(instance.client.$el);
var button = new instance.im.ImTopButton(this);
@@ -45,7 +53,7 @@ openerp.im = function(instance) {
this.set("right_offset", 0);
this.set("current_search", "");
this.users = [];
- this.c_manager = new instance.im.ConversationManager(this);
+ this.c_manager = new im_common.ConversationManager(this);
this.on("change:right_offset", this.c_manager, _.bind(function() {
this.c_manager.set("right_offset", this.get("right_offset"));
}, this));
@@ -148,323 +156,4 @@ openerp.im = function(instance) {
},
});
- instance.im.ImUser = instance.web.Class.extend(instance.web.PropertiesMixin, {
- init: function(parent, user_rec) {
- instance.web.PropertiesMixin.init.call(this, parent);
- user_rec.image_url = instance.session.url("/im/static/src/img/avatar/avatar.jpeg");
- if (user_rec.user)
- user_rec.image_url = instance.session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: user_rec.user[0]});
- this.set(user_rec);
- this.set("watcher_count", 0);
- this.on("change:watcher_count", this, function() {
- if (this.get("watcher_count") === 0)
- this.destroy();
- });
- },
- destroy: function() {
- this.trigger("destroyed");
- instance.web.PropertiesMixin.destroy.call(this);
- },
- add_watcher: function() {
- this.set("watcher_count", this.get("watcher_count") + 1);
- },
- remove_watcher: function() {
- this.set("watcher_count", this.get("watcher_count") - 1);
- },
- });
-
- instance.im.ConversationManager = instance.web.Controller.extend({
- init: function(parent) {
- this._super(parent);
- this.set("right_offset", 0);
- this.conversations = [];
- this.users = {};
- this.on("change:right_offset", this, this.calc_positions);
- this.set("window_focus", true);
- this.set("waiting_messages", 0);
- this.focus_hdl = _.bind(function() {
- this.set("window_focus", true);
- }, this);
- $(window).bind("focus", this.focus_hdl);
- this.blur_hdl = _.bind(function() {
- this.set("window_focus", false);
- }, this);
- $(window).bind("blur", this.blur_hdl);
- this.on("change:window_focus", this, this.window_focus_change);
- this.window_focus_change();
- this.on("change:waiting_messages", this, this.messages_change);
- this.messages_change();
- this.create_ting();
- this.activated = false;
- this.users_cache = {};
- this.last = null;
- this.unload_event_handler = _.bind(this.unload, this);
- },
- start_polling: function() {
- var self = this;
- return new instance.web.Model("im.user").call("get_by_user_id", [instance.session.uid]).then(function(my_id) {
- self.my_id = my_id["id"];
- return self.ensure_users([self.my_id]).then(function() {
- var me = self.users_cache[self.my_id];
- delete self.users_cache[self.my_id];
- self.me = me;
- self.rpc("/longpolling/im/activated", {}, {shadow: true}).then(function(activated) {
- if (activated) {
- self.activated = true;
- $(window).on("unload", self.unload_event_handler);
- self.poll();
- }
- }, function(a, e) {
- e.preventDefault();
- });
- });
- });
- },
- unload: function() {
- return new instance.web.Model("im.user").call("im_disconnect", [], {context: new instance.web.CompoundContext()});
- },
- ensure_users: function(user_ids) {
- var no_cache = {};
- _.each(user_ids, function(el) {
- if (! this.users_cache[el])
- no_cache[el] = el;
- }, this);
- var self = this;
- if (_.size(no_cache) === 0)
- return $.when();
- else
- return new instance.web.Model("im.user").call("read", [_.values(no_cache), ["name", "user", "uuid", "im_status"]],
- {context: new instance.web.CompoundContext()}).then(function(users) {
- self.add_to_user_cache(users);
- });
- },
- add_to_user_cache: function(user_recs) {
- _.each(user_recs, function(user_rec) {
- if (! this.users_cache[user_rec.id]) {
- var user = new instance.im.ImUser(this, user_rec);
- this.users_cache[user_rec.id] = user;
- user.on("destroyed", this, function() {
- delete this.users_cache[user_rec.id];
- });
- }
- }, this);
- },
- get_user: function(user_id) {
- return this.users_cache[user_id];
- },
- poll: function() {
- var self = this;
- var user_ids = _.map(this.users_cache, function(el) {
- return el.get("id");
- });
- this.rpc("/longpolling/im/poll", {
- last: this.last,
- users_watch: user_ids,
- context: instance.web.pyeval.eval('context', {}),
- }, {shadow: true}).then(function(result) {
- _.each(result.users_status, function(el) {
- if (self.get_user(el.id))
- self.get_user(el.id).set(el);
- });
- self.last = result.last;
- var user_ids = _.pluck(_.pluck(result.res, "from_id"), 0);
- self.ensure_users(user_ids).then(function() {
- _.each(result.res, function(mes) {
- var user = self.get_user(mes.from_id[0]);
- self.received_message(mes, user);
- });
- self.poll();
- });
- }, function(unused, e) {
- e.preventDefault();
- setTimeout(_.bind(self.poll, self), ERROR_DELAY);
- });
- },
- get_activated: function() {
- return this.activated;
- },
- create_ting: function() {
- if (typeof(Audio) === "undefined") {
- this.ting = {play: function() {}};
- return;
- }
- var kitten = jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined;
- this.ting = new Audio(instance.webclient.session.origin + "/im/static/src/audio/" + (kitten ? "purr" : "Ting") +
- (new Audio().canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3"));
- },
- window_focus_change: function() {
- if (this.get("window_focus")) {
- this.set("waiting_messages", 0);
- }
- },
- messages_change: function() {
- if (! instance.webclient.set_title_part)
- return;
- instance.webclient.set_title_part("aa_im_messages", this.get("waiting_messages") === 0 ? undefined :
- _.str.sprintf(_t("%d Messages"), this.get("waiting_messages")));
- },
- activate_user: function(user, focus) {
- var conv = this.users[user.get('id')];
- if (! conv) {
- conv = new instance.im.Conversation(this, user, this.me);
- conv.appendTo(instance.client.$el);
- conv.on("destroyed", this, function() {
- this.conversations = _.without(this.conversations, conv);
- delete this.users[conv.user.get('id')];
- this.calc_positions();
- });
- this.conversations.push(conv);
- this.users[user.get('id')] = conv;
- this.calc_positions();
- }
- if (focus)
- conv.focus();
- return conv;
- },
- received_message: function(message, user) {
- if (! this.get("window_focus")) {
- this.set("waiting_messages", this.get("waiting_messages") + 1);
- this.ting.play();
- this.create_ting();
- }
- var conv = this.activate_user(user);
- conv.received_message(message);
- },
- calc_positions: function() {
- var current = this.get("right_offset");
- _.each(_.range(this.conversations.length), function(i) {
- this.conversations[i].set("right_position", current);
- current += this.conversations[i].$el.outerWidth(true);
- }, this);
- },
- destroy: function() {
- $(window).off("unload", this.unload_event_handler);
- $(window).unbind("blur", this.blur_hdl);
- $(window).unbind("focus", this.focus_hdl);
- this._super();
- },
- });
-
- instance.im.Conversation = instance.web.Widget.extend({
- "template": "Conversation",
- events: {
- "keydown input": "send_message",
- "click .oe_im_chatview_close": "destroy",
- "click .oe_im_chatview_header": "show_hide",
- },
- init: function(parent, user, me) {
- this._super(parent);
- this.me = me;
- this.user = user;
- this.user.add_watcher();
- this.set("right_position", 0);
- this.shown = true;
- this.set("pending", 0);
- },
- start: function() {
- var change_status = function() {
- this.$el.toggleClass("oe_im_chatview_disconnected_status", this.user.get("im_status") === false);
- this.$(".oe_im_chatview_online").toggle(this.user.get("im_status") === true);
- this._go_bottom();
- };
- this.user.on("change:im_status", this, change_status);
- change_status.call(this);
-
- this.on("change:right_position", this, this.calc_pos);
- this.full_height = this.$el.height();
- this.calc_pos();
- this.on("change:pending", this, _.bind(function() {
- if (this.get("pending") === 0) {
- this.$(".oe_im_chatview_nbr_messages").text("");
- } else {
- this.$(".oe_im_chatview_nbr_messages").text("(" + this.get("pending") + ")");
- }
- }, this));
- },
- show_hide: function() {
- if (this.shown) {
- this.$el.animate({
- height: this.$(".oe_im_chatview_header").outerHeight(),
- });
- } else {
- this.$el.animate({
- height: this.full_height,
- });
- }
- this.shown = ! this.shown;
- if (this.shown) {
- this.set("pending", 0);
- }
- },
- calc_pos: function() {
- this.$el.css("right", this.get("right_position"));
- },
- received_message: function(message) {
- if (this.shown) {
- this.set("pending", 0);
- } else {
- this.set("pending", this.get("pending") + 1);
- }
- this._add_bubble(this.user, message.message, message.date);
- },
- send_message: function(e) {
- if(e && e.which !== 13) {
- return;
- }
- var mes = this.$("input").val();
- if (! mes.trim()) {
- return;
- }
- this.$("input").val("");
- var send_it = _.bind(function() {
- var model = new instance.web.Model("im.message");
- return model.call("post", [mes, this.user.get('id')],
- {context: new instance.web.CompoundContext()});
- }, this);
- var tries = 0;
- send_it().then(_.bind(function() {
- this._add_bubble(this.me, mes, instance.web.datetime_to_str(new Date()));
- }, this), function(error, e) {
- e.preventDefault();
- tries += 1;
- if (tries < 3)
- return send_it();
- });
- },
- _add_bubble: function(user, item, date) {
- var items = [item];
- if (user === this.last_user) {
- this.last_bubble.remove();
- items = this.last_items.concat(items);
- }
- this.last_user = user;
- this.last_items = items;
- date = instance.web.str_to_datetime(date);
- var now = new Date();
- var diff = now - date;
- if (diff > (1000 * 60 * 60 * 24)) {
- date = $.timeago(date);
- } else {
- date = date.toString(Date.CultureInfo.formatPatterns.shortTime);
- }
-
- this.last_bubble = $(QWeb.render("Conversation.bubble", {"items": items, "user": user, "time": date}));
- $(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble);
- this._go_bottom();
- },
- _go_bottom: function() {
- this.$(".oe_im_chatview_content").scrollTop($(this.$(".oe_im_chatview_content").children()[0]).height());
- },
- focus: function() {
- this.$(".oe_im_chatview_input").focus();
- if (! this.shown)
- this.show_hide();
- },
- destroy: function() {
- this.user.remove_watcher();
- this.trigger("destroyed");
- return this._super();
- },
- });
-
-} \ No newline at end of file
+})(); \ No newline at end of file
diff --git a/addons/im/static/src/js/im_common.js b/addons/im/static/src/js/im_common.js
new file mode 100644
index 00000000000..a6314a978e2
--- /dev/null
+++ b/addons/im/static/src/js/im_common.js
@@ -0,0 +1,408 @@
+
+/*
+ This file must compile in EcmaScript 3 and work in IE7.
+
+ Prerequisites to use this module:
+ - load the im_common.xml qweb template into openerp.qweb
+ - implement all the stuff defined later
+*/
+
+(function() {
+
+function declare($, _, openerp) {
+ /* jshint es3: true */
+ "use strict";
+
+ var im_common = {};
+
+ /*
+ All of this must be defined to use this module
+ */
+ _.extend(im_common, {
+ notification: function(message) {
+ throw new Error("Not implemented");
+ },
+ connection: null
+ });
+
+ var _t = openerp._t;
+
+ var ERROR_DELAY = 5000;
+
+ im_common.ImUser = openerp.Class.extend(openerp.PropertiesMixin, {
+ init: function(parent, user_rec) {
+ openerp.PropertiesMixin.init.call(this, parent);
+ user_rec.image_url = im_common.connection.url("/im/static/src/img/avatar/avatar.jpeg");
+
+ // TODO : check it works correctly
+ if (user_rec.user)
+ user_rec.image_url = im_common.connection.url('/web/binary/image', {model:'res.users', field: 'image_small', id: user_rec.user[0]});
+ /*if (user_rec.image)
+ user_rec.image_url = "data:image/png;base64," + user_rec.image;*/
+
+ this.set(user_rec);
+ this.set("watcher_count", 0);
+ this.on("change:watcher_count", this, function() {
+ if (this.get("watcher_count") === 0)
+ this.destroy();
+ });
+ },
+ destroy: function() {
+ this.trigger("destroyed");
+ openerp.PropertiesMixin.destroy.call(this);
+ },
+ add_watcher: function() {
+ this.set("watcher_count", this.get("watcher_count") + 1);
+ },
+ remove_watcher: function() {
+ this.set("watcher_count", this.get("watcher_count") - 1);
+ }
+ });
+
+ im_common.ConversationManager = openerp.Class.extend(openerp.PropertiesMixin, {
+ init: function(parent, options) {
+ openerp.PropertiesMixin.init.call(this, parent);
+ this.options = _.clone(options) || {};
+ _.defaults(this.options, {
+ inputPlaceholder: _t("Say something..."),
+ defaultMessage: null,
+ userName: _t("Anonymous"),
+ anonymous_mode: false
+ });
+ this.set("right_offset", 0);
+ this.set("bottom_offset", 0);
+ this.conversations = [];
+ this.users = {};
+ this.on("change:right_offset", this, this.calc_positions);
+ this.on("change:bottom_offset", this, this.calc_positions);
+ this.set("window_focus", true);
+ this.set("waiting_messages", 0);
+ this.focus_hdl = _.bind(function() {
+ this.set("window_focus", true);
+ }, this);
+ $(window).bind("focus", this.focus_hdl);
+ this.blur_hdl = _.bind(function() {
+ this.set("window_focus", false);
+ }, this);
+ $(window).bind("blur", this.blur_hdl);
+ this.on("change:window_focus", this, this.window_focus_change);
+ this.window_focus_change();
+ this.on("change:waiting_messages", this, this.messages_change);
+ this.messages_change();
+ this.create_ting();
+ this.activated = false;
+ this.users_cache = {};
+ this.last = null;
+ this.unload_event_handler = _.bind(this.unload, this);
+ },
+ start_polling: function() {
+ var self = this;
+
+ var auth_def = null;
+ var user_id = null;
+
+ if (this.options.anonymous_mode) {
+ var uuid = localStorage["oe_livesupport_uuid"];
+ var def = $.when(uuid);
+
+ if (! uuid) {
+ def = im_common.connection.rpc("/longpolling/im/gen_uuid", {});
+ }
+ var anonymous_user_id = null;
+ auth_def = def.then(function(uuid) {
+ localStorage["oe_livesupport_uuid"] = uuid;
+ return im_common.connection.model("im.user").call("get_by_user_id", [uuid]);
+ }).then(function(my_id) {
+ user_id = my_id["id"];
+ return im_common.connection.model("im.user").call("assign_name", [uuid, self.options.userName]);
+ });
+ } else {
+ auth_def = im_common.connection.model("im.user").call("get_by_user_id",
+ [im_common.connection.uid]).then(function(my_id) {
+ user_id = my_id["id"];
+ });
+ }
+
+ return auth_def.then(function() {
+ self.my_id = user_id;
+ return self.ensure_users([self.my_id]);
+ }).then(function() {
+ var me = self.users_cache[self.my_id];
+ delete self.users_cache[self.my_id];
+ self.me = me;
+ me.set("name", _t("You"));
+ return im_common.connection.rpc("/longpolling/im/activated", {}, {shadow: true});
+ }).then(function(activated) {
+ if (activated) {
+ self.activated = true;
+ $(window).on("unload", self.unload_event_handler);
+ self.poll();
+ } else {
+ return $.Deferred().reject();
+ }
+ }, function(a, e) {
+ e.preventDefault();
+ });
+ },
+ unload: function() {
+ return im_common.connection.model("im.user").call("im_disconnect", [], {uuid: this.me.get("uuid"), context: {}});
+ },
+ ensure_users: function(user_ids) {
+ var no_cache = {};
+ _.each(user_ids, function(el) {
+ if (! this.users_cache[el])
+ no_cache[el] = el;
+ }, this);
+ var self = this;
+ if (_.size(no_cache) === 0)
+ return $.when();
+ else
+ return im_common.connection.model("im.user").call("read", [_.values(no_cache), []]).then(function(users) {
+ self.add_to_user_cache(users);
+ });
+ },
+ add_to_user_cache: function(user_recs) {
+ _.each(user_recs, function(user_rec) {
+ if (! this.users_cache[user_rec.id]) {
+ var user = new im_common.ImUser(this, user_rec);
+ this.users_cache[user_rec.id] = user;
+ user.on("destroyed", this, function() {
+ delete this.users_cache[user_rec.id];
+ });
+ }
+ }, this);
+ },
+ get_user: function(user_id) {
+ return this.users_cache[user_id];
+ },
+ poll: function() {
+ var self = this;
+ var user_ids = _.map(this.users_cache, function(el) {
+ return el.get("id");
+ });
+ im_common.connection.rpc("/longpolling/im/poll", {
+ last: this.last,
+ users_watch: user_ids,
+ uuid: self.me.get("uuid")
+ }, {shadow: true}).then(function(result) {
+ _.each(result.users_status, function(el) {
+ if (self.get_user(el.id))
+ self.get_user(el.id).set(el);
+ });
+ self.last = result.last;
+ var user_ids = _.pluck(_.pluck(result.res, "from_id"), 0);
+ self.ensure_users(user_ids).then(function() {
+ _.each(result.res, function(mes) {
+ var user = self.get_user(mes.from_id[0]);
+ self.received_message(mes, user);
+ });
+ self.poll();
+ });
+ }, function(unused, e) {
+ e.preventDefault();
+ setTimeout(_.bind(self.poll, self), ERROR_DELAY);
+ });
+ },
+ get_activated: function() {
+ return this.activated;
+ },
+ create_ting: function() {
+ if (typeof(Audio) === "undefined") {
+ this.ting = {play: function() {}};
+ return;
+ }
+ var kitten = jQuery.deparam !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined;
+ this.ting = new Audio(im_common.connection.url(
+ "/im/static/src/audio/" +
+ (kitten ? "purr" : "Ting") +
+ (new Audio().canPlayType("audio/ogg; codecs=vorbis") ? ".ogg": ".mp3")
+ ));
+ },
+ window_focus_change: function() {
+ if (this.get("window_focus")) {
+ this.set("waiting_messages", 0);
+ }
+ },
+ messages_change: function() {
+ if (! openerp.webclient || !openerp.webclient.set_title_part)
+ return;
+ openerp.webclient.set_title_part("im_messages", this.get("waiting_messages") === 0 ? undefined :
+ _.str.sprintf(_t("%d Messages"), this.get("waiting_messages")));
+ },
+ activate_user: function(user, focus) {
+ var conv = this.users[user.get('id')];
+ if (! conv) {
+ conv = new im_common.Conversation(this, user, this.me, this.options);
+ conv.appendTo($("body"));
+ conv.on("destroyed", this, function() {
+ this.conversations = _.without(this.conversations, conv);
+ delete this.users[conv.user.get('id')];
+ this.calc_positions();
+ });
+ this.conversations.push(conv);
+ this.users[user.get('id')] = conv;
+ this.calc_positions();
+ }
+ if (focus)
+ conv.focus();
+ return conv;
+ },
+ received_message: function(message, user) {
+ if (! this.get("window_focus")) {
+ this.set("waiting_messages", this.get("waiting_messages") + 1);
+ this.ting.play();
+ this.create_ting();
+ }
+ var conv = this.activate_user(user);
+ conv.received_message(message);
+ },
+ calc_positions: function() {
+ var current = this.get("right_offset");
+ _.each(_.range(this.conversations.length), function(i) {
+ this.conversations[i].set("bottom_position", this.get("bottom_offset"));
+ this.conversations[i].set("right_position", current);
+ current += this.conversations[i].$().outerWidth(true);
+ }, this);
+ },
+ destroy: function() {
+ $(window).off("unload", this.unload_event_handler);
+ $(window).unbind("blur", this.blur_hdl);
+ $(window).unbind("focus", this.focus_hdl);
+ openerp.PropertiesMixin.destroy.call(this);
+ }
+ });
+
+ im_common.Conversation = openerp.Widget.extend({
+ className: "openerp_style oe_im_chatview",
+ events: {
+ "keydown input": "send_message",
+ "click .oe_im_chatview_close": "destroy",
+ "click .oe_im_chatview_header": "show_hide"
+ },
+ init: function(parent, user, me, options) {
+ this._super(parent);
+ this.options = options;
+ this.me = me;
+ this.user = user;
+ this.user.add_watcher();
+ this.set("right_position", 0);
+ this.set("bottom_position", 0);
+ this.shown = true;
+ this.set("pending", 0);
+ this.inputPlaceholder = this.options.defaultInputPlaceholder;
+ },
+ start: function() {
+ this.$().append(openerp.qweb.render("im_common.conversation", {widget: this, to_url: _.bind(im_common.connection.url, im_common.connection)}));
+ var change_status = function() {
+ this.$().toggleClass("oe_im_chatview_disconnected_status", this.user.get("im_status") === false);
+ this.$(".oe_im_chatview_online").toggle(this.user.get("im_status") === true);
+ this._go_bottom();
+ };
+ this.user.on("change:im_status", this, change_status);
+ change_status.call(this);
+
+ this.on("change:right_position", this, this.calc_pos);
+ this.on("change:bottom_position", this, this.calc_pos);
+ this.full_height = this.$().height();
+ this.calc_pos();
+ this.on("change:pending", this, _.bind(function() {
+ if (this.get("pending") === 0) {
+ this.$(".oe_im_chatview_nbr_messages").text("");
+ } else {
+ this.$(".oe_im_chatview_nbr_messages").text("(" + this.get("pending") + ")");
+ }
+ }, this));
+ },
+ show_hide: function() {
+ if (this.shown) {
+ this.$().animate({
+ height: this.$(".oe_im_chatview_header").outerHeight()
+ });
+ } else {
+ this.$().animate({
+ height: this.full_height
+ });
+ }
+ this.shown = ! this.shown;
+ if (this.shown) {
+ this.set("pending", 0);
+ }
+ },
+ calc_pos: function() {
+ this.$().css("right", this.get("right_position"));
+ this.$().css("bottom", this.get("bottom_position"));
+ },
+ received_message: function(message) {
+ if (this.shown) {
+ this.set("pending", 0);
+ } else {
+ this.set("pending", this.get("pending") + 1);
+ }
+ this._add_bubble(this.user, message.message, openerp.str_to_datetime(message.date));
+ },
+ send_message: function(e) {
+ if(e && e.which !== 13) {
+ return;
+ }
+ var mes = this.$("input").val();
+ if (! mes.trim()) {
+ return;
+ }
+ this.$("input").val("");
+ var send_it = _.bind(function() {
+ var model = im_common.connection.model("im.message");
+ return model.call("post", [mes, this.user.get('id')], {uuid: this.me.get("uuid"), context: {}});
+ }, this);
+ var tries = 0;
+ send_it().then(_.bind(function() {
+ this._add_bubble(this.me, mes, new Date());
+ }, this), function(error, e) {
+ e.preventDefault();
+ tries += 1;
+ if (tries < 3)
+ return send_it();
+ });
+ },
+ _add_bubble: function(user, item, date) {
+ var items = [item];
+ if (user === this.last_user) {
+ this.last_bubble.remove();
+ items = this.last_items.concat(items);
+ }
+ this.last_user = user;
+ this.last_items = items;
+ var zpad = function(str, size) {
+ str = "" + str;
+ return new Array(size - str.length + 1).join('0') + str;
+ };
+ date = "" + zpad(date.getHours(), 2) + ":" + zpad(date.getMinutes(), 2);
+
+ this.last_bubble = $(openerp.qweb.render("im_common.conversation_bubble", {"items": items, "user": user, "time": date}));
+ $(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble);
+ this._go_bottom();
+ },
+ _go_bottom: function() {
+ this.$(".oe_im_chatview_content").scrollTop($(this.$(".oe_im_chatview_content").children()[0]).height());
+ },
+ focus: function() {
+ this.$(".oe_im_chatview_input").focus();
+ if (! this.shown)
+ this.show_hide();
+ },
+ destroy: function() {
+ this.user.remove_watcher();
+ this.trigger("destroyed");
+ return this._super();
+ }
+ });
+
+ return im_common;
+}
+
+if (typeof(define) !== "undefined") {
+ define(["jquery", "underscore", "openerp"], declare);
+} else {
+ window.im_common = declare($, _, openerp);
+}
+
+})();
diff --git a/addons/im/static/src/xml/im.xml b/addons/im/static/src/xml/im.xml
index 795d1b3e698..1acb538db4f 100644
--- a/addons/im/static/src/xml/im.xml
+++ b/addons/im/static/src/xml/im.xml
@@ -27,37 +27,4 @@
<img t-att-src="_s +'/im/static/src/img/green.png'" class="oe_im_user_online"/>
</div>
</t>
- <t t-name="Conversation">
- <div class="oe_im_chatview">
- <div class="oe_im_chatview_header">
- <img t-att-src="_s +'/im/static/src/img/green.png'" class="oe_im_chatview_online"/>
- <t t-esc="widget.user.get('name') || 'Anonymous'"/>
- <scan class="oe_im_chatview_nbr_messages" />
- <button class="oe_im_chatview_close">×</button>
- </div>
- <div class="oe_im_chatview_disconnected">
- <t t-esc='_.str.sprintf(_t("%s is offline. He/She will receive your messages on his/her next connection."), widget.user.get("name") || "Anonymous")'/>
- </div>
- <div class="oe_im_chatview_content">
- <div></div>
- </div>
- <div class="oe_im_chatview_footer">
- <input class="oe_im_chatview_input" t-att-placeholder="_t('Say something...')" />
- </div>
- </div>
- </t>
- <t t-name="Conversation.bubble">
- <div class="oe_im_chatview_bubble">
- <div class="oe_im_chatview_clip">
- <img class="oe_im_chatview_avatar" t-att-src='user.get("image_url")'/>
- </div>
- <div class="oe_im_chatview_from"><t t-esc="user.get('name') || 'Anonymous'"/></div>
- <div class="oe_im_chatview_bubble_list">
- <t t-foreach="items" t-as="item">
- <div class="oe_im_chatview_bubble_item"><t t-esc="item"/></div>
- </t>
- </div>
- <div class="oe_im_chatview_time"><t t-esc="time"/></div>
- </div>
- </t>
</templates> \ No newline at end of file
diff --git a/addons/im/static/src/xml/im_common.xml b/addons/im/static/src/xml/im_common.xml
new file mode 100644
index 00000000000..4286d0c9710
--- /dev/null
+++ b/addons/im/static/src/xml/im_common.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<templates>
+<t t-name="im_common.conversation">
+ <div class="oe_im_chatview_header">
+ <img t-att-src="to_url('/im/static/src/img/green.png')" class="oe_im_chatview_online"/>
+ <t t-esc="widget.user.get('name')"/>
+ <scan class="oe_im_chatview_nbr_messages" />
+ <button class="oe_im_chatview_close">×</button>
+ </div>
+ <div class="oe_im_chatview_disconnected">
+ <t t-esc='widget.user.get("name") + _t(" is offline. He/She will receive your messages on his/her next connection.")'/>
+ </div>
+ <div class="oe_im_chatview_content">
+ <div></div>
+ </div>
+ <div class="oe_im_chatview_footer">
+ <input class="oe_im_chatview_input" t-att-placeholder="widget.inputPlaceholder" />
+ </div>
+</t>
+
+<t t-name="im_common.conversation_bubble">
+ <div class="oe_im_chatview_bubble">
+ <div class="oe_im_chatview_clip">
+ <img class="oe_im_chatview_avatar" t-att-src="user.get('image_url')"/>
+ </div>
+ <div class="oe_im_chatview_from"><t t-esc="user.get('name')"/></div>
+ <div class="oe_im_chatview_bubble_list">
+ <t t-foreach="items" t-as="item">
+ <div class="oe_im_chatview_bubble_item"><t t-esc="item"/></div>
+ </t>
+ </div>
+ <div class="oe_im_chatview_time"><t t-esc="time"/></div>
+ </div>
+</t>
+</templates> \ No newline at end of file