PageRenderTime 98ms CodeModel.GetById 4ms app.highlight 80ms RepoModel.GetById 1ms app.codeStats 1ms

/web/studio/ASC.Web.Studio/UserControls/Common/SmallChat/js/smallChat.js

https://github.com/dc0d/ONLYOFFICE-Server
JavaScript | 2561 lines | 2352 code | 144 blank | 65 comment | 485 complexity | 5833df017fb0fde8a9ba44cedd27334e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2(c) Copyright Ascensio System SIA 2010-2014
   3
   4This program is a free software product.
   5You can redistribute it and/or modify it under the terms 
   6of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
   7Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
   8to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of 
   9any third-party rights.
  10
  11This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty 
  12of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For details, see 
  13the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
  14
  15You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
  16
  17The  interactive user interfaces in modified source and object code versions of the Program must 
  18display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
  19 
  20Pursuant to Section 7(b) of the License you must retain the original Product logo when 
  21distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under 
  22trademark law for use of our trademarks.
  23 
  24All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
  25content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
  26International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
  27*/
  28
  29/*
  30    Copyright (c) Ascensio System SIA 2014. All rights reserved.
  31    http://www.teamlab.com
  32*/
  33
  34var SmallChat = (function () {
  35    var chat = jq.connection.c,
  36        currentAccount = null,
  37        already = false,
  38        bufferStates = {},
  39        offlineBuffer = [],
  40        ONLINE = "online",
  41        AWAY = "away",
  42        NOT_AVAILABLE = "not_available",
  43        OFFLINE = "offline",
  44        NUMBER_ONLINE = 1,
  45        NUMBER_AWAY = 2,
  46        NUMBER_NOT_AVAILABLE = 3,
  47        NUMBER_OFFLINE = 4,
  48        IMAGE_ONLINE = "image_online",
  49        IMAGE_AWAY = "image_away",
  50        IMAGE_NOT_AVAILABLE = "image_not_available",
  51        IMAGE_OFFLINE = "image_offline",
  52        PX = "px",
  53        HEIGHT_OFFSET = 100,
  54        OPEN_USER_DETAIL_LIST_INTERVAL = 650,
  55        PING_TIMEOUT_INTERVAL = 100000,
  56        INT_MAX_VALUE = 2147483647,
  57        SEARCH_CONTACT_INTERVAL = 750,
  58        TITLE_INTERVAL = 400,
  59        ENTER_KEY_CODE = 13,
  60        CONVERSATION_BLOCK_HEIGHT = 300,
  61        CONVERSATION_BLOCK_WIDTH = 282,
  62        LEFT_PANEL_AND_INDENT_WIDTH = 370,
  63        MESSAGE_WINDOW_ITEM_HEIGHT = 21,
  64        currentStatus = ONLINE,
  65        currentImage = IMAGE_ONLINE,
  66        NUMBER_OF_RECENT_MSGS = 10,
  67        oldWindowHeight = jq(window).height(),
  68        oldWindowWidth = jq(window).width(),
  69        originalTitle = null,
  70        isActive = false,
  71        titleTimerId = null,
  72        starsNumber = 1,
  73        soundPath = null,
  74        sendTypingSignalTimeout = null,
  75        typingSignalTimeout = null,
  76        shouldOpenUserDialogs = [],
  77        disableUser = false,
  78        timersForMessages = {},
  79        pingTimerId = null,
  80        flashBlocks = {},
  81        shouldOpenContacts = false,
  82        jabberTalkWindow = null,
  83        initEmoticons = false,
  84        maxDialogNumber = null,
  85        isMobile = jq.browser.mobile,
  86        connectionStartTimer = null,
  87        reloadPage = false,
  88        disconnectCompleted = true,
  89        messageContract = {
  90            i: "Id",
  91            u: "UserName",
  92            t: "Text",
  93            d: "DateTimeText"
  94        },
  95        userStrContract = {
  96            u: "UserName",
  97            d: "DisplayUserName",
  98            s: "State"
  99        },
 100        userInformations = {},
 101        sessionStorageManager = function () {
 102            var isAvailable;
 103            try {
 104                isAvailable = "sessionStorage" in window && window["sessionStorage"] !== null;
 105            } catch (e) {
 106                return { isAvailable: false };
 107            }
 108            var getItem = function (key) {
 109                if (!key) {
 110                    return null;
 111                }
 112                return JSON.parse(sessionStorage.getItem(key));
 113            };
 114            var setItem = function (key, value) {
 115                if (!key) {
 116                    return;
 117                }
 118                try {
 119                    sessionStorage.setItem(key, JSON.stringify(value));
 120                } catch (e) {
 121                    if (typeof QUOTA_EXCEEDED_ERR != undefined && e == QUOTA_EXCEEDED_ERR) {
 122                        throw "Session storage is full";
 123                    }
 124                }
 125            };
 126            var removeItem = function (key) {
 127                if (!key) {
 128                    return null;
 129                }
 130                sessionStorage.removeItem(key);
 131            };
 132
 133            var clear = function () {
 134                sessionStorage.clear();
 135            };
 136
 137            return {
 138                isAvailable: isAvailable,
 139                getItem: getItem,
 140                setItem: setItem,
 141                removeItem: removeItem,
 142                clear: clear
 143            };
 144        }();
 145    localStorageManager = function () {
 146        var isAvailable;
 147        try {
 148            isAvailable = "localStorage" in window && window["localStorage"] !== null;
 149        } catch (e) {
 150            return { isAvailable: false };
 151        }
 152        var getItem = function (key) {
 153            if (!key) {
 154                return null;
 155            }
 156            return JSON.parse(localStorage.getItem(key));
 157        };
 158        var setItem = function (key, value) {
 159            if (!key) {
 160                return;
 161            }
 162            try {
 163                localStorage.setItem(key, JSON.stringify(value));
 164            } catch (e) {
 165                if (typeof QUOTA_EXCEEDED_ERR != undefined && e == QUOTA_EXCEEDED_ERR) {
 166                    throw "Local storage is full";
 167                }
 168            }
 169        };
 170        var removeItem = function (key) {
 171            if (!key) {
 172                return null;
 173            }
 174            localStorage.removeItem(key);
 175        };
 176
 177        var clear = function () {
 178            localStorage.clear();
 179        };
 180
 181        return {
 182            isAvailable: isAvailable,
 183            getItem: getItem,
 184            setItem: setItem,
 185            removeItem: removeItem,
 186            clear: clear
 187        };
 188    }();
 189    if (Teamlab.profile.id && sessionStorageManager.getItem("currentUserId") != Teamlab.profile.id) {
 190        sessionStorageManager.clear();
 191        sessionStorageManager.setItem("currentUserId", Teamlab.profile.id);
 192    }
 193
 194    if (typeof (chat) !== undefined) {
 195        // initDataRetrieved
 196        chat.client.idr = function (userName, showUserName, users, tenantId, tenantName, path) {
 197            currentAccount = {
 198                TenantName: tenantName,
 199                TenantId: tenantId,
 200                UserName: userName,
 201                ShowUserName: showUserName
 202            };
 203            soundPath = path;
 204            var $userList = jq(".user_list"),
 205                usersOnline = {},
 206                usersAway = {},
 207                usersNotAvailable = {},
 208                usersOffline = {},
 209                stateNumber;
 210            if ($userList.length) {
 211                jq(".user_list").remove();
 212            }
 213            for (var i = 0; i < users.length; i++) {
 214                users[i] = reMap(users[i], userStrContract);
 215                stateNumber = users[i].State;
 216                users[i].State = getImageByNumber(stateNumber);
 217                switch (stateNumber) {
 218                    case NUMBER_ONLINE:
 219                        usersOnline[i] = users[i];
 220                        break;
 221                    case NUMBER_AWAY:
 222                        usersAway[i] = users[i];
 223                        break;
 224                    case NUMBER_NOT_AVAILABLE:
 225                        usersNotAvailable[i] = users[i];
 226                        break;
 227                    default:
 228                        usersOffline[i] = users[i];
 229                }
 230            }
 231            var tenantGuid = sessionStorageManager.getItem("TenantGuid");
 232            if (!tenantGuid) {
 233                tenantGuid = guid();
 234                sessionStorageManager.setItem("TenantGuid", tenantGuid);
 235            }
 236            var html = jq("#contactListTmpl").tmpl({
 237                UsersOnline: usersOnline,
 238                UsersAway: usersAway,
 239                UsersNotAvailable: usersNotAvailable,
 240                UsersOffline: usersOffline
 241            }),
 242                htmlTenant = jq("#tenantBlockTmpl").tmpl({
 243                    TenantGuid: tenantGuid,
 244                    TenantName: currentAccount.TenantName
 245                }),
 246                smallChatHeight = sessionStorageManager.getItem("SmallChatHeight");
 247            if (smallChatHeight) {
 248                jq(".small_chat_main_window").css("height", smallChatHeight);
 249            }
 250            jq(".chat_contact_loader").addClass("display-none");
 251            jq(".conversation_block").removeClass("display-none");
 252            jq(html).appendTo(".contact_container");
 253            jq(".contact_container").find(".tenant_user_list").append(htmlTenant);
 254            sessionStorageManager.setItem("WasConnected", true);
 255            searchContact();
 256            var status = sessionStorageManager.getItem("CurrentStatus");
 257            if (status && status != OFFLINE) {
 258                changeStatus(getUserNumberByState(status));
 259            }
 260            for (var un in bufferStates) {
 261                setState(un, bufferStates[un]);
 262            }
 263            bufferStates = {};
 264
 265            for (var i = 0; i < offlineBuffer.length; i++) {
 266                if (offlineBuffer[i] == "") {
 267                    offlineBuffer[i] = sessionStorageManager.getItem("TenantGuid");
 268                }
 269                if (isDialogOpen(offlineBuffer[i])) {
 270                    closeConversationBlock(offlineBuffer[i]);
 271                }
 272                removeUserInSessionIfExists(offlineBuffer[i]);
 273                openMessageDialog(offlineBuffer[i]);
 274                var result = flashConversationBlock(offlineBuffer[i], true);
 275                if (result) {
 276                    flashBlocks[offlineBuffer[i]] = result;
 277                }
 278            }
 279            offlineBuffer = [];
 280            loadMessageDialogs();
 281            jq(".extend_chat_icon").off("click").on("click", extendChat);
 282            ASC.Controls.JabberClient.extendChat = extendChat;
 283            setPingSending();
 284        };
 285
 286        // sendInvite
 287        chat.client.si = function (message) {
 288            message = reMap(message, messageContract);
 289            if (!isDialogOpen(sessionStorageManager.getItem("TenantGuid"))) {
 290                openMessageDialog(sessionStorageManager.getItem("TenantGuid"));
 291            }
 292            putMessage({
 293                IsMessageOfCurrentUser: false,
 294                Name: message.UserName,
 295                DateTime: Teamlab.getDisplayTime(new Date()),
 296                Message: addBr(ASC.Resources.Master.ChatResource.ChatRoomInvite + " " + message.Text + ". " + ASC.Resources.Master.ChatResource.GoTalk),
 297                NotRead: true
 298            }, sessionStorageManager.getItem("TenantGuid"));
 299        };
 300
 301        // send
 302        chat.client.s = function (message, calleeUserName, isTenantUser) {
 303            message = reMap(message, messageContract);
 304            if (currentAccount != null) {
 305                var userName = undefined,
 306                    showUserName = undefined,
 307                    $document = jq(document),
 308                    isMessageOfCurrentUser = undefined;
 309                if (!isTenantUser) {
 310                    userName = message.UserName;
 311                    isMessageOfCurrentUser = (userName == currentAccount.UserName);
 312                    showUserName = isMessageOfCurrentUser ?
 313                        currentAccount.ShowUserName :
 314                        jq(".contact_block[data-username='" + message.UserName + "']").find(".contact_record").text();
 315                } else {
 316                    userName = sessionStorageManager.getItem("TenantGuid");
 317                    showUserName = currentAccount.TenantName;
 318                    isMessageOfCurrentUser = (userName == currentAccount.UserName);
 319                }
 320                var realUserName = isMessageOfCurrentUser ? calleeUserName : userName;
 321                if (isDialogOpen(userName) || isDialogOpen(calleeUserName)) {
 322                    if (!isMessageOfCurrentUser) {
 323                        var $conversationBlock = jq(".conversation_block[data-username='" + userName + "']");
 324                        hideTypingMessageNotification($conversationBlock, $conversationBlock.find(".message_bus_container"));
 325                    }
 326                    putMessage({
 327                        IsMessageOfCurrentUser: isMessageOfCurrentUser,
 328                        Name: showUserName,
 329                        DateTime: Teamlab.getDisplayTime(new Date()),
 330                        Message: addBr(message.Text),
 331                        NotRead: !isMessageOfCurrentUser
 332                    }, realUserName);
 333                } else if (isActive && !isMessageOfCurrentUser) {
 334                    showMessageNotification({
 335                        UserName: userName,
 336                        Message: addBr(message.Text),
 337                        ShowUserName: showUserName,
 338                    });
 339                }
 340                if (!isMessageOfCurrentUser) {
 341                    if (!isMobile && soundPath && localStorageManager.getItem("EnableSound")) {
 342                        playSound(soundPath);
 343                    }
 344                    if (!isActive) {
 345                        shouldOpenUserDialogs[shouldOpenUserDialogs.length] = userName;
 346                        if (!titleTimerId) {
 347                            originalTitle = $document.find("title").text();
 348                            $document.find("title").text("*" + ASC.Resources.Master.ChatResource.NewMessageLabel + " " + originalTitle);
 349                            titleTimerId = setInterval(function () {
 350                                starsNumber++;
 351                                $document.find("title").text(getStars(starsNumber) +
 352                                    ASC.Resources.Master.ChatResource.NewMessageLabel + " " + originalTitle);
 353                                if (starsNumber == 3) {
 354                                    starsNumber = 0;
 355                                }
 356                            }, TITLE_INTERVAL);
 357                        }
 358                    }
 359                } else {
 360                    openMessageDialog(calleeUserName);
 361                }
 362            }
 363        };
 364
 365        // sendOfflineMessages
 366        chat.client.som = function (userNames) {
 367            offlineBuffer = userNames;
 368            if (currentAccount && sessionStorageManager.getItem("WasConnected")) {
 369                for (var i = 0; i < offlineBuffer.length; i++) {
 370                    if (offlineBuffer[i] == "") {
 371                        offlineBuffer[i] = sessionStorageManager.getItem("TenantGuid");
 372                    }
 373                    if (isDialogOpen(offlineBuffer[i])) {
 374                        closeConversationBlock(offlineBuffer[i]);
 375                    }
 376                    removeUserInSessionIfExists(offlineBuffer[i]);
 377                    openMessageDialog(offlineBuffer[i]);
 378                    var result = flashConversationBlock(offlineBuffer[i], true);
 379                    if (result) {
 380                        flashBlocks[offlineBuffer[i]] = result;
 381                    }
 382                }
 383                offlineBuffer = [];
 384            }
 385        };
 386
 387        // sendTypingSignal
 388        chat.client.sts = function (userName) {
 389            if (isDialogOpen(userName)) {
 390                var $conversationBlock = jq(".conversation_block[data-username='" + userName + "']"),
 391                    $typingMessageNotification = $conversationBlock.find(".typing_message_notification");
 392
 393                if ($typingMessageNotification.hasClass("display-none")) {
 394                    $messageBusContainer = $conversationBlock.find(".message_bus_container");
 395
 396                    $typingMessageNotification.css("bottom", $conversationBlock.find(".message_input_area").outerHeight() + PX);
 397                    $typingMessageNotification.removeClass("display-none");
 398                }
 399                if (typingSignalTimeout) {
 400                    clearTimeout(typingSignalTimeout);
 401                    typingSignalTimeout = null;
 402                }
 403                typingSignalTimeout = setTimeout(function () {
 404                    hideTypingMessageNotification($conversationBlock, $conversationBlock.find(".message_bus_container"));
 405                }, 5000);
 406            }
 407        };
 408
 409        // setState
 410        chat.client.ss = function (userName, stateNumber, isJabberClient) {
 411            if (currentAccount != null) {
 412                setState(userName, stateNumber, isJabberClient);
 413            } else {
 414                bufferStates[userName] = stateNumber;
 415            }
 416        };
 417
 418        // statesRetrieved
 419        chat.client.sr = function (states) {
 420            var keys = Object.keys(states),
 421                status = sessionStorageManager.getItem("CurrentStatus"),
 422                $conversationBlocks = jq(".conversation_block"),
 423                $chatMessagesLoading,
 424                $conversationBlock;
 425            $conversationBlocks.removeClass("display-none");
 426            for (var i = 0; i < keys.length; i++) {
 427                setState(keys[i], states[keys[i]]);
 428            }
 429            sessionStorageManager.setItem("WasConnected", true);
 430            for (var i = 0; i < $conversationBlocks.length; i++) {
 431                $conversationBlock = jq($conversationBlocks[i]);
 432                if (!$conversationBlock.find(".chat_messages_loading").hasClass("display-none")) {
 433                    getRecentMessagesOnStart($conversationBlock.attr("data-username"));
 434                }
 435            }
 436            if (status && status != OFFLINE) {
 437                changeStatus(getUserNumberByState(status));
 438            }
 439            for (var userName in bufferStates) {
 440                setState(userName, bufferStates[userName]);
 441            }
 442            searchContact();
 443            bufferStates = {};
 444
 445            for (var i = 0; i < offlineBuffer.length; i++) {
 446                if (offlineBuffer[i] == "") {
 447                    offlineBuffer[i] = sessionStorageManager.getItem("TenantGuid");
 448                }
 449                if (isDialogOpen(offlineBuffer[i])) {
 450                    closeConversationBlock(offlineBuffer[i]);
 451                }
 452                openMessageDialog(offlineBuffer[i]);
 453                var result = flashConversationBlock(offlineBuffer[i], true);
 454                if (result) {
 455                    flashBlocks[offlineBuffer[i]] = result;
 456                }
 457            }
 458            offlineBuffer = [];
 459            jq(".extend_chat_icon").off("click").on("click", extendChat);
 460            ASC.Controls.JabberClient.extendChat = extendChat;
 461            setPingSending();
 462        };
 463
 464        // setStatus
 465        chat.client.sst = function (number) {
 466            changeStatus(number);
 467        };
 468
 469        // error
 470        chat.client.e = function () {
 471            showErrorNotification();
 472            connectionStop();
 473            closeChat();
 474        };
 475    } else {
 476        throw "Error! Chat proxy is undefined!!!";
 477    }
 478
 479    function getRecentMessagesOnStart(u) {
 480        var userName = u;
 481        chat.server.grm(userName == sessionStorageManager.getItem("TenantGuid") ? "" : userName, INT_MAX_VALUE).done(function (recentMessages) {
 482            var $cb = jq(".conversation_block[data-username='" + userName + "']"),
 483                $chatLoading = $cb.find(".chat_messages_loading");
 484            receiveRecentMessages(userName, recentMessages, $chatLoading, $cb, 0);
 485        });
 486    }
 487
 488    function removeUserInSessionIfExists(currentUserName) {
 489        var dialogsNumber = sessionStorageManager.getItem("dialogsNumber");
 490        for (var i = 0; i < dialogsNumber; i++) {
 491            if (currentUserName == sessionStorageManager.getItem("userName" + i)) {
 492                for (var j = i; j < dialogsNumber; j++) {
 493                    sessionStorageManager.setItem("userName" + j, sessionStorageManager.getItem("userName" + (j + 1)));
 494                }
 495                sessionStorageManager.removeItem("userName" + dialogsNumber - 1);
 496                sessionStorageManager.setItem("dialogsNumber", dialogsNumber - 1);
 497                break;
 498            }
 499        }
 500    }
 501
 502    function s4() {
 503        return (0 | ((1 + Math.random()) * 0x10000)).toString(16).substring(1);
 504    }
 505
 506    function guid() {
 507        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
 508    }
 509
 510    function setPingSending() {
 511        if (pingTimerId) {
 512            clearInterval(pingTimerId);
 513        }
 514        pingTimerId = setInterval(function () {
 515            if (sessionStorageManager.getItem("WasConnected")) {
 516                // ping
 517                chat.server.p();
 518            } else {
 519                clearInterval(pingTimerId);
 520                pingTimerId = null;
 521            }
 522        }, PING_TIMEOUT_INTERVAL);
 523    }
 524
 525    function hideTypingMessageNotification(conversationBlock, messageBusContainer) {
 526        jq(conversationBlock).find(".typing_message_notification").addClass("display-none");
 527        jq(messageBusContainer).css({ "height": "100%", "padding-bottom": "0" });
 528        if (typingSignalTimeout) {
 529            clearTimeout(typingSignalTimeout);
 530            typingSignalTimeout = null;
 531        }
 532    }
 533
 534    function getStars(starsNumber) {
 535        switch (starsNumber) {
 536            case 1: return "*";
 537            case 2: return "**";
 538            case 3: return "***";
 539        }
 540    }
 541
 542    function loadMessageDialogs() {
 543        var userName,
 544            dialogsNumber = sessionStorageManager.getItem("dialogsNumber"),
 545            dialogsNumberInMenu = sessionStorageManager.getItem("dialogsNumberInMenu");
 546        if (dialogsNumber) {
 547            for (var i = 0; i < dialogsNumber; i++) {
 548                userName = sessionStorageManager.getItem("userName" + i);
 549                openMessageDialog(userName, true, i);
 550                // if user was disabled
 551                if (disableUser) {
 552                    i--;
 553                    dialogsNumber--;
 554                    disableUser = false;
 555                }
 556                if (sessionStorageManager.getItem("MiniCB" + userName)) {
 557                    minimize(jq(".conversation_block[data-username='" + userName + "']"));
 558                }
 559            }
 560        }
 561
 562        if (dialogsNumberInMenu) {
 563            for (var i = 0; i < dialogsNumberInMenu; i++) {
 564                userName = sessionStorageManager.getItem("dn_userName" + i);
 565                var $contactRecord = jq(".contact_block[data-username='" + userName + "']").find(".contact_record");
 566                // if user was disabled
 567                if (!$contactRecord.length) {
 568                    sessionStorageManager.setItem("dialogsNumberInMenu", dialogsNumberInMenu - 1);
 569                    for (var j = i; j < dialogsNumberInMenu - 1; j++) {
 570                        sessionStorageManager.setItem("dn_userName" + j, sessionStorageManager.getItem("dn_userName" + (j + 1)));
 571                    }
 572                    sessionStorageManager.removeItem("dn_userName" + (dialogsNumberInMenu - 1));
 573                    i--;
 574                    dialogsNumberInMenu--;
 575                    continue;
 576                }
 577                addToMenu({
 578                    UserName: userName,
 579                    ShowUserName: $contactRecord.text(),
 580                }, true);
 581            }
 582            // if user was disabled
 583            if (getMaxDialogNumber() > sessionStorageManager.getItem("dialogsNumber")) {
 584                userName = jq("#messageDialogPopupID").find(".message_dialog_item").first().attr("data-username");
 585                closeMessageDialogItem(userName);
 586                openMessageDialog(userName);
 587            }
 588        }
 589        jq(".message_input_area").blur();
 590    }
 591
 592    function isDialogOpen(userName) {
 593        if (jq(".conversation_block[data-username='" + userName + "']").length) {
 594            return true;
 595        }
 596        return false;
 597    }
 598
 599    function isDialogInMenu(userName) {
 600        if (jq(".message_dialog_item[data-username='" + userName + "']").length) {
 601            return true;
 602        }
 603        return false;
 604    }
 605
 606    function removeParametersOfDialogInMenu(userName) {
 607        var $block = jq(".message_dialog_item[data-username='" + userName + "']"),
 608            $items = jq(".message_dialog_item"),
 609            dialogsNumberInMenu = sessionStorageManager.getItem("dialogsNumberInMenu") - 1,
 610            $messageDialogItem = jq(".message_dialog_item");
 611        for (var i = 0; i < $messageDialogItem.length; i++) {
 612            var nextIndex = $items.index($messageDialogItem[i]);
 613            if ($items.index($block) < nextIndex) {
 614                sessionStorageManager.setItem("dn_userName" + (nextIndex - 1),
 615                    sessionStorageManager.getItem("dn_userName" + nextIndex));
 616            }
 617        }
 618        $block.remove();
 619        sessionStorageManager.removeItem("dn_userName" + dialogsNumberInMenu);
 620        sessionStorageManager.setItem("dialogsNumberInMenu", dialogsNumberInMenu);
 621    }
 622
 623    function setParametersOfDialog(userName) {
 624        var dialogsNumber = sessionStorageManager.getItem("dialogsNumber");
 625        if (dialogsNumber) {
 626            sessionStorageManager.setItem("userName" + dialogsNumber, userName);
 627            jq(".conversation_block[data-username='" + userName + "']").attr("data-dialog-number", dialogsNumber);
 628            sessionStorageManager.setItem("dialogsNumber", ++dialogsNumber);
 629        } else {
 630            sessionStorageManager.setItem("userName0", userName);
 631            jq(".conversation_block[data-username='" + userName + "']").attr("data-dialog-number", "0");
 632            sessionStorageManager.setItem("dialogsNumber", 1);
 633        }
 634    }
 635
 636    function setParametersOfDialogInMenu(userName) {
 637        var dialogsNumberInMenu = sessionStorageManager.getItem("dialogsNumberInMenu");
 638        if (dialogsNumberInMenu) {
 639            sessionStorageManager.setItem("dn_userName" + dialogsNumberInMenu, userName);
 640            sessionStorageManager.setItem("dialogsNumberInMenu", ++dialogsNumberInMenu);
 641        } else {
 642            sessionStorageManager.setItem("dn_userName0", userName);
 643            sessionStorageManager.setItem("dialogsNumberInMenu", 1);
 644        }
 645    }
 646
 647    function getContactNumberBeforeCurrent(name, ulClass) {
 648        var $lists = jq("." + ulClass).children();
 649        for (var i = 0; i < $lists.length; i++) {
 650            if (name <= jq($lists[i]).find(".contact_record").text().toLowerCase()) {
 651                break;
 652            }
 653        }
 654        return i;
 655    }
 656
 657    function setState(userName, stateNumber, isJabberClient) {
 658        if (userName != currentAccount.UserName) {
 659            var newState = getImageByNumber(stateNumber),
 660                $contactBlock = jq(".contact_block[data-username='" + userName + "']"),
 661                ulClass;
 662            if ($contactBlock.length) {
 663                var $elem = $contactBlock.find(".chat_user_state");
 664                if ($elem.attr("class") != undefined) {
 665                    var oldState = $elem.attr("class").split(" ")[1];
 666                    $elem.removeClass(oldState);
 667                    $elem.addClass(newState);
 668                    $contactBlock.detach();
 669                    switch (stateNumber) {
 670                        case NUMBER_ONLINE:
 671                            ulClass = "online_user_list";
 672                            break;
 673                        case NUMBER_AWAY:
 674                            ulClass = "away_user_list";
 675                            break;
 676                        case NUMBER_NOT_AVAILABLE:
 677                            ulClass = "not_available_user_list";
 678                            break;
 679                        default:
 680                            ulClass = "offline_user_list";
 681                    }
 682                    var contactNumber = getContactNumberBeforeCurrent($contactBlock.find(".contact_record").text().toLowerCase(), ulClass);
 683                    if (contactNumber) {
 684                        $contactBlock.insertAfter("." + ulClass + " li:nth-child(" + contactNumber + ")");
 685                    } else {
 686                        $contactBlock.prependTo("." + ulClass);
 687                    }
 688                }
 689            } else {
 690                if (sessionStorageManager.getItem("WasConnected")) {
 691                    // getContactInfo
 692                    chat.server.gci(userName).done(function (contact) {
 693                        switch (contact.Item2) {
 694                            case NUMBER_ONLINE:
 695                                ulClass = "online_user_list";
 696                                break;
 697                            case NUMBER_AWAY:
 698                                ulClass = "away_user_list";
 699                                break;
 700                            case NUMBER_NOT_AVAILABLE:
 701                                ulClass = "not_available_user_list";
 702                                break;
 703                            default:
 704                                ulClass = "offline_user_list";
 705                        }
 706
 707                        createContactInfo({
 708                            UserName: userName,
 709                            ShowUserName: contact.Item1,
 710                            StateClass: ulClass
 711                        }, ulClass);
 712                    }).fail(function (error) {
 713                        showErrorNotification();
 714                    });
 715                }
 716            }
 717            if (isDialogOpen(userName)) {
 718                var $stateBlock = jq(".conversation_block[data-username='" + userName + "']").find(".conversation_block_user_state");
 719                if ($stateBlock.attr("class") != undefined) {
 720                    var oldState = $stateBlock.attr("class").split(" ")[1];
 721                    $stateBlock.removeClass(oldState);
 722                    $stateBlock.addClass(newState);
 723                    $stateBlock.attr("title", getRealStateByNumber(stateNumber));
 724                }
 725            }
 726        } else if (!isJabberClient) {
 727            changeStatus(stateNumber);
 728        }
 729    }
 730
 731    function createContactInfo(contact, ulClass) {
 732        var contactNumber = getContactNumberBeforeCurrent(contact.UserName, ulClass),
 733            html = jq("#contactTmpl").tmpl(contact);
 734        if (contactNumber) {
 735            html.insertAfter("." + ulClass + " li:nth-child(" + contactNumber + ")");
 736        } else {
 737            html.prependTo("." + ulClass);
 738        }
 739    }
 740
 741
 742    function changeStatus(stateNumber, NoIconGreen) {
 743        var state = getUserStateByNumber(stateNumber);
 744        if (state != currentStatus) {
 745            var prevStatus = currentStatus,
 746                $showSmallChatIcon = jq(".show_small_chat_icon"),
 747                $smallChatPopupID = jq("#smallChatPopupID"),
 748                $smallChatTextStatus = jq(".small_chat_text_status"),
 749                $currentStatus = jq("." + currentStatus),
 750                realStatus;
 751            $smallChatPopupID.removeClass("display_block");
 752            $smallChatPopupID.addClass("display-none");
 753            $currentStatus.removeClass("disable");
 754            $smallChatTextStatus.removeClass(currentImage);
 755            $currentStatus.click(chooseStatus);
 756            if (state == OFFLINE) {
 757                $showSmallChatIcon.addClass("small_chat_icon_white");
 758                $showSmallChatIcon.removeClass("small_chat_icon_green");
 759                connectionStop();
 760            } else if (!NoIconGreen) {
 761                $showSmallChatIcon.addClass("small_chat_icon_green");
 762                $showSmallChatIcon.removeClass("small_chat_icon_white");
 763            }
 764            currentImage = getImageByNumber(stateNumber);
 765            currentStatus = state;
 766            sessionStorageManager.setItem("CurrentStatus", currentStatus);
 767            $currentStatus = jq("." + currentStatus);
 768            $currentStatus.off("click");
 769            $currentStatus.addClass("disable");
 770            $smallChatTextStatus.addClass(currentImage);
 771            realStatus = getRealState(currentStatus);
 772            $smallChatTextStatus.text(realStatus);
 773            jq("small_chat_status_menu").attr("title", realStatus);
 774        }
 775    }
 776
 777    function showMessageNotification(object) {
 778        noty({
 779            text: jq("#messageNotificationTmpl").tmpl(object),
 780            layout: "bottomRight",
 781            theme: "defaultTheme",
 782            type: "alert",
 783            animation: {
 784                easing: "swing",
 785                open: { "height": "toggle" },
 786                close: { "height": "toggle" },
 787                speed: "400",
 788            },
 789            timeout: "7000",
 790            maxVisible: 7,
 791            force: true
 792        });
 793    }
 794
 795    function putMessage(object, userName) {
 796        var $conversationBlock = jq(".conversation_block[data-username='" + userName + "']"),
 797            $messageBusContainer = $conversationBlock.find(".message_bus_container"),
 798            $last = $messageBusContainer.find(".message_of_user:last");
 799        if ((($last.hasClass("message_of_other_user") && object.IsMessageOfCurrentUser) ||
 800            ($last.hasClass("message_of_current_user") && !object.IsMessageOfCurrentUser)) &&
 801            timersForMessages[userName]) {
 802            clearTimeout(timersForMessages[userName]);
 803            delete timersForMessages[userName];
 804        }
 805        if (!object.IsMessageOfCurrentUser && !(userName in flashBlocks)) {
 806            var result = flashConversationBlock(userName, true);
 807            if (result) {
 808                flashBlocks[userName] = result;
 809            }
 810        }
 811        if (!timersForMessages[userName]) {
 812            $messageBusContainer.append(jq("#messageTmpl").tmpl(object));
 813            $last = $messageBusContainer.find(".message_of_user:last");
 814        } else {
 815            $last.next().text(object.DateTime);
 816            $last.after("<div class='message_of_user " +
 817                ($last.hasClass("message_of_current_user") ? "message_of_current_user" : "message_of_other_user not_read_message") +
 818                "'>" + object.Message + "</div>");
 819            clearTimeout(timersForMessages[userName]);
 820            delete timersForMessages[userName];
 821        }
 822        timersForMessages[userName] = setTimeout(function () {
 823            clearTimeout(timersForMessages[userName]);
 824            delete timersForMessages[userName];
 825        }, 30000);
 826        $last = $messageBusContainer.find(".message_of_user:last");
 827        if (userName != sessionStorageManager.getItem("TenantGuid")) {
 828            emoticonize($last);
 829        }
 830        $last.linkify();
 831        scrollTopMessageContainer($messageBusContainer);
 832    }
 833
 834    function scrollTopMessageContainer(messageBusContainer) {
 835        var $messageBusContainer = jq(messageBusContainer);
 836        scrollHeight = $messageBusContainer.prop("scrollHeight");
 837        if (scrollHeight > $messageBusContainer.scrollTop() && scrollHeight > $messageBusContainer.height()) {
 838            $messageBusContainer.scrollTop(scrollHeight);
 839        }
 840    }
 841
 842    function putSmileInMessage(e) {
 843        var $smile = jq(e.currentTarget),
 844            $smileMenu = jq(".smile_menu"),
 845            dn = $smileMenu.attr("id").split("smilesPopupID")[1],
 846            $conversationBlock = jq(".conversation_block[data-dialog-number='" + dn + "']"),
 847            $messageInputArea = $conversationBlock.find(".message_input_area"),
 848            cursorPos = $messageInputArea.prop("selectionStart"),
 849            v = $messageInputArea.val(),
 850            smileString = " " + $smile.text();
 851
 852        $messageInputArea.val(v.substring(0, cursorPos) + smileString + v.substring(cursorPos, v.length));
 853        $messageInputArea.val($messageInputArea.val() + " ");
 854        setCaretPosition($messageInputArea, cursorPos + smileString.length);
 855        $messageInputArea.trigger("autosize.resize");
 856        $messageInputArea.scrollTop($messageInputArea.prop("scrollHeight"));
 857        sendTypingSignal($conversationBlock.attr("data-username"));
 858        $smileMenu.css("display", "none");
 859    }
 860
 861    function getRealState(state) {
 862        switch (state) {
 863            case ONLINE:
 864                return ASC.Resources.Master.ChatResource.StatusOnline;
 865            case AWAY:
 866                return ASC.Resources.Master.ChatResource.StatusAway;
 867            case NOT_AVAILABLE:
 868                return ASC.Resources.Master.ChatResource.StatusNA;
 869            default:
 870                return ASC.Resources.Master.ChatResource.StatusOffline;
 871        }
 872    }
 873
 874    function getRealStateByNumber(stateNumber) {
 875        switch (stateNumber) {
 876            case NUMBER_ONLINE:
 877                return ASC.Resources.Master.ChatResource.StatusOnline;
 878            case NUMBER_AWAY:
 879                return ASC.Resources.Master.ChatResource.StatusAway;
 880            case NUMBER_NOT_AVAILABLE:
 881                return ASC.Resources.Master.ChatResource.StatusNA;
 882            default:
 883                return ASC.Resources.Master.ChatResource.StatusOffline;
 884        }
 885    }
 886
 887    function getRealStateByImageState(stateNumber) {
 888        switch (stateNumber) {
 889            case IMAGE_ONLINE:
 890                return ASC.Resources.Master.ChatResource.StatusOnline;
 891            case IMAGE_AWAY:
 892                return ASC.Resources.Master.ChatResource.StatusAway;
 893            case IMAGE_NOT_AVAILABLE:
 894                return ASC.Resources.Master.ChatResource.StatusNA;
 895            default:
 896                return ASC.Resources.Master.ChatResource.StatusOffline;
 897        }
 898    }
 899
 900    function getUserStateByImageState(imageState) {
 901        switch (imageState) {
 902            case IMAGE_ONLINE:
 903                return ONLINE;
 904            case IMAGE_AWAY:
 905                return AWAY;
 906            case IMAGE_NOT_AVAILABLE:
 907                return NOT_AVAILABLE;
 908            default:
 909                return OFFLINE;
 910        }
 911    }
 912
 913    function getImageByNumber(stateNumber) {
 914        switch (stateNumber) {
 915            case NUMBER_ONLINE:
 916                return IMAGE_ONLINE;
 917            case NUMBER_AWAY:
 918                return IMAGE_AWAY;
 919            case NUMBER_NOT_AVAILABLE:
 920                return IMAGE_NOT_AVAILABLE;
 921            default:
 922                return IMAGE_OFFLINE;
 923        }
 924    }
 925
 926    function getUserStateByNumber(stateNumber) {
 927        switch (stateNumber) {
 928            case NUMBER_ONLINE:
 929                return ONLINE;
 930            case NUMBER_AWAY:
 931                return AWAY;
 932            case NUMBER_NOT_AVAILABLE:
 933                return NOT_AVAILABLE;
 934            default:
 935                return OFFLINE;
 936        }
 937    }
 938
 939    function getUserNumberByState(state) {
 940        switch (state) {
 941            case ONLINE:
 942                return NUMBER_ONLINE;
 943            case AWAY:
 944                return NUMBER_AWAY;
 945            case NOT_AVAILABLE:
 946                return NUMBER_NOT_AVAILABLE;
 947            default:
 948                return NUMBER_OFFLINE;
 949        }
 950    }
 951
 952    function getUserNumberByStateForConnection() {
 953        var state = getUserNumberByState(sessionStorageManager.getItem("CurrentStatus"));
 954        if (!state || state == NUMBER_OFFLINE) {
 955            state = NUMBER_ONLINE;
 956        }
 957        return state;
 958    }
 959
 960    function openUserDetailList(e) {
 961        if (isMobile) {
 962            return;
 963        }
 964        closeUserDetailList();
 965        if (sessionStorageManager.getItem("WasConnected")) {
 966            var $contactBlock = jq(e.currentTarget),
 967                userName = $contactBlock.attr("data-username");
 968            if (!$contactBlock.is(":hover") || userName == sessionStorageManager.getItem("TenantGuid")) {
 969                return;
 970            }
 971            var timeoutId = setTimeout(function () {
 972                if (!$contactBlock.is(":hover") || !sessionStorageManager.getItem("WasConnected")) {
 973                    return;
 974                }
 975                var $detailUserList = jq(".detail_user_list");
 976                if ($detailUserList.length) {
 977                    $detailUserList.remove();
 978                }
 979                if (userInformations[userName]) {
 980                    openDetail(userName, userInformations[userName], $contactBlock);
 981                } else {
 982                    Teamlab.getProfile({}, userName, {
 983                        success: function (params, data) {
 984                            var departments = {};
 985                            if (data.groups) {
 986                                for (var i = 0; i < data.groups.length; i++) {
 987                                    departments[data.groups[i].id] = data.groups[i].name;
 988                                }
 989                            }
 990                            userInformation = {
 991                                UserName: userName,
 992                                ShowUserName: data.displayName,
 993                                Email: data.email,
 994                                UserType: data.isVisitor ? ASC.Resources.Master.Guest : ASC.Resources.Master.User,
 995                                Title: data.title,
 996                                PhotoURL: data.avatar,
 997                                Departments: departments
 998                            };
 999                            userInformations[userName] = userInformation;
1000                            openDetail(userName, userInformation, $contactBlock);
1001                        }
1002                    });
1003                }
1004            }, OPEN_USER_DETAIL_LIST_INTERVAL);
1005            $contactBlock.data("timeoutId", timeoutId);
1006        }
1007    }
1008
1009    function openDetail(userName, data, $contactBlock) {
1010        if (!jq($contactBlock).is(":hover")) {
1011            return;
1012        }
1013        if (data) {
1014            var html = jq("#detailUserListTmpl").tmpl(data),
1015            $detailUserList = jq(html).appendTo(".mainPageTableSidePanel"),
1016            positionOfContact = jq(".contact_block[data-username='" + userName + "']").offset(),
1017            top = positionOfContact.top - 30,
1018            $window = jq(window),
1019            scrollTop = $window.scrollTop();
1020
1021            $detailUserList.css("left", positionOfContact.left + 220 + PX);
1022            if (top + $detailUserList.outerHeight(true) < $window.height() + scrollTop) {
1023                $detailUserList.css("top", top - scrollTop + PX);
1024            } else {
1025                $detailUserList.css("bottom", 15 + PX);
1026            }
1027            $detailUserList.fadeIn(170);
1028            $detailUserList.removeClass("display-none");
1029        }
1030    }
1031
1032    function closeUserDetailList(e) {
1033        if (isMobile) {
1034            return;
1035        }
1036        if (sessionStorageManager.getItem("WasConnected")) {
1037            setTimeout(function () {
1038                var $detailUserList = jq(".detail_user_list");
1039                if (e) {
1040                    clearTimeout(jq(e.currentTarget).data("timeoutId"));
1041                }
1042                if ($detailUserList.length && !$detailUserList.hasClass("hover")) {
1043                    $detailUserList.fadeOut(100);
1044                }
1045            }, 50);
1046        }
1047    }
1048
1049    function showChat() {
1050        var smallChatHeight = sessionStorageManager.getItem("SmallChatHeight"),
1051            $showSmallChatIcon = jq(".show_small_chat_icon"),
1052            $smallChatMainWindow = jq(".small_chat_main_window");
1053        jq(".extend_chat_icon").off("click");
1054        ASC.Controls.JabberClient.extendChat = function () { ASC.Controls.JabberClient.open(); }
1055        sessionStorageManager.setItem("WasLoad", true);
1056        $smallChatMainWindow.addClass("small_chat_main_window_full");
1057        $showSmallChatIcon.addClass("small_chat_icon_green");
1058        $showSmallChatIcon.removeClass("small_chat_icon_white");
1059        jq(".contact_container").removeClass("display-none");
1060        jq(".small_chat_top_panel").removeClass("display-none");
1061        jq(".icon_ch_size").removeClass("display-none");
1062        resizeChat();
1063        jq(".conversation_block").removeClass("display-none");
1064        jq(".message_dialog_btn").removeClass("display-none");
1065        if (smallChatHeight) {
1066            $smallChatMainWindow.css("height", smallChatHeight);
1067        }
1068        if (!currentAccount) {
1069            jq(".chat_contact_loader").removeClass("display-none");
1070        }
1071        if (currentStatus == OFFLINE) {
1072            chooseUserStatus(ONLINE);
1073        }
1074    }
1075
1076    function closeChat() {
1077        var smallChatHeight = sessionStorageManager.getItem("SmallChatHeight"),
1078            $showSmallChatIcon = jq(".show_small_chat_icon"),
1079            $smallChatMainWindow = jq(".small_chat_main_window"),
1080            $notifications = jq(".notification_username"),
1081            $noRemove = jq(".chat_contact_loader"),
1082            $contactContainer = jq(".contact_container");
1083        sessionStorageManager.setItem("WasConnected", false);
1084        sessionStorageManager.setItem("WasLoad", false);
1085        var dialogsNumber = sessionStorageManager.getItem("dialogsNumber"),
1086                dialogsNumberInMenu = sessionStorageManager.getItem("dialogsNumberInMenu") || 0;
1087        for (var i = 0; i < dialogsNumber; i++) {
1088            sessionStorageManager.removeItem("userName" + i);
1089        }
1090        for (var i = 0; i < dialogsNumberInMenu; i++) {
1091            sessionStorageManager.removeItem("dn_userName");
1092        }
1093        sessionStorageManager.setItem("dialogsNumber", 0);
1094        sessionStorageManager.setItem("dialogsNumberInMenu", 0);
1095        jq(".conversation_block").remove();
1096        jq(".message_dialog_btn").remove();
1097        jq(".detail_user_list").remove();
1098        $contactContainer.empty();
1099        $contactContainer.html($noRemove);
1100        currentAccount = null;
1101        if ($notifications.length) {
1102            for (var i = 0; i < $notifications.length; i++) {
1103                jq($notifications[i]).closest("li").remove();
1104            }
1105        }
1106
1107        if (smallChatHeight) {
1108            $smallChatMainWindow.css("height", "initial");
1109        }
1110        $smallChatMainWindow.removeClass("small_chat_main_window_full");
1111        $showSmallChatIcon.addClass("small_chat_icon_white");
1112        $showSmallChatIcon.removeClass("small_chat_icon_green");
1113        jq(".contact_container").addClass("display-none");
1114        jq(".small_chat_top_panel").addClass("display-none");
1115        jq(".icon_ch_size").addClass("display-none");
1116        if (!isMobile) {
1117            $smallChatMainWindow.resizable("disable").removeClass("ui-state-disabled");
1118        }
1119        //restore default state
1120        changeStatus(NUMBER_ONLINE, true);
1121    }
1122
1123    function hideChat() {
1124        var smallChatHeight = sessionStorageManager.getItem("SmallChatHeight"),
1125            $showSmallChatIcon = jq(".show_small_chat_icon"),
1126            $smallChatMainWindow = jq(".small_chat_main_window");
1127        if (smallChatHeight) {
1128            $smallChatMainWindow.css("height", "initial");
1129        }
1130        $smallChatMainWindow.removeClass("small_chat_main_window_full");
1131        $showSmallChatIcon.addClass("small_chat_icon_white");
1132        $showSmallChatIcon.removeClass("small_chat_icon_green");
1133        jq(".contact_container").addClass("display-none");
1134        jq(".small_chat_top_panel").addClass("display-none");
1135        jq(".icon_ch_size").addClass("display-none");
1136        if (!isMobile) {
1137            $smallChatMainWindow.resizable("disable").removeClass("ui-state-disabled");
1138        }
1139        jq(".conversation_block").addClass("display-none");
1140        jq(".message_dialog_btn").addClass("display-none");
1141        jq(".detail_user_list").remove();
1142        //restore default state
1143        changeStatus(NUMBER_ONLINE, true);
1144    }
1145
1146    function showOrHideSmallChat() {
1147        if (!sessionStorageManager.getItem("WasConnected") && currentStatus != OFFLINE) {
1148            if (jq.connection.hub.state === jq.connection.connectionState.connected) {
1149                connectionStart();
1150                showChat();
1151            }
1152        } else {
1153            connectionStop();
1154            hideChat();
1155        }
1156    }
1157
1158    function openContacts() {
1159        if (shouldOpenContacts) {
1160            shouldOpenContacts = false;
1161            var dialogsNumber = sessionStorageManager.getItem("dialogsNumber"),
1162                dialogsNumberInMenu = sessionStorageManager.getItem("dialogsNumberInMenu") || 0,
1163                allDialogsNumber = dialogsNumber + dialogsNumberInMenu,
1164                userName;
1165            for (var i = 0; i < dialogsNumber; i++) {
1166                userName = sessionStorageManager.getItem("userName" + i);
1167                if (userName == sessionStorageManager.getItem("TenantGuid")) {
1168                    ASC.Controls.JabberClient.openTenant(currentAccount.TenantName);
1169                } else {
1170                    ASC.Controls.JabberClient.openContact(userName);
1171                }
1172            }
1173            for (var i = dialogsNumber; i < allDialogsNumber; i++) {
1174                userName = sessionStorageManager.getItem("dn_userName" + (i - dialogsNumber));
1175                if (userName == sessionStorageManager.getItem("TenantGuid")) {
1176                    ASC.Controls.JabberClient.openTenant(currentAccount.TenantName);
1177                } else {
1178                    ASC.Controls.JabberClient.openContact(userName);
1179                }
1180            }
1181        }
1182    }
1183
1184    function extendChat() {
1185        if (!jabberTalkWindow || jabberTalkWindow.closed) {
1186            jabberTalkWindow = ASC.Controls.JabberClient.open();
1187            if (!sessionStorageManager.getItem("WasConnected") && currentStatus != OFFLINE) {
1188                shouldOpenContacts = false;
1189            } else {
1190                shouldOpenContacts = true;
1191            }
1192            setTimeout(function () {
1193                if (session

Large files files are truncated, but you can click here to view the full file