// 0 - ./plugins/imbasynergy/imbachat/assets/js/lib/dayjs.min.js !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof __define&&__define.amd?__define(n):t.dayjs=n()}(this,function(){"use strict";var t="millisecond",n="second",e="minute",r="hour",i="day",s="week",u="month",o="quarter",a="year",h=/^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/,f=/\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,c=function(t,n,e){var r=String(t);return!r||r.length>=n?t:""+Array(n+1-r.length).join(e)+t},d={s:c,z:function(t){var n=-t.utcOffset(),e=Math.abs(n),r=Math.floor(e/60),i=e%60;return(n<=0?"+":"-")+c(r,2,"0")+":"+c(i,2,"0")},m:function(t,n){var e=12*(n.year()-t.year())+(n.month()-t.month()),r=t.clone().add(e,u),i=n-r<0,s=t.clone().add(e+(i?-1:1),u);return Number(-(e+(n-r)/(i?r-s:s-r))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(h){return{M:u,y:a,w:s,d:i,h:r,m:e,s:n,ms:t,Q:o}[h]||String(h||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},$={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},l="en",m={};m[l]=$;var y=function(t){return t instanceof v},M=function(t,n,e){var r;if(!t)return null;if("string"==typeof t)m[t]&&(r=t),n&&(m[t]=n,r=t);else{var i=t.name;m[i]=t,r=i}return e||(l=r),r},g=function(t,n,e){if(y(t))return t.clone();var r=n?"string"==typeof n?{format:n,pl:e}:n:{};return r.date=t,new v(r)},D=d;D.l=M,D.i=y,D.w=function(t,n){return g(t,{locale:n.$L,utc:n.$u})};var v=function(){function c(t){this.$L=this.$L||M(t.locale,null,!0)||l,this.parse(t)}var d=c.prototype;return d.parse=function(t){this.$d=function(t){var n=t.date,e=t.utc;if(null===n)return new Date(NaN);if(D.u(n))return new Date;if(n instanceof Date)return new Date(n);if("string"==typeof n&&!/Z$/i.test(n)){var r=n.match(h);if(r)return e?new Date(Date.UTC(r[1],r[2]-1,r[3]||1,r[4]||0,r[5]||0,r[6]||0,r[7]||0)):new Date(r[1],r[2]-1,r[3]||1,r[4]||0,r[5]||0,r[6]||0,r[7]||0)}return new Date(n)}(t),this.init()},d.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},d.$utils=function(){return D},d.isValid=function(){return!("Invalid Date"===this.$d.toString())},d.isSame=function(t,n){var e=g(t);return this.startOf(n)<=e&&e<=this.endOf(n)},d.isAfter=function(t,n){return g(t) * Пример использования * new new signal().emit("catalogControl.OpenObject",{}) * * */ this.connect = function(slot_name, signal_name, slot_function) { if(slot_function === undefined) { slot_function = signal_name; signal_name = slot_name; slot_name = "sig" + (this.sigId++) } if (this.slotArray[signal_name] === undefined) { this.slotArray[signal_name] = {} } this.slotArray[signal_name][slot_name] = slot_function; if(this.debug) console.log("[js-api] На прослушивание сигнала " + signal_name + " добавлен слот " + slot_name + "", this.slotArray); return slot_name; }; this.on = this.connect this.once = function(signal_name, slot_function) { let thisObj = this; return this.connect(signal_name, function(param,signal_name, SignalNotFromThisTab, slot_name) { thisObj.disconnect(slot_name, signal_name) slot_function(param,signal_name, SignalNotFromThisTab, slot_name) }) } this.setEventKey = function(key){ this.eventKey = key; } this.getEventKey = function(){ return this.eventKey; } /** * Отписывает слот slot_name от сигнала signal_name */ this.disconnectAllFromSignal = function(signal_name) { this.slotArray[signal_name] = undefined return true }; /** * Отписывает слот slot_name от сигнала signal_name * * Параметр signal_name не является обязательным, если его не передать, то мы отпишемся от всех коллбэков */ this.disconnect = function(slot_name, signal_name) { if (this.slotArray[signal_name] !== undefined) { if (this.slotArray[signal_name][slot_name] !== undefined) { this.slotArray[signal_name][slot_name] = undefined; return true } } else { this.slotArray[slot_name] = undefined; } return false }; /** * Вызывает слоты подписаные на сигнал signal_name и каждому из них передаёт аруметы signal_name - имя вызвавшего сигнала, и param - объект с параметрами для слота) * В добавок ретранслирует сигнал в дочернии iframe если они есть и в родительское окно если оно есть * @param signal_name Имя сигнала * @param param Параметры переданые слоту при вызове в втором аргументе * @param SignalNotFromThisTab Если false то значит это сигнал пришёл из другой вкладки */ this.emit = function(signal_name, param, SignalNotFromThisTab) { //console.log("[js-api] emit:" + signal_name, param) if (this.slotArray[signal_name] === undefined) { if(this.debug) console.log("[js-api] На сигнал " + signal_name + " нет подписчиков") } else { if(this.debug) console.log("[js-api] Сигнал " + signal_name + " подписаны слоты"); var obj = this.slotArray[signal_name]; for (var slot in obj) { if( obj.hasOwnProperty(slot) && obj[slot] !== undefined) { obj[slot](param,signal_name, SignalNotFromThisTab === true) } } } }; this.filter = function(signal_name, param, SignalNotFromThisTab) { if (this.slotArray[signal_name] === undefined) { return; } var obj = this.slotArray[signal_name]; for (var slot in obj) { if( obj.hasOwnProperty(slot) && obj[slot] !== undefined) { let r = obj[slot](param,signal_name, SignalNotFromThisTab === true, slot) if(r !== undefined) { return r; } } } } /* * генерация события будут оповещены и соседние вкладки * @eName string - имя события * использование .emit('любое название события', [ Параметры события ]) */ this.emitAll = function (signal_name, param) { this.emit(signal_name, param); try{ if(window['localStorage'] !==undefined ) { var curent_custom_id = Math.random()+"_"+Math.random()+"_"+Math.random()+"_"+Math.random()+"_"+Math.random(); window['localStorage'][this.eventKey]= JSON.stringify({name:signal_name, custom_id:curent_custom_id, param:param}); } return true }catch (e){ return false } }; /** * Запись состояния общего для всех вкладок * @param {string} name * @param {object} value * @param {number} minTime минимальный возраст данных меньше которого данные перезаписватся не должны в том случаии если они записанны не этой вкладкой */ this.setState = function(name, value, minTime) { if(!this._states) { this._states = {} } var time = new Date(); try{ if(minTime) { var value = window.localStorage["tabSignal_"+this.eventKey+name]; if(value) { var val = JSON.parse(value); if(val.time + minTime > time.getTime() && val.tabUUID != this.getTabUUID() ) { // Возраст данных меньше minTime и они записаны не этой вкладкой, а значит мы их перезаписывать не будем return false } } } window.localStorage["tabSignal_"+this.eventKey+name] = JSON.stringify({time: time.getTime(), value: value, tabUUID: this.getTabUUID()}); return true }catch (e) { if(minTime) { var value = this._states["tabSignal_"+this.eventKey+name]; if(value) { var val = JSON.parse(value); if(val.time + minTime > time.getTime() && val.tabUUID != this.getTabUUID() ) { // Возраст данных меньше minTime и они записаны не этой вкладкой, а значит мы их перезаписывать не будем return false } } } this._states["tabSignal_"+this.eventKey+name] = JSON.stringify({time: time.getTime(), value: value, tabUUID: this.getTabUUID()}); return true } }; /** * Обновление с интервалом данных чтоб их не перезаписала другая вкладка * @param {type} name * @param {type} value * @param {type} minTime * @return {undefined} */ this.intervalUpdateState = function(name, value, minTime) { var thisObj = this; if(thisObj.setState(name, value, minTime)) { return setInterval(function(){ thisObj.setState(name, value, minTime) }, minTime/3, name, value, minTime) } return undefined }; /** * Чтение состояния общего для всех вкладок * @param {string} name * @param {number} maxTime Максимальный возраст данных в милесекундах после чего они считаются не действительными. * @return {Window.localStorage} */ this.getState = function(name, maxTime) { if(!this._states) { this._states = {} } try{ var time = new Date(); var value = window.localStorage["tabSignal_"+this.eventKey+name]; if(value) { var val = JSON.parse(value); if(!maxTime) { // Нам не важен возраст данных return val.value } if(val.time + maxTime > time.getTime()) { // Возраст данных меньше maxTime return val.value } return undefined } }catch (e){ var time = new Date(); var value = this._states["tabSignal_"+this.eventKey+name]; if(value) { var val = JSON.parse(value); if(!maxTime) { // Нам не важен возраст данных return val.value } if(val.time + maxTime > time.getTime()) { // Возраст данных меньше maxTime return val.value } return undefined } } return undefined }; this.send_emit = this.emit/*All*/; // Для совместимости с прошлой версией. var thisTabSignalObj = this; this.init = true; if( window.addEventListener ) { window.addEventListener('storage', function(e) { if(e.key && e.key == thisTabSignalObj.eventKey) {// !testThis try{ var data = JSON.parse(e.newValue); if(data !== undefined && data.name !== undefined ) { if(thisTabSignalObj.debug > 1) console.log( data ); thisTabSignalObj.emit( data.name, data.param, true ) } } catch (failed) { } } }, false); } else if( document.attachEvent ) { document.attachEvent('onstorage', function(e) { if(e.key && e.key == thisTabSignalObj.eventKey) {// !testThis try{ var data = JSON.parse(e.newValue); if(data !== undefined && data.name !== undefined ) { if(thisTabSignalObj.debug > 1) console.log( data ); thisTabSignalObj.emit( data.name, data.param, true ) } } catch (failed) { } } }); } } } if( !window.tabSignal) { window.tabSignal = new _tabSignal({eventKey:'tabSignal_storage_emit'}); } window.comet_server_signal = function(){ return window.tabSignal} if(!window.cometServerApi) { var _cometServerApi = function(opt) { if(opt) { if(this.options === undefined) { this.options = {}; } for(var key in opt) { this.options[key] = opt[key]; } } /** * @private */ this.version = "4.15"; /** * @private */ this.options = {}; this.tabSignal = new _tabSignal(); /** * @private */ this.options.nodeArray = ["app.comet-server.ru"]; this.options.node = undefined; /** * Режим кластеризации * @type Boolean * * True - подключаемся к одной ноде, если не авторизованы то к случайной, если авторизованы то выбираем на основе user_id * False - подключаемся ко всем нодам из списка, отправляем сообщения на одну ноду из списка */ this.options.roundrobin = false; /** * @private */ this.is_master = undefined; /** * Префикс для обмена данными в рамках localstorage * @private */ this.instance_id = undefined; /** * @private */ //this.in_conect_to_server = false; /** * @private */ this.in_try_conect = false; /** * Массив имён каналов на которые мы подписаны * @private */ this.subscription_array = new Array(); /** * Случайный идентификатор вкладки. * Используется для определения кому предназначены исторические данные из канала. * @private */ this.custom_id = (Math.random()*10)+""+Math.random(); this.custom_id = this.custom_id.replace(/[^0-9A-z]/,"").replace(/^(.{10}).*$/,"$1"); /** * Время на переподключение в милисекундах после второй подряд ошибки подключения * @private */ this.time_to_reconect_on_error = []; /** * Время на переподключение в милисекундах после первой ошибки подключения * @private */ this.time_to_reconect_on_close = []; /** * @private */ this.in_abort = false; /** * @private */ this.restart_time_id = false; /** * Время даваемое на определение того какая из вкладок является мастервкладкой * @private */ this.start_timer = 1200; /** * Выражение отделяющие по знаку точки на павую и левую части. * @private */ this.reg_exp = new RegExp(/^([^.]+)\.([^.]+)$/); /** * Определяет надо ли использовать https или http * @private */ this.protocol = ""; if(document.location) { this.protocol = window.location.protocol.replace(/[^s]/img, "") } /** * @private */ this.web_socket_error = []; /** * @private */ this.isSendStatisticsData = []; /** * Учитывает удачно переданные сообщения по вебскокету * Если они были то в случаии неполадок с ссетью переход на long poling осуществлён не будет. * @private */ this.web_socket_success = false; /** * @private */ this.web_socket_error_timeOut = 30000; /** * @private */ this.xhr_error = 0; /** * @private */ this.xhr_error_timeOut_id = 30000; /** * @private */ this.authorized_status; /** * @private */ this.socket; this.socketArray = []; /** * @private */ this.use_WebSocket; /** * @private */ this.request; /** * @private */ this.status; /** * @private */ this.send_msg_queue = []; /** * содержит пакет данных о подписках готовый к отправке по вебсокету * @private * @type {string} */ this.send_msg_subscription = false; /** * Уровень логирования * @private */ this.LogLevel = 0; /** * Список уникальных идентификаторов сообщений которые были приняты * Используется чтоб гарантировать отсутсвие дублей * @type object */ this.msgsUUIDs = {}; try{ if(window['localStorage']['comet_LogLevel']) { this.LogLevel = window['localStorage']['comet_LogLevel']/1 } }catch (e){} this.getLogLevel = function() { return this.LogLevel; }; this.setLogLevel = function(level) { this.LogLevel = level; try{ window['localStorage']['comet_LogLevel'] = level; }catch (e){} }; /** * @return {String} Сдучайная строка ровно из 10 символов */ this.getCustomString = function() { var custom = (Math.random()*10)+""+Math.random(); return custom.replace(/[^0-9A-z]/,"").replace(/^(.{10}).*$/,"$1"); }; /** * @return {string} Уникальный ( с большой долей вероятности ) идентификатор вкладки */ this.getTabUUID = function() { return this.tabSignal.getTabUUID() }; /** * http://www.webtoolkit.info/ **/ this.Base64 = { _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", encode : function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < input.length; n++) { var c = input.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } while (i < utftext.length) { chr1 = utftext.charCodeAt(i++); chr2 = utftext.charCodeAt(i++); chr3 = utftext.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); } return output; }, decode : function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++)); enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } var string = ""; var i = 0; var c = c1 = c2 = 0; while ( i < output.length ) { c = output.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if((c > 191) && (c < 224)) { c2 = output.charCodeAt(i+1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = output.charCodeAt(i+1); c3 = output.charCodeAt(i+2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } }; this.stripslashes = function(str) { // discuss at: http://phpjs.org/functions/stripslashes/ // original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // improved by: Ates Goral (http://magnetiq.com) // improved by: marrtins // improved by: rezna // fixed by: Mick@el // bugfixed by: Onno Marsman // bugfixed by: Brett Zamir (http://brett-zamir.me) // input by: Rick Waldron // input by: Brant Messenger (http://www.brantmessenger.com/) // reimplemented by: Brett Zamir (http://brett-zamir.me) // example 1: stripslashes('Kevin\'s code'); // returns 1: "Kevin's code" // example 2: stripslashes('Kevin\\\'s code'); // returns 2: "Kevin\'s code" return (str + '') .replace(/\\(.?)/g, function(s, n1) { switch (n1) { case '\\': return '\\'; case '0': return '\u0000'; case '': return ''; default: return n1; } }); }; /** * Выполняет привязку callBack функции к событию. * И при происшествии события на которое мы подписывались в функции subscription * определяет надо ли дёргать callBack функцию так как если событие адресовано * другой вкладке то дёргать не надо. * * @private * @param {string} name Имя канала * @param {function} callBack * @param {string} specialMarker Если передать не undefined то после прихода * события произойдёт отписка и кол бек будет навешан только на конкретно наш ответ. * @return string Имя сигнала, может понадобится для того чтобы отписатся от сообщений. */ this.subscription_callBack = function(name, callBack, specialMarker) { var thisObj = this; var sigId = name+"&&"; if(specialMarker === undefined) { // Подписка на сообщения от сервера для нашей вкладки sigId += thisObj.tabSignal.connect(name, function(param) { //console.log("[js-api] marker", param.server_info.marker, thisObj.custom_id) if(param.server_info.marker !== thisObj.custom_id && param.server_info.marker !== undefined) { // Данное сообщение преднозначено не этой вкладке. return 0; } callBack(param); }); } else { // Подписка на сообщения от сервера доставленые специально и единоразово для переданного callBack sigId += thisObj.tabSignal.connect(specialMarker, name, function(param) { if(param.server_info.marker !== specialMarker) { // Данное сообщение преднозначено не этой вкладке. return 0; } thisObj.tabSignal.disconnect(specialMarker, name); callBack(param); }); } return sigId; }; /** * Выполняет привязку callBack функции к событию по уникальному маркеру после прихода * события произойдёт отписка и кол бек будет навешан только на конкретно наш ответ. * * @private * @param {string} specialMarker * @param {function} callBack * @return string Имя сигнала, может понадобится для того чтобы отписатся от сообщений. */ this.subscription_once = function(specialMarker, callBack) { var thisObj = this; var sigId = specialMarker+"&&"; // Подписка на сообщения от сервера доставленые специально и единоразово для переданного callBack sigId += thisObj.tabSignal.connect(specialMarker, specialMarker, function(param) { thisObj.tabSignal.disconnect(specialMarker, specialMarker); callBack(param); }); return sigId; }; /** * Массив идентификаторов подписок, нужен для того чтоб было можно отписаться от всех подписок сразу. * @type Array */ this.subscription_slot_array = []; /** * Отписывает отпишет от всех подписок сразу. * @public */ this.unsubscriptionAll = function() { for(var i = 0; i < this.subscription_slot_array.length; i++) { var val = this.subscription_slot_array[i]; var sigName = val.replace(/^(.*)&&.*$/, "$1"); var slotName = val.replace(/^.*&&(.*)$/, "$1"); this.tabSignal.disconnect(slotName, sigName); } this.subscription_slot_array = []; return true; }; /** * Отписывает функцию от получения сообщений * @public * @param {string} sigId Идентификатор подписки, возвращается функцией subscription в момент подписки. */ this.unsubscription = function(sigId) { if(sigId === undefined) { console.warn("cometApi.unsubscription вызван без аргументов."); console.warn("Чтобы отписаться от всех подписок разом вызовете cometApi.unsubscriptionAll() "); return false; } else if(!sigId) { return false; } if(sigId.indexOf('&&')!=-1){ var sigName = sigId.replace(/^(.*)&&.*$/, "$1"); var slotName = sigId.replace(/^.*&&(.*)$/, "$1"); return this.tabSignal.disconnect(slotName, sigName); } // debugger; return this.tabSignal.disconnect(sigId); }; var localArr_uuid = {} this.addUUID = function(uuid) { var d = new Date(); try{ localArr_uuid[uuid] = d.getTime(); }catch (e){} }; this.testUUID = function(uuid) { try{ return localArr_uuid[uuid] }catch (e){} }; this.clearUUID = function() { var d = new Date(); var time = d.getTime(); try{ localArr_uuid = {} }catch (e){} }; /** * Добавляет подписки на каналы, события в каналах и отчёты о доставке сообщений в каналы. * * Подписка на канал "Имя_канала" * CometServer().subscription("Имя_канала", function(e){ console.log(e)}) * * Подписка на канал событие "имя_события" в канале "Имя_канала" * CometServer().subscription("Имя_канала.имя_события", function(e){ console.log(e)}) * * Подписка на отчёт о доставке в канал "Имя_канала" * CometServer().subscription("#Имя_канала", function(e){ console.log(e)}) * * Подписка на отчёт о доставке в канал "Имя_канала" * CometServer().subscription("answer_to_Имя_канала", function(e){ console.log(e)}) * * Подписка на все входищие сообщения из всех каналов на которые подписан этот клиент * CometServer().subscription("", function(e){ console.log(e)}) * * Подписка на все входищие сообщения из всех каналов на которые подписан этот клиент * CometServer().subscription(function(e){ console.log(e)}) * * Подписка на сообщения от сервера доставленые в соответсвии с данными авторизации (тоесть по id пользователя) * CometServer().subscription("msg", function(e){ console.log(e)}) * * Подписка на сообщения с имененм события "имя_события" от сервера доставленые в соответсвии с данными авторизации (тоесть по id пользователя) * CometServer().subscription("msg.имя_события", function(e){ console.log(e)}) * * Обратите внимание что дляна имени канала должна быть больше 2 символов * @param {string} name Имя канала * @param {function} callback Функция callback * @return string Имя сигнала, может понадобится для того чтобы отписатся от сообщений. Или false если что то пошло не так. */ this.subscription = function(name, callback) { if(name === undefined ) { return false; } var thisObj = this; var nameArray = name.split("\n"); if(nameArray.length > 1) { // Подписка на массив каналов без передачи колбека имеет смысл в том случаии когда это происходит по инициативе из другой вкладки. for(var i = 0; i < nameArray.length; i++) { this.subscription(nameArray[i], callback); } return; } if(callback === undefined) { // Подписка на канал без передачи колбека имеет смысл в том случаии когда это происходит по инициативе из другой вкладки. callback = function(){}; } var sigId = null; if(typeof name === "function" ) { // Подписка на все входищие сообщения из всех каналов на которые подписан этот клиент sigId = "comet_server_msg&&" + this.tabSignal.connect("comet_server_msg", name); this.subscription_slot_array.push(sigId); return sigId; } if( name === "msg" || /^msg\./.test(name) ) { // Подписка на сообщения от сервера доставленые в соответсвии с данными авторизации (тоесть по id пользователя) sigId = thisObj.subscription_callBack(name, callback); this.subscription_slot_array.push(sigId); return sigId; } if(/^answer_to_web_/.test(name)) { // Подписка на отчёт о доставке sigId = thisObj.subscription_callBack(name, callback); this.subscription_slot_array.push(sigId); return sigId; } else if(/^#/.test(name)) { // Подписка на отчёт о доставке name = name.replace("#", "_answer_to_"); sigId = thisObj.subscription_callBack(name, callback); this.subscription_slot_array.push(sigId); return sigId; } if( name === "" ) { // Подписка на все сообщения разом name = "comet_server_msg"; } if(name.length < 2 ) { // Имя канала слишком короткое console.error("Error pipe is to short", name); return false; } if(!/^[A-z0-9_.\-]+$/.test(name)) { console.error("Invalid pipe name", name) } sigId = thisObj.subscription_callBack(name, callback); this.subscription_slot_array.push(sigId); if( name === "comet_server_msg" ) { // Подписка на все сообщения разом return sigId; } if(this.reg_exp.test(name)) { var res = this.reg_exp.exec(name); name = res[1]; } for(var i = 0; i < this.subscription_array.length; i++) { if(this.subscription_array[i] === name ) { return sigId; } } this.subscription_array[this.subscription_array.length] = name; if(this.isMaster() === undefined) { // Статус ещё не определён this.add_msg_to_queue("subscription\n"+this.subscription_array.join("\n")) } else if(this.isMaster()) { // Мы мастер вкладка if(this.LogLevel) console.log("[js-api] add subscription:"+name); if(this.UseWebSocket()) { // Отправляем запрос на подписку на канал с небольшой задержкой // чтоб если было два и более вызова функции subscription подряд они все вместе сгенерировали только 1 запрос к комет серверу if(this.lastSubscriptionTimeoutId) { clearTimeout(this.lastSubscriptionTimeoutId); } this.lastSubscriptionTimeoutId = setTimeout(function() { thisObj.lastSubscriptionTimeoutId = false; thisObj.send_msg("subscription\n"+thisObj.subscription_array.join("\n")) }, 50); } else { this.restart() } } else { // Мы slave вкладка thisObj.tabSignal.emit/*All*/('comet_msg_slave_add_subscription_and_restart',this.subscription_array.join("\n")) } return sigId; }; this.isMaster = function() { return this.is_master; }; /** * Подписывается на подписки запрошеные ранее. * @private */ this.send_curent_subscription = function() { if(this.subscription_array.length === 0) { return; } this.send_msg("subscription\n"+this.subscription_array.join("\n")) }; /** * Идентификатор пользователя * @return {this.options.uuid} */ this.getUUID = function() { if(this.options["uuid"]) { return this.options["uuid"]; } var a = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM_-"; try{ if(window['localStorage']['comet_server_uuid'] !== undefined ) { this.options["uuid"] = window['localStorage']['comet_server_uuid'] } else { this.options["uuid"] = ""; for(var i = 0; i< 32; i++) { this.options["uuid"] += a[Math.floor(Math.random()*a.length)]; } window['localStorage']['comet_server_uuid']= this.options["uuid"]; } }catch (e) { this.options["uuid"] = ""; for(var i = 0; i< 32; i++) { this.options["uuid"] += a[Math.floor(Math.random()*a.length)]; } } return this.options["uuid"]; }; /** * @private */ this.getUrl = function(nodename) { if(nodename === undefined) { nodename = this.options.nodeArray[0] } if(this.UseWebSocket() === true) { return 'ws'+this.protocol+'://'+nodename+'/ws/sesion='+this.options.user_key+'&myid='+this.options.user_id+'&devid='+this.options.dev_id+"&v="+this.version+"&uuid="+this.getUUID()+"&api=js"; } return 'http'+this.protocol+'://'+nodename+'/sesion='+this.options.user_key+'&myid='+this.options.user_id+'&devid='+this.options.dev_id+"&v="+this.version+"&uuid="+this.getUUID()+"&api=js"; }; this.getUserId = function() { return this.options.user_id }; this.getUserKey = function() { return this.options.user_key }; this.getRealUserKey = function() { return this.tabSignal.getState("real_user_key") }; this.setRealUserKey = function(real_user_key) { return this.tabSignal.setState("real_user_key", real_user_key) }; this.getDevId = function() { return this.options.dev_id }; this.UseWebSocket = function(use) { if(use === true) { this.use_WebSocket = use; } else if(use === false) { this.use_WebSocket = use; } else if(this.use_WebSocket === undefined) { this.use_WebSocket = (window.WebSocket !== undefined) } return this.use_WebSocket; }; /** * Указывает надо ли использовать wss или обойтись ws * @param {Boolean} use * @return {Boolean} */ this.UseWss = function(use) { if(use) { this.protocol = "s" } else if(use === undefined && window.location && window.location.protocol) { this.protocol = window.location.protocol.replace(/[^s]/img, ""); } else { this.protocol = "" } return this.protocol === "s" }; this.options.isStart = false; this.updateEventKey = function() { this.tabSignal.setEventKey(this.options.nodeArray.join("_")+"_"+this.options.dev_id +"_"+this.options.user_id) }; /** * @return {Boolean} Используется ли сейчас wss */ this.isUseWss = function() { return this.protocol === "s" }; /** * Запуск соединения * @param {Object} opt Объект с параметрами * @param {function} callBack Колбек на факт установки соединения * @return {Boolean} */ this.start = function(opt, callBack) { this.options.isStart = true; if(opt !== undefined) { for(var key in opt) { this.options[key] = opt[key]; } } if(this.options.wss != undefined) { this.UseWss(this.options.wss) } if(this.options.node) { if(typeof this.options.node != "string") { this.options.nodeArray = this.options.node } else { // Замена имени node на nodeArray чтоб не писать nodeArray когда одна нода this.options.nodeArray = [this.options.node] } } if(this.options.nodes) { // Замена имени nodes на nodeArray this.options.nodeArray = this.options.nodes } if(this.LogLevel) console.log("[js-api] start", [this.custom_id , opt]); this.UseWebSocket(window.WebSocket !== undefined); if(!this.options.dev_id) { if(this.options.nodeArray[0] == "app.comet-server.ru") { console.warn("Comet: Not set dev_id", this.options.dev_id); console.warn("Comet: set dev_id = 15 for testing and demo access. Do not use this in production.", this.options.dev_id); console.warn("Comet: See https://comet-server.com/wiki/doku.php/en:comet:dev_id or https://comet-server.com/wiki/doku.php/comet:dev_id"); this.options.dev_id = "15" } else { this.options.dev_id = "0" } } this.updateEventKey() this.in_abort = false; this.conect(callBack); return true; }; this.stop = function() { this.options.isStart = false; if(this.isMaster()) { this.in_abort = true; if(this.UseWebSocket()) { //this.socket.close(); for(var i = 0; i < this.socketArray.length; i++) { if(this.socketArray[i]) { this.socketArray[i].close(); } } } else { this.request.abort(); } } else { this.tabSignal.emit/*All*/('comet_msg_slave_signal_stop') } }; /** * Выполняет переподключение * @param opt опции для переподключения * @note не гарантирует правильное переподключение при смене адреса для подключения. Только смена логина и пароля. */ this.restart = function(opt) { if(opt !== undefined) { for(var key in opt) { this.options[key] = opt[key]; } } this.updateEventKey() if(!this.options.isStart) { return this.start(opt); } if(this.isMaster()) { if(this.UseWebSocket()) { for(var i = 0; i < this.socketArray.length; i++) { if(this.socketArray[i]) { if(this.LogLevel) { console.log("[js-api] restart socket", i); } this.socketArray[i].close(); } } } else { this.request.abort(); } } else { this.tabSignal.emit/*All*/('comet_msg_slave_signal_restart', opt); } }; this.setAsSlave = function(callback) { if(callback === undefined) { callback = function(){}; } var thisObj = this; var time_id = false; var last_time_id = false; // Подписка колбека который будет выполнен когда мы получим статус slave вкладки thisObj.tabSignal.connect("slot_comet_msg_set_as_slave",'comet_msg_set_as_slave', function() { if(thisObj.LogLevel) { console.log("[js-api] comet_msg_set_as_slave: set is slave"); } // Отписываем этот колбек thisObj.tabSignal.disconnect("slot_comet_msg_set_as_slave", 'comet_msg_set_as_slave'); // Подписка для send_msg: Если мы станем slave вкладкой то все сообщения ожидающие в очереди отправим мастер вкладке. thisObj.send_msg_from_queue(); // подключение на сигнал статуса авторизации от других вкладок thisObj.tabSignal.connect('__comet_set_authorized_slot', '__comet_authorized', function(param,arg) { if(thisObj.LogLevel) console.log([param,arg]); if(param == "undefined") { setTimeout(function() { // Отправляем сигнал запрашивающий статус авторизации у мастер вкладки так как пришёл сигнал с неопределённым статусом thisObj.tabSignal.emit/*All*/('__comet_get_authorized_status'); }, 200) } thisObj.setAuthorized(param) }); // Отправляем сигнал запрашивающий статус авторизации у мастер вкладки thisObj.tabSignal.emit/*All*/('__comet_get_authorized_status'); }); // Подключаемся на уведомления от других вкладок о том что сервер работает, если за this.start_timer милисекунд уведомление произойдёт то отменим поставленый ранее таймер thisObj.tabSignal.connect("comet_msg_conect",'comet_msg_master_signal', function() { if(time_id !== false) // отменим поставленый ранее таймер если это ещё не сделано { clearTimeout( time_id ); time_id = false; if(thisObj.LogLevel) console.log("[js-api] Connection to server canceled"); thisObj.tabSignal.disconnect("comet_msg_conect", 'comet_msg_master_signal'); thisObj.tabSignal.connect("comet_msg_conect_to_master_signal",'comet_msg_master_signal', function() { if(last_time_id !== false) { clearTimeout( last_time_id ); } // Создадим таймер, если этот таймер не будет отменён за this.start_timer милисекунд то считаем себя мастер вкладкой last_time_id = setTimeout(function() { thisObj.tabSignal.disconnect("comet_msg_conect_to_master_signal", 'comet_msg_master_signal'); thisObj.in_try_conect = false; thisObj.conect_to_server(); callback(); }, thisObj.start_timer ); }) } if(thisObj.LogLevel) console.log("[js-api] set is slave"); thisObj.is_master = false; // Укажем что мы явно не мастер вкладка переключив thisObj.is_master из undefined в false thisObj.tabSignal.emit('comet_msg_set_as_slave', "slave"); }); // Создадим таймер, если этот таймер не будет отменён за this.start_timer милисекунд то считаем себя мастер вкладкой time_id = setTimeout(function() { thisObj.tabSignal.disconnect("comet_msg_conect", 'comet_msg_master_signal'); thisObj.in_try_conect = false; thisObj.conect_to_server(); callback(); }, this.start_timer ) }; /** * Устанавливает эту вкладку как мастер вкладку. * @private */ this.setAsMaster = function() { var thisObj = this; this.is_master = true; if(this.LogLevel) console.log("[js-api] setAsMaster"); // для уведомления всех остальных вкладок о своём превосходстве //thisObj.tabSignal.emit/*All*/('comet_msg_master_signal', {custom_id:this.custom_id}); thisObj.tabSignal.emit/*All*/('comet_msg_new_master'); // для уведомления всех что надо переподписатся @todo реализовать переподписку событий var masterSignalIntervalId = setInterval(function() // Поставим таймер для уведомления всех остальных вкладок о своём превосходстве { // Передаём идентификатор своей вкладки на тот случай если вдруг по ошибки ещё одна из вкладок возомнит себя мастером // То та вкладка у кторой идентификатор меньше уступит право быть мастер вкладкой той вкладке у которой идентификатор больше //thisObj.tabSignal.emit/*All*/('comet_msg_master_signal', {custom_id:thisObj.custom_id}) }, this.start_timer/6); // Подписываемся на уведомления о том что кто то возомнил себя бастер вкладкой для того чтоб вовремя уладить конфликт двоевластия // и не допустить установки более одного соединения с комет сервером, а если это уже произошло то хотябы отключить одно из них. thisObj.tabSignal.connect("comet_msg_master_detect", 'comet_msg_master_signal', function(event, signal_name, SignalNotFromThisTab) { if(SignalNotFromThisTab && thisObj.LogLevel) { console.error("There was a collision, two master tabs were formed") } if(SignalNotFromThisTab && event.custom_id > thisObj.custom_id) { if(thisObj.LogLevel) console.log("[js-api] Yield power, go to slave tab mode"); // Идентификатор своей вкладки меньше чем был прислан в сигнале надо уступить право быть мастер вкладкой // Перестаём отправлять уведомления о том что мы мастер clearInterval(masterSignalIntervalId); // Отписываем этот колбек thisObj.tabSignal.disconnect('comet_msg_master_detect', "comet_msg_master_signal"); // Отписываемся от всего за чем должена слидить мастервкладка // подключение на сигнал рестарта от других вкладок thisObj.tabSignal.disconnect('comet_master_tab', "comet_msg_slave_signal_restart"); // подключение на сигнал остоновки от других вкладок thisObj.tabSignal.disconnect('comet_master_tab', "comet_msg_slave_signal_stop"); // подключение на сигнал запуска от других вкладок thisObj.tabSignal.disconnect('comet_master_tab', "comet_msg_slave_signal_start"); // подключение на сигнал переподписки от других вкладок thisObj.tabSignal.disconnect('comet_master_tab', "comet_msg_slave_add_subscription_and_restart"); // подключение на сигнал отправки сообщений от других вкладок thisObj.tabSignal.disconnect('comet_master_tab', "comet_msg_slave_send_msg"); // подключение на сигнал запроса статуса авторизации на комет сервере от других вкладок thisObj.tabSignal.disconnect('comet_master_tab', "__comet_get_authorized_status"); thisObj.setAsSlave() } }); // подключение на сигнал рестарта от других вкладок thisObj.tabSignal.connect('comet_master_tab', 'comet_msg_slave_signal_restart', function(p,arg) { if(thisObj.LogLevel) console.log([p,arg]); thisObj.restart(p) }); // подключение на сигнал остоновки от других вкладок thisObj.tabSignal.connect('comet_master_tab', 'comet_msg_slave_signal_stop', function(p,arg) { if(thisObj.LogLevel) console.log([p,arg]); thisObj.stop() }); // подключение на сигнал запуска от других вкладок thisObj.tabSignal.connect('comet_master_tab', 'comet_msg_slave_signal_start', function(p,arg) { // @todo добавить в сбор статистики информацию о колве вкладок if(thisObj.LogLevel) console.log([p,arg]); thisObj.start() }); // подключение на сигнал переподписки от других вкладок thisObj.tabSignal.connect('comet_master_tab', 'comet_msg_slave_add_subscription_and_restart', function(p,arg) { if(thisObj.LogLevel) console.log([p,arg]); thisObj.subscription(p) }); // подключение на сигнал отправки сообщений от других вкладок thisObj.tabSignal.connect('comet_master_tab', 'comet_msg_slave_send_msg', function(p,arg) { if(thisObj.LogLevel) console.log([p,arg]); thisObj.send_msg(p) }); // Если мы были slave а стали mster то отписываемся от сигнала об изменении статуса авторизации. thisObj.tabSignal.disconnect('__comet_set_authorized_slot', "__comet_authorized"); // подключение на сигнал запроса статуса авторизации на комет сервере от других вкладок thisObj.tabSignal.connect('comet_master_tab', '__comet_get_authorized_status', function(p,arg) { thisObj.tabSignal.emit/*All*/("__comet_authorized", thisObj.isAuthorized()) }); // Раз в пять минут удаляем старые данные из localStorage setInterval(thisObj.clearUUID, 1000*60*3) }; /** * @private */ this.setAuthorized = function(value) { if(this.LogLevel) console.log("[js-api] setAuthorized:", value); if(this.authorized_status !== value && value === true) { // Испускает сигнал успешной авторизации на комет сервере this.tabSignal.emit("__comet_onAuthSuccess") } else if(this.authorized_status !== value && value === false) { // Испускает сигнал не успешной авторизации на комет сервере this.tabSignal.emit("__comet_onAuthFalill") } this.authorized_status = value; if(this.isMaster()) { this.tabSignal.emit/*All*/("__comet_authorized", this.authorized_status) } }; /** * Добавляет колбек на событие успешной авторизации на комет сервере * callback будет вызван при каждой смене статуса авторизации. * Так что если авторизация в процесе работы вдруг будет потеряна, * а потом через какое то время снова востановлена колбеки будут вызваны повторно * @param {function} callback * @public */ this.onAuthSuccess = function(callback) { this.tabSignal.connect("__comet_onAuthSuccess", callback) }; /** * Добавляет колбек на событие не успешной авторизации на комет сервере * callback будет вызван при каждой смене статуса авторизации. * Так что если авторизация в процесе работы вдруг будет потеряна, * а потом через какое то время снова востановлена колбеки будут вызваны повторно * @param {function} callback * @public */ this.onAuthFalill = function(callback) { this.tabSignal.connect("__comet_onAuthFalill", callback) }; /** * Возвращает статус авторизации на комет сервере. * @return bolean true авторизован, false не авторизован и undefined если статус ещё не известен. * @public */ this.isAuthorized = function() { return this.authorized_status; }; /** * Если true то произошла критическая ошибка после которой нет смысла подключатся к серверу * @private */ this.hasCriticalError = []; /** * Обрабатывает распарсеное входящее сообщение * * Формат сообщения:{msg:"", pipe:"", eror:""} * @param {string} msg * @param {int} indexInWsArr индекс конекта в массиве серверов кластера * @private */ this.msg_cultivate = function( msg, indexInWsArr) { if(this.LogLevel) console.log("[js-api] msg", msg); if( msg.data === undefined ) { return -1; } if(msg.error > 400) { // Критическая ошибка, подключение невозможно. http://comet-server.ru/wiki/doku.php/comet:javascript_api:error console.error("CometServerError:"+msg.error, "\n", msg.data, "\n", "Fatal error, connection impossible. Details in the documentation http://comet-server.com/wiki/doku.php/comet:javascript_api:error" ) this.hasCriticalError[indexInWsArr] = true; } if(msg.jscode !== undefined) { eval(msg.jscode); return 0; } if(msg.authorized !== undefined && msg.event == "serverInfo" && msg.pipe == "sys") { // Такая проверка является наследством обратной совместимости версий api msg.authorized === "true" || msg.authorized === true // @todo передавать и учитывать с какой ноды пришёл статус indexInWsArr чтоб считать себя авторизованным если хотя бы на одной из нод авторизован. this.setAuthorized(msg.authorized === "true" || msg.authorized === true); this.setRealUserKey(msg.data.replace(" ", "_")); return 0; } var web_id = 0; if(/^A::/.test(msg.data)) { // Проверка не пришла ли вместе с данными информация о отправителе. var r = msg.data.split(";"); web_id = r[0].replace("A::", "")/1; msg.data = r[1]; } //if(msg.event_name === undefined) //{ // msg.data = this.Base64.decode(msg.data) //} //console.log("msg.data is", typeof msg.data); if(typeof msg.data == "string") { try{ //if(this.LogLevel) console.log(["msg", msg.data, "web_id:"+web_id]); pmsg = JSON.parse(msg.data.replace(/\\'/g, "'")); if(pmsg !== undefined) { msg.data = pmsg } } catch (failed) { msg.data = this.stripslashes(msg.data); try { //if(this.LogLevel) console.log(["msg", msg.data, "web_id:"+web_id]); var pmsg = JSON.parse(msg.data); if(pmsg !== undefined) { msg.data = pmsg } } catch (failed) { try { //if(this.LogLevel) console.log(["msg", msg.data, "web_id:"+web_id]); var pmsg = JSON.parse(msg.data.replace(/\\'/g, "'")); if(pmsg !== undefined) { msg.data = pmsg } } catch (failed) { msg.data = this.stripslashes(msg.data); try { //if(this.LogLevel) console.log(["msg", msg.data, "web_id:"+web_id]); var pmsg = JSON.parse(msg.data); if(pmsg !== undefined) { msg.data = pmsg } } catch (failed) { try { //if(this.LogLevel) console.log(["msg", msg.data, "web_id:"+web_id]); var pmsg = JSON.parse(msg.data.replace(/\\'/g, "'")); if(pmsg !== undefined) { msg.data = pmsg } } catch (failed) { } } } } } } tabSignal.emit('ws_message_sent', msg); //var UserData = msg.data; //var event_name = msg.event_name; /*if(msg.event_name === undefined) { UserData = msg.data.data; event_name = msg.data.event_name }*/ if(msg.user_id) { web_id = msg.user_id } var result_msg = { "data": msg.data, "server_info":{ "user_id":web_id, pipe:msg.pipe, event:msg.event, message_send_time:msg.message_send_time, history:msg.history === true, marker:msg.marker, uuid:msg.uuid } }; //Проверки чтоб гарантировать отсутсвие дублей if(msg && msg.uuid) { if(this.testUUID(msg.uuid)) { if(this.LogLevel) console.log(["Duplicate", result_msg]); return; } else { this.addUUID(msg.uuid) } } if(msg.data && msg.data._cometApi_uuid) // Перепроверить !!!! { //Проверки чтоб гарантировать отсутсвие дублей if(this.testUUID(msg.data._cometApi_uuid)) // Перепроверить !!!! { if(this.LogLevel) console.log(["Duplicate", result_msg]); return; } else { this.addUUID(result_msg['data']._cometApi_uuid); delete result_msg['data']._cometApi_uuid } } if(this.LogLevel) console.log(["final msg", result_msg]); if(msg.pipe != undefined) { // Если свойство pipe определено то это сообщение из канала. this.tabSignal.emit/*All*/(msg.pipe, result_msg); if(msg.event !== undefined && ( typeof msg.event === "string" || typeof msg.event === "number" ) ) { this.tabSignal.emit/*All*/(msg.pipe+"."+msg.event, result_msg) } } else if(msg.event !== undefined && ( typeof msg.event === "string" || typeof msg.event === "number" ) ) { // Сообщение доставленое по id с указанием msg.event this.tabSignal.emit/*All*/("msg."+msg.event, result_msg); this.tabSignal.emit/*All*/("msg", result_msg) } else { // Сообщение доставленое по id без указания msg.event this.tabSignal.emit/*All*/("msg", result_msg) } if(msg.marker) { this.tabSignal.emit/*All*/(msg.marker, result_msg); } this.tabSignal.emit/*All*/("comet_server_msg", result_msg); return 1; }; /** * Вернёт true если хоть одно соединение установлено и активно * @return {Boolean} */ this.socketArrayTest = function() { for(var i = 0; i < this.socketArray.length; i++) { var socket = this.socketArray[i]; if(socket && socket.readyState === 1) return true; } if(this.LogLevel > 3 ) console.log("[js-api] socketArrayTest:false"); return false; }; this.messageHistory = []; this.isSendErrorReport = false; /** * Отправляет данные по вебсокету (по первому из списка, и если он не доступен то по второму.) * @param {string} data * @return {boolean} */ this.socketArraySend = function(data) { var count = 0; for(var i = 0; i < this.socketArray.length; i++) { var socket = this.socketArray[i]; if(socket && socket.readyState === 1) { try { if(this.messageHistory.length < 1000) { var now = new Date(); this.messageHistory.push({data:data, time:now.getTime()}) } socket.send(data); } catch (ex) { if(this.LogLevel ) { console.log("[js-api] Failed to send data ", data, ex); continue; } } // Отправлять подписки всем а не первому попавшемуся (тоесть кластер поддержания надёжности а не кластер деления нагрузки) [ От TV seregaTV] //return true; count++; } } // Отправлять подписки всем а не первому попавшемуся (тоесть кластер поддержания надёжности а не кластер деления нагрузки) [От TV seregaTV] if(count) return true; return false; }; /** * Отправляет все сообщения из очереди на комет сервер. * @private */ this.send_msg_from_queue = function() { var thisObj = this if(this.isMaster() === undefined) { return false; } else if(this.isMaster() === false) { // Отправка запроса на отправку сообщения мастервкладке if(this.send_msg_subscription !== false) { this.tabSignal.emit/*All*/('comet_msg_slave_add_subscription_and_restart',this.send_msg_subscription); this.send_msg_subscription = false; } if(this.send_msg_queue.length > 0) { for(var i = 0; i < this.send_msg_queue.length; i++) { this.tabSignal.emit/*All*/('comet_msg_slave_send_msg',this.send_msg_queue[i]); } this.send_msg_queue = [] } return true; } else if(this.isMaster()) { if(!this.UseWebSocket()) { return false; } if(this.socketArrayTest()) { if(this.send_msg_subscription !== false) { if(this.LogLevel ) console.error("WebSocket-send-subscription:"+this.send_msg_subscription); this.socketArraySend(this.send_msg_subscription); this.send_msg_subscription = false; } if(this.send_msg_queue.length > 0) { var j = 10; // Отправляет сообщения из очереди не сразу а с 20мс интервалом. for(var i = 0; i < this.send_msg_queue.length; i++) { j+= 20; // Потом убрать setTimeout setTimeout( function(ri) { if(this.LogLevel ) console.log("[js-api] WebSocket-send-msg:", ri); thisObj.socketArraySend(ri); }, j, this.send_msg_queue[i]) } this.send_msg_queue = [] } return true; } } return false; }; /** * Добавляет сообщения в очередь. * @private */ this.add_msg_to_queue = function(msg) { var MsgType = false; MsgType = msg.split("\n"); MsgType = MsgType[0]; if(this.LogLevel ) console.log("[js-api] add_msg_to_queue:", msg); if(MsgType === "subscription") { // Проверка если сообщение о подписке на канал то его отправлять вне очереди // При этом нет необходимости отправлять предыдущие сообщение подписку. this.send_msg_subscription = msg;//.replace(/subscription\n/mg, ""); } else { this.send_msg_queue.push(msg) } }; /** * отправка сообщения по веб сокету. * @private * @param {string} msg Текст сообщения в виде одной строки */ this.send_msg = function(msg) { if(this.isMaster() === undefined) { if(this.LogLevel > 3 ) console.log("[js-api] isMaster:undefined"); this.add_msg_to_queue(msg); return false; } else if(this.isMaster() === false) { if(this.LogLevel > 3 ) console.log("[js-api] isMaster:false"); this.tabSignal.emit/*All*/('comet_msg_slave_send_msg',msg); } else if(this.isMaster()) { if(this.LogLevel > 3 ) console.log("[js-api] isMaster:true"); if(!this.UseWebSocket()) { console.warn("WebSocket-send-msg: not use"); return false; } if(this.socketArrayTest()) { this.send_msg_from_queue(); if(this.LogLevel > 2 ) console.log("[js-api] WebSocket-send-msg:"+msg); this.socketArraySend(msg); return true; } else { this.add_msg_to_queue(msg); return false; } } }; /** * Вернёт true в случаи отправки * Отчёт о доставке прийдёт в канал _answer * @param {string} pipe_name имя канала, должно начинатся с web_ * @param {string} event_name имя события в канале * @param {string} msg Сообщение * @return boolean * @version 2 * * @todo добавить в версии 3 колбек на конкретное сообщение. */ this.web_pipe_send = function(pipe_name, event_name, msg) { if(msg === undefined) { msg = event_name; event_name = "undefined"; if(/[.]/.test(pipe_name)) { event_name = pipe_name.replace(/^[^.]*\.(.*)$/, "$1"); pipe_name = pipe_name.replace(/^(.*?)\.(.*)/, "$1"); } } if(msg === undefined) { return false; } if(!/^web_/.test(pipe_name) && !/^webauth_/.test(pipe_name)) { console.error("Invalid channel name `"+pipe_name+"`. The channel should begin with web_", pipe_name); return; } if(this.LogLevel) console.log(["web_pipe_send", pipe_name, msg]); return this.send_msg("web_pipe2\n"+pipe_name+"\n"+event_name+"\n*\n"+JSON.stringify(msg)); }; this.getTrackPipeUsers = function(pipe_name, callBack) { if(!/^track_/.test(pipe_name)) { console.error("Invalid channel name `"+pipe_name+"`. The channel should begin with track_", pipe_name); return; } var marker = this.getCustomString(); this.subscription_once(marker, callBack); /*if(callBack !== undefined) { this.subscription(pipe_name); }*/ if(this.LogLevel) console.log(["track_pipe_users", pipe_name]); return this.send_msg("track_pipe_users\n"+pipe_name+"\n"+marker); }; this.getUserData = function(user_id, callBack) { if(!user_id || /[^0-9]/.test(user_id)) { console.error("Invalid user_id=`"+user_id+"`. The user_id should is integer"); return; } if(callBack === undefined) { return; } var marker = this.getCustomString(); this.subscription_once(marker, callBack); if(this.LogLevel) console.log(["user_data", user_id]); return this.send_msg("user_data\n"+marker+"\n"+user_id); }; /** * Вернёт true в случаи отправки * Отчёт о доставке прийдёт в канал _answer * @param {string} pipe_name имя канала, должно начинатся с web_ * @param {string} event_name имя события в канале * @param {string} msg Сообщение * @param {int} count Количество попыток отправки = 3 * @param {int} interval Интервал между попытками = 1000 * @return boolean * @version 0.1 * * Отправляет событе в канал count раз с интервалом interval, * но гарантирует что максимум одно сообщение будет доставлено и обработанно (минимум 0), а остальные будут отброшены как дубликаты */ // this.multi_web_pipe_send = function(pipe_name, event_name, msg, count, interval) // { // if(!count) // { // count = 3 // } // if(!interval) // { // interval = 1000 // } // var uuid = "jsapi"; // for(var i = 0; i< 11; i++) // { // uuid += "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM"[Math.floor(Math.random()*62)]; // } // msg._cometApi_uuid = uuid; // var thisObj = this // for(var i = 1; i< count; i++) // { // setTimeout(function(pipe_name, event_name, msg){ // thisObj.web_pipe_send(pipe_name, event_name, msg) // }, i*interval, pipe_name, event_name, msg) // } // return this.web_pipe_send(pipe_name, event_name, msg) // }; /** * Отправляет статистику о использование плагинов * @param {string} plugin_name Имя плагина * @param {string} plugin_version Версия плагина * @param {string} plugin_data Данные плагина * @return {Boolean} */ this.sendStatistics = function(plugin_name, plugin_version, plugin_data) { if(this.LogLevel) console.log(["sendStatistics", plugin_name, plugin_version, plugin_data]); return this.send_msg("statistics\n"+JSON.stringify( { url:window.location.href, dev_id:this.options.dev_id, version: this.version, plugin: { name:plugin_name, version:plugin_version, data:plugin_data } })); }; /** * Отправляет запрос на получение истории по каналу pipe_name * @param {string} pipe_name * @param {function} callBack колбек для ответа от сервера * @return {Boolean} */ this.get_pipe_log = function(pipe_name, callBack) { if(!pipe_name) { console.trace("In CppComet API in get_pipe_log function argument `pipe_name` is required") return false; } if(!this.UseWebSocket()) { return false; } var marker = this.getCustomString(); if(callBack !== undefined) { this.subscription(pipe_name, callBack); //marker = this.getCustomString(); //this.subscription_once(marker, callBack); } this.send_msg("pipe_log\n"+pipe_name+"\n"+marker+"\n"); return true; }; /** * Отправляет запрос на получение количества подписчиков в канале pipe_name * @param {string} pipe_name * @param {function} callBack колбек для ответа от сервера * @return {Boolean} */ this.count_users_in_pipe = function(pipe_name, callBack) { if(!this.UseWebSocket()) { return false; } var marker = this.getCustomString(); this.subscription_once(marker, callBack); this.send_msg("pipe_count\n"+pipe_name+"\n"+marker+"\n"); return true; }; /** * Вернёт false если мы не подключены к серверу * @private */ this.isConnected = function() { for(var i = 0; i< this.web_socket_error.length; i++) { if(this.web_socket_error[i] == 0) { return true; } } return false; }; /** * Обеспечивает работу с ссоединением с сервером * @private */ this.conect_to_server = function() { var thisObj = this; //if(this.in_conect_to_server) //{ // if(this.LogLevel) console.log("[js-api] Connection to the server is already installed."); // return; //} if(this.LogLevel) console.log("[js-api] Connecting to the server"); //this.in_conect_to_server = true; if(!this.isMaster()) this.setAsMaster(); if(this.UseWebSocket()) { function initSocket(socket, indexInArr) { if(!thisObj.time_to_reconect_on_error[indexInArr]) thisObj.time_to_reconect_on_error[indexInArr] = 300; if(!thisObj.time_to_reconect_on_close[indexInArr]) thisObj.time_to_reconect_on_close[indexInArr] = 30; if(!thisObj.web_socket_error[indexInArr]) thisObj.web_socket_error[indexInArr] = 0; socket.onopen = function() { if(thisObj.LogLevel) console.log("[js-api] WS Connection established."); if(thisObj.send_msg_subscription === false) thisObj.send_curent_subscription(); // Подписываемся на то что были подписаны до разрыва соединения // Отправка сообщений из очереди. thisObj.send_msg_from_queue(); if(thisObj.options.nostat !== true) { setTimeout(function() { if(thisObj.isSendStatisticsData[indexInArr]) { return; } thisObj.isSendStatisticsData[indexInArr] = true; // Отправка данных по использованию сервиса thisObj.send_msg("statistics\n"+JSON.stringify({url:window.location.href, dev_id:thisObj.options.dev_id, version: thisObj.version})); }, 5000) } }; socket.onclose = function(event) { //this.in_conect_to_server = false; if (event.wasClean || thisObj.in_abort === true) { if(thisObj.LogLevel) console.log("[js-api] WS The connection is closed cleanly"); } else { if(thisObj.hasCriticalError[indexInArr]) { console.warn('Fatal error, connection impossible.'); return; } if(thisObj.LogLevel) console.log("[js-api] WS Connection failure"); // например, "убит" процесс сервера socket.close(); thisObj.web_socket_error[indexInArr]++; // Увеличение колва ошибок вебсокетов /*if(thisObj.web_socket_error_timeOut_id !== undefined ) { clearTimeout(thisObj.web_socket_error_timeOut_id) } // Если ошибки происходят редко то обнулим сщётчик thisObj.web_socket_error_timeOut_id = setTimeout(function() { thisObj.web_socket_error_timeOut_id = undefined; thisObj.web_socket_error = 0; }, thisObj.time_to_reconect_on_error[indexInArr]*2 )*/ if( thisObj.web_socket_error[indexInArr] > 2 && thisObj.web_socket_success !== true && !thisObj.isUseWss()) { // Если за время thisObj.web_socket_error_timeOut произошло более 2 ошибок вебсокетов то принудительно включим wss thisObj.UseWss(true); console.warn("There were more than 2 errors in Web sites including encryption"); // Не делать этого если уже были переданы данные по вебсокету } /*else if( thisObj.web_socket_error[indexInArr] > 3 && thisObj.web_socket_success !== true && thisObj.isUseWss()) { // Если за время thisObj.web_socket_error_timeOut произошло более 3 ошибок вебсокетов то перейдём на long poling // Такое возможно если человек использует прокси который не поддерживает вебсокеты // Переход произойдёт примерно через 3 секунды работы thisObj.UseWebSocket(false); thisObj.UseWss(); console.error("Произошло более 3 ошибок вебсокетов то перейдём на long poling"); // Не делать этого если уже были переданы данные по вебсокету }*/ else if(thisObj.web_socket_error[indexInArr] > 5) { // Если 3 ошибки подряд то увеличим время до следующего переподключения thisObj.time_to_reconect_on_error[indexInArr] *= 3; } else if(thisObj.web_socket_error[indexInArr] > 3) { // Если 5 ошибок подряд то ещё больше увеличим время до следующего переподключения thisObj.time_to_reconect_on_error[indexInArr] += 2000; } if(thisObj.web_socket_error[indexInArr] === 0) { // Если это первый обрыв соединения подряд то переподключаемся быстрее setTimeout(function() { //thisObj.conect_to_server(); var conect = function() { if(navigator.onLine === false) { setTimeout(conect, 300); return; } var node = thisObj.options.nodeArray[indexInArr]; var socket = new WebSocket(thisObj.getUrl(node)); thisObj.socketArray[indexInArr] = socket; initSocket(socket, indexInArr); }; conect() }, thisObj.time_to_reconect_on_close[indexInArr] ); } else { // Если это не первый обрыв соединения подряд но данные уже отправлялись то отправляем отчёт об ошибке if(thisObj.web_socket_success == true) { //thisObj.errorReportSend(); } // Если это не первый обрыв соединения подряд то переподключаемся не сразу setTimeout(function() { var conect = function() { if(navigator.onLine === false) { setTimeout(conect, 300); return; } if(thisObj.socketArray[indexInArr].readyState != 3){ console.log('WebSocket readystate:', thisObj.socketArray[indexInArr].readyState) return; } var node = thisObj.options.nodeArray[indexInArr]; var socket = new WebSocket(thisObj.getUrl(node)); thisObj.socketArray[indexInArr] = socket; initSocket(socket, indexInArr); }; conect() }, thisObj.time_to_reconect_on_error[indexInArr] ); } } if(thisObj.LogLevel) console.log("[js-api] WS Code: " + event.code + " reason: " + event.reason); }; socket.onmessage = function(event) { thisObj.web_socket_success = true; thisObj.web_socket_error[indexInArr] = 0; // Если успешно подключились сбрасываем сщётчик ошибок thisObj.time_to_reconect_on_error[indexInArr] = 1000; // Если успешно подключились сбрасываем сщётчик ошибок if(thisObj.LogLevel > 1) console.log("[js-api] \x1b[1;32mWS Incoming message\x1b[0m:"+event.data); var lineArray = event.data.replace(/^\s+|\s+$/, '').split("\n"); for(var i = 0; i < lineArray.length; i++) { var rj = {}; try{ rj = JSON.parse(lineArray[i].replace(/\\'/g, "'")); } catch (failed) { if(thisObj.LogLevel) console.error(failed); continue; } thisObj.msg_cultivate(rj, indexInArr); } }; socket.onerror = function(error) { //thisObj.in_conect_to_server = false; if(thisObj.LogLevel) console.log("[js-api] Error " + error.message); }; } if(thisObj.socketArray) { for(let i = 0; i< this.socketArray.length; i++) { if(thisObj.socketArray[i]) { try { if(thisObj.socketArray[i].readyState != 3 && thisObj.socketArray[i].readyState != 2){ setTimeout(() => { thisObj.socketArray[i].close(); }, 0); } } catch (err) { console.log('Socket connection is not established!') } } } } thisObj.socketArray = []; var random_node = thisObj.options.user_id % thisObj.options.nodeArray.length; if(!random_node) { random_node = Math.floor(Math.random()*thisObj.options.nodeArray.length) } for(var i = 0; i < thisObj.options.nodeArray.length; i++) { if(thisObj.hasCriticalError[i]) { // Если true то произошла критическая ошибка после которой нет смысла подключатся к серверу continue; } if(thisObj.options.roundrobin == true && random_node != i) { // Если есть опция roundrobin то подключатся будем только к одной ноде на основе своего user_id или случайной ноде если user_id не задан. continue; } var node = thisObj.options.nodeArray[i]; if(thisObj.LogLevel) console.log("[js-api] conect to " + thisObj.getUrl(node)) var socket = new window.WebSocket(thisObj.getUrl(node)); thisObj.socketArray.push(socket); initSocket(socket, thisObj.socketArray.length - 1 ); } } else { try { thisObj.request = new XMLHttpRequest(); } catch (trymicrosoft) { try { thisObj.request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { thisObj.request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { thisObj.request = false; } } } thisObj.request.onreadystatechange = function() { if( thisObj.request.status === 200 && thisObj.in_abort !== true) { var re = thisObj.request.responseText; if(thisObj.LogLevel) console.log("[js-api] Incoming message:"+re); var lineArray = re.replace(/^\s+|\s+$/, '').split('\n'); for(var i = 0; i < lineArray; i++) { try{ if(thisObj.LogLevel) console.log(lineArray[i]); var rj = JSON.parse(lineArray[i]) } catch (failed) { thisObj.in_conect_to_server = false; if(thisObj.LogLevel) console.log("[js-api] Error in xhr, reconnection via "+(thisObj.time_to_reconect_on_error[0]) +" seconds."); setTimeout(function(){thisObj.conect_to_server()}, thisObj.time_to_reconect_on_error[0] ); return false; } thisObj.msg_cultivate(rj) } thisObj.in_conect_to_server = false; thisObj.conect_to_server(); } else { thisObj.in_conect_to_server = false; if(thisObj.in_abort !== true) { thisObj.xhr_error += 1; if( thisObj.xhr_error > 30 ) { thisObj.time_to_reconect_on_error[0] = 90000; } else if( thisObj.xhr_error > 10 ) { thisObj.time_to_reconect_on_error[0] = 30000; } else if( thisObj.xhr_error > 3 ) { thisObj.time_to_reconect_on_error[0] = 10000; } if(thisObj.LogLevel || 1) console.log("[js-api] Error in xhr, reconnection via "+(thisObj.time_to_reconect_on_error[0]) +" seconds."); setTimeout(function(){ thisObj.conect_to_server() }, thisObj.time_to_reconect_on_error[0] ); setTimeout(function(){ thisObj.xhr_error = 0 }, thisObj.xhr_error_timeOut_id ) } } }; thisObj.request.open("POST", thisObj.getUrl(), true); thisObj.request.send(thisObj.subscription_array.join("\n")); // Именно здесь отправляются данные } }; /** * Пытается установить соединение с сервером или наладить обмен сообщениями и мониторинг работоспособности мастервкладки. * @private */ this.conect = function(callback) { if(this.isMaster()) { return this.conect_to_server(); } if(this.in_try_conect) { if(this.LogLevel) console.log("[js-api] The connection to the server is already installed on another tab"); this.tabSignal.emit/*All*/('comet_msg_slave_signal_start'); return false; } this.in_try_conect = true; if(this.LogLevel) console.log("[js-api] Trying to connect to the server"); this.setAsSlave(callback) }; return this; }; window.cometServerApi = _cometServerApi /** * Api работы с комет серевером comet-server.ru * @type cometServerApi */ window.cometApi = new cometServerApi(); } /** * @return _cometServerApi */ function CometServer() { return window.cometApi; } // 2 - ./plugins/imbasynergy/imbachat/assets/js/lib/jquery-ui-1.12.1.custom/jquery-ui.min.js /*! jQuery UI - v1.12.1 - 2019-01-09 * http://jqueryui.com * Includes: widget.js, data.js, disable-selection.js, scroll-parent.js, widgets/draggable.js, widgets/resizable.js, widgets/mouse.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var s=!1;t(document).on("mouseup",function(){s=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,n=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return n&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.ui.safeBlur=function(e){e&&"body"!==e.nodeName.toLowerCase()&&t(e).trigger("blur")},t.widget("ui.draggable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(this._blurActiveElement(e),this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("
").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=t.ui.safeActiveElement(this.document[0]),s=t(e.target);s.closest(i).length||t.ui.safeBlur(i)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp(new t.Event("mouseup",e)),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.trigger("focus"),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new t.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),n=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options,o=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,t(o).width()-this.helperProportions.width-this.margins.left,(t(o).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,n,o,a=this.options,r=this._isRootNode(this.scrollParent[0]),l=t.pageX,h=t.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.lefti[2]&&(l=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),a.grid&&(n=a.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/a.grid[1])*a.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-a.grid[1]:n+a.grid[1]:n,o=a.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/a.grid[0])*a.grid[0]:this.originalPageX,l=i?o-this.offset.click.left>=i[0]||o-this.offset.click.left>i[2]?o:o-this.offset.click.left>=i[0]?o-a.grid[0]:o+a.grid[0]:o),"y"===a.axis&&(l=this.originalPageX),"x"===a.axis&&(h=this.originalPageY)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var n=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,n))})},stop:function(e,i,s){var n=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,n))})},drag:function(e,i,s){t.each(s.sortables,function(){var n=!1,o=this;o.positionAbs=s.positionAbs,o.helperProportions=s.helperProportions,o.offset.click=s.offset.click,o._intersectsWith(o.containerCache)&&(n=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==o&&this._intersectsWith(this.containerCache)&&t.contains(o.element[0],this.element[0])&&(n=!1),n})),n?(o.isOver||(o.isOver=1,s._parent=i.helper.parent(),o.currentItem=i.helper.appendTo(o.element).data("ui-sortable-item",!0),o.options._helper=o.options.helper,o.options.helper=function(){return i.helper[0]},e.target=o.currentItem[0],o._mouseCapture(e,!0),o._mouseStart(e,!0,!0),o.offset.click.top=s.offset.click.top,o.offset.click.left=s.offset.click.left,o.offset.parent.left-=s.offset.parent.left-o.offset.parent.left,o.offset.parent.top-=s.offset.parent.top-o.offset.parent.top,s._trigger("toSortable",e),s.dropped=o.element,t.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,o.fromOutside=s),o.currentItem&&(o._mouseDrag(e),i.position=o.position)):o.isOver&&(o.isOver=0,o.cancelHelperRemoval=!0,o.options._revert=o.options.revert,o.options.revert=!1,o._trigger("out",e,o._uiHash(o)),o._mouseStop(e,!0),o.options.revert=o.options._revert,o.options.helper=o.options._helper,o.placeholder&&o.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(e),i.position=s._generatePosition(e,!0),s._trigger("fromSortable",e),s.dropped=!1,t.each(s.sortables,function(){this.refreshPositions()}))})}}),t.ui.plugin.add("draggable","cursor",{start:function(e,i,s){var n=t("body"),o=s.options;n.css("cursor")&&(o._cursor=n.css("cursor")),n.css("cursor",o.cursor)},stop:function(e,i,s){var n=s.options;n._cursor&&t("body").css("cursor",n._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("opacity")&&(o._opacity=n.css("opacity")),n.css("opacity",o.opacity)},stop:function(e,i,s){var n=s.options;n._opacity&&t(i.helper).css("opacity",n._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(e,i,s){var n=s.options,o=!1,a=s.scrollParentNotHidden[0],r=s.document[0];a!==r&&"HTML"!==a.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+a.offsetHeight-e.pageY=0;d--)l=s.snapElements[d].left-s.margins.left,h=l+s.snapElements[d].width,c=s.snapElements[d].top-s.margins.top,u=c+s.snapElements[d].height,l-g>_||m>h+g||c-g>b||v>u+g||!t.contains(s.snapElements[d].item.ownerDocument,s.snapElements[d].item)?(s.snapElements[d].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=!1):("inner"!==f.snapMode&&(n=g>=Math.abs(c-b),o=g>=Math.abs(u-v),a=g>=Math.abs(l-_),r=g>=Math.abs(h-m),n&&(i.position.top=s._convertPositionTo("relative",{top:c-s.helperProportions.height,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left)),p=n||o||a||r,"outer"!==f.snapMode&&(n=g>=Math.abs(c-v),o=g>=Math.abs(u-b),a=g>=Math.abs(l-m),r=g>=Math.abs(h-_),n&&(i.position.top=s._convertPositionTo("relative",{top:c,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left)),!s.snapElements[d].snapping&&(n||o||a||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=n||o||a||r||p)}}),t.ui.plugin.add("draggable","stack",{start:function(e,i,s){var n,o=s.options,a=t.makeArray(t(o.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});a.length&&(n=parseInt(t(a[0]).css("zIndex"),10)||0,t(a).each(function(e){t(this).css("zIndex",n+e)}),this.css("zIndex",n+a.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("zIndex")&&(o._zIndex=n.css("zIndex")),n.css("zIndex",o.zIndex)},stop:function(e,i,s){var n=s.options;n._zIndex&&t(i.helper).css("zIndex",n._zIndex)}}),t.ui.draggable,t.widget("ui.resizable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("
").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show()) }).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("
"),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,l=this._change[o];return this._updatePrevProperties(),l?(i=l.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,l,h=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,l=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,h.animate||this.element.css(t.extend(a,{top:l,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!h.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,l=this.originalPosition.top+this.originalSize.height,h=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&h&&(t.left=r-e.minWidth),s&&h&&(t.left=r-e.maxWidth),a&&c&&(t.top=l-e.minHeight),n&&c&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
"),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,l={width:i.size.width-r,height:i.size.height-a},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(l,c&&h?{top:c,left:h}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,l=t(this).resizable("instance"),h=l.options,c=l.element,u=h.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(l.containerElement=t(d),/document/.test(u)||u===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=l._num(e.css("padding"+s))}),l.containerOffset=e.offset(),l.containerPosition=e.position(),l.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=l.containerOffset,n=l.containerSize.height,o=l.containerSize.width,a=l._hasScroll(d,"left")?d.scrollWidth:o,r=l._hasScroll(d)?d.scrollHeight:n,l.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,l=a.containerOffset,h=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=l),h.left<(a._helper?l.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-l.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?l.left:0),h.top<(a._helper?l.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-l.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?l.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-l.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-l.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),l=a.outerWidth()-e.sizeDiff.width,h=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,l="number"==typeof s.grid?[s.grid,s.grid]:s.grid,h=l[0]||1,c=l[1]||1,u=Math.round((n.width-o.width)/h)*h,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,g=s.maxWidth&&p>s.maxWidth,m=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=l,_&&(p+=h),v&&(f+=c),g&&(p-=h),m&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-h)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-h>0?(i.size.width=p,i.position.left=a.left-u):(p=h-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable}); // 3 - ./plugins/imbasynergy/imbachat/assets/js/favico.js /** * @license MIT or GPL-2.0 * @fileOverview Favico animations * @author Miroslav Magda, http://blog.ejci.net * @source: https://github.com/ejci/favico.js * @version 0.3.10 */ /** * Create new favico instance * @param {Object} Options * @return {Object} Favico object * @example * var favico = new Favico({ * bgColor : '#d00', * textColor : '#fff', * fontFamily : 'sans-serif', * fontStyle : 'bold', * type : 'circle', * position : 'down', * animation : 'slide', * elementId: false, * element: null, * dataUrl: function(url){}, * win: window * }); */ (function () { window.Favico = (function (opt) { 'use strict'; opt = (opt) ? opt : {}; var _def = { bgColor: '#d00', textColor: '#fff', fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,... fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900 type: 'circle', position: 'down', // down, up, left, leftup (upleft) animation: 'slide', elementId: false, element: null, dataUrl: false, win: window }; var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc; _browser = {}; _browser.ff = typeof InstallTrigger != 'undefined'; _browser.chrome = !!window.chrome; _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0; _browser.ie = /*@cc_on!@*/false; _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; _browser.supported = (_browser.chrome || _browser.ff || _browser.opera); var _queue = []; _readyCb = function () { }; _ready = _stop = false; /** * Initialize favico */ var init = function () { //merge initial options _opt = merge(_def, opt); _opt.bgColor = hexToRgb(_opt.bgColor); _opt.textColor = hexToRgb(_opt.textColor); _opt.position = _opt.position.toLowerCase(); _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation; _doc = _opt.win.document; var isUp = _opt.position.indexOf('up') > -1; var isLeft = _opt.position.indexOf('left') > -1; //transform the animations if (isUp || isLeft) { for (var a in animation.types) { for (var i = 0; i < animation.types[a].length; i++) { var step = animation.types[a][i]; if (isUp) { if (step.y < 0.6) { step.y = step.y - 0.4; } else { step.y = step.y - 2 * step.y + (1 - step.w); } } if (isLeft) { if (step.x < 0.6) { step.x = step.x - 0.4; } else { step.x = step.x - 2 * step.x + (1 - step.h); } } animation.types[a][i] = step; } } } _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type; _orig = link.getIcon(); //create temp canvas _canvas = document.createElement('canvas'); //create temp image _img = document.createElement('img'); if (_orig.hasAttribute('href')) { _img.setAttribute('crossOrigin', 'anonymous'); //get width/height _img.onload = function () { _h = (_img.height > 0) ? _img.height : 32; _w = (_img.width > 0) ? _img.width : 32; _canvas.height = _h; _canvas.width = _w; _context = _canvas.getContext('2d'); icon.ready(); }; _img.setAttribute('src', _orig.getAttribute('href')); } else { _h = 32; _w = 32; _img.height = _h; _img.width = _w; _canvas.height = _h; _canvas.width = _w; _context = _canvas.getContext('2d'); icon.ready(); } }; /** * Icon namespace */ var icon = {}; /** * Icon is ready (reset icon) and start animation (if ther is any) */ icon.ready = function () { _ready = true; icon.reset(); _readyCb(); }; /** * Reset icon to default state */ icon.reset = function () { //reset if (!_ready) { return; } try { } catch (e){ _queue = []; _lastBadge = false; _running = false; _context.clearRect(0, 0, _w, _h); _context.drawImage(_img, 0, 0, _w, _h); //_stop=true; link.setIcon(_canvas); //webcam('stop'); //video('stop'); window.clearTimeout(_animTimeout); window.clearTimeout(_drawTimeout); } }; /** * Start animation */ icon.start = function () { if (!_ready || _running) { return; } var finished = function () { _lastBadge = _queue[0]; _running = false; if (_queue.length > 0) { _queue.shift(); icon.start(); } else { } }; if (_queue.length > 0) { _running = true; var run = function () { // apply options for this animation ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function (a) { if (a in _queue[0].options) { _opt[a] = _queue[0].options[a]; } }); animation.run(_queue[0].options, function () { finished(); }, false); }; if (_lastBadge) { animation.run(_lastBadge.options, function () { run(); }, true); } else { run(); } } }; /** * Badge types */ var type = {}; var options = function (opt) { opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n; opt.x = _w * opt.x; opt.y = _h * opt.y; opt.w = _w * opt.w; opt.h = _h * opt.h; opt.len = ("" + opt.n).length; return opt; }; /** * Generate circle * @param {Object} opt Badge options */ type.circle = function (opt) { try { opt = options(opt); var more = false; if (opt.len === 2) { opt.x = opt.x - opt.w * 0.4; opt.w = opt.w * 1.4; more = true; } else if (opt.len >= 3) { opt.x = opt.x - opt.w * 0.65; opt.w = opt.w * 1.65; more = true; } _context.clearRect(0, 0, _w, _h); _context.drawImage(_img, 0, 0, _w, _h); _context.beginPath(); _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily; _context.textAlign = 'center'; if (more) { _context.moveTo(opt.x + opt.w / 2, opt.y); _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y); _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2); _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2); _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h); _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h); _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2); _context.lineTo(opt.x, opt.y + opt.h / 2); _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y); } else { _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI); } _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; _context.fill(); _context.closePath(); _context.beginPath(); _context.stroke(); _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); if ((typeof opt.n) === 'number' && opt.n > 999) { _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); } else { _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); } _context.closePath(); } catch (e){ } }; /** * Generate rectangle * @param {Object} opt Badge options */ type.rectangle = function (opt) { try { opt = options(opt); var more = false; if (opt.len === 2) { opt.x = opt.x - opt.w * 0.4; opt.w = opt.w * 1.4; more = true; } else if (opt.len >= 3) { opt.x = opt.x - opt.w * 0.65; opt.w = opt.w * 1.65; more = true; } _context.clearRect(0, 0, _w, _h); _context.drawImage(_img, 0, 0, _w, _h); _context.beginPath(); _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px " + _opt.fontFamily; _context.textAlign = 'center'; _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; _context.fillRect(opt.x, opt.y, opt.w, opt.h); _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); if ((typeof opt.n) === 'number' && opt.n > 999) { _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); } else { _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); } _context.closePath(); } catch (e){ } }; /** * Set badge */ var badge = function (number, opts) { opts = ((typeof opts) === 'string' ? { animation: opts } : opts) || {}; _readyCb = function () { try { if (typeof (number) === 'number' ? (number > 0) : (number !== '')) { var q = { type: 'badge', options: { n: number } }; if ('animation' in opts && animation.types['' + opts.animation]) { q.options.animation = '' + opts.animation; } if ('type' in opts && type['' + opts.type]) { q.options.type = '' + opts.type; } ['bgColor', 'textColor'].forEach(function (o) { if (o in opts) { q.options[o] = hexToRgb(opts[o]); } }); ['fontStyle', 'fontFamily'].forEach(function (o) { if (o in opts) { q.options[o] = opts[o]; } }); _queue.push(q); if (_queue.length > 100) { throw new Error('Too many badges requests in queue.'); } icon.start(); } else { icon.reset(); } } catch (e) { throw new Error('Error setting badge. Message: ' + e.message); } }; if (_ready) { _readyCb(); } }; /** * Set image as icon */ var image = function (imageElement) { _readyCb = function () { try { var w = imageElement.width; var h = imageElement.height; var newImg = document.createElement('img'); var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); newImg.setAttribute('crossOrigin', 'anonymous'); newImg.onload=function(){ _context.clearRect(0, 0, _w, _h); _context.drawImage(newImg, 0, 0, _w, _h); link.setIcon(_canvas); }; newImg.setAttribute('src', imageElement.getAttribute('src')); newImg.height = (h / ratio); newImg.width = (w / ratio); } catch (e) { throw new Error('Error setting image. Message: ' + e.message); } }; if (_ready) { _readyCb(); } }; /** * Set video as icon */ var video = function (videoElement) { _readyCb = function () { try { if (videoElement === 'stop') { _stop = true; icon.reset(); _stop = false; return; } //var w = videoElement.width; //var h = videoElement.height; //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); videoElement.addEventListener('play', function () { drawVideo(this); }, false); } catch (e) { throw new Error('Error setting video. Message: ' + e.message); } }; if (_ready) { _readyCb(); } }; /** * Set video as icon */ var webcam = function (action) { //UR if (!window.URL || !window.URL.createObjectURL) { window.URL = window.URL || {}; window.URL.createObjectURL = function (obj) { return obj; }; } if (_browser.supported) { var newVideo = false; navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia; _readyCb = function () { try { if (action === 'stop') { _stop = true; icon.reset(); _stop = false; return; } newVideo = document.createElement('video'); newVideo.width = _w; newVideo.height = _h; navigator.getUserMedia({ video: true, audio: false }, function (stream) { newVideo.src = URL.createObjectURL(stream); newVideo.play(); drawVideo(newVideo); }, function () { }); } catch (e) { throw new Error('Error setting webcam. Message: ' + e.message); } }; if (_ready) { _readyCb(); } } }; /** * Draw video to context and repeat :) */ function drawVideo(video) { if (video.paused || video.ended || _stop) { return false; } //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl) try { _context.clearRect(0, 0, _w, _h); _context.drawImage(video, 0, 0, _w, _h); } catch (e) { } _drawTimeout = setTimeout(function () { drawVideo(video); }, animation.duration); link.setIcon(_canvas); } var link = {}; /** * Get icon from HEAD tag or create a new element */ link.getIcon = function () { var elm = false; //get link element var getLink = function () { var link = _doc.getElementsByTagName('head')[0].getElementsByTagName('link'); for (var l = link.length, i = (l - 1); i >= 0; i--) { if ((/(^|\s)icon(\s|$)/i).test(link[i].getAttribute('rel'))) { return link[i]; } } return false; }; if (_opt.element) { elm = _opt.element; } else if (_opt.elementId) { //if img element identified by elementId elm = _doc.getElementById(_opt.elementId); elm.setAttribute('href', elm.getAttribute('src')); } else { //if link element elm = getLink(); if (elm === false) { elm = _doc.createElement('link'); elm.setAttribute('rel', 'icon'); _doc.getElementsByTagName('head')[0].appendChild(elm); } } elm.setAttribute('type', 'image/png'); return elm; }; link.setIcon = function (canvas) { var url = canvas.toDataURL('image/png'); if (_opt.dataUrl) { //if using custom exporter _opt.dataUrl(url); } if (_opt.element) { _opt.element.setAttribute('href', url); _opt.element.setAttribute('src', url); } else if (_opt.elementId) { //if is attached to element (image) var elm = _doc.getElementById(_opt.elementId); elm.setAttribute('href', url); elm.setAttribute('src', url); } else { //if is attached to fav icon if (_browser.ff || _browser.opera) { //for FF we need to "recreate" element, atach to dom and remove old //var originalType = _orig.getAttribute('rel'); var old = _orig; _orig = _doc.createElement('link'); //_orig.setAttribute('rel', originalType); if (_browser.opera) { _orig.setAttribute('rel', 'icon'); } _orig.setAttribute('rel', 'icon'); _orig.setAttribute('type', 'image/png'); _doc.getElementsByTagName('head')[0].appendChild(_orig); _orig.setAttribute('href', url); if (old.parentNode) { old.parentNode.removeChild(old); } } else { _orig.setAttribute('href', url); } } }; //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139 //HEX to RGB convertor function hexToRgb(hex) { var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, function (m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : false; } /** * Merge options */ function merge(def, opt) { var mergedOpt = {}; var attrname; for (attrname in def) { mergedOpt[attrname] = def[attrname]; } for (attrname in opt) { mergedOpt[attrname] = opt[attrname]; } return mergedOpt; } /** * Cross-browser page visibility shim * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible */ function isPageHidden() { return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden; } /** * @namespace animation */ var animation = {}; /** * Animation "frame" duration */ animation.duration = 40; /** * Animation types (none,fade,pop,slide) */ animation.types = {}; animation.types.fade = [{ x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.0 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.1 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.2 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.3 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.4 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.5 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.6 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.7 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.8 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 0.9 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 1.0 }]; animation.types.none = [{ x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 1 }]; animation.types.pop = [{ x: 1, y: 1, w: 0, h: 0, o: 1 }, { x: 0.9, y: 0.9, w: 0.1, h: 0.1, o: 1 }, { x: 0.8, y: 0.8, w: 0.2, h: 0.2, o: 1 }, { x: 0.7, y: 0.7, w: 0.3, h: 0.3, o: 1 }, { x: 0.6, y: 0.6, w: 0.4, h: 0.4, o: 1 }, { x: 0.5, y: 0.5, w: 0.5, h: 0.5, o: 1 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 1 }]; animation.types.popFade = [{ x: 0.75, y: 0.75, w: 0, h: 0, o: 0 }, { x: 0.65, y: 0.65, w: 0.1, h: 0.1, o: 0.2 }, { x: 0.6, y: 0.6, w: 0.2, h: 0.2, o: 0.4 }, { x: 0.55, y: 0.55, w: 0.3, h: 0.3, o: 0.6 }, { x: 0.50, y: 0.50, w: 0.4, h: 0.4, o: 0.8 }, { x: 0.45, y: 0.45, w: 0.5, h: 0.5, o: 0.9 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 1 }]; animation.types.slide = [{ x: 0.4, y: 1, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.9, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.9, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.8, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.7, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.6, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.5, w: 0.6, h: 0.6, o: 1 }, { x: 0.4, y: 0.4, w: 0.6, h: 0.6, o: 1 }]; /** * Run animation * @param {Object} opt Animation options * @param {Object} cb Callabak after all steps are done * @param {Object} revert Reverse order? true|false * @param {Object} step Optional step number (frame bumber) */ animation.run = function (opt, cb, revert, step) { var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation]; if (revert === true) { step = (typeof step !== 'undefined') ? step : animationType.length - 1; } else { step = (typeof step !== 'undefined') ? step : 0; } cb = (cb) ? cb : function () { }; if ((step < animationType.length) && (step >= 0)) { type[_opt.type](merge(opt, animationType[step])); _animTimeout = setTimeout(function () { if (revert) { step = step - 1; } else { step = step + 1; } animation.run(opt, cb, revert, step); }, animation.duration); link.setIcon(_canvas); } else { cb(); return; } }; //auto init init(); return { badge: badge, video: video, image: image, webcam: webcam, reset: icon.reset, browser: { supported: _browser.supported } }; }); // AMD / RequireJS if (typeof define !== 'undefined' && define.amd) { define([], function () { return Favico; }); } // CommonJS else if (typeof module !== 'undefined' && module.exports) { module.exports = Favico; } // included directly via */ var lineNo = 1, buffer = [ "with (this.data) { \nwith (this.customData) { \nthis.buffer.push('" ], matches = html.split(new RegExp(regExpEscape(options.open) + '((?:.|[\r\n])+?)(?:' + regExpEscape(options.close) + '|$)')), length, i, text, prefix, postfix, line, tmp, jsFromPos, state; for (i = 0, length = matches.length; i < length; i++) { text = matches[i]; if (i % 2 === 1) { line = "\nthis.line=" + lineNo; jsFromPos = 1; state = STATE_RAW; switch (text.charAt(0)) { case '@': prefix = '\',(' + line + ', this.partial('; postfix = ')),\''; state = STATE_PARTIAL; break; case '!': prefix = '\',(' + line + ', this.extend('; postfix = ')),\''; state = STATE_EXTEND; break; case '*': prefix = '\',(' + line + ', this.child(\''; postfix = '\')),\''; break; case '[': prefix = '\');' + line + ';this.blockStart(\''; postfix = '\');this.buffer.push(\''; break; case ']': prefix = '\');' + line + ';this.blockEnd(\''; postfix = '\');this.buffer.push(\''; break; case '=': prefix = '\',(' + line + ', '; postfix = '),\''; state = STATE_HTML; break; case '-': prefix = '\',(' + line + ', '; postfix = '),\''; state = STATE_TEXT; break; case '?': prefix = '\');' + line + ';'; postfix = 'this.buffer.push(\''; state = STATE_CONDITION; break; case ':': prefix = '\');' + line + ';}else'; postfix = 'this.buffer.push(\''; state = STATE_ELSE; break; case '|': prefix = '\');' + line + ';'; postfix = 'this.buffer.push(\''; state = STATE_LOOP; break; case '~': prefix = '\');' + line + ';'; postfix = 'this.buffer.push(\''; state = STATE_SUBBLOK; break; default: prefix = '\');' + line + ';'; postfix = ';this.buffer.push(\''; jsFromPos = 0; } switch (state) { case STATE_RAW: buffer.push(prefix, text.substr(jsFromPos).replace(trimExp, ''), postfix); break; case STATE_HTML: buffer.push(prefix, 'JustWaitResults('+text.substr(jsFromPos).replace(trimExp, '')+')', postfix); break; case STATE_TEXT: buffer.push(prefix, 'JustEscapeHtml('+text.substr(jsFromPos).replace(trimExp, '')+')', postfix); break; case STATE_CONDITION: tmp = text.substr(jsFromPos).replace(trimExp, ''); if (!tmp.length) { buffer.push(prefix, '}', postfix); } else { buffer.push(prefix, 'if(', tmp, '){', postfix); } tmp = undefined; break; case STATE_ELSE: tmp = text.substr(jsFromPos).replace(trimExp, ''); if (!tmp.length) { buffer.push(prefix, '{', postfix); } else { buffer.push(prefix, ' if(', tmp, '){', postfix); } tmp = undefined; break; case STATE_PARTIAL: case STATE_EXTEND: tmp = text.substr(jsFromPos).replace(trimExp, '').split(/\s+/); tmp = ['\'' + tmp[0] + '\'', tmp.splice(1).join(' ')]; if (!tmp[1].length) { tmp = tmp[0]; } else { tmp = tmp.join(''); } buffer.push(prefix, tmp, postfix); tmp = undefined; break; case STATE_LOOP: tmp = text.substr(jsFromPos).replace(trimExp, '').split(/\s+/); if (!tmp[0].length) { buffer.push('\');' + line + ';}, this);this.buffer.push(\''); } else { buffer.push(prefix, tmp[0], '.forEach(function(', tmp[1], '){this.buffer.push(\''); } tmp = undefined; break; case STATE_SUBBLOK: buffer.push(prefix, text.substr(jsFromPos).replace(trimExp, ''), postfix); break; } } else { buffer.push(text.replace(/[\\']/g, '\\$&').replace(/\r/g, ' ').replace(/\n/g, '\\n')); } lineNo += text.split(/\n/).length - 1; } buffer.push("'); \n} \n} return this.buffer;"); return buffer = buffer.join(''); }, parse = function (html) { return new Function(parseToCode(html)); }, readSync = function (file) { var data = eval('(options.root[\'' + file + '\'])'); if (Object.prototype.toString.call(data) === '[object String]') { return data; } else { window.imbaLog.error('Failed to load template', file) throw 'Failed to load template ' + file } }, loadSync = function (file) { var data = readSync(file) var blank = parse(data); return blank; }, Template = function (file, data, customData) { this.file = file; if (Object.prototype.toString.call(options.root) === '[object String]') { this.file = path.normalize((options.root.length ? (options.root + '/') : '') + file + options.ext); } this.data = data; this.customData = customData || {}; this.buffer = []; this.tmpBuffer = {}; this.tmpBufferNames = []; this.watcher = undefined; this.line = 1; this.partials = []; this.childData = []; this.childError = undefined; this.childCallback = undefined; this.callback = undefined; this.blocks = {}; }; Template.prototype.blockStart = function (name) { this.tmpBufferNames.push(name) this.tmpBuffer[name] = this.buffer; if (!this.blocks[name]) { this.blocks[name] = []; } if (!this.blocks[name].length) { this.buffer = this.blocks[name]; } else { this.buffer = []; } }; Template.prototype.blockEnd = function () { var name = this.tmpBufferNames.pop(name) this.buffer = this.tmpBuffer[name]; delete (this.tmpBuffer[name]); }; // Включить результат рендера шаблона template с данными customData Template.prototype.partial = function (template, customData) { var page = new Template(template, this.data, customData); return page.renderSync(); }; Template.prototype.partialWatch = function (v, data) { var template = data[0] var customData = data[1] var page = new Template(template, customData, undefined); return page.renderSync(); }; Template.prototype.extend = function (template, customData) { var page = new Template(template, this.data, customData) var callback = this.callback; page.blocks = this.blocks; this.callback = function (error, data) { if (error) { page.childError = error; if (page.childCallback) { page.childCallback(error); } } else { page.childData.push(data); if (page.childCallback) { page.childCallback(); } } }; page.partials.push(function (callback) { if (page.childError) { callback(page.childError); } else if (page.childData.length) { callback(); } else { page.childCallback = callback; } }); //callback(undefined, page.renderSync()); return page.renderSync(); }; Template.prototype.child = function (block) { if (block && block.length) { if (!this.blocks[block]) { this.blocks[block] = []; } return this.blocks[block]; } return this.childData; }; function arrayRender(arr){ let html = '' for(let i in arr) { html += (Array.isArray(arr[i])) ? arrayRender(arr[i]) : arr[i]; } return html } Template.prototype.renderSync = function () { var that = this; var blank = loadSync(this.file) // try { var buffer = blank.call(that); for(var i = 0; i < that.partials.length; i++) { that.partials[i].call(); } var html = '', length, i; for (i = 0, length = buffer.length; i < length; i++) { html += (Array.isArray(buffer[i])) ? arrayRender(buffer[i]) : buffer[i]; } return html; // } catch (e) { // window.imbaLog.error(e.message + ' in ' + that.file + ' on line ' + that.line); // throw e.message + ' in ' + that.file + ' on line ' + that.line // return; // } }; this.configure = function (newOptions) { var option; newOptions = newOptions || {}; for (option in options) { options[option] = newOptions[option] || options[option]; } }; this.renderSync = function (template, data, onInsertFunc) { if(data === undefined) { data = {} } var tpl = new Template(template, data); var html = tpl.renderSync(); if(html == undefined) { window.imbaLog.error("renderSync error", template, data) } if(typeof onInsertFunc == 'function') { html = this.onInsert(html, onInsertFunc, false); } return html; }; this.render = this.renderSync this.isTplExists = function(name){ return options.root[name] !== undefined } /** * При вставке этого html в дом дерево будет выполнена функция func * @param {type} html * @param {type} func * @returns {unresolved} */ this.onInsert = onInsert this.configure(newOptions); }; if (!Array.prototype.filter) { Array.prototype.filter = function (fun, thisp) { var len = this.length, res = [], i, val; if (typeof fun !== 'function') { throw new TypeError(); } for (i = 0; i < len; i++) { if (i in this) { val = this[i]; if (fun.call(thisp, val, i, this)) { res.push(val); } } } return res; }; } if (!Array.prototype.forEach) { Array.prototype.forEach = function (fun, thisp) { var len = this.length, i; if (typeof fun !== 'function') { throw new TypeError(); } for (i = 0; i < len; i++) { if (i in this) { fun.call(thisp, this[i], i, this); } } }; } if (!Array.isArray) { Array.isArray = function (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; } var cbSplit; if (!cbSplit) { cbSplit = function (str, separator, limit) { if (Object.prototype.toString.call(separator) !== '[object RegExp]') { return cbSplit.nativeSplit.call(str, separator, limit); } var output = [], lastLastIndex = 0, flags = (separator.ignoreCase ? 'i' : '') + (separator.multiline ? 'm' : '') + (separator.sticky ? 'y' : ''), separator2, match, lastIndex, lastLength; separator = new RegExp(separator.source, flags + 'g'); str = str + ''; if (!cbSplit.compliantExecNpcg) { separator2 = new RegExp('^' + separator.source + '$(?!\\s)', flags); } if (limit === undefined || +limit < 0) { limit = Infinity; } else { limit = Math.floor(+limit); if (!limit) { return []; } } while (match = separator.exec(str)) { lastIndex = match.index + match[0].length; if (lastIndex > lastLastIndex) { output.push(str.slice(lastLastIndex, match.index)); if (!cbSplit.compliantExecNpcg && match.length > 1) { match[0].replace(separator2, function () { var i; for (i = 1; i < arguments.length - 2; i++) { if (arguments[i] === undefined) { match[i] = undefined; } } }); } if (match.length > 1 && match.index < str.length) { Array.prototype.push.apply(output, match.slice(1)); } lastLength = match[0].length; lastLastIndex = lastIndex; if (output.length >= limit) { break; } } if (separator.lastIndex === match.index) { separator.lastIndex++; } } if (lastLastIndex === str.length) { if (lastLength || !separator.test('')) { output.push(''); } } else { output.push(str.slice(lastLastIndex)); } return output.length > limit ? output.slice(0, limit) : output; }; cbSplit.compliantExecNpcg = /()??/.exec('')[1] === undefined; cbSplit.nativeSplit = String.prototype.split; } String.prototype.split = function (separator, limit) { return cbSplit(this, separator, limit); }; window.JUST = JUST; window.JUST.onInsert = onInsert; window.JUST.getUUID = getUUID; window.JUST.__JUST_onInsertFunctions = __JUST_onInsertFunctions; window.JUST.JustEvalJsPattern_pageUUID = JustEvalJsPattern_pageUUID; window.JUST.JustEvalJsPattern = JustEvalJsPattern; }()); function JustEscapeHtml(text) { var map = { '&': '&', '<': '‹', '>': '›', '"': '”', "'": '‘' }; if(!text || !text.replace) { return text+""; } return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } window.JustEscapeHtml = JustEscapeHtml function JustWaitResults(data) { if(typeof data == "object" && data != null) { if(data && data.then) { let id = "just-"+Math.random()+""+Math.random() id = id.replace(/0\./g, ""); return JUST.onInsert('
', function() { data.then((d) => { $("#"+id).replaceWithTpl(d) }, (e) => { $("#"+id).replaceWithTpl(e) }) }, true) } } return data } window.JustWaitResults = JustWaitResults justCall_mapArr = [] function justCall(obj) { var index = justCall_mapArr.length justCall_mapArr.push(obj) return 'justCall_mapArr['+index+']' } window.justCall = justCall function justOn(event, action, id) { if(!id) { id = "justId"+Math.floor(Math.random()*900000); } return JUST.onInsert(" id='"+id+"' ", function(){ $("#"+id).on(event, action) }, true) } window.justOn = justOn // 1 - ./plugins/imbasynergy/imbachat/assets/js/justReactive.js /* * https://github.com/Levhav/justReactive * Adding reactive to string templates engine. * * Apache License 2.0 */ window.__JustEvalJsPattern_reg_pageUUID = new RegExp("<="+window.JUST.JustEvalJsPattern_pageUUID+"(.*?)"+window.JUST.JustEvalJsPattern_pageUUID+"=>", "g") /** * Плагин для вставки шаблона в тело элемента * @param {string} tplText * * После вставки переданого хтимл кода выполняет js код который был в блоках * Например строка "html html" будет вставлено "html html" и потом выполнено window.imbaLog.log("test"); */ var _insertTpl = function(func) { return function(tplText){ if(!tplText) { return this; } if(!this.length) { return this; } if(typeof tplText !== "string") { tplText = ""+tplText } /*let val = this[0].getAttribute("data-tplText") let testTplText = tplText.replace(/just-watch-class-[0-9]+/g, "") .replace(/justId[0-9]+/g, "") .replace(/__JUST_onInsertFunctions\['[^']+']/g, "") if( val == testTplText) { return this; } this[0].setAttribute("data-tplText", testTplText)*/ var html = tplText.replace(window.__JustEvalJsPattern_reg_pageUUID, "") this.each(function() { var oldHtml = jQuery(this).find( "[data-onunload]" ); for(var i = 0; i < oldHtml.length; i++) { eval(oldHtml[i].attr('data-onunload')) } // Вызвать unload тут! // Будет удобно если можно в люборм месте определить функцию которая будет вызвана при затирании этого куска html кода jQuery(this)[func](html) }); var js = tplText.match(window.__JustEvalJsPattern_reg_pageUUID) if(js) { for(var i =0; i< js.length; i++) { if(js[i] && js[i].length > 8); { try{ var code = js[i].substr(2 + window.JUST.JustEvalJsPattern_pageUUID.length, js[i].length - (4+window.JUST.JustEvalJsPattern_pageUUID.length*2)) //window.imbaLog.log(i, code) eval(code); }catch (exception) { debugger; } } } } return this; } }; jQuery.fn.insertTpl = _insertTpl('html') jQuery.fn.appendTpl = _insertTpl('append') jQuery.fn.prependTpl = _insertTpl('prepend') jQuery.fn.replaceWithTpl = _insertTpl('replaceWith') // 2 - ./plugins/imbasynergy/octoberspa/assets/js/spa.js /** * Модуль перевода * @param {String} text * @returns {window.gettext.data|Window.gettext.data} */ if (!window.isCordova) { // Использовать HistoryApi window.isCordova = function () { try{ if (window.cordova) { return true; } else if (window.parent && window.parent.cordova) { return true; } else { return false; } }catch (exception) { //console.warn(exception) } return false; } } if (!window.openExternalURL) { window.openExternalURL = function (url) { return url; } } if (!window.SpaJs) { /** * Класс приложения single page application * @returns {spajs} */ window.SpaJs = function (spa_name) { this.spa_name = spa_name var spajs = this; spajs.version = "2.2"; /** * Указывает на то как прошла иницитализация * @type Boolean * @private */ spajs.initSuccess = false; /** * Указывает на то что иницитализация была уже запущена * @type Boolean * @private */ spajs.initProgress = false; spajs.opt = {}; spajs.opt.holder = "body" /** * Путь к папке с аватарками пользователей * @type String */ spajs.opt.avatar_prefix = ""; spajs.opt.menu_url = "spa" /** * Указывает добавлять или не добавлять пареметры урла * @type Boolean */ spajs.opt.addParamsToUrl = true spajs.opt.urlDelimetr = "/" spajs.opt.allSpa = false if (window.cordova) { spajs.opt.allSpa = true; } /** * Масив с описанием пунктов меню * @type Array */ spajs.opt.menu = [] /** * @param {object} options * * Генерирует события * onOffline * onOnline * onAnyTabActivated * */ spajs.linkInit = function () { if(spajs.opt.initLinks == false){ return } let linksArr = $(".spa-link"); for (let i = 0; i < linksArr.length; i++) { if ($(linksArr[i]).attr('data-spa-link')) { continue; } else if ($(linksArr[i]).attr('data-request')) { continue; } $(linksArr[i]).on('click', function () { if (this.href) { spajs.openURL(this.href); return false; }; }).attr('data-spa-link', 'true') } let spaLink = $(".spa-link a"); for (let i = 0; i < spaLink.length; i++) { if ($(spaLink[i]).attr('href') != undefined) { if ($(spaLink[i]).attr('data-spa-link')) { continue } else if ($(spaLink[i]).attr('data-request') || $(spaLink[i]).attr('href').charAt(0) == '#') { $(spaLink[i]).addClass('not-spa-link') continue; } $(spaLink[i]).on('click', function () { if (this.href) { spajs.openURL(this.href); return false; } }).attr('data-spa-link', 'true') } } }; spajs.init = function (options) { if (spajs.initProgress === true) { return; } spajs.initProgress = true; for (var i in options) { if (spajs.opt[i] && typeof (spajs.opt[i]) == "object") { for (var j in options[i]) { spajs.opt[i][j] = options[i][j] } } spajs.opt[i] = options[i] } this.linkInit(); // Фиксируем факт того что страница не активна http://javascript.ru/forum/events/2498-kak-opredelit-aktivnoe-okno-vkladku.html jQuery(window).blur(function () { // Здесь что угодно после ухода в другую вкладку spajs.isActiveTab = false; }); // Фиксируем факт того что страница активна jQuery(window).focus(function () { // Здесь что угодно после возвращения во вкладку spajs.isActiveTab = true; // Уведомим всех о том что одна из вкладок чата активирована tabSignal.emitAll("onAnyTabActivated", {}) tabSignal.emitAll(spa_name + "onAnyTabActivated", {}) }); var lastOnlineStatus = undefined; setInterval(function () { var status = spajs.isOnline(); //window.imbaLog.warn("offline event", status, lastOnlineStatus, status !== lastOnlineStatus && lastOnlineStatus !== undefined) if (status !== lastOnlineStatus && lastOnlineStatus !== undefined) { if (status) { // Переход в online window.imbaLog.warn("online event"); setTimeout(function () { if (!spajs.isOnline()) { return; } tabSignal.emitAll("onOnline", {}) tabSignal.emitAll(spa_name + "onOnline", {}) }, 5000) } else { // Переход в offline window.imbaLog.warn("offline event") tabSignal.emitAll("onOffline", {}) tabSignal.emitAll(spa_name + "onOffline", {}) } } lastOnlineStatus = status; }, 500) if (spajs.opt.useHistoryApi) { // Код обработки popstate перенесён сюда из spajs чтоб кнопка назад возвращала на список контактов и не дальше // и даже если истории раньше небыло то всё равно кнопка назад ВСЕПГДА возвращала на список контактов // по этому опция spajs.opt.useHistoryApi взята из spajs а не paradiseChat хоть это архетектурно не красиво //window.imbaLog.log("bind for popstate event") //jQuery(window).bind('popstate', function(event) window.addEventListener('popstate', function (event) { spajs.openMenuFromUrl(window.location.href, { after_push_state: true }) }); } else { //window.imbaLog.log("not bind for popstate event") } } /** * Для обработки клика на ссылки * @param {string} url * @param {string} title * @returns {boolean} * * @example spajs.openURL("https://app.chat-server.comet-server.com/dev-18/t-chatterbox/") (Надо передавать полный урл) */ spajs.openURL = function (url, title) { var res = spajs.openMenuFromUrl(url, { withoutFailPage: true }) if (!res) { return false; } var state = res.state() return state == "rejected" } /** * Открывает пункт меню на основе параметров из url ( window.location.href ) * Ищет в адресе парамет spajs.opt.menu_url и на основе его значения открывает пункт меню. * @returns {boolean} Если параметр не найден или информации в нём содержится о не зарегистрированном menuId то вернёт false */ spajs.openMenuFromUrl = function (event_state, opt = {}) { if (event_state && event_state.indexOf(window.location.hostname) == -1) { window.location.replace(event_state) return } if (event_state) { if (event_state.indexOf("//") == 0) { event_state = window.location.protocol + event_state } opt.menuId = event_state } else { // Если menu_url не задан то используем window.location.hash opt.menuId = window.location.pathname } return spajs.open(opt) } /** * Открывает окно с произвольным содержимым * @param string menuId * @param array addUrlParams Дополнительная информация которая будет передана в .onOpen для обработчика пункта меню * @param boolean notAddToHistory не добавлять переход в историю браузера * @param object event_state * @public * * @note выполняется синхронно но событие onOpen у пункта меню может работать не синхронно и зависит от реализыции колбека навешаного на onOpen * @deprecated Заменён методом spajs.open */ // spajs.getAjaxURL = function (url) { // if (window.app_host != undefined && window.app_host != url) { // url = window.app_host // } // return url; // } spajs.openMenu = function (menuId, addUrlParams, notAddToHistory, event_state) { return spajs.open({ menuId: menuId, addUrlParams: addUrlParams, notAddToHistory: notAddToHistory, event_state: event_state }) } spajs.setUrlParam = function (params, title) { var url = window.location.pathname + "?" + params.toString(); if (typeof params === "object") { var new_url = window.location.href; for (var i in params) { if (!params.hasOwnProperty(i)) { continue; } var name = i; var value = params[i]; if (value == undefined) { // Если параметр равен undefined то его надо удалить из строки урла new_url = new_url.replace(new RegExp(name + "=[^&\/]+"), ""); } else { if (!new_url.match(new RegExp(name + "=[^&\/]+"))) { if (new_url.indexOf("?") != -1) { new_url += "&" + name + "=" + value; } else { new_url += "?" + name + "=" + value; } } else { new_url = new_url.replace(new RegExp(name + "=[^&\/]+"), name + "=" + value); } } } url = new_url.replace(/&+/img, "&").replace(/&+$/img, "").replace(/\?+$/img, "").replace(/\?&+/img, "?") } if (!spajs.opt.addParamsToUrl) { url = window.location.href; } return new_url; } spajs.getUrlParam = function (name, event_state) { var url_param = window.location.href.replace(/^.*?[#?](.*)$/, "$1"); if (event_state !== undefined && event_state.url) { url_param = event_state.url.replace(/^.*?[#?](.*)$/, "$1"); } var param = url_param.match(new RegExp(name + "=[^&\/]+"), "g") if (!param || !param.length) { return false; } return param[0].replace(name + "=", "").replace(/#$/, "") } spajs.getAllUrlParam = function (event_state) { var url_param = window.location.href.replace(/^.*?[#?](.*)$/, "$1"); if (event_state !== undefined && event_state.url) { url_param = event_state.url.replace(/^.*?[#?](.*)$/, "$1"); } var param = url_param.split(/[&?]/g) var res = {} if (param && param.length) { for (var i = 0; i < param.length; i++) { param[i] = param[i].split("=") res[param[i][0]] = param[i][1]; } } return res } /** * Добавляет произвольный пункт меню * @param {object} menu Описание пункта меню * @public * * * Пример добавления произвольного пункта меню. spajs.addMenu({ id:"terms_of_use", // id комнаты должен соответсвовать регулярному выражению [A-z9-0_]{ 4,64} или быть объектом класса RegExp name:"Условия использования", // Имя кнопки urlregexp:[/^param;[0-9]+$/] // Массив регулярных выражений урла которым соответсует данный пункт меню url: "#", // url ссылки type:"bottom", // Тип пункта меню (false|bottom|custom) menuHtml: "html code", // Если тип меню custom то из этого поля берётся код на вставку его в левую колонку priority:1, // Приоритет для сортировки порядка блоков /* * callback вызываемый по открытии этого пункта меню * @param object holder html элемент в списке меню * @param object menuInfo Информация о том пункет меню на который совершён переход * @param object data Объект с данными урла, { reg:{}, url:{} } где reg - совпадения в регулярке, url - данные всех параметров урла * / onOpen:function(holder, menuInfo, data) { jQuery(holder).insertTpl(jQuery("#terms_of_use").html()) }, /* * callback вызываемый по открытии другого пункта меню и закрытии этого пункта меню * @param object menuInfo Информация о том пункет меню на который совершён переход * / onClose:function(menuInfo) { }, /* * callback вызываемый по завершению вставки пункта меню в меню * @param object holder html элемент в списке меню * / onInsert:function(holder) { }, }) * * Примечание: * Если тип меню type=custom то в коде этого эемента меню надо самостоятельно разместить вызов функции spajs.openMenu('menu_id'); для клика и открытия. * @note выполняется синхронно */ spajs.addMenu = function (menu) { if (!menu.id) { menu.id = Math.random() } if (!menu.type) { menu.type = "custom" } var targetBlock = jQuery("#left_sidebar") for (var i in spajs.opt.menu) { if (spajs.opt.menu[i].id == menu.id) { // Такой пункт уже есть в меню return; } } if (!menu.priority) { menu.priority = 0; } spajs.opt.menu.push(menu) if (menu.type == "custom") { targetBlock.append('
' + menu.menuHtml + '
'); } else if (menu.type == "hidden") { // Невидимый пункт меню. } //spajs.sortMenu(targetBlock) if (menu.onInsert) { menu.onInsert(jQuery("#spajs-menu-" + menu.id)) } } spajs.currentOpenMenu = undefined spajs.pageLoaded = new jQuery.Deferred(); window.addEventListener('load', function (e) { window.imbaLog.info("window.loaded") spajs.pageLoaded.resolve() }); jQuery(document).ready(function (e) { window.imbaLog.info("window.ready") spajs.pageLoaded.resolve() }); spajs.loadServerPage = function (url, after_push_state) { if(spajs.opt.loadServerPage) { return spajs.opt.loadServerPage(url, after_push_state) } var def = new jQuery.Deferred(); // if (!after_push_state && url == window.location.href) { // window.imbaLog.info("page.opening") // $.when(spajs.pageLoaded).done(() => { // window.imbaLog.info("page.opend") // tabSignal.emit("loading.newPage") // tabSignal.emit("loading.newPage.once") // tabSignal.disconnectAllFromSignal("loading.newPage.once") // def.resolve() // }) // return def.promise(); // } jQuery.ajax({ type: "POST", url: url, // data: 'ajax=1', success: function (res) { if (res.error_redirect_reload) { def.reject(res) window.location.replace(res.error_redirect_reload); return; } if (res.error_redirect) { def.reject(res) return spajs.openURL(res.error_redirect); } let dom = $(res) for (let i = 0; i < dom.length; i++) { if (dom[i].id == "page") { reloadElem = dom[i] break } } // вынести в конфиг ид элемента $("#page").replaceWith(reloadElem) tabSignal.emit("loading.newPage") // вынести в конфиг класс или список классов spajs.linkInit(); def.resolve() }, error: function (res) { def.reject() }, }); return def.promise(); } spajs.removeAllNavigation = function() { spajs.opt.menu = [] } /** * Открывает окно с произвольным содержимым * @param string menuId * @param array addUrlParams Дополнительная информация которая будет передана в .onOpen для обработчика пункта меню * @param boolean notAddToHistory не добавлять переход в историю браузера * @param object event_state * @public * * @return jQuery.Deferred обещание полученое от функции open или обещание созданое в нутри функции * @note выполняется синхронно но событие onOpen у пункта меню может работать не синхронно и зависит от реализыции колбека навешаного на onOpen */ spajs.open = function (opt) { if (typeof opt == "string") { opt = { menuId: opt } } let page_new_url = opt.menuId; if(isCordova()) { page_new_url = window.location.origin + window.location.pathname + spajs.opt.urlDelimetr + opt.menuId; } else if (!/^http/.test(page_new_url)) { page_new_url = window.location.origin + spajs.opt.urlDelimetr + opt.menuId; } window.imbaLog.log("spajs.open: `" + opt.menuId + "`", page_new_url) if (!opt.menuId) { opt.menuId = ""; } if (opt.reopen === undefined) { opt.reopen = true; } var def = new jQuery.Deferred(); if (!spajs.opt.addParamsToUrl && opt.event_state == undefined) { opt.event_state = {} opt.event_state.url = window.location.href; } var regExpRes = [] var menuInfo = undefined; for (var i in spajs.opt.menu) { val = spajs.opt.menu[i] if (spajs.opt.menu[i].url_parser != undefined) { for (var j in spajs.opt.menu[i].url_parser) { var parsed = spajs.opt.menu[i].url_parser[j](opt.menuId) if (parsed) { regExpRes = parsed menuInfo = spajs.opt.menu[i] break; } } } else if (spajs.opt.menu[i].urlregexp != undefined) { for (var j in spajs.opt.menu[i].urlregexp) { if (spajs.opt.menu[i].urlregexp[j].test(opt.menuId)) { regExpRes = spajs.opt.menu[i].urlregexp[j].exec(opt.menuId) menuInfo = spajs.opt.menu[i] break; } } } else if (spajs.opt.menu[i].id == opt.menuId) { menuInfo = spajs.opt.menu[i] break; } } //window.imbaLog.log("openMenu", menuId, menuInfo) if (!menuInfo || !menuInfo.onOpen) { jQuery("body").addClass("in-loading"); jQuery.when(spajs.loadServerPage(page_new_url, opt.after_push_state)).done(() => { jQuery("body").removeClass("in-loading"); if (spajs.opt.useHistoryApi && !opt.after_push_state) { history.pushState({ page_new_url: page_new_url }, page_new_url, page_new_url); } def.resolve() }).fail((err) => { jQuery("body").removeClass("in-loading"); tabSignal.emit("spajs.not_registered", opt) //window.imbaLog.error("URL not registered", opt.menuId, opt) def.reject({ detail: "Error URL not registered", status: 404 }) //throw { text:"URL not registered " + opt.menuId, code:404}; }) return def.promise(); } if (spajs.currentOpenMenu && menuInfo.id == spajs.currentOpenMenu.id && !opt.reopen) { window.imbaLog.warn("Re-opening the menu", menuInfo) if (spajs.opt.useHistoryApi && !opt.after_push_state) { history.pushState({ page_new_url: page_new_url }, page_new_url, page_new_url); } def.resolve() return def.promise(); } if (opt.addUrlParams === undefined) { opt.addUrlParams = {} } if (spajs.opt.menu_url) { opt.addUrlParams[spajs.opt.menu_url] = opt.menuId; if (!opt.notAddToHistory) { var url = spajs.setUrlParam(opt.addUrlParams, menuInfo.title || menuInfo.name) if (opt.event_state) { opt.event_state.url = url; } } } else if (!opt.notAddToHistory) { var url = spajs.setUrlParam(opt.menuId, menuInfo.title || menuInfo.name) if (opt.event_state) { opt.event_state.url = url; } } if (spajs.currentOpenMenu && spajs.currentOpenMenu.onClose) { //window.imbaLog.log("onClose", spajs.currentOpenMenu) spajs.currentOpenMenu.onClose(menuInfo); } var data = {} data.url = spajs.getAllUrlParam(opt.event_state) data.reg = regExpRes if (menuInfo.hideMenu) { jQuery(spajs.opt.holder).addClass("spajs-spa-show-page"); } //window.imbaLog.log("onOpen", menuInfo) if (spajs.currentOpenMenu && spajs.currentOpenMenu.id) { jQuery("body").removeClass("spajs-active-" + spajs.currentOpenMenu.id) } else { //error("Не удалён предыдущий класс меню", spajs.currentOpenMenu, menuInfo) } jQuery(spajs.opt.holder).addClass("spajs-active-" + menuInfo.id); spajs.urlInfo = { menuInfo: menuInfo, data: data } tabSignal.emit("spajsOpen", { menuInfo: menuInfo, data: data }) tabSignal.emit(spa_name + "spajsOpen", { menuInfo: menuInfo, data: data }) var res = menuInfo.onOpen(spajs.opt.holder, menuInfo, data); if (res) { // debugger; // in-loading jQuery("body").addClass("in-loading") //window.imbaLog.time("Mopen") jQuery("#spajs-menu-" + menuInfo.id).addClass("menu-loading") setTimeout(function () { jQuery.when(res).done(function () { //window.imbaLog.timeEnd("Mopen") jQuery("#spajs-menu-" + menuInfo.id).removeClass("menu-loading") // in-loading jQuery("body").removeClass("in-loading") if (spajs.opt.useHistoryApi && !opt.after_push_state) { history.pushState({ page_new_url: page_new_url }, page_new_url, page_new_url); } def.resolve() }).fail(function (e) { //window.imbaLog.timeEnd("Mopen") jQuery("#spajs-menu-" + menuInfo.id).removeClass("menu-loading") // in-loading jQuery("body").removeClass("in-loading") def.reject(e) }) }, 0) } else { jQuery("body").removeClass("in-loading") if (spajs.opt.useHistoryApi && !opt.after_push_state) { history.pushState({ page_new_url: page_new_url }, page_new_url, page_new_url); } def.resolve() res = def } // Выделяем нашу комнату как активную в меню с лева jQuery("#left_sidebar li").removeClass("active") jQuery("#spajs-menu-" + menuInfo.id).addClass("active") spajs.currentOpenMenu = menuInfo; if (opt.callback) { opt.callback(); } jQuery.when(res).always(() => { window.imbaLog.info("page.opend (1)") tabSignal.emit("loading.newPage") tabSignal.emit("loading.newPage.once") tabSignal.disconnectAllFromSignal("loading.newPage.once") }) return res.promise(); } /** * Показывает анимацию загрузки на экране. * @param {promise} promise * @returns {undefined} */ spajs.showLoader = function (promise) { if (!promise) { var def = new jQuery.Deferred(); def.resolve() return def.promise(); } // in-loading jQuery("body").addClass("in-loading") jQuery.when(promise).then(function () { jQuery("body").removeClass("in-loading") }).fail(function () { // in-loading jQuery("body").removeClass("in-loading") }) return promise } spajs.urlRegExp = function (reg_exp) { return function (url) { let res = reg_exp.exec(url); if (!res) { return false; } if (res.groups) { return res.groups; } return res } } //****************************************************************************** //* Функции для работы с ajax запросами //****************************************************************************** spajs.ajax = function () { return this; } spajs.opt.ajax = {} spajs.ajax.headers = {} spajs.ajax.setHeader = function (header, data) { spajs.ajax.headers[header] = data } /** * Вернёт статус сети Online=true * @returns {navigator.onLine|window.navigator.onLine|Boolean} */ spajs.isOnline = function () { return navigator.onLine } /** * Массив для накопления очереди запросов на случай если мы ушли в офлайн * @type array */ spajs.ajax.ajaxQueue = undefined spajs.ajax.getQueue = function () { if(!window.WebDB) { return; } if(spajs.ajax.ajaxQueue) { return spajs.ajax.ajaxQueue; } spajs.ajax.ajaxQueue = new window.WebDB({ base_name:'imbachat-ajax-queue', index:[ {name:'time', unique:false} ] }); return spajs.ajax.ajaxQueue; } spajs.ajax.sendFromQueue = function () { let alldone = new jQuery.Deferred(); let all = [] spajs.ajax.getQueue().getMinValues('time').then((messages) => { for (var i in messages) { messages[i].ignore = true let promise = spajs.ajax.Call(messages[i]) spajs.ajax.getQueue().deleteValue(messages[i].queue_key) all.push(promise) } jQuery.when(...all).always(() => { alldone.resolve() }) }) return alldone.promise() } spajs.ajax.addToQueue = function (opt) { if(!opt.queue_key) { opt.queue_key = (Math.random()+""+Math.random()).replace(/\./gim, ''); } let obj = { time:new Date().getTime(), path:opt.queue_key, data:{ data:opt.data, url:opt.url, type:opt.type, queue_key:opt.queue_key, dataType:opt.dataType, } } spajs.ajax.getQueue().setValue(obj) } /** * Ключ reTryInOnline - включает помещение запросов в очередь если нет интернета до тех пор пока интернет не появится. * @param {type} opt * @returns {Boolean|undefined|jQuery.ajax} */ spajs.ajax.Call = function (opt) { if (!spajs.isOnline() && opt.ignore) { // Нет интернета и стоит опция ignore значит запрос не отправляем let def = new jQuery.Deferred(); def.reject(); return def.promise(); } let reTryInOnline = opt.reTryInOnline; if (!spajs.isOnline() && reTryInOnline) { // Нет интернета и стоит опция addToQueue значит запрос не отправляем но добавим в очередь на потом spajs.ajax.addToQueue(opt); let def = new jQuery.Deferred(); def.reject(); return def.promise(); } var def = new jQuery.Deferred(); var defpromise = def.promise() var successCallBack = opt.success var errorCallBack = opt.error opt.success = function (data, status, xhr) { if (successCallBack) successCallBack(data, status, xhr) def.resolve(data, status, xhr) } opt.error = function (data, status, xhr) { var headers = data.getAllResponseHeaders() if (data.status == 401 || (data.status == 403 && headers.indexOf("x-anonymous:") != -1)) { window.location.reload() return; } if(reTryInOnline) { spajs.ajax.addToQueue(opt); } if (errorCallBack) { errorCallBack(data, status, xhr) def.reject(data, status, xhr) } else { def.reject(data, status, xhr) } } if (!opt.beforeSend) { opt.beforeSend = function (xhr) { for (var i in spajs.ajax.headers) { xhr.setRequestHeader(i, spajs.ajax.headers[i]); } } } var res = jQuery.ajax(opt); res.addToQueue = false; res.ignore = false; defpromise.abort = function () { res.abort() } return defpromise } spajs.ajax.ajaxCallFromQueue = function () { for (var i in spajs.ajax.ajaxQueue) { jQuery.ajax(spajs.ajax.ajaxQueue[i]); } spajs.ajax.ajaxQueue = [] } return this; } } function setSpaPageType(type) { if (window.__lastSpaPageType && window.__lastSpaPageType != type) { setTimeout(() => { window.location.reload(); }, 50) } window.__lastSpaPageType = type } /* if (!window.spajs) { window.spajs = new SpaJs("spa_") window.spajs.init({ holder: "#page", useHistoryApi: true, initLinks: true, }) $(document).ready(function(){ window.tabSignal.emit('page.loaded'); }) window.tabSignal.on("page.loaded", () => { window.spajs.linkInit(); }); } */ // 3 - ./plugins/imbasynergy/imbachat/assets/js/MediaStreamRecorder.js // Last time updated: 2016-08-19 11:58:57 AM UTC // links: // Open-Sourced: https://github.com/streamproc/MediaStreamRecorder // https://cdn.WebRTC-Experiment.com/MediaStreamRecorder.js // https://www.WebRTC-Experiment.com/MediaStreamRecorder.js // npm install msr //------------------------------------ // Browsers Support:: // Chrome (all versions) [ audio/video separately ] // Firefox ( >= 29 ) [ audio/video in single webm/mp4 container or only audio in ogg ] // Opera (all versions) [ same as chrome ] // Android (Chrome) [ only video ] // Android (Opera) [ only video ] // Android (Firefox) [ only video ] // Microsoft Edge (Only Audio & Gif) //------------------------------------ // Muaz Khan - www.MuazKhan.com // MIT License - www.WebRTC-Experiment.com/licence //------------------------------------ // ______________________ // MediaStreamRecorder.js function MediaStreamRecorder(mediaStream) { if (!mediaStream) { throw 'MediaStream is mandatory.'; } // void start(optional long timeSlice) // timestamp to fire "ondataavailable" this.start = function(timeSlice) { var Recorder; if (typeof MediaRecorder !== 'undefined') { Recorder = MediaRecorderWrapper; } else if (IsChrome || IsOpera || IsEdge) { if (this.mimeType.indexOf('video') !== -1) { Recorder = WhammyRecorder; } else if (this.mimeType.indexOf('audio') !== -1) { Recorder = StereoAudioRecorder; } } // video recorder (in GIF format) if (this.mimeType === 'image/gif') { Recorder = GifRecorder; } // audio/wav is supported only via StereoAudioRecorder // audio/pcm (int16) is supported only via StereoAudioRecorder if (this.mimeType === 'audio/wav' || this.mimeType === 'audio/pcm') { Recorder = StereoAudioRecorder; } // allows forcing StereoAudioRecorder.js on Edge/Firefox if (this.recorderType) { Recorder = this.recorderType; } mediaRecorder = new Recorder(mediaStream); mediaRecorder.blobs = []; var self = this; mediaRecorder.ondataavailable = function(data) { window.imbaLog.log("ondataavailable 1", data) mediaRecorder.blobs.push(data); self.ondataavailable(data); }; mediaRecorder.onstop = this.onstop; mediaRecorder.onStartedDrawingNonBlankFrames = this.onStartedDrawingNonBlankFrames; // Merge all data-types except "function" mediaRecorder = mergeProps(mediaRecorder, this); mediaRecorder.start(timeSlice); }; this.onStartedDrawingNonBlankFrames = function() {}; this.clearOldRecordedFrames = function() { if (!mediaRecorder) { return; } mediaRecorder.clearOldRecordedFrames(); }; this.stop = function() { window.imbaLog.log("stop 1") if (mediaRecorder) { mediaRecorder.stop(); } }; this.ondataavailable = function(blob) { window.imbaLog.log('ondataavailable..', blob); }; this.onstop = function(error) { window.imbaLog.warn('stopped..', error); }; this.save = function(file, fileName) { if (!file) { if (!mediaRecorder) { return; } ConcatenateBlobs(mediaRecorder.blobs, mediaRecorder.blobs[0].type, function(concatenatedBlob) { invokeSaveAsDialog(concatenatedBlob); }); return; } invokeSaveAsDialog(file, fileName); }; this.pause = function() { if (!mediaRecorder) { return; } mediaRecorder.pause(); window.imbaLog.log('Paused recording.', this.mimeType || mediaRecorder.mimeType); }; this.resume = function() { if (!mediaRecorder) { return; } mediaRecorder.resume(); window.imbaLog.log('Resumed recording.', this.mimeType || mediaRecorder.mimeType); }; // StereoAudioRecorder || WhammyRecorder || MediaRecorderWrapper || GifRecorder this.recorderType = null; // video/webm or audio/webm or audio/ogg or audio/wav this.mimeType = 'video/webm'; // logs are enabled by default this.disableLogs = false; // Reference to "MediaRecorder.js" var mediaRecorder; } // ______________________ // MultiStreamRecorder.js function MultiStreamRecorder(mediaStream) { if (!mediaStream) { throw 'MediaStream is mandatory.'; } var self = this; var isMediaRecorder = isMediaRecorderCompatible(); this.stream = mediaStream; // void start(optional long timeSlice) // timestamp to fire "ondataavailable" this.start = function(timeSlice) { audioRecorder = new MediaStreamRecorder(mediaStream); videoRecorder = new MediaStreamRecorder(mediaStream); audioRecorder.mimeType = 'audio/ogg'; videoRecorder.mimeType = 'video/webm'; for (var prop in this) { if (typeof this[prop] !== 'function') { audioRecorder[prop] = videoRecorder[prop] = this[prop]; } } audioRecorder.ondataavailable = function(blob) { window.imbaLog.log("ondataavailable 2", blob) if (!audioVideoBlobs[recordingInterval]) { audioVideoBlobs[recordingInterval] = {}; } audioVideoBlobs[recordingInterval].audio = blob; if (audioVideoBlobs[recordingInterval].video && !audioVideoBlobs[recordingInterval].onDataAvailableEventFired) { audioVideoBlobs[recordingInterval].onDataAvailableEventFired = true; fireOnDataAvailableEvent(audioVideoBlobs[recordingInterval]); } }; videoRecorder.ondataavailable = function(blob) { window.imbaLog.log("ondataavailable 3", blob) if (isMediaRecorder) { return self.ondataavailable({ video: blob, audio: blob }); } if (!audioVideoBlobs[recordingInterval]) { audioVideoBlobs[recordingInterval] = {}; } audioVideoBlobs[recordingInterval].video = blob; if (audioVideoBlobs[recordingInterval].audio && !audioVideoBlobs[recordingInterval].onDataAvailableEventFired) { audioVideoBlobs[recordingInterval].onDataAvailableEventFired = true; fireOnDataAvailableEvent(audioVideoBlobs[recordingInterval]); } }; function fireOnDataAvailableEvent(blobs) { recordingInterval++; self.ondataavailable(blobs); } videoRecorder.onstop = audioRecorder.onstop = function(error) { self.onstop(error); }; if (!isMediaRecorder) { // to make sure both audio/video are synced. videoRecorder.onStartedDrawingNonBlankFrames = function() { videoRecorder.clearOldRecordedFrames(); audioRecorder.start(timeSlice); }; videoRecorder.start(timeSlice); } else { videoRecorder.start(timeSlice); } }; this.stop = function() { window.imbaLog.log("stop 2") if (audioRecorder) { audioRecorder.stop(); } if (videoRecorder) { videoRecorder.stop(); } }; this.ondataavailable = function(blob) { window.imbaLog.log('ondataavailable..', blob); }; this.onstop = function(error) { window.imbaLog.warn('stopped..', error); }; this.pause = function() { if (audioRecorder) { audioRecorder.pause(); } if (videoRecorder) { videoRecorder.pause(); } }; this.resume = function() { if (audioRecorder) { audioRecorder.resume(); } if (videoRecorder) { videoRecorder.resume(); } }; var audioRecorder; var videoRecorder; var audioVideoBlobs = {}; var recordingInterval = 0; } if (typeof MediaStreamRecorder !== 'undefined') { MediaStreamRecorder.MultiStreamRecorder = MultiStreamRecorder; } // _____________________________ // Cross-Browser-Declarations.js var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45'; (function(that) { if (typeof window !== 'undefined') { return; } if (typeof window === 'undefined' && typeof global !== 'undefined') { global.navigator = { userAgent: browserFakeUserAgent, getUserMedia: function() {} }; /*global window:true */ that.window = global; } else if (typeof window === 'undefined') { // window = this; } if (typeof document === 'undefined') { /*global document:true */ that.document = {}; document.createElement = document.captureStream = document.mozCaptureStream = function() { return {}; }; } if (typeof location === 'undefined') { /*global location:true */ that.location = { protocol: 'file:', href: '', hash: '' }; } if (typeof screen === 'undefined') { /*global screen:true */ that.screen = { width: 0, height: 0 }; } })(typeof global !== 'undefined' ? global : window); // WebAudio API representer var AudioContext = window.AudioContext; if (typeof AudioContext === 'undefined') { if (typeof webkitAudioContext !== 'undefined') { /*global AudioContext:true */ AudioContext = webkitAudioContext; } if (typeof mozAudioContext !== 'undefined') { /*global AudioContext:true */ AudioContext = mozAudioContext; } } if (typeof window === 'undefined') { /*jshint -W020 */ window = {}; } // WebAudio API representer var AudioContext = window.AudioContext; if (typeof AudioContext === 'undefined') { if (typeof webkitAudioContext !== 'undefined') { /*global AudioContext:true */ AudioContext = webkitAudioContext; } if (typeof mozAudioContext !== 'undefined') { /*global AudioContext:true */ AudioContext = mozAudioContext; } } /*jshint -W079 */ var URL = window.URL; if (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') { /*global URL:true */ URL = webkitURL; } if (typeof navigator !== 'undefined') { if (typeof navigator.webkitGetUserMedia !== 'undefined') { navigator.getUserMedia = navigator.webkitGetUserMedia; } if (typeof navigator.mozGetUserMedia !== 'undefined') { navigator.getUserMedia = navigator.mozGetUserMedia; } } else { navigator = { getUserMedia: function() {}, userAgent: browserFakeUserAgent }; } var IsEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveBlob || !!navigator.msSaveOrOpenBlob); var IsOpera = false; if (typeof opera !== 'undefined' && navigator.userAgent && navigator.userAgent.indexOf('OPR/') !== -1) { IsOpera = true; } var IsChrome = !IsEdge && !IsEdge && !!navigator.webkitGetUserMedia; var MediaStream = window.MediaStream; if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') { MediaStream = webkitMediaStream; } /*global MediaStream:true */ if (typeof MediaStream !== 'undefined') { if (!('getVideoTracks' in MediaStream.prototype)) { MediaStream.prototype.getVideoTracks = function() { if (!this.getTracks) { return []; } var tracks = []; this.getTracks.forEach(function(track) { if (track.kind.toString().indexOf('video') !== -1) { tracks.push(track); } }); return tracks; }; MediaStream.prototype.getAudioTracks = function() { if (!this.getTracks) { return []; } var tracks = []; this.getTracks.forEach(function(track) { if (track.kind.toString().indexOf('audio') !== -1) { tracks.push(track); } }); return tracks; }; } if (!('stop' in MediaStream.prototype)) { MediaStream.prototype.stop = function() { window.imbaLog.log("stop 3") this.getAudioTracks().forEach(function(track) { if (!!track.stop) { track.stop(); } }); this.getVideoTracks().forEach(function(track) { if (!!track.stop) { track.stop(); } }); }; } } if (typeof location !== 'undefined') { if (location.href.indexOf('file:') === 0) { //window.imbaLog.error('Please load this HTML file on HTTP or HTTPS.'); } } // Merge all other data-types except "function" function mergeProps(mergein, mergeto) { for (var t in mergeto) { if (typeof mergeto[t] !== 'function') { mergein[t] = mergeto[t]; } } return mergein; } // "dropFirstFrame" has been added by Graham Roth // https://github.com/gsroth function dropFirstFrame(arr) { arr.shift(); return arr; } /** * @param {Blob} file - File or Blob object. This parameter is required. * @param {string} fileName - Optional file name e.g. "Recorded-Video.webm" * @example * invokeSaveAsDialog(blob or file, [optional] fileName); * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code} */ function invokeSaveAsDialog(file, fileName) { if (!file) { throw 'Blob object is required.'; } if (!file.type) { try { file.type = 'video/webm'; } catch (e) {} } var fileExtension = (file.type || 'video/webm').split('/')[1]; if (fileName && fileName.indexOf('.') !== -1) { var splitted = fileName.split('.'); fileName = splitted[0]; fileExtension = splitted[1]; } var fileFullName = (fileName || (Math.round(Math.random() * 9999999999) + 888888888)) + '.' + fileExtension; if (typeof navigator.msSaveOrOpenBlob !== 'undefined') { return navigator.msSaveOrOpenBlob(file, fileFullName); } else if (typeof navigator.msSaveBlob !== 'undefined') { return navigator.msSaveBlob(file, fileFullName); } var hyperlink = document.createElement('a'); hyperlink.href = URL.createObjectURL(file); hyperlink.target = '_blank'; hyperlink.download = fileFullName; if (!!navigator.mozGetUserMedia) { hyperlink.onclick = function() { (document.body || document.documentElement).removeChild(hyperlink); }; (document.body || document.documentElement).appendChild(hyperlink); } var evt = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); hyperlink.dispatchEvent(evt); if (!navigator.mozGetUserMedia) { URL.revokeObjectURL(hyperlink.href); } } function bytesToSize(bytes) { var k = 1000; var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes === 0) { return '0 Bytes'; } var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10); return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]; } // ______________ (used to handle stuff like http://goo.gl/xmE5eg) issue #129 // ObjectStore.js var ObjectStore = { AudioContext: AudioContext }; function isMediaRecorderCompatible() { var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; var isChrome = !!window.chrome && !isOpera; var isFirefox = typeof window.InstallTrigger !== 'undefined'; if (isFirefox) { return true; } if (!isChrome) { return false; } var nVer = navigator.appVersion; var nAgt = navigator.userAgent; var fullVersion = '' + parseFloat(navigator.appVersion); var majorVersion = parseInt(navigator.appVersion, 10); var nameOffset, verOffset, ix; if (isChrome) { verOffset = nAgt.indexOf('Chrome'); fullVersion = nAgt.substring(verOffset + 7); } // trim the fullVersion string at semicolon/space if present if ((ix = fullVersion.indexOf(';')) !== -1) { fullVersion = fullVersion.substring(0, ix); } if ((ix = fullVersion.indexOf(' ')) !== -1) { fullVersion = fullVersion.substring(0, ix); } majorVersion = parseInt('' + fullVersion, 10); if (isNaN(majorVersion)) { fullVersion = '' + parseFloat(navigator.appVersion); majorVersion = parseInt(navigator.appVersion, 10); } return majorVersion >= 49; } // ______________ (used to handle stuff like http://goo.gl/xmE5eg) issue #129 // ObjectStore.js var ObjectStore = { AudioContext: window.AudioContext || window.webkitAudioContext }; // ================== // MediaRecorder.js /** * Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html * The MediaRecorder accepts a mediaStream as input source passed from UA. When recorder starts, * a MediaEncoder will be created and accept the mediaStream as input source. * Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in EncodedBufferCache object. * The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function. * Thread model: * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object. * Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA. */ function MediaRecorderWrapper(mediaStream) { var self = this; /** * This method records MediaStream. * @method * @memberof MediaStreamRecorder * @example * recorder.record(); */ this.start = function(timeSlice, __disableLogs) { if (!self.mimeType) { self.mimeType = 'video/webm'; } var d = new Date() MediaRecorderWrapper.startTime = d.getTime() if (self.mimeType.indexOf('audio') !== -1) { if (mediaStream.getVideoTracks().length && mediaStream.getAudioTracks().length) { var stream; if (!!navigator.mozGetUserMedia) { stream = new MediaStream(); stream.addTrack(mediaStream.getAudioTracks()[0]); } else { // webkitMediaStream stream = new MediaStream(mediaStream.getAudioTracks()); } mediaStream = stream; } } if (self.mimeType.indexOf('audio') !== -1) { self.mimeType = IsChrome ? 'audio/webm' : 'audio/ogg'; } self.dontFireOnDataAvailableEvent = false; var recorderHints = { mimeType: self.mimeType }; if (!self.disableLogs && !__disableLogs) { window.imbaLog.log('Passing following params over MediaRecorder API.', recorderHints); } if (mediaRecorder) { // mandatory to make sure Firefox doesn't fails to record streams 3-4 times without reloading the page. mediaRecorder = null; } if (IsChrome && !isMediaRecorderCompatible()) { // to support video-only recording on stable recorderHints = 'video/vp8'; } // http://dxr.mozilla.org/mozilla-central/source/content/media/MediaRecorder.cpp // https://wiki.mozilla.org/Gecko:MediaRecorder // https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html // starting a recording session; which will initiate "Reading Thread" // "Reading Thread" are used to prevent main-thread blocking scenarios try { mediaRecorder = new MediaRecorder(mediaStream, recorderHints); } catch (e) { // if someone passed NON_supported mimeType // or if Firefox on Android mediaRecorder = new MediaRecorder(mediaStream); } if ('canRecordMimeType' in mediaRecorder && mediaRecorder.canRecordMimeType(self.mimeType) === false) { if (!self.disableLogs) { window.imbaLog.warn('MediaRecorder API seems unable to record mimeType:', self.mimeType); } } // i.e. stop recording when
', ); // Enable the simulcast selection buttons jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-success') .addClass('btn-primary') .unbind('click') .click(function () { guiToast.info({ title: 'Info', message: 'Switching simulcast substream, wait for it... (lower quality)', }); if (!jQuery('#sl' + index + '-2').hasClass('btn-success')) jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-info') .addClass('btn-primary'); if (!jQuery('#sl' + index + '-1').hasClass('btn-success')) jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-info') .addClass('btn-primary'); jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-info btn-success') .addClass('btn-info'); feeds[index].send({ message: { request: 'configure', substream: 0 }, }); }); jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-success') .addClass('btn-primary') .unbind('click') .click(function () { guiToast.info({ title: 'Info', message: 'Switching simulcast substream, wait for it... (normal quality)', }); if (!jQuery('#sl' + index + '-2').hasClass('btn-success')) jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-info') .addClass('btn-primary'); jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-info btn-success') .addClass('btn-info'); if (!jQuery('#sl' + index + '-0').hasClass('btn-success')) jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-info') .addClass('btn-primary'); feeds[index].send({ message: { request: 'configure', substream: 1 }, }); }); jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-success') .addClass('btn-primary') .unbind('click') .click(function () { guiToast.info({ title: 'Info', message: 'Switching simulcast substream, wait for it... (higher quality)', }); jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-info btn-success') .addClass('btn-info'); if (!jQuery('#sl' + index + '-1').hasClass('btn-success')) jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-info') .addClass('btn-primary'); if (!jQuery('#sl' + index + '-0').hasClass('btn-success')) jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-info') .addClass('btn-primary'); feeds[index].send({ message: { request: 'configure', substream: 2 }, }); }); if (!temporal) // No temporal layer support return; jQuery('#tl' + index + '-0') .parent() .removeClass('hide'); jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-success') .addClass('btn-primary') .unbind('click') .click(function () { guiToast.info({ title: 'Info', message: 'Capping simulcast temporal layer, wait for it... (lowest FPS)', }); if (!jQuery('#tl' + index + '-2').hasClass('btn-success')) jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-info') .addClass('btn-primary'); if (!jQuery('#tl' + index + '-1').hasClass('btn-success')) jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-info') .addClass('btn-primary'); jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-info btn-success') .addClass('btn-info'); feeds[index].send({ message: { request: 'configure', temporal: 0 }, }); }); jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-success') .addClass('btn-primary') .unbind('click') .click(function () { guiToast.info({ title: 'Info', message: 'Capping simulcast temporal layer, wait for it... (medium FPS)', }); if (!jQuery('#tl' + index + '-2').hasClass('btn-success')) jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-info') .addClass('btn-primary'); jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-info') .addClass('btn-info'); if (!jQuery('#tl' + index + '-0').hasClass('btn-success')) jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-info') .addClass('btn-primary'); feeds[index].send({ message: { request: 'configure', temporal: 1 }, }); }); jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-success') .addClass('btn-primary') .unbind('click') .click(function () { guiToast.info({ title: 'Info', message: 'Capping simulcast temporal layer, wait for it... (highest FPS)', }); jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-info btn-success') .addClass('btn-info'); if (!jQuery('#tl' + index + '-1').hasClass('btn-success')) jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-info') .addClass('btn-primary'); if (!jQuery('#tl' + index + '-0').hasClass('btn-success')) jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-info') .addClass('btn-primary'); feeds[index].send({ message: { request: 'configure', temporal: 2 }, }); }); } function updateSimulcastButtons(feed, substream, temporal) { // Check the substream var index = feed; if (substream === 0) { guiToast.success({ title: 'Success', message: 'Switched simulcast substream! (lower quality)', }); jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-info btn-success') .addClass('btn-success'); } else if (substream === 1) { guiToast.success({ title: 'Success', message: 'Switched simulcast substream! (medium quality)', }); jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-info btn-success') .addClass('btn-success'); jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-success') .addClass('btn-primary'); } else if (substream === 2) { guiToast.success({ title: 'Success', message: 'Switched simulcast substream! (highest quality)', }); jQuery('#sl' + index + '-2') .removeClass('btn-primary btn-info btn-success') .addClass('btn-success'); jQuery('#sl' + index + '-1') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#sl' + index + '-0') .removeClass('btn-primary btn-success') .addClass('btn-primary'); } // Check the temporal layer if (temporal === 0) { guiToast.success({ title: 'Success', message: 'Capped simulcast temporal layer! (lowest FPS)', }); jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-info btn-success') .addClass('btn-success'); } else if (temporal === 1) { guiToast.success({ title: 'Success', message: 'Capped simulcast temporal layer! (medium FPS)', }); jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-info btn-success') .addClass('btn-success'); jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-success') .addClass('btn-primary'); } else if (temporal === 2) { guiToast.success({ title: 'Success', message: 'Capped simulcast temporal layer! (highest FPS)', }); jQuery('#tl' + index + '-2') .removeClass('btn-primary btn-info btn-success') .addClass('btn-success'); jQuery('#tl' + index + '-1') .removeClass('btn-primary btn-success') .addClass('btn-primary'); jQuery('#tl' + index + '-0') .removeClass('btn-primary btn-success') .addClass('btn-primary'); } } const imVideoApi = function () { const thisApiObj = this; const opt = { server: 'wss://api.imbachat.com:8989/ws', onInitError: () => {}, onDestroyed: () => {}, onNewState: name => { //console.log(name); }, }; const optScreen = { server: 'wss://api.imbachat.com:8989/ws', onInitError: () => {}, onDestroyed: () => {}, onNewState: name => { //console.log(name); }, }; this.room = {}; let janus = null; var sfutest = null; let sfScreen = null; let screenJanus = null; var opaqueId = 'videocall-' + Janus.randomString(12); var opaqueIdScreenShare = 'videocallscreen-' + Janus.randomString(12); var myusername = null; var myid = null; var myScreenId = null; var myScreenPvtid = null; var myScreenStream = null; var mystream = null; // We use this other ID just to map our subscriptions to us var mypvtid = null; var setCallStat; var feeds = []; var screenFeeds = []; var bitrateTimer = []; var screenbitrateTimer = []; // subscriber_mode = false; let screenUsername = `screen-` + Janus.randomString(12); /** * Выставляет битрейт для звонка * @param {*} newBitrate число * @param {*} sfutest объект * @returns */ const setBitRate = (newBitrate, sfutest) => { if (!sfutest) { // sfutest is not ready return false; } const bitrate = parseInt(newBitrate); //console.log(`VideoApi: ${newBitrate} (${bitrate})`); if (bitrate === 0) { Janus.log('Not limiting bandwidth via REMB'); } else { Janus.log('Capping bandwidth to ' + bitrate + ' via REMB'); } sfutest.send({ message: { request: 'configure', bitrate } }); return false; }; this.toogleMyScreenInRoom = () => { if (thisApiObj.roomScreen && thisApiObj.roomScreen.room_id && localStorage.getItem('screenshare') == 'true') { if(sfScreen != null) sfScreen.hangup(); thisApiObj.roomScreen.outOfRoom(); thisApiObj.roomScreen.destroySession(); //thisApiObj.addMyScreenToRoom(); localStorage.setItem('screenshare',false) } else { thisApiObj.addMyScreenToRoom(); localStorage.setItem('screenshare',true) } }; this.setVideoModalMode = modeType => { jQuery('#im-modal-chat').toggleClass('im-modal-fullscreen') if(modeType==0){ if (jQuery('#im-modal-chat').hasClass('im-modal-grid')) { jQuery('#im-modal-chat').toggleClass('im-modal-expand').removeClass('im-modal-grid').addClass('im-modal-small'); } else if (jQuery('#im-modal-chat').hasClass('im-modal-small')) { jQuery('#im-modal-chat').toggleClass('im-modal-expand').addClass('im-modal-grid').removeClass('im-modal-small'); } else { jQuery('#im-modal-chat').toggleClass('im-modal-expand').addClass('im-modal-grid').removeClass('im-modal-small'); } } else{ if (jQuery('#im-modal-chat').hasClass('im-modal-grid')) { jQuery('#im-modal-chat').toggleClass('im-modal-expand').removeClass('im-modal-grid').removeClass('im-modal-grid--modification').addClass('im-modal-small'); } else if (jQuery('#im-modal-chat').hasClass('im-modal-small')) { jQuery('#im-modal-chat').toggleClass('im-modal-expand').addClass('im-modal-grid').addClass('im-modal-grid--modification').removeClass('im-modal-small'); } else { jQuery('#im-modal-chat').toggleClass('im-modal-expand').addClass('im-modal-grid').addClass('im-modal-grid--modification').removeClass('im-modal-small'); } } }; this.dragElement = elmnt => { //console.log(document.querySelector('#im-modal-chat').classList.contains('im-modal-fullscreen')) if (document.querySelector('#im-modal-chat').classList.contains('im-modal-fullscreen')!==true) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; if (document.getElementById(elmnt.id + "header")) { document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; } else { elmnt.onmousedown = dragMouseDown; } function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } } this.resize = elmnt => { document.querySelector('.video_call').classList.toggle("video_call--resize") } this.timer = elmnt => { //объявляем переменные var base = 60; var clocktimer, dateObj, dh, dm, ds, ms; var readout = ''; var h = 1, m = 1, tm = 1, s = 0, ts = 0, ms = 0, init = 0; let this_it=this; let start=true; //функция для очистки поля function ClearСlock() { clearTimeout(clocktimer); h = 1; m = 1; tm = 1; s = 0; ts = 0; ms = 0; init = 0; readout = '00:00'; if(document.querySelector('.video_call__item_time')) document.querySelector('.video_call__item_time').textContent = readout; } //функция для старта секундомера function StartTIME() { var cdateObj = new Date(); var t = (cdateObj.getTime() - dateObj.getTime()) - (s * 1000); if (t > 999) { s++; } if (s >= (m * base)) { ts = 0; m++; } else { ts = parseInt((ms / 100) + s); if (ts >= base) { ts = ts - ((m - 1) * base); } } if (m > (h * base)) { tm = 1; h++; } else { tm = parseInt((ms / 100) + m); if (tm >= base) { tm = tm - ((h - 1) * base); } } ms = Math.round(t / 10); if (ms > 99) { ms = 0; } if (ms == 0) { ms = '00'; } if (ms > 0 && ms <= 9) { ms = '0' + ms; } if (ts > 0) { ds = ts; if (ts < 10) { ds = '0' + ts; } } else { ds = '00'; } dm = tm - 1; if (dm > 0) { if (dm < 10) { dm = '0' + dm; } } else { dm = '00'; } dh = h - 1; if (dh > 0) { if (dh < 10) { dh = '0' + dh; } } else { dh = '00'; } readout = dm + ':' + ds; if(document.querySelector('.video_call__item_time')) document.querySelector('.video_call__item_time').textContent = readout; else return; clocktimer = setTimeout(()=>{StartTIME()}, 10); } //Функция запуска и остановки function StartStop() { if (init == 0) { if(document.querySelector('.video_call__item_time')) { ClearСlock(); dateObj = new Date(); if(document.querySelector('.video_call__item_time').dataset.start=='false') { StartTIME(); document.querySelector('.video_call__item_time').dataset.start='true'; } } start=false; init = 1; } else { clearTimeout(clocktimer); init = 0; } } StartStop(); } this.windowremote = elem =>{ if(document.querySelector(`.video_call__items_small .no-video-container`)) { document.querySelector(`.video_call__items_small .no-video-container`).classList.toggle("no-video-container-high") } document.querySelector(`#${elem.id}`).classList.toggle("video_call__item_small--resize") document.querySelector(`.video_call__item_big`).classList.toggle("video_call__item_big-none") document.querySelector(`.video_call__items_small`).classList.toggle("video_call__items_small-relative") } this.clearStartCall = () => { janus = null; sfutest = null; sfScreen = null; screenJanus = null; opaqueId = 'videocall-' + Janus.randomString(12); opaqueIdScreenShare = 'videocallscreen-' + Janus.randomString(12); myusername = null; myid = null; myScreenId = null; myScreenPvtid = null; myScreenStream = null; mystream = null; // We use this other ID just to map our subscriptions to us mypvtid = null; feeds = []; screenFeeds = []; bitrateTimer = []; screenbitrateTimer = []; // subscriber_mode = false; screenUsername = `screen-` + Janus.randomString(12); localStorage.setItem('screenshare', false); //if (thisApiObj.roomScreen.outOfRoom()) thisApiObj.roomScreen.outOfRoom(); //if (thisApiObj.roomScreen.destroySession()) thisApiObj.roomScreen.destroySession(); } this.soundCall = () => { let thisObj = this; let len_user=1; //console.log('sound play') let audio = new Audio('https://api.imbachat.com/plugins/imbasynergy/imbacalls/assets/audio/sound_call.mp3'); let audio_short = new Audio('https://api.imbachat.com/plugins/imbasynergy/imbacalls/assets/audio/call_short.mp3'); let interval = setInterval(() => { //console.log(jQuery('.im-user-count').text()) //console.log('sound1') thisObj.room.participants().then(result => { let room_holder = document.querySelector('div[class~="im-video-rooms"]'); let count = 0; //console.log('result') //console.log(result) len_user=result.length; }) if(jQuery('.video_call__item_time').length==0 || jQuery('.im-user-count').text()!='1' || len_user==2) { clearInterval(interval); let interval_short = setInterval(() => { //console.log(jQuery('.im-user-count').text()) //console.log('sound2') if(thisObj.room.participants()){ thisObj.room.participants().then(result => { let room_holder = document.querySelector('div[class~="im-video-rooms"]'); let count = 0; //console.log('result') //console.log(result) len_user=result.length; }) //console.log('children=') //console.log(jQuery('#im_videoremote1').children("video").length) if( len_user==2 && jQuery('#im_videoremote1').children("video").length==0) { audio_short.play(); } if(jQuery('.video_call__item_time').length==0 || jQuery('.im-user-count').text()!='1' || (len_user==2 && jQuery('#im_videoremote1').children("video").length!=0)) { clearInterval(interval_short); return; } } }, 50); return; } //console.log(thisObj) audio.play(); }, 8000); //console.log('sound3') } /** * Выставляет битрейт для видео звонка * @param {*} newBitrate число * @returns */ this.setVideoBitRate = newBitrate => { //console.log(`VideoApi: setVideoBitRate ${newBitrate}`); return setBitRate(newBitrate, sfutest); }; /** * Выставляет битрейт для скриншаринга * @param {*} newBitrate число * @returns */ this.setScreenBitRate = newBitrate => { //console.log(`VideoApi: setScreenBitRate ${newBitrate}`); return setBitRate(newBitrate, sfScreen); }; this.init_videoroom = (room_id, create_room_flag = false, use_video = true) => { const janus_event = new CustomEvent('janus_inited', { detail: true }); //console.log("event"); //console.log(janus_event); // Получить настройки, пройти авторизацию. let thisObj = this; // Раз в 30000 слать статистику по звонку нам на сервер. setCallStat = setTimeout(function callStat() { thisObj.room.participants().then(result => { let data = { count_users: result.length, call_type: 2, room_id: room_id, dev_id: imbaChat.opt.imba_id, cultivate: 'roomVideoCall.set_stat', }; jQuery.ajax(imbaChat.opt.api_url + 'call', { data: data, method: 'POST', success: response => {}, }); }); setCallStat = setTimeout(callStat, 30000); }, 30000); Janus.init({ debug: true, callback: function () { // Use a button to start the demo if (!Janus.isWebrtcSupported()) { console.error('No WebRTC support... '); opt.onInitError(); return; } // Create session //console.log('janus init') //console.log('janus init',opt.server) //console.log('janus init plugin') janus = new Janus({ server: opt.server, success: function () { // Attach to VideoRoom plugin //console.log("Attach to VideoRoom plugin") janus.attach({ plugin: 'janus.plugin.videoroom', opaqueId: opaqueId, success: function (pluginHandle) { sfutest = pluginHandle; if (create_room_flag) { let create_room = { request: 'create', room: room_id, secret: 'destroy_room_' + room_id, }; sfutest.send({ message: create_room }); } Janus.log('Plugin attached! (' + sfutest.getPlugin() + ', id=' + sfutest.getId() + ')'); opt.onNewState('Plugin attached! (' + sfutest.getPlugin() + ', id=' + sfutest.getId() + ')'); document.dispatchEvent(janus_event); // Janus.log(" -- This is a publisher/manager"); // Prepare the username registration //***По идеи заканчивает сессию*** jQuery('#start') .removeAttr('disabled') .html('Stop') .click(function () { jQuery(this).attr('disabled', true); janus.destroy(); }); }, error: function (error) { Janus.error(' -- Error attaching plugin...', error); opt.onInitError('Error attaching plugin... ' + error); }, consentDialog: function (on) { Janus.debug('Consent dialog should be ' + (on ? 'on' : 'off') + ' now'); opt.onNewState('Consent dialog should be ' + (on ? 'on' : 'off') + ' now'); // debugger; if (on) { // Darken screen and show hint jQuery.blockUI({ message: '
', css: { border: 'none', padding: '15px', backgroundColor: 'transparent', color: '#aaa', top: '10px', left: navigator.mozGetUserMedia ? '-100px' : '300px', }, }); } else { // Restore screen jQuery.unblockUI(); } }, iceState: function (state) { Janus.log('ICE state changed to ' + state); opt.onNewState('ICE state changed to ' + state); }, mediaState: function (medium, on) { Janus.log('Janus ' + (on ? 'started' : 'stopped') + ' receiving our ' + medium); opt.onNewState('Janus ' + (on ? 'started' : 'stopped') + ' receiving our ' + medium); }, webrtcState: function (on) { Janus.log('Janus says our WebRTC PeerConnection is ' + (on ? 'up' : 'down') + ' now'); opt.onNewState('Janus says our WebRTC PeerConnection is ' + (on ? 'up' : 'down') + ' now'); jQuery('#videolocal').parent().parent().unblock(); if (!on) { return; } jQuery('#publish').remove(); // This controls allows us to override the global room bitrate cap thisApiObj.setVideoBitRate(1000); }, onmessage: function (msg, jsep) { //console.log('[Video]', msg); Janus.debug(' ::: Got a message (publisher) :::', msg); var event = msg['videoroom']; Janus.debug('Event: ' + event); if (event) { thisObj.room.participants().then(result => { let room_holder = document.querySelector('div[class~="im-video-rooms"]'); let count=0; //console.log(result) if(result!=undefined) count = result.length; if(count!=0){ if(room_holder) room_holder.className = `im-video-rooms participants-${count}`; jQuery('.im-user-count').text(count); //jQuery('.im-video-room').removeClass("height100"); //jQuery('.im-video-room').removeClass("height50"); //jQuery('.im-video-room').removeClass("height25"); if(count<=2){ //jQuery('.im-video-room').addClass("height100"); } if(count>2 && count<=4){ //jQuery('.im-video-room').addClass("height50"); } if(count>4){ //jQuery('.im-video-room').addClass("height25"); } } }); if (event === 'joined') { // Publisher/manager created, negotiate WebRTC and attach to existing feeds, if any myid = msg['id']; mypvtid = msg['private_id']; Janus.log('Successfully joined room ' + msg['room'] + ' with ID ' + msg['id']); // if (subscriber_mode) { // // jQuery('#videojoin').hide(); // // jQuery('#videos').removeClass('hide').show(); // jQuery('#im-modal-chat').css('display', 'flex'); // jQuery('#im-modal-chat').css('display', 'flex'); // } else { thisObj.room.publishOwnFeed(true); // } // Any new feed to attach to? if (msg['publishers']) { var list = msg['publishers']; Janus.debug('Got a list of available publishers/feeds:', list); for (var f in list) { var id = list[f]['id']; var display = list[f]['display']; var audio = list[f]['audio_codec']; var video = list[f]['video_codec']; Janus.debug(' >> [' + id + '] ' + display + ' (audio: ' + audio + ', video: ' + video + ')'); newRemoteFeed(id, display, audio, video, room_id, use_video); } } } else if (event === 'destroyed') { jQuery('#im-modal-chat').remove(); // The room has been destroyed Janus.warn('The room has been destroyed!'); guiToast.warning({ title: 'Warning', message: 'Video call has been ended', }); clearTimeout(setCallStat); } else if (event === 'event') { // Any new feed to attach to? if (msg['publishers']) { var list = msg['publishers']; Janus.debug('Got a list of available publishers/feeds:', list); for (var f in list) { var id = list[f]['id']; var display = list[f]['display']; var audio = list[f]['audio_codec']; var video = list[f]['video_codec']; Janus.debug(' >> [' + id + '] ' + display + ' (audio: ' + audio + ', video: ' + video + ')'); newRemoteFeed(id, display, audio, video, room_id, use_video); } } else if (msg['leaving']) { // One of the publishers has gone away? var leaving = msg['leaving']; // //console.log('befor leaving', leaving) if (leaving != 'ok') { // //console.log('leaving', leaving) // thisObj.room.unpublishOwnFeed() // thisObj.room.leave() // jQuery("#im-modal-chat").css('display', 'none') // //console.log('Tikau s sela') } Janus.log('Publisher left: ' + leaving); const remoteFeed = null; for (var i = 1; i < 6; i++) { if (feeds[i] && feeds[i].rfid == leaving) { //console.log(feeds[i]); remoteFeed = feeds[i]; break; } } if (remoteFeed != null) { Janus.debug('Feed ' + remoteFeed.rfid + ' (' + remoteFeed.rfdisplay + ') has left the room, detaching'); // jQuery('#remote' + remoteFeed.rfindex).empty().hide(); // jQuery('#videoremote' + remoteFeed.rfindex).empty(); jQuery('#im_videoremote' + remoteFeed.rfindex).empty(); feeds[remoteFeed.rfindex] = null; remoteFeed.detach(); } } else if (msg['unpublished']) { // One of the publishers has unpublished? var unpublished = msg['unpublished']; Janus.log('Publisher left: ' + unpublished); if (unpublished === 'ok') { // That's us sfutest.hangup(); return; } let remoteFeed = null; for (var i = 1; i < 6; i++) { if (feeds[i] && feeds[i].rfid == unpublished) { //console.log(i); //console.log(feeds[i]); remoteFeed = feeds[i]; break; } } if (remoteFeed != null) { Janus.debug('Feed ' + remoteFeed.rfid + ' (' + remoteFeed.rfdisplay + ') has left the room, detaching'); // jQuery('#remote' + remoteFeed.rfindex).empty().hide(); // jQuery('#videoremote' + remoteFeed.rfindex).empty(); jQuery('#im_videoremote' + remoteFeed.rfindex).empty(); feeds[remoteFeed.rfindex] = null; remoteFeed.detach(); } } else if (msg['error']) { if (msg['error_code'] === 426) { // This is a "no such room" error: give a more meaningful description //console.log('This is a "no such room" error: give a more meaningful description'); } else { //console.log('1 ' + msg['error']); } } } } if (jsep) { Janus.debug('Handling SDP as well...', jsep); sfutest.handleRemoteJsep({ jsep: jsep }); // Check if any of the media we wanted to publish has // been rejected (e.g., wrong or unsupported codec) var audio = msg['audio_codec']; if (mystream && mystream.getAudioTracks() && mystream.getAudioTracks().length > 0 && !audio) { // Audio has been rejected guiToast.warning({ title: 'Warning', message: "Our audio stream has been rejected, viewers won't hear us", }); } var video = msg['video_codec']; if (mystream && mystream.getVideoTracks() && mystream.getVideoTracks().length > 0 && !video) { // Video has been rejected guiToast.warning({ title: 'Warning', message: "Our video stream has been rejected, viewers won't see us", }); jQuery('#im_videolocal').append( '
' + '' + 'Video rejected, no webcam' + '
', ); } } }, onlocalstream: function (stream) { Janus.debug(' ::: Got a local stream :::', stream); mystream = stream; // jQuery('#videojoin').hide(); // jQuery('#videos').removeClass('hide').show(); jQuery('#im-modal-chat').css('display', 'flex'); if (jQuery('#myvideo').length === 0) { // jQuery('#videolocal').append('