// Определение ОС var OSName = 'Unknown'; if (navigator.appVersion.indexOf('Win') != -1) OSName = 'Windows'; if (navigator.appVersion.indexOf('Mac') != -1) OSName = 'MacOS'; if (navigator.appVersion.indexOf('X11') != -1) OSName = 'UNIX'; if (navigator.appVersion.indexOf('Linux') != -1) OSName = 'Linux'; console.log('Операционная система: ' + OSName); var someTime = new Date().getTime(); var messengerFrameReload = true; // Оболочку открывают в iframe на чужом домене (просмотрщик тепловых карт Я.Метрики — // metrika.yandex.ru). Там браузер с заблокированными сторонними куками бросает на // localStorage SecurityError: необработанное исключение обрывало window.onload, // фрейм CRM не создавался и в просмотрщике был белый экран. Читаем/пишем через обёртки. function lsGet(key) { try { return localStorage.getItem(key); } catch (e) { return null; } } function lsSet(key, value) { try { localStorage.setItem(key, value); } catch (e) {} } // Пропускаем только адреса нашего же сайта. Иначе в src фрейма попадал мусор: // просмотрщик Метрики добавляет к URL свои параметры (?_ym_...&...), а старый разбор // брал всё после первого '&' без проверки — фрейм уходил на несуществующий URL. function sameOriginUrl(url) { if (!url) return ''; url = String(url); if (url.charAt(0) === '/') return url; if (url.indexOf(location.origin + '/') === 0) return url; return ''; } // Глубокая ссылка вида /JustCRM&<полный URL страницы CRM> (см. tasks/controller.inc.php). // Берём всё после ПЕРВОГО '&': в самом целевом URL тоже могут быть '&' (split их терял). function deepLinkFromHref() { var i = location.href.indexOf('&'); return (i === -1) ? '' : sameOriginUrl(location.href.slice(i + 1)); } // Выполнение при загрузке var first = false; window.onload = function() { var deepLink = deepLinkFromHref(); if (deepLink) lsSet('JustCRM_lastURL', deepLink); // Необходимо для того, чтобы при обновлении страницы загружался фрейм с текущим URL CRM var src = sameOriginUrl(lsGet('JustCRM_lastURL')) || 'index.php'; var fragment = create(''); document.body.insertBefore(fragment, document.body.childNodes[0]); }; function loadChat() { var fragment = create(''); document.body.insertBefore(fragment, document.body.childNodes[1]); var fragment1 = create(''); document.body.insertBefore(fragment1, document.body.childNodes[2]); } function create(htmlStr) { var frag = document.createDocumentFragment(), temp = document.createElement('div'); temp.innerHTML = htmlStr; while (temp.firstChild) { frag.appendChild(temp.firstChild); } return frag; } // Операции с Websocket var socket; var timer; var forbidden = false; function socketConnect() { //socket = new WebSocket('wss://'+window.location.hostname+'/ws'); socket = new WebSocket('wss://crm1.sskindustry.ru/ws'); //socket = new WebSocket('ws://46.0.207.45/ws'); socket.onmessage = function(event) { // Ответ сервера //console.log(event.data); var serverEvent = {}; try { serverEvent = JSON.parse(event.data); } catch (err) {}; // Если авторизация отклонена, то удаляем таймер для попыток соединения if (serverEvent.wssstatus && serverEvent.wssstatus == '0') { clearTimeout(timer); forbidden = true; } // Если обнаружен дубликат сессии, то завершаем соединение if (serverEvent.wssstatus == '2') { console.log('Отключаем соединение.'); socket.close(); // Аннулируем авторизацию в CRM window.frames[0].deleteCookie('PHPSESSID'); window.location.reload(); } // Если это первый ответ сервера с id пользователя, присвоенный сервером, то отправляем серверу настройки с методами if (typeof serverEvent.first_responce_server != "undefined") { var firstResponceClient = '{"first_responce_client":"true", "id":"' + window.frames[0].id + '", "id_client":"' + window.frames[0].guid + '", "id_client_server":"' + serverEvent.id_client_server + '", "client_name":"", "ext_client":"' + window.frames[0].ext_phone + '", "incoming_call":"true", "incoming_task":"true"}'; console.log('Ответ серверу отправлен.'); try { socket.send(firstResponceClient); } catch (err) {}; } else { // Иначе это событие, которое надо обработать if (JSON.stringify(serverEvent) != '{}') { console.log('Получено событие.'); console.log(serverEvent); noty(serverEvent); } } }; socket.onopen = function() { console.log('Соединение установлено.'); socketStatus(socket.readyState); }; socket.onerror = function() { console.log('Ошибка соединения с сервером уведомлений!'); socketStatus(socket.readyState); }; socket.onclose = function() { console.log('Соединение закрыто.'); socketStatus(socket.readyState); delete socket; if (!forbidden) { if (window.frames[0].guid != '' && window.frames[0].system_type == '10') { timer = setTimeout(function() { console.log('Пробуем подключиться...'); socketConnect(); }, 5000); } } }; } // Оболочка открыта во фрейме на чужом домене — это просмотрщик тепловых карт Метрики. function isForeignTop() { try { void window.top.document; return false; } catch (e) { return true; } } function crmLoad() { // В просмотрщике карт вебсокет уведомлений не нужен, и он там вреден: сервер событий // считает дубликатом ЛЮБОЕ второе подключение того же пользователя (сверка по GUID, // см. nodejs/events.server.js) и шлёт wssstatus:2. Обработчик делает reload, обе // стороны — обычная вкладка и фрейм — начинают перезагружать друг друга по кругу // (выйти из системы при этом не удаётся: deleteCookie зовут с именем PHPSESSID, // а реальная кука SSKPHPSESSID). Во фрейме этот цикл и выглядит белым экраном. if (isForeignTop()) { // Внутренний фрейм загрузился и сообщает, авторизован ли пользователь. // Если нет — предлагаем запросить доступ к куке сессии (Safari и любой браузер // с заблокированными сторонними куками). Запрашивать умеет только оболочка: // она прямой потомок чужого окна, вложенному фрейму Safari откажет. try { if (window.frames[0] && window.frames[0].guid === '' && window.JustCRMStorageAccess) { window.JustCRMStorageAccess.promptIfNeeded(); } } catch (e) {} return; } if (typeof socket != 'undefined' && socket) socketStatus(socket.readyState); // Если GUID отсутствует, но есть соединение с сокетом, значит был произведен выход из CRM, необходим реконнект if ((window.frames[0].guid == '' && socket && socket.readyState == 1) || window.frames[0].system_type != '1' && typeof socket != 'undefined') { // Закрываем сокет socket.close(); location.reload(); } // Вызываем сокет, только если соединение отсутствует if (socket && socket.readyState == 1) { forbidden = false; return; } // Вызываем соединение если только имеется GUID if (window.frames[0].guid != '' && window.frames[0].system_type == '10') { console.log('Пользователь авторизован в JustCRM.'); delete socket; socketConnect(); } } function socketStatus(status) { var cls = (status == 1) ? 'green-b' : 'red-b'; var txt = (status == 1) ? 'Соединение установлено' : 'Нет соединения с сервером событий!'; try { var sts = window.frames[0].document.getElementById('link-status'); sts.className = cls; sts.parentElement.attributes[0].value = txt; } catch (err) {} } // Операции с Websocket - Конец // Отправка уведомлений function noty(content) { var f = window.frames[0]; // Cобытия PBX if (content.eventPbx) { switch (content.eventPbx) { case 'IncomingCall': if (content.clientId) { f.notifyMe('Входящий вызов!', 'Клиент: ' + content.clientName + '.\nНомер: ' + content.clientNumber, '{"success" : "true", "clientId":"' + content.clientId + '", "clientName":"' + content.clientName + '", "clientNumber":"' + content.clientNumber + '", "clientType":"' + content.clientType + '", "lidasType":"' + content.lidasType + '"}'); } else { f.notifyMe('Входящий вызов!', 'Клиент не идентифицирован.\nНомер: ' + content.clientNumber, '{"success" : "true", "clientId":"new", "clientNumber":"' + content.clientNumber + '"}'); } break; case 'BlindTransfer': case 'AttendedTransfer': if (content.clientId) { f.notifyMe('Переадресация вызова от: ' + content.userNumber, 'Клиент: ' + content.clientName + '.\nНомер: ' + content.clientNumber, '{"success" : "true", "clientId":"' + content.clientId + '", "clientName":"' + content.clientName + '", "clientNumber":"' + content.clientNumber + '", "clientType":"' + content.clientType + '", "lidasType":"' + content.lidasType + '"}'); } else { f.notifyMe('Переадресация вызова от: ' + content.userNumber, 'Клиент не идентифицирован.\nНомер: ' + content.clientNumber, '{"success" : "true", "clientId":"new", "clientNumber":"' + content.clientNumber + '"}'); } break; case 'DroppedCall': if (content.clientId) { f.notifyMe('Пропущенный вызов!', 'Клиент: ' + content.clientName + '.\nНомер: ' + content.clientNumber, '{"command" : "dropped_call", "clientName":"' + content.clientName + '", "clientNumber":"' + content.clientNumber + '", "clientType":"' + content.clientType + '", "lidasType":"' + content.lidasType + '"}'); } else { f.notifyMe('Пропущенный вызов!', 'Номер: ' + content.clientNumber, '{"command" : "dropped_call", "clientNumber":"' + content.clientNumber + '"}'); } break; } } // События CRM if (content.command) { switch (content.command) { case 'new_task': f.notifyMe('Для Вас новая задача!', 'Автор: ' + content.client_name + '\nНажмите, чтобы перейти к документу.', '{"command" : "new_task", "task_url":"' + content.task_url + '"}'); break; case 'new_task_api': f.notifyMe(content.subject, 'Нажмите, чтобы перейти к документу.', '{"command" : "new_task_api"}'); break; case 'new_eventslist_api': f.notifyMe(content.subject, content.text, '{"command" : "new_eventslist_api"}'); break; } } }