<commit_before>/*
 * Q-municate chat application
 *
 * Message View Module
 *
 */

define(['jquery', 'config', 'quickblox', 'underscore', 'minEmoji', 'timeago'],
        function($, QMCONFIG, QB, _, minEmoji) {

  var User, Message, ContactList, Dialog;
  var clearTyping, typingList = []; // for typing statuses
  var self;

  function MessageView(app) {
    this.app = app;
    User = this.app.models.User;
    Dialog = this.app.models.Dialog;
    Message = this.app.models.Message;
    ContactList = this.app.models.ContactList;
    self = this;
  }

  MessageView.prototype = {

    // this needs only for group chats: check if user exist in group chat
    checkSenderId: function(senderId, callback) {
      if (senderId !== User.contact.id) {
        ContactList.add([senderId], null, function() {
          callback();
        });
      } else {
        callback();
      }
    },

    addItem: function(message, isCallback, isMessageListener, recipientId) {
      var DialogView = this.app.views.Dialog,
          ContactListMsg = this.app.models.ContactList,
          chat = $('.l-chat[data-dialog="'+message.dialog_id+'"]'),
          i, len, user;

      if (typeof chat[0] === 'undefined' || (!message.notification_type && !message.callType && !message.attachment && !message.body)) return true;

      if (message.sessionID && $('.message[data-session="'+message.sessionID+'"]')[0]) return true;

      this.checkSenderId(message.sender_id, function() {

        var contacts = ContactListMsg.contacts,
            contact = message.sender_id === User.contact.id ? User.contact : contacts[message.sender_id],
            type = message.notification_type || (message.callState && (parseInt(message.callState) + 7).toString()) || 'message',
            attachType = message.attachment && message.attachment['content-type'] || message.attachment && message.attachment.type || null,
            // attachUrl = message.attachment && QB.content.privateUrl(message.attachment.id) || null,
            attachUrl = message.attachment && message.attachment.url || null,
            recipient = contacts[recipientId] || null,
            occupants_names = '',
            occupants_ids,
            html;

        switch (type) {
        case '1':
          occupants_ids = _.without(message.current_occupant_ids.split(',').map(Number), contact.id);

          for (i = 0, len = occupants_ids.length, user; i < len; i++) {
            user = contacts[occupants_ids[i]] && contacts[occupants_ids[i]].full_name;
            if (user)
              occupants_names = (i + 1) === len ? occupants_names.concat(user) : occupants_names.concat(user).concat(', ');
            else if (occupants_ids[i] === User.contact.id)
              occupants_names = (i + 1) === len ? occupants_names.concat(User.contact.full_name) : occupants_names.concat(User.contact.full_name).concat(', ');
          }

          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';
          html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has added '+occupants_names+' to the group chat</h4>';
          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '2':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.added_occupant_ids) {
            occupants_ids = message.added_occupant_ids.split(',').map(Number);

            for (i = 0, len = occupants_ids.length, user; i < len; i++) {
              user = contacts[occupants_ids[i]] && contacts[occupants_ids[i]].full_name;
              if (user)
                occupants_names = (i + 1) === len ? occupants_names.concat(user) : occupants_names.concat(user).concat(', ');
              else if (occupants_ids[i] === User.contact.id)
                occupants_names = (i + 1) === len ? occupants_names.concat(User.contact.full_name) : occupants_names.concat(User.contact.full_name).concat(', ');
            }

            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has added '+occupants_names+'</h4>';
          }

          if (message.deleted_occupant_ids) {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has left</h4>';
          }

          if (message.room_name) {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has changed the chat name to "'+message.room_name+'"</h4>';
          }

          if (message.room_photo) {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has changed the chat picture</h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '4':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">Your request has been sent</h4>';
          } else {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has sent a request to you</h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '5':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_ok">&#10003;</span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">You have accepted a request</h4>';
          } else {
            html += '<h4 class="message-author">Your request has been accepted</h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '6':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_cancel">&#10005;</span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">You have rejected a request';
          } else {
            html += '<h4 class="message-author">Your request has been rejected <button class="btn btn_request_again"><img class="btn-icon btn-icon_request" src="images/icon-request.svg" alt="request">Send Request Again</button></h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '7':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">You have deleted '+recipient.full_name+' from your contact list';
          } else {
            html += '<h4 class="message-author">You have been deleted from the contact list <button class="btn btn_request_again btn_request_again_delete"><img class="btn-icon btn-icon_request" src="images/icon-request.svg" alt="request">Send Request Again</button></h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        // calls messages
        case '8':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'" data-session="'+message.sessionID+'">';

            if (message.caller === User.contact.id) {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_outgoing' : 'request-audio_outgoing')+'"></span>';
            } else {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_incoming' : 'request-audio_incoming')+'"></span>';
            }

            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">Call to '+contacts[message.callee].full_name+', duration '+message.duration;
            } else {
              html += '<h4 class="message-author">Call from '+contacts[message.caller].full_name+', duration '+message.duration;
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        case '9':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';

            if (message.caller === User.contact.id) {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_ended' : 'request-audio_ended')+'"></span>';
            } else {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_missed' : 'request-audio_missed')+'"></span>';
            }

            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">Call to '+contacts[message.callee].full_name+', no answer';
            } else {
              html += '<h4 class="message-author">Missed call from '+contacts[message.caller].full_name;
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        case '10':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
            html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_ended' : 'request-audio_ended')+'"></span>';
            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">Call to '+contacts[message.callee].full_name+', busy';
            } else {
              html += '<h4 class="message-author">Call from '+contacts[message.caller].full_name+', busy';
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        case '11':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
            html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_ended' : 'request-audio_ended')+'"></span>';
            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">'+contacts[message.callee].full_name+' doesn\'t have camera and/or microphone.';
            } else {
              html += '<h4 class="message-author">Camera and/or microphone wasn\'t found.';
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        default:
          if (message.sender_id === User.contact.id) {
            html = '<article id="'+message.id+'" class="message is-own l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          } else {
            html = '<article id="'+message.id+'" class="message l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          }

          html += '<div class="message-avatar avatar contact-avatar_message profileUserAvatar'+(message.stack ? ' is-hidden' : '')+
                  '" style="background-image:url('+contact.avatar_url+')" data-id="'+message.sender_id+'"></div>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';
          html += '<h4 class="message-author'+(message.stack ? ' is-hidden' : '')+'"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span></h4>';

          if (attachType && attachType.indexOf('image') > -1) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += '<div class="preview preview-photo" data-url="'+attachUrl+'" data-name="'+message.attachment.name+'">';
            html += '<img id="attach_'+message.id+'" src="'+attachUrl+'" alt="attach">';
            html += '</div></div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+'</time>';
            html += '<div class="message-status is-hidden">Not delivered yet</div>';
          } else if (attachType && attachType.indexOf('audio') > -1) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += message.attachment.name+'<br><br>';
            html += '<audio id="'+message.id+'" src="'+attachUrl+'" controls></audio>';
            html += '</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+' ';
            html += '<a href="'+attachUrl+'" download="'+message.attachment.name+'">Download</a></time>';
          } else if (attachType && attachType.indexOf('video') > -1) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += message.attachment.name+'<br><br>';
            html += '<div id="'+message.id+'" class="preview preview-video" data-url="'+attachUrl+'" data-name="'+message.attachment.name+'"></div>';
            html += '</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+'</time>';
            html += '<div class="message-status is-hidden">Not delivered yet</div>';
          } else if (attachType) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += '<a id="'+message.id+'" class="attach-file" href="'+attachUrl+'" download="'+message.attachment.name+'">'+message.attachment.name+'</a>';
            html += '<span class="attach-size">'+getFileSize(message.attachment.size)+'</span>';
            html += '</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+' ';
            html += '<a href="'+attachUrl+'" download="'+message.attachment.name+'">Download</a></time>';
          } else {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">'+minEmoji(parser(message.body))+'</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+'</time>';
            html += '<div class="message-status is-hidden">Not delivered yet</div>';
          }

          html += '</div></div></article>';
          break;
        }

        if (isCallback) {
          if (isMessageListener) {
            chat.find('.l-chat-content .mCSB_container').append(html);
          } else {
            chat.find('.l-chat-content .mCSB_container').prepend(html);
          }
        } else {
          if (chat.find('.l-chat-content .mCSB_container')[0]) {
            chat.find('.l-chat-content .mCSB_container').prepend(html);
          } else {
            chat.find('.l-chat-content').prepend(html);
          }
        }

        if (attachType) {
          $('#attach_'+message.id).load(function() { fixScroll(chat); });
        } else {
          fixScroll(chat);
        }

        if (message.sender_id == User.contact.id && message.delivered_ids.length > 0) {
          self.addStatusMessages(message.id, message.dialog_id, 'delivered', false);
        }
        if (message.sender_id == User.contact.id && message.read_ids.length > 1) {
          self.addStatusMessages(message.id, message.dialog_id, 'displayed', false);
        }

      });

    },

    addStatusMessages: function(messageId, dialogId, messageStatus, isListener) {
      var DialogView = this.app.views.Dialog,
          ContactListMsg = this.app.models.ContactList,
          chat = $('.l-chat[data-dialog="'+dialogId+'"]'),
          time = chat.find('article#'+messageId+' .message-container-wrap .message-container .message-time'),
          statusHtml = chat.find('article#'+messageId+' .message-container-wrap .message-container .message-status');

      if (messageStatus === 'displayed') {
        statusHtml.hasClass('delivered') ? statusHtml.removeClass('delivered').addClass('displayed').html('Seen') : statusHtml.addClass('displayed').html('Seen');
      } else if (statusHtml.hasClass('displayed') && messageStatus === 'delivered') {
        return true;
      } else {
        statusHtml.hasClass('delivered') ? statusHtml.html('Delivered') : statusHtml.addClass('delivered').html('Delivered');
      }

      if (isListener) {
        setTimeout(function() {
          time.removeClass('is-hidden');
          statusHtml.addClass('is-hidden');
        }, 1000);
        time.addClass('is-hidden');
        statusHtml.removeClass('is-hidden');
      }
    },

    sendMessage: function(form) {
      var jid = form.parents('.l-chat').data('jid'),
          id = form.parents('.l-chat').data('id'),
          dialog_id = form.parents('.l-chat').data('dialog'),
          val = form.find('.textarea').text().trim(),
          time = Math.floor(Date.now() / 1000),
          type = form.parents('.l-chat').is('.is-group') ? 'groupchat' : 'chat',
          chat = $('.l-chat[data-dialog="'+dialog_id+'"]'),
          dialogItem = (type === 'groupchat') ? $('.l-list-wrap section:not(#searchList) .dialog-item[data-dialog="'+dialog_id+'"]') : $('.l-list-wrap section:not(#searchList) .dialog-item[data-id="'+id+'"]'),
          copyDialogItem, lastMessage;

      if (val.length > 0) {
        if (form.find('.textarea > span').length > 0) {
          form.find('.textarea > span').each(function() {
            $(this).after($(this).find('span').data('unicode')).remove();
          });
          val = form.find('.textarea').text().trim();
        }

        // send message
        var msg = {
          type: type,
          body: val,
          extension: {
            save_to_history: 1,
            dialog_id: dialog_id,
            date_sent: time
          },
          markable: 1
        };
        QB.chat.send(jid, msg);

        message = Message.create({
          chat_dialog_id: dialog_id,
          body: val,
          date_sent: time,
          sender_id: User.contact.id,
          _id: msg.id
        });

        if (QMCONFIG.debug) console.log(message);

        if (type === 'chat') {<commit_msg>fixed smiles and string transfer<commit_after>/*
 * Q-municate chat application
 *
 * Message View Module
 *
 */

define(['jquery', 'config', 'quickblox', 'underscore', 'minEmoji', 'timeago'],
        function($, QMCONFIG, QB, _, minEmoji) {

  var User, Message, ContactList, Dialog;
  var clearTyping, typingList = []; // for typing statuses
  var self;

  function MessageView(app) {
    this.app = app;
    User = this.app.models.User;
    Dialog = this.app.models.Dialog;
    Message = this.app.models.Message;
    ContactList = this.app.models.ContactList;
    self = this;
  }

  MessageView.prototype = {

    // this needs only for group chats: check if user exist in group chat
    checkSenderId: function(senderId, callback) {
      if (senderId !== User.contact.id) {
        ContactList.add([senderId], null, function() {
          callback();
        });
      } else {
        callback();
      }
    },

    addItem: function(message, isCallback, isMessageListener, recipientId) {
      var DialogView = this.app.views.Dialog,
          ContactListMsg = this.app.models.ContactList,
          chat = $('.l-chat[data-dialog="'+message.dialog_id+'"]'),
          i, len, user;

      if (typeof chat[0] === 'undefined' || (!message.notification_type && !message.callType && !message.attachment && !message.body)) return true;

      if (message.sessionID && $('.message[data-session="'+message.sessionID+'"]')[0]) return true;

      this.checkSenderId(message.sender_id, function() {

        var contacts = ContactListMsg.contacts,
            contact = message.sender_id === User.contact.id ? User.contact : contacts[message.sender_id],
            type = message.notification_type || (message.callState && (parseInt(message.callState) + 7).toString()) || 'message',
            attachType = message.attachment && message.attachment['content-type'] || message.attachment && message.attachment.type || null,
            // attachUrl = message.attachment && QB.content.privateUrl(message.attachment.id) || null,
            attachUrl = message.attachment && message.attachment.url || null,
            recipient = contacts[recipientId] || null,
            occupants_names = '',
            occupants_ids,
            html;

        switch (type) {
        case '1':
          occupants_ids = _.without(message.current_occupant_ids.split(',').map(Number), contact.id);

          for (i = 0, len = occupants_ids.length, user; i < len; i++) {
            user = contacts[occupants_ids[i]] && contacts[occupants_ids[i]].full_name;
            if (user)
              occupants_names = (i + 1) === len ? occupants_names.concat(user) : occupants_names.concat(user).concat(', ');
            else if (occupants_ids[i] === User.contact.id)
              occupants_names = (i + 1) === len ? occupants_names.concat(User.contact.full_name) : occupants_names.concat(User.contact.full_name).concat(', ');
          }

          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';
          html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has added '+occupants_names+' to the group chat</h4>';
          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '2':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.added_occupant_ids) {
            occupants_ids = message.added_occupant_ids.split(',').map(Number);

            for (i = 0, len = occupants_ids.length, user; i < len; i++) {
              user = contacts[occupants_ids[i]] && contacts[occupants_ids[i]].full_name;
              if (user)
                occupants_names = (i + 1) === len ? occupants_names.concat(user) : occupants_names.concat(user).concat(', ');
              else if (occupants_ids[i] === User.contact.id)
                occupants_names = (i + 1) === len ? occupants_names.concat(User.contact.full_name) : occupants_names.concat(User.contact.full_name).concat(', ');
            }

            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has added '+occupants_names+'</h4>';
          }

          if (message.deleted_occupant_ids) {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has left</h4>';
          }

          if (message.room_name) {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has changed the chat name to "'+message.room_name+'"</h4>';
          }

          if (message.room_photo) {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has changed the chat picture</h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '4':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">Your request has been sent</h4>';
          } else {
            html += '<h4 class="message-author"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span> has sent a request to you</h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '5':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_ok">&#10003;</span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">You have accepted a request</h4>';
          } else {
            html += '<h4 class="message-author">Your request has been accepted</h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '6':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_cancel">&#10005;</span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">You have rejected a request';
          } else {
            html += '<h4 class="message-author">Your request has been rejected <button class="btn btn_request_again"><img class="btn-icon btn-icon_request" src="images/icon-request.svg" alt="request">Send Request Again</button></h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        case '7':
          html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          html += '<span class="message-avatar contact-avatar_message request-button_pending"></span>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';

          if (message.sender_id === User.contact.id) {
            html += '<h4 class="message-author">You have deleted '+recipient.full_name+' from your contact list';
          } else {
            html += '<h4 class="message-author">You have been deleted from the contact list <button class="btn btn_request_again btn_request_again_delete"><img class="btn-icon btn-icon_request" src="images/icon-request.svg" alt="request">Send Request Again</button></h4>';
          }

          html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
          html += '</div></div></article>';
          break;

        // calls messages
        case '8':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'" data-session="'+message.sessionID+'">';

            if (message.caller === User.contact.id) {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_outgoing' : 'request-audio_outgoing')+'"></span>';
            } else {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_incoming' : 'request-audio_incoming')+'"></span>';
            }

            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">Call to '+contacts[message.callee].full_name+', duration '+message.duration;
            } else {
              html += '<h4 class="message-author">Call from '+contacts[message.caller].full_name+', duration '+message.duration;
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        case '9':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';

            if (message.caller === User.contact.id) {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_ended' : 'request-audio_ended')+'"></span>';
            } else {
              html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_missed' : 'request-audio_missed')+'"></span>';
            }

            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">Call to '+contacts[message.callee].full_name+', no answer';
            } else {
              html += '<h4 class="message-author">Missed call from '+contacts[message.caller].full_name;
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        case '10':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
            html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_ended' : 'request-audio_ended')+'"></span>';
            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">Call to '+contacts[message.callee].full_name+', busy';
            } else {
              html += '<h4 class="message-author">Call from '+contacts[message.caller].full_name+', busy';
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        case '11':
          if (message.caller) {
            html = '<article class="message message_service l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
            html += '<span class="message-avatar contact-avatar_message request-call '+(message.callType === '1' ? 'request-video_ended' : 'request-audio_ended')+'"></span>';
            html += '<div class="message-container-wrap">';
            html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
            html += '<div class="message-content">';

            if (message.caller === User.contact.id) {
              html += '<h4 class="message-author">'+contacts[message.callee].full_name+' doesn\'t have camera and/or microphone.';
            } else {
              html += '<h4 class="message-author">Camera and/or microphone wasn\'t found.';
            }

            html += '</div><time class="message-time">'+getTime(message.date_sent)+'</time>';
            html += '</div></div></article>';
          }
          break;

        default:
          if (message.sender_id === User.contact.id) {
            html = '<article id="'+message.id+'" class="message is-own l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          } else {
            html = '<article id="'+message.id+'" class="message l-flexbox l-flexbox_alignstretch" data-id="'+message.sender_id+'" data-type="'+type+'">';
          }

          html += '<div class="message-avatar avatar contact-avatar_message profileUserAvatar'+(message.stack ? ' is-hidden' : '')+
                  '" style="background-image:url('+contact.avatar_url+')" data-id="'+message.sender_id+'"></div>';
          html += '<div class="message-container-wrap">';
          html += '<div class="message-container l-flexbox l-flexbox_flexbetween l-flexbox_alignstretch">';
          html += '<div class="message-content">';
          html += '<h4 class="message-author'+(message.stack ? ' is-hidden' : '')+'"><span class="profileUserName" data-id="'+message.sender_id+'">'+contact.full_name+'</span></h4>';

          if (attachType && attachType.indexOf('image') > -1) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += '<div class="preview preview-photo" data-url="'+attachUrl+'" data-name="'+message.attachment.name+'">';
            html += '<img id="attach_'+message.id+'" src="'+attachUrl+'" alt="attach">';
            html += '</div></div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+'</time>';
            html += '<div class="message-status is-hidden">Not delivered yet</div>';
          } else if (attachType && attachType.indexOf('audio') > -1) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += message.attachment.name+'<br><br>';
            html += '<audio id="'+message.id+'" src="'+attachUrl+'" controls></audio>';
            html += '</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+' ';
            html += '<a href="'+attachUrl+'" download="'+message.attachment.name+'">Download</a></time>';
          } else if (attachType && attachType.indexOf('video') > -1) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += message.attachment.name+'<br><br>';
            html += '<div id="'+message.id+'" class="preview preview-video" data-url="'+attachUrl+'" data-name="'+message.attachment.name+'"></div>';
            html += '</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+'</time>';
            html += '<div class="message-status is-hidden">Not delivered yet</div>';
          } else if (attachType) {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">';
            html += '<a id="'+message.id+'" class="attach-file" href="'+attachUrl+'" download="'+message.attachment.name+'">'+message.attachment.name+'</a>';
            html += '<span class="attach-size">'+getFileSize(message.attachment.size)+'</span>';
            html += '</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+' ';
            html += '<a href="'+attachUrl+'" download="'+message.attachment.name+'">Download</a></time>';
          } else {
            html += '<div class="message-body'+(message.stack ? ' indent' : '')+'">'+minEmoji(parser(message.body))+'</div>';
            html += '</div><time class="message-time" data-time="'+message.date_sent+'">'+getTime(message.date_sent)+'</time>';
            html += '<div class="message-status is-hidden">Not delivered yet</div>';
          }

          html += '</div></div></article>';
          break;
        }

        if (isCallback) {
          if (isMessageListener) {
            chat.find('.l-chat-content .mCSB_container').append(html);
          } else {
            chat.find('.l-chat-content .mCSB_container').prepend(html);
          }
        } else {
          if (chat.find('.l-chat-content .mCSB_container')[0]) {
            chat.find('.l-chat-content .mCSB_container').prepend(html);
          } else {
            chat.find('.l-chat-content').prepend(html);
          }
        }

        if (attachType) {
          $('#attach_'+message.id).load(function() { fixScroll(chat); });
        } else {
          fixScroll(chat);
        }

        if (message.sender_id == User.contact.id && message.delivered_ids.length > 0) {
          self.addStatusMessages(message.id, message.dialog_id, 'delivered', false);
        }
        if (message.sender_id == User.contact.id && message.read_ids.length > 1) {
          self.addStatusMessages(message.id, message.dialog_id, 'displayed', false);
        }

      });

    },

    addStatusMessages: function(messageId, dialogId, messageStatus, isListener) {
      var DialogView = this.app.views.Dialog,
          ContactListMsg = this.app.models.ContactList,
          chat = $('.l-chat[data-dialog="'+dialogId+'"]'),
          time = chat.find('article#'+messageId+' .message-container-wrap .message-container .message-time'),
          statusHtml = chat.find('article#'+messageId+' .message-container-wrap .message-container .message-status');

      if (messageStatus === 'displayed') {
        statusHtml.hasClass('delivered') ? statusHtml.removeClass('delivered').addClass('displayed').html('Seen') : statusHtml.addClass('displayed').html('Seen');
      } else if (statusHtml.hasClass('displayed') && messageStatus === 'delivered') {
        return true;
      } else {
        statusHtml.hasClass('delivered') ? statusHtml.html('Delivered') : statusHtml.addClass('delivered').html('Delivered');
      }

      if (isListener) {
        setTimeout(function() {
          time.removeClass('is-hidden');
          statusHtml.addClass('is-hidden');
        }, 1000);
        time.addClass('is-hidden');
        statusHtml.removeClass('is-hidden');
      }
    },

    sendMessage: function(form) {
      var jid = form.parents('.l-chat').data('jid'),
          id = form.parents('.l-chat').data('id'),
          dialog_id = form.parents('.l-chat').data('dialog'),
          val = form.find('.textarea').html().trim(),
          time = Math.floor(Date.now() / 1000),
          type = form.parents('.l-chat').is('.is-group') ? 'groupchat' : 'chat',
          chat = $('.l-chat[data-dialog="'+dialog_id+'"]'),
          dialogItem = (type === 'groupchat') ? $('.l-list-wrap section:not(#searchList) .dialog-item[data-dialog="'+dialog_id+'"]') : $('.l-list-wrap section:not(#searchList) .dialog-item[data-id="'+id+'"]'),
          copyDialogItem, lastMessage;

      if (val.length > 0) {
        if (form.find('.textarea > span').length > 0) {
          form.find('.textarea > span').each(function() {
            $(this).after($(this).find('span').data('unicode')).remove();
          });
          val = form.find('.textarea').text().trim();
        }
        if (form.find('.textarea > div').length > 0) {
          val = form.find('.textarea').text().trim();
        }
        val = val.replace(/<br>/gi, '\n');

        // send message
        var msg = {
          type: type,
          body: val,
          extension: {
            save_to_history: 1,
            dialog_id: dialog_id,
            date_sent: time
          },
          markable: 1
        };
        QB.chat.send(jid, msg);

        message = Message.create({
          chat_dialog_id: dialog_id,
          body: val,
          date_sent: time,
          sender_id: User.contact.id,
          _id: msg.id
        });

        if (QMCONFIG.debug) console.log(message);

        if (type === 'chat') {
{'fraction_non_alphanumeric': 0.18612133365653716, 'fraction_numerical': 0.003944555851825735, 'mean_word_length': 2.747625732925923, 'pattern_counts': {'":': 0, '<': 484, '<?xml version=': 0, '>': 500, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4983273', 'n_tokens_mistral': 13328, 'n_tokens_neox': 13114, 'n_words': 2341}