🇬🇧United Kingdom @rblake87

Account created on 1 March 2022, over 2 years ago
#

Recent comments

🇬🇧United Kingdom rblake87
/**
 * @file
 * Adds JS functionality to the Private Message Windows module.
 */

/*global jQuery, Drupal, drupalSettings, window*/

(function ($, Drupal, drupalSettings, window) {
  Drupal.PrivateMessageInbox = Drupal.PrivateMessageInbox || {};
//  Drupal.PrivateMessageInbox.updateInbox = Drupal.PrivateMessageInbox.updateInbox || {};

  "use strict";

  var pmWindows, updateInterval, inboxRequesting, blockSettings;
  var requesting = { initedNeed: 1, thread: {}, threadRefresh: {}, msgSend: {}, newThread: 0, prevMsgsLoading: {}, userSearch: 0, threadDelete: {} };
  var winWidth = 260;
  var langPrefix = '';

  /**
   * Used to manually trigger Drupal's JavaScript commands.
   * @param {Object} data The data.
   */
  function triggerCommands(data) {
    var ajaxObject = Drupal.ajax({
      url: '',
      base: false,
      element: false,
      progress: false
    });

    // Trigger any ajax commands in the response.
    ajaxObject.success(data, 'success');
  }

  /**
   * Initializes the private message inbox window.
   */
  function init(context) {
    if(!requesting.initedNeed) return;
        var  block= $('.block-private-message-windows-block');

    var elements = once('overlay-block-ajax', '.block-private-message-windows-block');
    if(!block.length) { // block is still absent in the site DOM
      requesting.initedNeed++;
      setTimeout(function(){ init(context); }, 300);
      return;
    }
    requesting.initedNeed = 0;


    elements.forEach(function (element) {
      var displayWidth = $(document).width();
      if(displayWidth <= 600) {
        winWidth = Math.round(displayWidth*0.49);
      }else if(displayWidth <= 768) {
        winWidth = Math.round(displayWidth*0.32);
      }

      if(!drupalSettings.privateMessageWindowsBlock) return;

      blockSettings = drupalSettings.privateMessageWindowsBlock;
      langPrefix = blockSettings.currentLanguagePrefix;

      pmWindows = $(element).first().find('.block-content:first');

      var mainTitle = $(block).find('.main-title');
      mainTitle.find('.x').on('click touchstart', function () { pmWindows.hide(); return false; });
      mainTitle.find('.add-new').on('click touchstart', function () { $(block).find('.new-user-search').toggle(); return false; });
      mainTitle.after('<div class="new-user-search"><input placeholder="Type user name" /><div class="user-list"></div></div>');

      pmWindows.append('<style>' +
          '@-webkit-keyframes pmsgblink {' +
          '50% { color: ' + blockSettings.titleTextColor +
          '; } 51% { color: ' + blockSettings.titleNewMsgTextColor +
          '; } 100% { color: ' + blockSettings.titleNewMsgTextColor + '; }' +
        '} @keyframes pmsgblink {' +
          '50% { color: ' + blockSettings.titleTextColor +
          '; } 51% { color: ' + blockSettings.titleNewMsgTextColor +
          '; } 100% { color: ' + blockSettings.titleNewMsgTextColor + '; }' +
        '}' +
        '.block-private-message-windows-block .label { color: ' +
          blockSettings.titleTextColor +
          '; background-color: '+
          blockSettings.titleTextBackground +
        '; } .block-private-message-windows-block .block-content.new .label.main-title {' +
        '  color: ' + blockSettings.titleNewMsgTextColor + ';' +
        '  -webkit-animation: pmsgblink 2s linear infinite;' +
        '  animation: blink 2s pmsgblink infinite;' +
        '} </style>');
      if(newThreadCount()>0) {
        pmWindows.addClass('new');
      }else if(blockSettings.defaultHidden > 0) pmWindows.hide();

      updateInterval = blockSettings.ajaxRefreshRate * 1000;
      if (updateInterval) {
        window.setTimeout(loadUpdateInbox, updateInterval);
      }

      pmWindows.find('.inbox-thread-list .thread').on('click touchstart', inboxThreadLinkListenerHandler);
      pmWindows.find('.label').on('click touchstart', showHideWin);

      $('a[href*="/private-message/create?recipient="]').on('click touchstart', newRecipientMessage);

      $('a[href^="'+langPrefix+'/private-messages/"]').on('click touchstart', existsRecipientThread);

      $('a[href="'+langPrefix+'/private-messages"]').on('click touchstart', showMainWindow);

      pmWindows.find('.new-user-search input').on('input', getUserList4NewThread);
    });
  }


  /**
   * Show main window by click
   */
  function showMainWindow(e) {
    e.preventDefault();
    pmWindows.addClass('active');
    pmWindows.show();
  }


  /**
   * Show and hide inbox or thread window
   */
  function showHideWin() {
    $(this).parent().toggleClass('active');
  }


  /**
   * Request to delete the thread
   */
  function runDeleteThread() {
    var threadDiv = $(this).closest('.thread');
    var messagesDiv = threadDiv.find('.messages');
    if(messagesDiv.find('.request').length) return;

    messagesDiv.after('<div class="request"><div class="question">' + blockSettings['deleteThreadQuestion'] + '</div><div><div class="butt ok">OK</div><div class="butt cancel">Cancel</div></div><div class="note">' + blockSettings['deleteThreadNote'] + '</div></div>');

    threadDiv.find('.request .butt.ok').on('click touchstart', function () {
      execDeleteThread(threadDiv.attr('data-thread-id'));
      return false;
    });
    threadDiv.find('.request .butt.cancel').on('click touchstart', function () {
      $(this).closest('.request').empty().remove();
      return false;
    });
  }

  /**
   * Execute thread delete command
   *
   * @param threadId
   * @returns {boolean}
   */
  function execDeleteThread(threadId) {
    if(requesting.threadDelete[threadId]) return;
    requesting.threadDelete[threadId] = 1;

    $(this).addClass('exec');

    $.ajax({
      url: blockSettings.deleteThreadUrl,
      method: "POST",
      data: { thread_id: threadId },
      success: function (data) {
        triggerCommands(data);
        requesting.threadDelete[threadId] = 0;
      }
    });

    return false;
  }


  /**
   * Run update the inbox.
   */
  function loadUpdateInbox() {
    if (!inboxRequesting) {
      inboxRequesting = true;

      var ids = {};
      pmWindows.find('.inbox-thread-list .thread').each(function () {
        ids[$(this).attr('data-thread-id')] = $(this).attr('data-last-update');
      });

      $.ajax({
        url: blockSettings.loadNewThreadUrl,
        method: "POST",
        data: { ids: ids },
        success: function (data) {
          inboxRequesting = false;
          triggerCommands(data);
          if (updateInterval) {
            window.setTimeout(loadUpdateInbox, updateInterval);
          }
        }
      });
    }
  }


  /**
   * Refresh inbox after ajax call
   * @param threadIds - ids of threads
   * @param newThreads - updated threads
   */
  function updateInbox(threadIds, newThreads) {
    var revertedIndex = [];
    for(var index in threadIds) {
      revertedIndex.unshift(index);
    }

    var inboxWrapper = pmWindows.find('.inbox-thread-list');
    if(threadIds.length) inboxWrapper.find('.no-thread').remove();

    for(var i in revertedIndex) {
      var index = revertedIndex[i];
      var threadId = threadIds[index];
      var message = inboxWrapper.find('.thread-'+threadId);

      if(message.length) {
        message.remove().empty();
        loadThreadRefresh(threadId);
      }
      inboxWrapper.prepend(newThreads[threadId]);
      var newThread = inboxWrapper.find('.thread-'+threadId);
      if(!newThread.hasClass('own') && newThread.hasClass('new')) {
        pmWindows.addClass('new').show();
        newThreadCount(); //update count number
      }

      inboxWrapper.find('.thread-'+threadId).on('click touchstart', inboxThreadLinkListenerHandler);
    };
  }


  /**
   * Click Handler executed when private message threads are clicked.
   *
   * Loads the thread into the private message window.
   */
  var inboxThreadLinkListenerHandler = function (e) {
    e.preventDefault();
    var threadId = $(this).attr("data-thread-id");
    var inboxThread = pmWindows.find('.inbox-thread-list .thread-'+threadId);
    var thWrapper = pmWindows.find('.threads-wrapper');
    var thread =  thWrapper.find('.thread-'+threadId);
    if (thread.length) {
      thread.addClass('active');
      if(inboxThread.hasClass('new')) {
        inboxThreadDeNew(inboxThread);
        thread.find('textarea').focus();
      }else {
        if(thread.is(':hidden')) {
          hideLastWin(thWrapper);
        }
        thread.toggle();
      }
    } else {
      inboxThreadDeNew(inboxThread);
      loadThread(threadId);
    }
  };

  var newThreadCount = function () {
    var newThreadCnt = pmWindows.find('.inbox-thread-list .thread.new').length;
    pmWindows.find('.unreaded-cnt').empty().html(newThreadCnt);
    return newThreadCnt;
  };


  var inboxThreadDeNew = function(inboxThread) {
    inboxThread.removeClass('new');

    if(!newThreadCount()) {
      pmWindows.removeClass('new');
    }
  };

  /**
   * Loading upper(older) messages to the thread message list
   */
  function loadPrevMessages() {
    var loadPrevDiv = $(this);
    loadPrevDiv.addClass('loading');
    var threadDiv = loadPrevDiv.parent();

    var threadId = threadDiv.attr('data-thread-id');
    var firstMsgId = threadDiv.attr('data-first-msg-id');

    requesting.prevMsgsLoading[threadId] = true;

    $.ajax({
      url: blockSettings.loadPrevMsgsUrl,
      data: { thread_id:threadId, message_id:firstMsgId },
      success: function (data) {
        loadPrevDiv.removeClass('loading');
        requesting.prevMsgsLoading[threadId] = false;
        triggerCommands(data);
      }
    });

  }

  /**
   * Downloading thread content
   * @param threadId
   */
  function loadThread(threadId) {
    var threadDiv = pmWindows.find('.thread-wrapper .thread-'+threadId);
    if(threadDiv.length) return;

    if(requesting.thread[threadId]) return;
    requesting.thread[threadId] = true;

    var inboxThreadDiv = pmWindows.find('.inbox-thread-list .thread-'+threadId);
    inboxThreadDiv.addClass('opening');

    $.ajax({
      url: blockSettings.loadThreadMsgsUrl,
      data: { threadid:threadId },
      success: function (data) {
        inboxThreadDiv.removeClass('opening');
        requesting.thread[threadId] = false;
        triggerCommands(data);
      }
    });
  }

  function hideLastWin(thWrapper) {
    var thWrapperW = thWrapper.width();
    var winLimit = Math.floor(thWrapperW/winWidth);
    var existsThreads = thWrapper.find('.thread:visible');
    if(existsThreads.length >= winLimit) {
      existsThreads.each(function(i,item) {
        if((i-(-1)) >= winLimit) $(item).hide();
      });
    }

  }

  /**
   * Filling thread after ajax request
   * @param threadId - thread id
   * @param firstMsgId - begin message id
   * @param lastMsgId - end message id
   * @param userId - user id
   * @param userName - name of user
   * @param userPicture - image url of user icon
   * @param messages - html of thread messages
   * @param loadPrevTitle - "load previous" text in the right language
   */
  function loadThreadResponse(threadId, firstMsgId, lastMsgId, userId, userName, userPicture, messages, loadPrevTitle) {
    var thWrapper = pmWindows.find('.threads-wrapper');
    hideLastWin(thWrapper);
    var userPictureTag = userPicture ?  '<img src="' + userPicture + '" />' : '';
    var deleteThreadMsg = blockSettings['deleteThreadMsg'];

    thWrapper.prepend('<div class="thread thread-' + threadId + ' thread-user-' + userId + ' active" data-thread-id="' +
      threadId + '" data-user-id="' + userId + '" data-last-msg-id="'+lastMsgId+'" data-first-msg-id="'+firstMsgId+'">' +
      '<div class="label"><div class="x">&#xd7;</div>' + userPictureTag + userName + '</div>' +
      '<div class="load-prev">' + loadPrevTitle + '</div>' +
      '<div class="messages"></div><div class="operations"><div class="delete-thread">' + deleteThreadMsg +  '</div><textarea></textarea><input type="button" value="Send" /></div></div>');
    var threadDiv = thWrapper.find('.thread-'+threadId);
    threadDiv.find('.load-prev').on('click touchstart', loadPrevMessages);
    var threadMsgDiv = threadDiv.find('.messages');
    threadMsgDiv.prepend(messages);
    threadDiv.find('input[type="button"]').on('click touchstart', sendMessage);
    threadDiv.find('.label').on('click touchstart', showHideWin);
    threadDiv.find('.label .x').on('click touchstart', function () { $(this).closest('.thread').hide(); return false; });
    threadDiv.find('.delete-thread').on('click touchstart', runDeleteThread);
    messagesScrollDown(threadDiv);
  }

  /**
   * Get user list for new thread creation
   */
  function getUserList4NewThread() {
    if(requesting.userSearch) requesting.userSearch.abort();

    var searchLine = $(this).val().trim();
    if((!searchLine) || (searchLine.length < 3)) return;

    requesting.userSearch = $.ajax({
      url: blockSettings.getUsersUrl,
      data: { user_name:searchLine },
      method: 'POST',
      success: function (data) {
        requesting.userSearch = false;
        triggerCommands(data);
      }
    });

  }

  /**
   * Show user list for start new thread
   *
   * @param userList
   */
  function showSearchedUserList(userList) {
    var userListTag = pmWindows.find('.new-user-search .user-list');
    userListTag.empty();
    for(var i in userList) {
      var user = userList[i];
      userListTag.append('<div class="user-for-new-thread" data-uid="' + user.uid + '">' + user.username + '</div>');
    }

    userListTag.find('.user-for-new-thread').on('click', function () {
      var uid = parseInt($(this).attr('data-uid'));
      userListTag.empty();
      pmWindows.find('.new-user-search input').val('');

      if(uid) openRecipientMessage(uid);

      return false;
    })

  }


  /**
   * Send message to user
   */
  function sendMessage() {
    var $this = $(this);
    var operations = $this.closest('.operations');
    operations.addClass('sending');
    var textArea = operations.find('textarea');
    var text = textArea.val();
    var thread = $this.closest('.thread');
    var threadId = thread.attr('data-thread-id');
    var msgId = thread.attr('data-last-msg-id');

    if(requesting.msgSend[threadId]) return;
    requesting.msgSend[threadId] = true;

    $.ajax({
      url: blockSettings.sendMsgUrl,
      data: { thread_id:threadId, text:text, last_message_id:msgId },
      method: 'POST',
      success: function (data) {
        textArea.val('');
        requesting.msgSend[threadId] = false;
        operations.removeClass('sending');
        triggerCommands(data);
      }
    });

  }


  /**
   * Respone to the ajax request of previous thread messages
   * @param oldMessages
   * @param threadId
   * @param firstMessageId
   * @param hasNext
   */
  function insertOldThreadMsgs(oldMessages, threadId, firstMessageId, hasNext) {
    var thread = pmWindows.find('.threads-wrapper .thread-'+threadId);
    var threadMessages = thread.find('.messages');
    
    
    //const elements = once('private-message-'+firstMessageId, 'private-message-'+firstMessageId);
   
     
    
    var messages = $('<div>' + oldMessages + '</div').find('.private-message');

    if(!messages.length) { thread.find('.load-prev').hide(); return; }

    var firstMsgId = thread.attr('data-first-msg-id');
    var messages4Insert = {};
    var newFirstMsgId = firstMsgId;
    //to avoid duplicate of messages, we need existing check
    messages.each(function(i, item) {
      var msg = $(item);
      var msgId = msg.attr('data-message-id');
      if(!threadMessages.find('#private-message-' + msgId).length && (firstMsgId > msgId)) {
        messages4Insert[msgId] = item;
        if(newFirstMsgId> msgId) newFirstMsgId = msgId;
      }
    });
    var ids = Object.keys(messages4Insert);
    ids.sort(function(a, b){ return b - a; });//inserting backward
    for(var i in ids) {
      threadMessages.prepend(messages4Insert[ids[i]]);
    }
    thread.attr('data-first-msg-id', newFirstMsgId);

  }


  /**
   * Response to the ajax request of message sending or thread update
   */
  function updateThreadMsgs(newMessages, threadId, lastMsgId) {
    var thread = pmWindows.find('.threads-wrapper .thread-'+threadId);
    var threadMessages = thread.find('.messages'); 
    
    const elements = once('private-message-'+lastMsgId, '.private-message');

    
    var messages = $('<div>' + newMessages + '</div').find('.private-message');
    var oldMsgId = thread.attr('data-last-msg-id');

    //to avoid duplicate of messages, we add each message with existing check
    messages.each(function(i, item) {
      var msg = $(item);
      var msgId = msg.attr('data-message-id');
      if(!threadMessages.find('#private-message-' + msgId).length && (oldMsgId < msgId)) {
        threadMessages.append(item);
        thread.attr('data-last-msg-id', msgId);
        oldMsgId = msgId;
      }
    });
    messagesScrollDown(thread);
    loadUpdateInbox();
  }


  /**
   * Refresh of thread window for new messages
   * @param threadId
   */
  function loadThreadRefresh(threadId) {
    var threadDiv = pmWindows.find('.threads-wrapper .thread-'+threadId);
    if(!threadDiv.length) return;

    if(requesting.threadRefresh[threadId]) return;
    requesting.threadRefresh[threadId] = true;

    var msgId = threadDiv.attr('data-last-msg-id');
    $.ajax({
      url: blockSettings.loadNewMsgsUrl,
      data: { thread_id:threadId, message_id: msgId },
      success: function (data) {
        requesting.threadRefresh[threadId] = false;
        triggerCommands(data);
      }
    });
  }

  /**
   * Moving message list to the last message
   * @param threadDiv
   */
  function messagesScrollDown(threadDiv) {
    var messages = threadDiv.find('.messages');
    var height = 0;
    messages.find('.private-message').each(function(i,item){
      height -= (-1)*$(item).outerHeight();
    });
    messages.scrollTop(height - (-1)*300);
  }

  /**
   * Click on the recipient link for new thread creation
   *
   * @param e
   */
  function newRecipientMessage(e) {
    e.stopPropagation();
    e.preventDefault();

    let aTag = $(this);
    let href = aTag.attr('href');
    let check = new RegExp('recipient=(\\d+)');
    let checkRes = check.exec(href);
    if(!checkRes[1] || !(checkRes[1]>0)) {
      window.location = href;
      return;
    }

    openRecipientMessage(checkRes[1])
  }

  /**
   * Click on the recipient link to open exists thread
   *
   * @param e
   */
  function existsRecipientThread(e) {
    e.stopPropagation();
    e.preventDefault();

    let aTag = $(this);
    let href = aTag.attr('href');
    let check = new RegExp('/private-messages/(\\d+)');
    let checkRes = check.exec(href);
    if(!checkRes[1] || !(checkRes[1]>0)) {
      window.location = href;
      return;
    }

    openThreadMessages(checkRes[1])
  }


  /**
   * Opening a window with exists thread
   *
   * @param threadId
   */
  function openThreadMessages(threadId) {
    if(requesting.existsThread) return;
    requesting.existsThread = true;

    pmWindows.show();

    let userThreadLink = pmWindows.find('.inbox-thread-list .thread-' + threadId);

    if (userThreadLink.length) {
      let userThread = pmWindows.find('.threads-wrapper .thread-' + threadId);

      if(userThread.length) {
        userThread.addClass('active').show();
      } else {
        userThreadLink.trigger('click');
      }

      requesting.existsThread = false;
      return;
    }

    $.ajax({
      url: blockSettings.loadThreadMsgsUrl,
      data: { threadid: threadId },
      method: 'POST',
      success: function (data) {
        requesting.existsThread = false;
        triggerCommands(data);
      }
    });
  }


  /**
   * Opening a window for new thread (with new contact)
   *
   * @param recipientId
   */
  function openRecipientMessage(recipientId) {
    if(requesting.newThread) return;
    requesting.newThread = true;

    pmWindows.show();

    var userThreadLink = pmWindows.find('.inbox-thread-list .thread-user-' + recipientId);

    if(userThreadLink.length) {
      var userThread = pmWindows.find('.threads-wrapper .thread-user-' + recipientId);

      if(userThread.length) {
        userThread.addClass('active').show();
      } else {
        userThreadLink.trigger('click');
      }

      requesting.newThread = false;
      return;
    }

    $.ajax({
      url: blockSettings.newThreadUrl,
      data: { recip_id:recipientId },
      method: 'POST',
      success: function (data) {
        requesting.newThread = false;
        triggerCommands(data);
      }
    });

  }


  /**
   * Cleaning interface after thread deleting
   * @param threadId
   */
  function deleteThread(threadId) {
    pmWindows.find('.inbox-thread-list .thread.thread-' + threadId).empty().remove();
    pmWindows.find('.threads-wrapper .thread-' + threadId).empty().remove();
  }


  Drupal.behaviors.privateMessageWindowsThreadList = {
    attach:function (context) {
      init(context);

      Drupal.AjaxCommands.prototype.
        PrivateMessageWindowsInboxUpdate = function (ajax, response) {
        ajax = ajax; // For jSlint compatibility.

        updateInbox(response.threadIds, response.newThreads);
      };

      Drupal.AjaxCommands.prototype.PrivateMessageWindowsFillThread = function (ajax, response) {
        ajax = ajax; // For jSlint compatibility.

        loadThreadResponse(response.threadId, response.firstMsgId, response.lastMsgId, response.userId, response.userName, response.userPicture, response.messages, response.loadPrevTitle);
      };

      Drupal.AjaxCommands.prototype.PrivateMessageThreadMsgsUpdate = function (ajax, response) {
        ajax = ajax; // For jSlint compatibility.

        updateThreadMsgs(response.messages, response.threadId, response.lastMsgId);
      };

      Drupal.AjaxCommands.prototype.PrivateMessageInsThreadOldMessages = function (ajax, response) {
        ajax = ajax; // For jSlint compatibility.

        insertOldThreadMsgs(response.messages, response.threadId, response.firstMessageId, response.hasNext);
      };

      Drupal.AjaxCommands.prototype.PrivateMessageShowUserList = function (ajax, response) {
        ajax = ajax; // For jSlint compatibility.

        showSearchedUserList(response.users);
      };

      Drupal.AjaxCommands.prototype.PrivateMessageDeleteThread = function (ajax, response) {
        ajax = ajax; // For jSlint compatibility.

        deleteThread(response.thread_id);
      };
    },
    detach:function (context) {
    }
  };


}(jQuery, Drupal, drupalSettings, window));







No time to create a patach, the above fixes the the Javascript updates needed for D10

Production build 0.71.5 2024