Files
Yajbir Singh f1b860b25c
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1972 lines
66 KiB
JavaScript

/*
* (c) Copyright Ascensio System SIA 2010-2024
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
"use strict";
(function(window, undefined) {
'use strict';
var Asc = window['Asc'];
var AscCommon = window['AscCommon'];
var ConnectionState = AscCommon.ConnectionState;
var c_oEditorId = AscCommon.c_oEditorId;
var c_oCloseCode = AscCommon.c_oCloseCode;
var c_oAscServerCommandErrors = AscCommon.c_oAscServerCommandErrors;
var c_oAscForceSaveTypes = AscCommon.c_oAscForceSaveTypes;
// Класс надстройка, для online и offline работы
function CDocsCoApi() {
this._CoAuthoringApi = new DocsCoApi();
this._onlineWork = false;
}
CDocsCoApi.prototype.init = function(user, docid, documentCallbackUrl, token, editorType, documentFormatSave, docInfo, shardKey, wopiSrc, userSessionId, headingsColor, openCmd) {
if (this._CoAuthoringApi && this._CoAuthoringApi.isRightURL()) {
var t = this;
this._CoAuthoringApi.onAuthParticipantsChanged = function(e, id) {
t.callback_OnAuthParticipantsChanged(e, id);
};
this._CoAuthoringApi.onParticipantsChanged = function(e) {
t.callback_OnParticipantsChanged(e);
};
this._CoAuthoringApi.onMessage = function(e, clear) {
t.callback_OnMessage(e, clear);
};
this._CoAuthoringApi.onServerVersion = function(e) {
t.callback_OnServerVersion(e);
};
this._CoAuthoringApi.onCursor = function(e) {
t.callback_OnCursor(e);
};
this._CoAuthoringApi.onMeta = function(e) {
t.callback_OnMeta(e);
};
this._CoAuthoringApi.onSession = function(e) {
t.callback_OnSession(e);
};
this._CoAuthoringApi.onExpiredToken = function(e) {
t.callback_OnExpiredToken(e);
};
this._CoAuthoringApi.onHasForgotten = function(e) {
t.callback_OnHasForgotten(e);
};
this._CoAuthoringApi.onForceSave = function(e) {
t.callback_OnForceSave(e);
};
this._CoAuthoringApi.onLocksAcquired = function(e) {
t.callback_OnLocksAcquired(e);
};
this._CoAuthoringApi.onLocksReleased = function(e, bChanges) {
t.callback_OnLocksReleased(e, bChanges);
};
this._CoAuthoringApi.onLocksReleasedEnd = function() {
t.callback_OnLocksReleasedEnd();
};
this._CoAuthoringApi.onDisconnect = function(e, code) {
t.callback_OnDisconnect(e, code);
};
this._CoAuthoringApi.onWarning = function(e) {
t.callback_OnWarning(e);
};
this._CoAuthoringApi.onFirstLoadChangesEnd = function(openedAt) {
t.callback_OnFirstLoadChangesEnd(openedAt);
};
this._CoAuthoringApi.onConnectionStateChanged = function(e) {
t.callback_OnConnectionStateChanged(e);
};
this._CoAuthoringApi.onSetIndexUser = function(e) {
t.callback_OnSetIndexUser(e);
};
this._CoAuthoringApi.onSpellCheckInit = function(e) {
t.callback_OnSpellCheckInit(e);
};
this._CoAuthoringApi.onSaveChanges = function(e, userId, bFirstLoad) {
t.callback_OnSaveChanges(e, userId, bFirstLoad);
};
this._CoAuthoringApi.onChangesIndex = function(changesIndex) {
t.callback_OnChangesIndex(changesIndex);
};
// Callback есть пользователей больше 1
this._CoAuthoringApi.onStartCoAuthoring = function(e, isWaitAuth) {
t.callback_OnStartCoAuthoring(e, isWaitAuth);
};
this._CoAuthoringApi.onEndCoAuthoring = function(e) {
t.callback_OnEndCoAuthoring(e);
};
this._CoAuthoringApi.onUnSaveLock = function() {
t.callback_OnUnSaveLock();
};
this._CoAuthoringApi.onRecalcLocks = function(e) {
t.callback_OnRecalcLocks(e);
};
this._CoAuthoringApi.onDocumentOpen = function(data) {
t.callback_OnDocumentOpen(data);
};
this._CoAuthoringApi.onFirstConnect = function() {
t.callback_OnFirstConnect();
};
this._CoAuthoringApi.onLicense = function(res) {
t.callback_OnLicense(res);
};
this._CoAuthoringApi.onLicenseChanged = function(res) {
t.callback_OnLicenseChanged(res);
};
this._CoAuthoringApi.onAiPluginSettings = function(res) {
t.callback_OnAiPluginSettings(res);
};
this._CoAuthoringApi.onMiscEvent = function(res) {
t.callback_OnMiscEvent(res);
};
this._CoAuthoringApi.init(user, docid, documentCallbackUrl, token, editorType, documentFormatSave, docInfo, shardKey, wopiSrc, userSessionId, headingsColor, openCmd);
this._onlineWork = true;
} else {
// Фиктивные вызовы
this.onFirstConnect();
this.onLicense(null);
}
};
CDocsCoApi.prototype.getDocId = function() {
if (this._CoAuthoringApi) {
return this._CoAuthoringApi.getDocId()
}
return undefined;
};
CDocsCoApi.prototype.setDocId = function(docId) {
if (this._CoAuthoringApi) {
return this._CoAuthoringApi.setDocId(docId)
}
};
CDocsCoApi.prototype.setBinaryChanges = function(binaryChanges) {
if (this._CoAuthoringApi) {
return this._CoAuthoringApi.binaryChanges = binaryChanges;
}
};
CDocsCoApi.prototype.auth = function(isViewer, opt_openCmd, opt_isIdle) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.auth(isViewer, opt_openCmd, opt_isIdle);
} else {
// Фиктивные вызовы
this.callback_OnSpellCheckInit('');
this.callback_OnSetIndexUser('123');
this.onFirstLoadChangesEnd();
}
};
CDocsCoApi.prototype.set_url = function(url) {
if (this._CoAuthoringApi) {
this._CoAuthoringApi.set_url(url);
}
};
CDocsCoApi.prototype.get_onlineWork = function() {
return this._onlineWork;
};
CDocsCoApi.prototype.get_state = function() {
if (this._CoAuthoringApi) {
return this._CoAuthoringApi.get_state();
}
return 0;
};
CDocsCoApi.prototype.openDocument = function(data) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.openDocument(data);
}
};
CDocsCoApi.prototype.sendRawData = function(data) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.sendRawData(data);
}
};
CDocsCoApi.prototype.getMessages = function() {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.getMessages();
}
};
CDocsCoApi.prototype.sendMessage = function(message) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.sendMessage(message);
}
};
CDocsCoApi.prototype.sendCursor = function(cursor) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.sendCursor(cursor);
}
};
CDocsCoApi.prototype.sendClientLog = function(level, msg) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.sendClientLog(level, msg);
}
};
CDocsCoApi.prototype.askLock = function(arrayBlockId, callback) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.askLock(arrayBlockId, callback);
} else {
var t = this;
window.setTimeout(function() {
if (callback) {
var lengthArray = (arrayBlockId) ? arrayBlockId.length : 0;
if (0 < lengthArray) {
callback({"lock": arrayBlockId[0]});
// Фиктивные вызовы
for (var i = 0; i < lengthArray; ++i) {
t.callback_OnLocksAcquired({"state": 2, "block": arrayBlockId[i]});
}
}
}
}, 1);
}
};
CDocsCoApi.prototype.askSaveChanges = function(callback) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.askSaveChanges(callback);
} else {
window.setTimeout(function() {
if (callback) {
// Фиктивные вызовы
callback({"saveLock": false});
}
}, 100);
}
};
CDocsCoApi.prototype.unSaveLock = function() {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.unSaveLock();
} else {
var t = this;
window.setTimeout(function() {
// Фиктивные вызовы
t.callback_OnUnSaveLock();
}, 100);
}
};
CDocsCoApi.prototype.saveChanges = function(arrayChanges, deleteIndex, excelAdditionalInfo, canUnlockDocument, canReleaseLocks) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.canUnlockDocument = canUnlockDocument;
this._CoAuthoringApi.canReleaseLocks = canReleaseLocks;
this._CoAuthoringApi.saveChanges(arrayChanges, null, deleteIndex, excelAdditionalInfo);
}
};
CDocsCoApi.prototype.unLockDocument = function(isSave, canUnlockDocument, deleteIndex, canReleaseLocks) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.canUnlockDocument = canUnlockDocument;
this._CoAuthoringApi.canReleaseLocks = canReleaseLocks;
this._CoAuthoringApi.unLockDocument(isSave, deleteIndex);
}
};
CDocsCoApi.prototype.getUsers = function() {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.getUsers();
}
};
CDocsCoApi.prototype.getUserConnectionId = function() {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.getUserConnectionId();
}
return null;
};
CDocsCoApi.prototype.getParticipantName = function(userId) {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.getParticipantName(userId);
}
return "";
};
CDocsCoApi.prototype.get_serverChangesSize = function() {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.get_serverChangesSize();
}
return 0;
};
CDocsCoApi.prototype.get_indexUser = function() {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.get_indexUser();
}
return null;
};
CDocsCoApi.prototype.get_isAuth = function() {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.get_isAuth();
}
return null;
};
CDocsCoApi.prototype.get_jwt = function() {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.get_jwt();
}
return null;
};
CDocsCoApi.prototype.releaseLocks = function(blockId) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.releaseLocks(blockId);
}
};
CDocsCoApi.prototype.disconnect = function(opt_code, opt_reason) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.disconnect(opt_code, opt_reason);
}
};
CDocsCoApi.prototype.connect = function() {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.connect();
}
};
CDocsCoApi.prototype.extendSession = function(idleTime) {
if (this._CoAuthoringApi && this._onlineWork) {
this._CoAuthoringApi.extendSession(idleTime);
}
};
CDocsCoApi.prototype.forceSave = function() {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.forceSave();
}
return false;
};
CDocsCoApi.prototype.callPRC = function(data, timeout, callback) {
if (this._CoAuthoringApi && this._onlineWork) {
return this._CoAuthoringApi.callPRC(data, timeout, callback);
}
return false;
};
CDocsCoApi.prototype.callback_OnAuthParticipantsChanged = function(e, id) {
if (this.onAuthParticipantsChanged) {
this.onAuthParticipantsChanged(e, id);
}
};
CDocsCoApi.prototype.callback_OnParticipantsChanged = function(e) {
if (this.onParticipantsChanged) {
this.onParticipantsChanged(e);
}
};
CDocsCoApi.prototype.callback_OnMessage = function(e, clear) {
if (this.onMessage) {
this.onMessage(e, clear);
}
};
CDocsCoApi.prototype.callback_OnServerVersion = function(e) {
if (this.onServerVersion) {
this.onServerVersion(e);
}
};
CDocsCoApi.prototype.callback_OnCursor = function(e) {
if (this.onCursor) {
this.onCursor(e);
}
};
CDocsCoApi.prototype.callback_OnMeta = function(e) {
if (this.onMeta) {
this.onMeta(e);
}
};
CDocsCoApi.prototype.callback_OnSession = function(e) {
if (this.onSession) {
this.onSession(e);
}
};
CDocsCoApi.prototype.callback_OnExpiredToken = function(e) {
if (this.onExpiredToken) {
this.onExpiredToken(e);
}
};
CDocsCoApi.prototype.callback_OnForceSave = function(e) {
if (this.onForceSave) {
this.onForceSave(e);
}
};
CDocsCoApi.prototype.callback_OnHasForgotten = function(e) {
if (this.onHasForgotten) {
this.onHasForgotten(e);
}
};
CDocsCoApi.prototype.callback_OnLocksAcquired = function(e) {
if (this.onLocksAcquired) {
this.onLocksAcquired(e);
}
};
CDocsCoApi.prototype.callback_OnLocksReleased = function(e, bChanges) {
if (this.onLocksReleased) {
this.onLocksReleased(e, bChanges);
}
};
CDocsCoApi.prototype.callback_OnLocksReleasedEnd = function() {
if (this.onLocksReleasedEnd) {
this.onLocksReleasedEnd();
}
};
/**
* Event об отсоединении от сервера
* @param {jQuery} e event об отсоединении с причиной
* @param {code: AscCommon.c_oCloseCode.drop} code
*/
CDocsCoApi.prototype.callback_OnDisconnect = function(e, code) {
if (this.onDisconnect) {
this.onDisconnect(e, code);
}
};
CDocsCoApi.prototype.callback_OnWarning = function(e) {
if (this.onWarning) {
this.onWarning(e);
}
};
CDocsCoApi.prototype.callback_OnFirstLoadChangesEnd = function(openedAt) {
if (this.onFirstLoadChangesEnd) {
this.onFirstLoadChangesEnd(openedAt);
}
};
CDocsCoApi.prototype.callback_OnConnectionStateChanged = function(e) {
if (this.onConnectionStateChanged) {
this.onConnectionStateChanged(e);
}
};
CDocsCoApi.prototype.callback_OnSetIndexUser = function(e) {
if (this.onSetIndexUser) {
this.onSetIndexUser(e);
}
};
CDocsCoApi.prototype.callback_OnSpellCheckInit = function(e) {
if (this.onSpellCheckInit) {
this.onSpellCheckInit(e);
}
};
CDocsCoApi.prototype.callback_OnSaveChanges = function(e, userId, bFirstLoad) {
if (this.onSaveChanges) {
this.onSaveChanges(e, userId, bFirstLoad);
}
};
CDocsCoApi.prototype.callback_OnChangesIndex = function(changesIndex) {
if (this.onChangesIndex) {
this.onChangesIndex(changesIndex);
}
};
CDocsCoApi.prototype.callback_OnStartCoAuthoring = function(e, isWaitAuth) {
if (this.onStartCoAuthoring) {
this.onStartCoAuthoring(e, isWaitAuth);
}
};
CDocsCoApi.prototype.callback_OnEndCoAuthoring = function(e) {
if (this.onEndCoAuthoring) {
this.onEndCoAuthoring(e);
}
};
CDocsCoApi.prototype.callback_OnUnSaveLock = function() {
if (this.onUnSaveLock) {
this.onUnSaveLock();
}
};
CDocsCoApi.prototype.callback_OnRecalcLocks = function(e) {
if (this.onRecalcLocks) {
this.onRecalcLocks(e);
}
};
CDocsCoApi.prototype.callback_OnDocumentOpen = function(e) {
if (this.onDocumentOpen) {
this.onDocumentOpen(e);
}
};
CDocsCoApi.prototype.callback_OnFirstConnect = function() {
if (this.onFirstConnect) {
this.onFirstConnect();
}
};
CDocsCoApi.prototype.callback_OnLicense = function(res) {
if (this.onLicense) {
this.onLicense(res);
}
};
CDocsCoApi.prototype.callback_OnLicenseChanged = function(res) {
if (this.onLicenseChanged) {
this.onLicenseChanged(res);
}
};
CDocsCoApi.prototype.callback_OnAiPluginSettings = function(res) {
if (this.onAiPluginSettings) {
this.onAiPluginSettings(res);
}
};
CDocsCoApi.prototype.callback_OnMiscEvent = function(res) {
if (this.onMiscEvent) {
this.onMiscEvent(res);
}
};
function LockBufferElement(arrayBlockId, callback) {
this._arrayBlockId = arrayBlockId ? arrayBlockId.slice() : null;
this._callback = callback;
}
function DocsCoApi() {
this._state = ConnectionState.None;
// Online-пользователи в документе
this._participants = {};
this._participantsTimestamp;
this._countEditUsers = 0;
this._countUsers = 0;
this._countCalls = 0;
this._waitingForResponse = {};
this.isLicenseInit = false;
this._locks = {};
this._msgBuffer = [];
this._msgInputBuffer = [];
this._lockCallbacks = {};
this._lockCallbacksErrorTimerId = {};
this._saveCallback = [];
this.saveLockCallbackErrorTimeOutId = null;
this.saveCallbackErrorTimeOutId = null;
this.unSaveLockCallbackErrorTimeOutId = null;
this._id = null;
this._sessionTimeConnect = null;
this._allChangesSaved = null;
this._lastForceSaveButtonTime = -2;//-2 to allow first save without changes
this._lastForceSaveTimeoutTime = null;
this._indexUser = -1;
// Если пользователей больше 1, то совместно редактируем
this.isCoAuthoring = false;
// Мы сами отключились от совместного редактирования
this.isCloseCoAuthoring = false;
//websocket payload size is limited by https://github.com/faye/faye-websocket-node#initialization-options (64 MiB)
//xhr payload size is limited by nginx param client_max_body_size (current 100MB)
//"1.5MB" is choosen to avoid disconnect(after 25s) while downloading/uploading oversized changes with 0.5Mbps connection
this.websocketMaxPayloadSize = 1572864;
this._serverChangesSize = 0;
// Текущий индекс для колличества изменений
this.currentIndex = 0;
this.currentIndexEnd = 0;
// Индекс, с которого мы начинаем сохранять изменения
this.deleteIndex = 0;
// Массив изменений
this.arrayChanges = null;
// Время последнего сохранения (для разрыва соединения)
this.lastOtherSaveTime = -1;
this.lastOwnSaveTime = -1;
// Локальный индекс изменений
this.changesIndex = 0;
//server changes index
//todo: replace changesIndex with syncChangesIndex. changesIndex has different value in single editing mode
this.syncChangesIndex = 0;
// Дополнительная информация для Excel
this.excelAdditionalInfo = null;
// Unlock document
this.canUnlockDocument = false;
// Release locks
this.canReleaseLocks = false;
this._url = "";
this.maxAttemptCount = 50;
this.reconnectInterval = 2000;
this.errorTimeOut = 10000;
this.errorTimeOutSave = 60000; // ToDo стоит переделать это, т.к. могут дублироваться изменения...
this._docid = null;
this._documentCallbackUrl = null;
this._token = null;
this._user = null;
this._userId = "Anonymous";
this.ownedLockBlocks = [];
this.socketio_url = null;
this.socketio = null;
this.editorType = -1;
this._isExcel = false;
this._isPresentation = false;
this._isAuth = false;
this._documentFormatSave = 0;
this.mode = undefined;
this.permissions = undefined;
this.lang = undefined;
this.openCmd = undefined;
this.jwtOpen = undefined;
this.jwtSession = undefined;
this.encrypted = undefined;
this.IsAnonymousUser = undefined;
this.coEditingMode = undefined;
this.headingsColor = undefined;
this._isReSaveAfterAuth = false; // Флаг для сохранения после повторной авторизации (для разрыва соединения во время сохранения)
this._lockBuffer = [];
this._saveChangesChunks = [];
this._authChanges = [];
this._authOtherChanges = [];
}
DocsCoApi.prototype.isRightURL = function() {
return ("" != this._url);
};
DocsCoApi.prototype.set_url = function(url) {
this._url = url;
};
DocsCoApi.prototype.get_state = function() {
return this._state;
};
DocsCoApi.prototype.check_state = function () {
return ConnectionState.Authorized === this._state || ConnectionState.SaveChanges === this._state ||
ConnectionState.AskSaveChanges === this._state;
};
DocsCoApi.prototype.get_indexUser = function() {
return this._indexUser;
};
DocsCoApi.prototype.get_serverChangesSize = function() {
return this._serverChangesSize;
};
DocsCoApi.prototype.get_isAuth = function() {
return this._isAuth;
};
DocsCoApi.prototype.get_jwt = function() {
return this.jwtSession || this.jwtOpen;
};
DocsCoApi.prototype.getSessionId = function() {
return this._id;
};
DocsCoApi.prototype.getUserConnectionId = function() {
return this._userId;
};
DocsCoApi.prototype.getParticipantName = function(userId) {
if (this._participants[userId])
return this._participants[userId].asc_getUserName();
return "";
};
DocsCoApi.prototype.getLocks = function() {
return this._locks;
};
DocsCoApi.prototype._sendBufferedLocks = function() {
var elem;
for (var i = 0, length = this._lockBuffer.length; i < length; ++i) {
elem = this._lockBuffer[i];
this.askLock(elem._arrayBlockId, elem._callback);
}
this._lockBuffer = [];
};
DocsCoApi.prototype.askLock = function(arrayBlockId, callback) {
if (ConnectionState.SaveChanges === this._state || ConnectionState.AskSaveChanges === this._state) {
// Мы в режиме сохранения. Lock-и запросим после окончания.
this._lockBuffer.push(new LockBufferElement(arrayBlockId, callback));
return;
}
// ask all elements in array
var t = this;
var i = 0;
var lengthArray = (arrayBlockId) ? arrayBlockId.length : 0;
var isLock = false;
var idLockInArray = null;
for (; i < lengthArray; ++i) {
idLockInArray = (this._isExcel || this._isPresentation || this._isPDF) ? arrayBlockId[i]['guid'] : arrayBlockId[i];
if (this._locks[idLockInArray] && 0 !== this._locks[idLockInArray].state) {
isLock = true;
break;
}
}
if (0 === lengthArray) {
isLock = true;
}
idLockInArray = (this._isExcel || this._isPresentation || this._isPDF) ? arrayBlockId[0]['guid'] : arrayBlockId[0];
if (!isLock) {
if (this._lockCallbacksErrorTimerId.hasOwnProperty(idLockInArray)) {
// Два раза для одного id нельзя запрашивать lock, не дождавшись ответа
return;
}
//Ask
this._locks[idLockInArray] = {'state': 1};//1-asked for block
if (callback) {
this._lockCallbacks[idLockInArray] = callback;
//Set reconnectTimeout
this._lockCallbacksErrorTimerId[idLockInArray] = window.setTimeout(function() {
if (t._lockCallbacks.hasOwnProperty(idLockInArray)) {
//Not signaled already
t._lockCallbacks[idLockInArray]({error: 'Timed out'});
delete t._lockCallbacks[idLockInArray];
delete t._lockCallbacksErrorTimerId[idLockInArray];
}
}, this.errorTimeOut);
}
this._send({"type": 'getLock', 'block': arrayBlockId});
} else {
// Вернем ошибку, т.к. залочены элементы
window.setTimeout(function() {
if (callback) {
callback({error: idLockInArray + '-lock'});
}
}, 100);
}
};
DocsCoApi.prototype.askSaveChanges = function(callback) {
if (this._saveCallback[this._saveCallback.length - 1]) {
// Мы еще не отработали старый callback и ждем ответа
return;
}
// Очищаем предыдущий таймер
if (null !== this.saveLockCallbackErrorTimeOutId) {
clearTimeout(this.saveLockCallbackErrorTimeOutId);
}
// Проверим состояние, если мы не подсоединились, то сразу отправим ошибку
if (ConnectionState.Authorized !== this._state) {
this.saveLockCallbackErrorTimeOutId = window.setTimeout(function() {
if (callback) {
// Фиктивные вызовы
callback({error: "No connection"});
}
}, 100);
return;
}
if (callback) {
var t = this;
var indexCallback = this._saveCallback.length;
this._saveCallback[indexCallback] = callback;
//Set reconnectTimeout
this.saveLockCallbackErrorTimeOutId = window.setTimeout(function() {
t.saveLockCallbackErrorTimeOutId = null;
var oTmpCallback = t._saveCallback[indexCallback];
if (oTmpCallback) {
t._saveCallback[indexCallback] = null;
//Not signaled already
oTmpCallback({error: "Timed out"});
t._state = ConnectionState.Authorized;
// Делаем отложенные lock-и
t._sendBufferedLocks();
}
}, this.errorTimeOut);
}
this._state = ConnectionState.AskSaveChanges;
this._send({"type": "isSaveLock", "syncChangesIndex": this.syncChangesIndex});
};
DocsCoApi.prototype.unSaveLock = function() {
// ToDo при разрыве соединения нужно перестать делать unSaveLock!
var t = this;
this.unSaveLockCallbackErrorTimeOutId = window.setTimeout(function() {
t.unSaveLockCallbackErrorTimeOutId = null;
t.unSaveLock();
}, this.errorTimeOut);
this._send({"type": "unSaveLock"});
};
DocsCoApi.prototype.releaseLocks = function(blockId) {
if (this._locks[blockId] && 2 === this._locks[blockId].state /*lock is ours*/) {
//Ask
this._locks[blockId] = {"state": 0};//0-released
}
};
DocsCoApi.prototype._reSaveChanges = function(reSaveType) {
this.saveChanges(this.arrayChanges, this.currentIndex, undefined, undefined, reSaveType);
};
DocsCoApi.prototype.saveChanges = function(arrayChanges, currentIndex, deleteIndex, excelAdditionalInfo, reSave) {
if (null === currentIndex) {
this.deleteIndex = deleteIndex;
if (null != this.deleteIndex && -1 !== this.deleteIndex) {
this.deleteIndex += this.changesIndex;
}
this.currentIndex = 0;
this.arrayChanges = arrayChanges;
this.excelAdditionalInfo = excelAdditionalInfo;
} else {
this.currentIndex = currentIndex;
}
var startIndex, endIndex;
startIndex = endIndex = this.currentIndex;
var curBytes = 0;
for (; endIndex < arrayChanges.length && curBytes < this.websocketMaxPayloadSize; ++endIndex) {
curBytes += arrayChanges[endIndex].length;
}
this.currentIndexEnd = endIndex;
if (endIndex === arrayChanges.length) {
for (var key in this._locks) if (this._locks.hasOwnProperty(key)) {
if (2 === this._locks[key].state /*lock is ours*/) {
delete this._locks[key];
}
}
}
//Set errorTimeout
var t = this;
this.saveCallbackErrorTimeOutId = window.setTimeout(function() {
t.saveCallbackErrorTimeOutId = null;
t._reSaveChanges(1);
}, this.errorTimeOutSave);
// Выставляем состояние сохранения
this._state = ConnectionState.SaveChanges;
if (!reSave) {
this._serverChangesSize += curBytes;
}
let _changes = this.binaryChanges ? arrayChanges.slice(startIndex, endIndex) : JSON.stringify(arrayChanges.slice(startIndex, endIndex));
this._send({'type': 'saveChanges', 'changes': _changes,
'startSaveChanges': (startIndex === 0), 'endSaveChanges': (endIndex === arrayChanges.length),
'isCoAuthoring': this.isCoAuthoring, 'isExcel': this._isExcel, 'deleteIndex': this.deleteIndex,
'excelAdditionalInfo': this.excelAdditionalInfo ? JSON.stringify(this.excelAdditionalInfo) : null,
'unlock': this.canUnlockDocument, 'releaseLocks': this.canReleaseLocks, 'reSave': reSave});
};
DocsCoApi.prototype.unLockDocument = function(isSave, deleteIndex) {
this.deleteIndex = deleteIndex;
if (null != this.deleteIndex && -1 !== this.deleteIndex) {
this.deleteIndex += this.changesIndex;
}
this._send({'type': 'unLockDocument', 'isSave': isSave, 'unlock': this.canUnlockDocument,
'deleteIndex': this.deleteIndex, 'releaseLocks': this.canReleaseLocks});
};
DocsCoApi.prototype.getUsers = function() {
// Специально для возможности получения после прохождения авторизации (Стоит переделать)
if (this.onAuthParticipantsChanged) {
this.onAuthParticipantsChanged(this._participants, this._userId);
}
};
DocsCoApi.prototype.connect = function() {
this.isCloseCoAuthoring = false;
this.socketio.connect();
};
DocsCoApi.prototype.disconnect = function(opt_code, opt_reason) {
// Отключаемся сами
this.isCloseCoAuthoring = true;
if (opt_code) {
this.onDisconnect(opt_reason, opt_code);
this.socketio.disconnect();
} else {
this._send({"type": "close"});
this._state = ConnectionState.ClosedCoAuth;
}
};
DocsCoApi.prototype.extendSession = function(idleTime) {
this._send({'type': 'extendSession', 'idletime': idleTime});
};
DocsCoApi.prototype.forceSave = function() {
var res = false;
var newForceSaveButtonTime = Math.max(this.lastOtherSaveTime, this.lastOwnSaveTime);
if (this._lastForceSaveButtonTime < newForceSaveButtonTime) {
this._lastForceSaveButtonTime = newForceSaveButtonTime;
this._send({'type': 'forceSaveStart'});
res = true;
}
return res;
};
DocsCoApi.prototype.callPRC = function(data, timeout, callback) {
var t = this;
var responseKey = ++this._countCalls;
this._waitingForResponse[responseKey] = callback;
if (timeout > 0) {
setTimeout(function() {
t._onPRC(responseKey, true, undefined);
}, timeout)
}
this._send({'type': 'rpc', 'responseKey': responseKey, 'data': data});
return true;
};
DocsCoApi.prototype._onPRC = function(responseKey, isTimeout, response) {
var callback = this._waitingForResponse[responseKey];
delete this._waitingForResponse[responseKey];
if (callback) {
callback(isTimeout, response);
}
};
DocsCoApi.prototype._onUpdateVersion = function() {
this._send({'type': 'updateVersion'});
};
DocsCoApi.prototype.openDocument = function(data) {
this._send({"type": "openDocument", "message": data});
};
DocsCoApi.prototype.sendRawData = function(data) {
this._sendRaw(data);
};
DocsCoApi.prototype.getMessages = function() {
this._send({"type": "getMessages"});
};
DocsCoApi.prototype.sendMessage = function(message) {
if (typeof message === 'string') {
this._send({"type": "message", "message": message});
}
};
DocsCoApi.prototype.sendCursor = function(cursor) {
if (typeof cursor === 'string') {
this._send({"type": "cursor", "cursor": cursor});
}
};
DocsCoApi.prototype.sendClientLog = function(level, msg) {
this._send({'type': 'clientLog', 'level': level, 'msg': msg});
};
DocsCoApi.prototype._applyPrebuffered = function () {
for (var i = 0; i < this._msgInputBuffer.length; ++i) {
this._msgInputBuffer[i]();
}
this._msgInputBuffer = [];
};
DocsCoApi.prototype._sendPrebuffered = function() {
for (var i = 0; i < this._msgBuffer.length; i++) {
this._sendRaw(this._msgBuffer[i]);
}
this._msgBuffer = [];
};
DocsCoApi.prototype._send = function(data, useEncryption) {
if (!useEncryption && data && data["type"] == "saveChanges" && AscCommon.EncryptionWorker && AscCommon.EncryptionWorker.isInit())
return AscCommon.EncryptionWorker.sendChanges(this, data, AscCommon.EncryptionMessageType.Encrypt);
if (data !== null && typeof data === "object") {
if (this._state > 0) {
this.socketio.emit("message", data);
} else {
this._msgBuffer.push(JSON.stringify(data));
}
}
};
DocsCoApi.prototype._sendRaw = function(data) {
if (data !== null && typeof data === "string") {
if (this._state > 0) {
this.socketio.emit("message", data);
} else {
this._msgBuffer.push(data);
}
}
};
DocsCoApi.prototype._onMessages = function(data, clear) {
if (this.check_state() && data["messages"] && this.onMessage) {
this.onMessage(data["messages"], clear);
}
};
DocsCoApi.prototype._onServerVersion = function (data) {
if (this.onServerVersion) {
this.onServerVersion(data['buildVersion'], data['buildNumber']);
}
};
DocsCoApi.prototype._onCursor = function(data) {
if (this.check_state() && data["messages"] && this.onCursor) {
this.onCursor(data["messages"]);
}
};
DocsCoApi.prototype._onMeta = function(data) {
if (data["messages"] && this.onMeta) {
this.onMeta(data["messages"]);
}
};
DocsCoApi.prototype._onSession = function(data) {
if (this.check_state() && data["messages"] && this.onSession) {
this.onSession(data["messages"]);
}
};
DocsCoApi.prototype._onExpiredToken = function(data) {
if (this.onExpiredToken) {
this.onExpiredToken(data);
}
};
DocsCoApi.prototype._onHasForgotten = function(data) {
if (this.onHasForgotten) {
this.onHasForgotten();
}
};
DocsCoApi.prototype._onRefreshToken = function(jwt) {
this.jwtOpen = undefined;
if (this.socketio) {
if (this.socketio["auth"]) {
this.socketio["auth"]["token"] = this.jwtOpen;
}
if (this.socketio.io && this.socketio.io.setOpenToken) {
this.socketio.io.setOpenToken(this.jwtOpen);
}
}
if (jwt) {
this.jwtSession = jwt;
if (this.socketio) {
if (this.socketio["auth"]) {
this.socketio["auth"]["session"] = this.jwtSession;
}
if (this.socketio.io && this.socketio.io.setSessionToken) {
this.socketio.io.setSessionToken(this.jwtSession);
}
}
}
};
DocsCoApi.prototype._onForceSaveStart = function(data) {
var code = data['code'];
if (code === c_oAscServerCommandErrors.NoError) {
this._lastForceSaveButtonTime = data['time'];
this.onForceSave({type: c_oAscForceSaveTypes.Button, start: true});
} else if (code === c_oAscServerCommandErrors.NotModified) {
this.onForceSave({type: c_oAscForceSaveTypes.Button, refuse: true});
} else {
this.onWarning(AscCommon.c_oAscServerError.Unknown);
}
};
DocsCoApi.prototype._onForceSave = function(data) {
var type = data['type'];
if (c_oAscForceSaveTypes.Button === type) {
if (this._lastForceSaveButtonTime == data['time']) {
this.onForceSave({type: type, success: data['success']});
}
} else {
if (data['start']) {
this.onForceSave({type: type, start: true});
this._lastForceSaveTimeoutTime = data['time'];
} else {
if (this._lastForceSaveTimeoutTime == data['time']) {
this.onForceSave({type: type, success: data['success']});
}
}
}
};
DocsCoApi.prototype._onGetLock = function(data) {
if (this.check_state() && data["locks"]) {
for (var key in data["locks"]) {
if (data["locks"].hasOwnProperty(key)) {
var lock = data["locks"][key], blockTmp = (this._isExcel || this._isPresentation || this._isPDF) ? lock["block"]["guid"] : key, blockValue = (this._isExcel || this._isPresentation || this._isPDF) ? lock["block"] : key;
if (lock !== null) {
var changed = true;
if (this._locks[blockTmp] && 1 !== this._locks[blockTmp].state /*asked for it*/) {
//Exists
//Check lock state
changed = !(this._locks[blockTmp].state === (lock["user"] === this._userId ? 2 : 3) && this._locks[blockTmp]["user"] === lock["user"] && this._locks[blockTmp]["time"] === lock["time"] && this._locks[blockTmp]["block"] === blockTmp);
}
if (changed) {
this._locks[blockTmp] = {"state": lock["user"] === this._userId ? 2 : 3, "user": lock["user"], "time": lock["time"], "block": blockTmp, "blockValue": blockValue};//2-acquired by me!
}
if (this._lockCallbacks.hasOwnProperty(blockTmp)) {
if (lock["user"] === this._userId) {
//Do call back
this._lockCallbacks[blockTmp]({"lock": this._locks[blockTmp]});
} else {
this._lockCallbacks[blockTmp]({"error": "Already locked by " + lock["user"]});
}
if (this._lockCallbacksErrorTimerId.hasOwnProperty(blockTmp)) {
clearTimeout(this._lockCallbacksErrorTimerId[blockTmp]);
delete this._lockCallbacksErrorTimerId[blockTmp];
}
delete this._lockCallbacks[blockTmp];
}
if (this.onLocksAcquired && changed) {
this.onLocksAcquired(this._locks[blockTmp]);
}
}
}
}
}
};
DocsCoApi.prototype._onReleaseLock = function(data) {
if (this.check_state() && data["locks"]) {
var bSendEnd = false;
for (var block in data["locks"]) {
if (data["locks"].hasOwnProperty(block)) {
var lock = data["locks"][block], blockTmp = (this._isExcel || this._isPresentation || this._isPDF) ? lock["block"]["guid"] : lock["block"];
if (lock !== null) {
this._locks[blockTmp] = {"state": 0, "user": lock["user"], "time": lock["time"], "changes": lock["changes"], "block": lock["block"]};
if (this.onLocksReleased) {
// false - user not save changes
this.onLocksReleased(this._locks[blockTmp], false);
bSendEnd = true;
}
}
}
}
if (bSendEnd && this.onLocksReleasedEnd) {
this.onLocksReleasedEnd();
}
}
};
DocsCoApi.prototype._documentOpen = function(data) {
this.onDocumentOpen(data);
};
DocsCoApi.prototype._onSaveChanges = function(data, useEncryption) {
if (!this.check_state()) {
if (!this.get_isAuth()) {
this._authOtherChanges.push(data);
}
return;
}
if (!data["endSaveChanges"]) {
this._saveChangesChunks.push(data["changes"]);
return;
} else if(this._saveChangesChunks.length > 0){
this._saveChangesChunks.push(data["changes"]);
var newChanges = [];
data["changes"] = newChanges.concat.apply(newChanges, this._saveChangesChunks);
this._saveChangesChunks = [];
}
if (!useEncryption && AscCommon.EncryptionWorker && AscCommon.EncryptionWorker.isInit())
return AscCommon.EncryptionWorker.sendChanges(this, data, AscCommon.EncryptionMessageType.Decrypt);
if (data["locks"]) {
var bSendEnd = false;
for (var block in data["locks"]) {
if (data["locks"].hasOwnProperty(block)) {
var lock = data["locks"][block], blockTmp = (this._isExcel || this._isPresentation || this._isPDF) ? lock["block"]["guid"] : lock["block"];
if (lock !== null) {
this._locks[blockTmp] = {"state": 0, "user": lock["user"], "time": lock["time"], "changes": lock["changes"], "block": lock["block"]};
if (this.onLocksReleased) {
// true - lock with save
this.onLocksReleased(this._locks[blockTmp], true);
bSendEnd = true;
}
}
}
}
if (bSendEnd && this.onLocksReleasedEnd) {
this.onLocksReleasedEnd();
}
}
this._updateChanges(data["changes"], data["changesIndex"], data["syncChangesIndex"], false);
if (this.onRecalcLocks) {
this.onRecalcLocks(data["excelAdditionalInfo"]);
}
};
DocsCoApi.prototype._onStartCoAuthoring = function(isStartEvent, isWaitAuth) {
if (isWaitAuth && false === this.isCoAuthoring && !this.onStartCoAuthoring) {
var errorMsg = 'Error: connection state changed waitAuth' +
';this.onStartCoAuthoring:' + !!this.onStartCoAuthoring;
this.sendClientLog("error", "changesError: " + errorMsg);
}
if (false === this.isCoAuthoring) {
this.isCoAuthoring = true;
if (this.onStartCoAuthoring) {
this.onStartCoAuthoring(isStartEvent, isWaitAuth);
}
} else if (isWaitAuth) {
//it is a stub for unexpected situation(no direct reproduce scenery)
//isCoAuthoring is true when more then one editor, but isWaitAuth mean than server has one editor
this.canUnlockDocument = true;
this.unLockDocument(false);
}
};
DocsCoApi.prototype._onEndCoAuthoring = function(isStartEvent) {
if (true === this.isCoAuthoring) {
this.isCoAuthoring = false;
if (this.onEndCoAuthoring) {
this.onEndCoAuthoring(isStartEvent);
}
}
};
DocsCoApi.prototype._onSaveLock = function (data) {
if (null != data["saveLock"]) {
var indexCallback = this._saveCallback.length - 1;
var oTmpCallback = this._saveCallback[indexCallback];
if (oTmpCallback) {
// Очищаем предыдущий таймер
if (null !== this.saveLockCallbackErrorTimeOutId) {
clearTimeout(this.saveLockCallbackErrorTimeOutId);
this.saveLockCallbackErrorTimeOutId = null;
}
this._saveCallback[indexCallback] = null;
oTmpCallback(data);
}
}
if (null == data["saveLock"] || data['error'] || data["saveLock"]) {
this._state = ConnectionState.Authorized;
// Делаем отложенные lock-и
this._sendBufferedLocks();
}
};
DocsCoApi.prototype._onUnSaveLock = function(data) {
// Очищаем предыдущий таймер сохранения
if (null !== this.saveCallbackErrorTimeOutId) {
clearTimeout(this.saveCallbackErrorTimeOutId);
this.saveCallbackErrorTimeOutId = null;
}
// Очищаем предыдущий таймер снятия блокировки
if (null !== this.unSaveLockCallbackErrorTimeOutId) {
clearTimeout(this.unSaveLockCallbackErrorTimeOutId);
this.unSaveLockCallbackErrorTimeOutId = null;
}
// Возвращаем состояние
this._state = ConnectionState.Authorized;
// Делаем отложенные lock-и
this._sendBufferedLocks();
if (-1 !== data['index']) {
this.changesIndex = data['index'];
}
if (-1 !== data['time']) {
this.lastOwnSaveTime = data['time'];
}
if (undefined !== data['syncChangesIndex'] && -1 !== data['syncChangesIndex']) {
this.syncChangesIndex = data['syncChangesIndex'];
}
if (this.onUnSaveLock) {
this.onUnSaveLock();
}
};
DocsCoApi.prototype._updateChanges = function(allServerChanges, changesIndex, syncChangesIndex, bFirstLoad) {
if (this.onSaveChanges) {
this.changesIndex = changesIndex;
if (undefined !== syncChangesIndex && -1 !== syncChangesIndex) {
this.syncChangesIndex = syncChangesIndex;
}
if (allServerChanges) {
for (var i = 0; i < allServerChanges.length; ++i) {
var change = allServerChanges[i];
var changesOneUser = this.binaryChanges ? new Uint8Array(change['change']) : JSON.parse(change['change']);
if (changesOneUser) {
if (change['user'] !== this._userId) {
this.lastOtherSaveTime = change['time'];
}
this._serverChangesSize += changesOneUser.length;
this.onSaveChanges(changesOneUser, change['useridoriginal'], bFirstLoad);
}
}
}
this.onChangesIndex(changesIndex);
}
};
DocsCoApi.prototype._onSetIndexUser = function(data) {
if (this.onSetIndexUser) {
this.onSetIndexUser(data);
}
};
DocsCoApi.prototype._onSpellCheckInit = function(data) {
if (this.onSpellCheckInit) {
this.onSpellCheckInit(data);
}
};
DocsCoApi.prototype._onSavePartChanges = function(data) {
// Очищаем предыдущий таймер
if (null !== this.saveCallbackErrorTimeOutId) {
clearTimeout(this.saveCallbackErrorTimeOutId);
this.saveCallbackErrorTimeOutId = null;
}
if (-1 !== data['changesIndex']) {
this.changesIndex = data['changesIndex'];
}
if (undefined !== data['syncChangesIndex'] && -1 !== data['syncChangesIndex']) {
this.syncChangesIndex = data['syncChangesIndex'];
}
this.saveChanges(this.arrayChanges, this.currentIndexEnd);
};
DocsCoApi.prototype._onPreviousLocks = function(locks, previousLocks) {
var i = 0;
if (locks && previousLocks) {
for (var block in locks) {
if (locks.hasOwnProperty(block)) {
var lock = locks[block];
if (lock !== null && lock["block"]) {
//Find in previous
for (i = 0; i < previousLocks.length; i++) {
if (previousLocks[i] === lock["block"] && lock["user"] === this._userId) {
//Lock is ours
previousLocks.remove(i);
break;
}
}
}
}
}
if (previousLocks.length > 0 && this.onRelockFailed) {
this.onRelockFailed(previousLocks);
}
previousLocks = [];
}
};
DocsCoApi.prototype._onParticipantsChanged = function(participants, needChanged) {
var participantsNew = {};
var countEditUsersNew = 0;
var countUsersNew = 0;
var tmpUser;
var i;
var usersStateChanged = [];
if (participants) {
for (i = 0; i < participants.length; ++i) {
tmpUser = new AscCommon.asc_CUser(participants[i]);
tmpUser.setState(true);
participantsNew[tmpUser.asc_getId()] = tmpUser;
if (!tmpUser.asc_getView()) {
++countEditUsersNew;
}
++countUsersNew;
}
}
if (needChanged) {
for (i in this._participants) {
if (!(participantsNew[i] && this._participants[i].isEqual(participantsNew[i]))) {
tmpUser = this._participants[i];
tmpUser.setState(false);
usersStateChanged.push(tmpUser);
}
}
for (i in participantsNew) {
if (!(this._participants[i] && this._participants[i].isEqual(participantsNew[i]))) {
tmpUser = participantsNew[i];
tmpUser.setState(true);
usersStateChanged.push(tmpUser);
}
}
}
this._participants = participantsNew;
this._countEditUsers = countEditUsersNew;
this._countUsers = countUsersNew;
return usersStateChanged;
};
DocsCoApi.prototype._onAuthParticipantsChanged = function(participants) {
this._participants = {};
this._countEditUsers = 0;
this._countUsers = 0;
if (participants) {
this._onParticipantsChanged(participants);
if (this.onAuthParticipantsChanged) {
this.onAuthParticipantsChanged(this._participants, this._userId);
}
// Посылаем эвент о совместном редактировании
if (1 < this._countEditUsers) {
this._onStartCoAuthoring(/*isStartEvent*/true);
} else {
this._onEndCoAuthoring(/*isStartEvent*/true);
}
}
};
DocsCoApi.prototype._onConnectionStateChanged = function(data) {
var t = this;
if (!this.check_state()) {
this._msgInputBuffer.push(function () {
t._onConnectionStateChanged(data);
});
return;
}
var isWaitAuth = data['waitAuth'];
var usersStateChanged;
if(isWaitAuth && !(this.onConnectionStateChanged && (!this._participantsTimestamp || this._participantsTimestamp <= data['participantsTimestamp']))) {
var errorMsg = 'Error: connection state changed waitAuth' +
';onConnectionStateChanged:' + !!this.onConnectionStateChanged +
';this._participantsTimestamp:' + this._participantsTimestamp +
';data.participantsTimestamp:' + data['participantsTimestamp'];
this.sendClientLog("error", "changesError: " + errorMsg);
}
if (this.onConnectionStateChanged && (!this._participantsTimestamp || this._participantsTimestamp <= data['participantsTimestamp'])) {
this._participantsTimestamp = data['participantsTimestamp'];
usersStateChanged = this._onParticipantsChanged(data['participants'], true);
if (isWaitAuth && !(usersStateChanged.length > 0 && 1 < this._countEditUsers)) {
var errorMsg = 'Error: connection state changed waitAuth' +
';usersStateChanged:' + JSON.stringify(usersStateChanged) +
';this._countEditUsers:' + this._countEditUsers;
this.sendClientLog("error", "changesError: " + errorMsg);
}
if (usersStateChanged.length > 0) {
// Посылаем эвент о совместном редактировании
if (1 < this._countEditUsers) {
this._onStartCoAuthoring(/*isStartEvent*/false, isWaitAuth);
} else {
this._onEndCoAuthoring(/*isStartEvent*/false);
}
this.onParticipantsChanged(this._participants);
for (var i = 0; i < usersStateChanged.length; ++i) {
this.onConnectionStateChanged(usersStateChanged[i]);
}
}
}
};
DocsCoApi.prototype._onLicenseChanged = function (data) {
this.onLicenseChanged(data);
};
DocsCoApi.prototype._onDisconnectReason = function(data) {
var code = data && data['code'] || c_oCloseCode.drop;
this.onDisconnect(data ? data['description'] : '', code);
};
DocsCoApi.prototype._onDrop = function(data) {
this.disconnect();
this._onDisconnectReason(data);
};
DocsCoApi.prototype._onWarning = function(data) {
this.onWarning(data.code);
};
DocsCoApi.prototype._onLicense = function(data) {
if (!this.isLicenseInit) {
this.isLicenseInit = true;
this.onLicense(data['license']);
if (this.onAiPluginSettings) {
this.onAiPluginSettings(data['aiPluginSettings']);
}
}
};
DocsCoApi.prototype._onAuth = function(data) {
var t = this;
this._onRefreshToken(data['jwt']);
if (true === this._isAuth) {
this._state = ConnectionState.Authorized;
this._onServerVersion(data);
// Мы должны только соединиться для получения файла. Совместное редактирование уже было отключено.
if (this.isCloseCoAuthoring) {
return;
}
this._onLicenseChanged(data);
// Мы уже авторизовывались, нужно обновить пользователей (т.к. пользователи могли входить и выходить пока у нас не было соединения)
this._onAuthParticipantsChanged(data['participants']);
//if (this.ownedLockBlocks && this.ownedLockBlocks.length > 0) {
// this._onPreviousLocks(data["locks"], this.ownedLockBlocks);
//}
this._onMessages(data, true);
this._onGetLock(data);
//Apply prebuffered
this._applyPrebuffered();
if (this._isReSaveAfterAuth) {
this._isReSaveAfterAuth = false;
var callbackAskSaveChanges = function(e) {
if (false === e["saveLock"]) {
t._reSaveChanges(2);
} else {
setTimeout(function() {
t.askSaveChanges(callbackAskSaveChanges);
}, 1000);
}
};
this.askSaveChanges(callbackAskSaveChanges);
}
return;
}
if (data['result'] === 1) {
// Выставляем флаг, что мы уже авторизовывались
this._isAuth = true;
//TODO: add checks
this._state = ConnectionState.Authorized;
this._id = data['sessionId'];
this._indexUser = data['indexUser'];
this._userId = this._user.asc_getId() + this._indexUser;
this._sessionTimeConnect = data['sessionTimeConnect'];
if (data['settings']) {
if (data['settings']['reconnection']) {
let attempts = data['settings']['reconnection']['attempts'];
let delay = data['settings']['reconnection']['delay'];
this.socketio.io.reconnectionAttempts(attempts);
this.socketio.io.reconnectionDelay(delay);
this.socketio.io.reconnectionDelayMax(delay);
}
if (data['settings']['websocketMaxPayloadSize']) {
this.websocketMaxPayloadSize = data['settings']['websocketMaxPayloadSize'];
}
}
this._onLicenseChanged(data);
this._onAuthParticipantsChanged(data['participants']);
this._onSpellCheckInit(data['g_cAscSpellCheckUrl']);
this._onSetIndexUser(this._indexUser);
this._onMessages(data, false);
this._onGetLock(data);
if (data['hasForgotten']) {
this._onHasForgotten();
}
// Применения изменений пользователя
if (window['AscApplyChanges'] && window['AscChanges']) {
var userOfflineChanges = window['AscChanges'], changeOneUser;
for (var i = 0; i < userOfflineChanges.length; ++i) {
changeOneUser = userOfflineChanges[i];
for (var j = 0; j < changeOneUser.length; ++j)
this.onSaveChanges(changeOneUser[j], null, true);
}
}
this._updateAuthChanges();
// Посылать нужно всегда, т.к. на это рассчитываем при открытии
if (this.onFirstLoadChangesEnd) {
this.onFirstLoadChangesEnd(data['openedAt']);
}
//Apply prebuffered
this._applyPrebuffered();
//Send prebuffered
this._sendPrebuffered();
}
//TODO: Add errors
};
DocsCoApi.prototype._onAuthChanges = function(data) {
this._authChanges.push(data["changes"]);
this._send({'type': 'authChangesAck'});
};
DocsCoApi.prototype._updateAuthChanges = function() {
//todo apply changes with chunk on arrival
var changesIndex = 0, i, changes, data, indexDiff;
for (i = 0; i < this._authChanges.length; ++i) {
changes = this._authChanges[i];
changesIndex += changes.length;
this._updateChanges(changes, changesIndex, changesIndex, true);
}
this._authChanges = [];
for (i = 0; i < this._authOtherChanges.length; ++i) {
data = this._authOtherChanges[i];
indexDiff = data["changesIndex"] - changesIndex;
if (indexDiff > 0) {
if (indexDiff >= data["changes"].length) {
changes = data["changes"];
} else {
changes = data["changes"].splice(data["changes"].length - indexDiff, indexDiff);
}
changesIndex += changes.length;
this._updateChanges(changes, changesIndex, changesIndex, true);
}
}
this._authOtherChanges = [];
};
DocsCoApi.prototype.init = function(user, docid, documentCallbackUrl, token, editorType, documentFormatSave, docInfo, shardKey, wopiSrc, userSessionId, headingsColor, openCmd) {
this._user = user;
this._docid = null;
this._documentCallbackUrl = documentCallbackUrl;
this._token = token;
this.ownedLockBlocks = [];
this.socketio_url = null;
this.editorType = editorType;
this._isExcel = c_oEditorId.Spreadsheet === editorType;
this._isPresentation = c_oEditorId.Presentation === editorType;
this._isPDF = Asc.editor.isPdfEditor();
this._isAuth = false;
this._documentFormatSave = documentFormatSave;
this.mode = docInfo.get_Mode();
this.permissions = docInfo.get_Permissions();
this.lang = docInfo.get_Lang();
this.openCmd = openCmd;
this.jwtOpen = docInfo.get_Token();
this.encrypted = docInfo.get_Encrypted() || docInfo.get_IsWebOpening();
this.IsAnonymousUser = docInfo.get_IsAnonymousUser();
this.coEditingMode = docInfo.asc_getCoEditingMode();
this.shardKey = shardKey;
this.wopiSrc = wopiSrc;
this.userSessionId = userSessionId;
this.headingsColor = headingsColor;
this.setDocId(docid);
this._initSocksJs();
};
DocsCoApi.prototype.getDocId = function() {
return this._docid;
};
DocsCoApi.prototype.setDocId = function(docid) {
//todo возможно надо менять sockjs_url
this._docid = docid;
this.socketio_url = AscCommon.getBaseUrlPathname() + '../../../../doc/' + docid + '/c';
};
DocsCoApi.prototype.getAuthCommand = function(opt_openCmd, opt_isIdle) {
if (this._locks) {
this.ownedLockBlocks = [];
//If we already have locks
for (var block in this._locks) if (this._locks.hasOwnProperty(block)) {
var lock = this._locks[block];
if (lock["state"] === 2) {
//Our lock.
this.ownedLockBlocks.push(lock["blockValue"]);
}
}
this._locks = {};
}
return {
'type': 'auth',
'docid': this._docid,
'documentCallbackUrl': this._documentCallbackUrl,
'token': this._token,
'user': {
'id': this._user.asc_getId(),
'username': this._user.asc_getUserName(),
'firstname': this._user.asc_getFirstName(),
'lastname': this._user.asc_getLastName(),
'indexUser': this._indexUser
},
'editorType': this.editorType,
'lastOtherSaveTime': this.lastOtherSaveTime,
'block': this.ownedLockBlocks,
'sessionId': this._id,
'sessionTimeConnect': this._sessionTimeConnect,
'sessionTimeIdle': opt_isIdle >= 0 ? opt_isIdle : 0,
'documentFormatSave': this._documentFormatSave,
'isCloseCoAuthoring': this.isCloseCoAuthoring,
'openCmd': opt_openCmd,
'lang': this.lang,
'mode': this.mode,
'permissions': this.permissions,
'encrypted': this.encrypted,
'IsAnonymousUser': this.IsAnonymousUser,
'timezoneOffset': (new Date()).getTimezoneOffset(),
'headingsColor': this.headingsColor,
'coEditingMode': this.coEditingMode,
'jwtOpen': this.jwtOpen,
'jwtSession': this.jwtSession,
'time': Math.round(performance.now()),
'supportAuthChangesAck': true
};
};
// Авторизация (ее нужно делать после выставления состояния редактора view-mode)
DocsCoApi.prototype.auth = function(isViewer, opt_openCmd, opt_isIdle) {
this._send(this.getAuthCommand(opt_openCmd, opt_isIdle));
};
function CNativeSocket(settings)
{
this.engine = window['SockJS'];
this.settings = settings;
this.io = this;
this.settings["type"] = "socketio";
this.events = {};
}
CNativeSocket.prototype.open = function() { return this.engine.open(this.settings); };
CNativeSocket.prototype.send = function(message) { return this.engine.send(message); };
CNativeSocket.prototype.close = function() { return this.engine.close(); };
CNativeSocket.prototype.emit = function(message, data) { return this.send(JSON.stringify(data)); };
CNativeSocket.prototype.reconnectionAttempts = function(val) { this.settings["reconnectionAttempts"] = val; };
CNativeSocket.prototype.reconnectionDelay = function(val) { this.settings["reconnectionDelay"] = val; };
CNativeSocket.prototype.reconnectionDelayMax = function(val) { this.settings["reconnectionDelayMax"] = val; };
CNativeSocket.prototype.randomizationFactor = function(val) { this.settings["randomizationFactor"] = val; };
CNativeSocket.prototype.setOpenToken = function (val) {
if (this.settings["auth"]) {
this.settings["auth"]["token"] = val;
}
};
CNativeSocket.prototype.setSessionToken = function (val) {
if (this.settings["auth"]) {
this.settings["auth"]["session"] = val;
}
};
CNativeSocket.prototype.on = function(name, callback) {
if (!this.events.hasOwnProperty(name))
this.events[name] = [];
this.events[name].push(callback);
};
CNativeSocket.prototype.onMessage = function() {
var name = arguments[0];
if (this.events.hasOwnProperty(name))
{
for (var i = 0; i < this.events[name].length; ++i)
this.events[name][i].apply(this, Array.prototype.slice.call(arguments, 1));
return true;
}
return false;
};
DocsCoApi.prototype._initSocksJs = function () {
var t = this;
let socket;
let firstConnection = true;
let options = {
"path": this.socketio_url,
"transports": ["websocket", "polling"],
"closeOnBeforeunload": false,
"reconnectionAttempts": 15,
"reconnectionDelay": 500,
"reconnectionDelayMax": 10000,
"randomizationFactor": 0.5,
"auth": {
"data": this.getAuthCommand(this.openCmd),
"token": this.jwtOpen,
"session": this.jwtSession
}
};
options["query"] = {};
if (this.shardKey) {
options["query"][Asc.c_sShardKeyName] = this.shardKey;
}
if (this.wopiSrc) {
options["query"][Asc.c_sWopiSrcName] = this.wopiSrc;
}
if (this.userSessionId) {
options["query"][Asc.c_sUserSessionIdName] = this.userSessionId;
}
if (window['IS_NATIVE_EDITOR']) {
socket = this.sockjs = new CNativeSocket(options);
socket.open();
} else {
let io = AscCommon.getSocketIO();
socket = io(options);
}
socket.on("connect", function () {
firstConnection = false;
t._onServerOpen();
});
socket.on("disconnect", function (reason) {
//(explicit disconnection), the client will not try to reconnect and you need to manually call
let explicit = 'io server disconnect' === reason || 'io client disconnect' === reason;
t._onServerClose(explicit);
if (!explicit) {
//explicit disconnect sends disconnect reason on its own
t.onDisconnect();
}
});
socket.on('connect_error', function (err) {
//cases: every connect error and reconnect
if (err.data) {
//cases: authorization
t._onServerClose(true);
t.onDisconnect(err.data.description, err.data.code);
} else if (firstConnection) {
firstConnection = false;
if (socket.io.opts) {
socket.io.opts.transports = ["polling", "websocket"];
}
}
});
socket.io.on("reconnect_failed", function () {
//Fired when couldn't reconnect within reconnectionAttempts.
t._onServerClose(true);
t.onDisconnect("reconnect_failed", c_oCloseCode.reconnectFailed);
});
socket.on("message", function (data) {
t._onServerMessage(data);
});
this.socketio = socket;
return socket;
};
DocsCoApi.prototype._onServerOpen = function () {
this._state = ConnectionState.WaitAuth;
this.onFirstConnect();
};
DocsCoApi.prototype._onServerMessage = function (data) {
//TODO: add checks and error handling
//Get data type
var dataObject = data;
switch (dataObject['type']) {
case 'auth' :
this._onAuth(dataObject);
break;
case 'message' :
this._onMessages(dataObject, false);
break;
case 'cursor' :
this._onCursor(dataObject);
break;
case 'meta' :
this._onMeta(dataObject);
break;
case 'getLock' :
this._onGetLock(dataObject);
break;
case 'releaseLock' :
this._onReleaseLock(dataObject);
break;
case 'connectState' :
this._onConnectionStateChanged(dataObject);
break;
case 'saveChanges' :
this._onSaveChanges(dataObject);
break;
case 'authChanges' :
this._onAuthChanges(dataObject);
break;
case 'saveLock' :
this._onSaveLock(dataObject);
break;
case 'unSaveLock' :
this._onUnSaveLock(dataObject);
break;
case 'savePartChanges' :
this._onSavePartChanges(dataObject);
break;
case 'drop' :
this._onDrop(dataObject);
break;
case 'disconnectReason':
this._onDisconnectReason(dataObject);
break;
case 'waitAuth' : /*Ждем, когда придет auth, документ залочен*/
break;
case 'error' : /*Старая версия sdk*/
this._onDrop(dataObject);
break;
case 'documentOpen' :
this._documentOpen(dataObject);
break;
case 'warning':
this._onWarning(dataObject);
break;
case 'license':
this._onLicense(dataObject);
break;
case 'session' :
this._onSession(dataObject);
break;
case 'refreshToken' :
this._onRefreshToken(dataObject["messages"]);
break;
case 'expiredToken' :
this._onExpiredToken(dataObject);
break;
case 'forceSaveStart' :
this._onForceSaveStart(dataObject["messages"]);
break;
case 'forceSave' :
this._onForceSave(dataObject["messages"]);
break;
case 'rpc' :
this._onPRC(dataObject["responseKey"], false, dataObject["data"]);
break;
case 'updateVersion' :
this.onMiscEvent(dataObject);
break;
}
};
DocsCoApi.prototype._onServerClose = function (explicit) {
if (ConnectionState.SaveChanges === this._state) {
// Мы сохраняли изменения и разорвалось соединение
this._isReSaveAfterAuth = true;
// Очищаем предыдущий таймер
if (null !== this.saveCallbackErrorTimeOutId) {
clearTimeout(this.saveCallbackErrorTimeOutId);
this.saveCallbackErrorTimeOutId = null;
}
}
if (explicit) {
this._state = ConnectionState.ClosedAll;
} else {
this._state = ConnectionState.Reconnect;
}
};
//----------------------------------------------------------export----------------------------------------------------
window['AscCommon'] = window['AscCommon'] || {};
window['AscCommon'].CDocsCoApi = CDocsCoApi;
})(window);