902 lines
35 KiB
JavaScript
902 lines
35 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2014
|
|
*
|
|
* 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 Lubanas st. 125a-25, Riga, Latvia,
|
|
* EU, LV-1021.
|
|
*
|
|
* 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
|
|
*
|
|
*/
|
|
var sockjs = require("sockjs"),
|
|
_ = require("underscore"),
|
|
dataBase = null,
|
|
http = require("http"),
|
|
config = require("./config.json");
|
|
if (config["mongodb"]) {
|
|
dataBase = require("./database");
|
|
}
|
|
var logger = require("./../../Common/sources/logger");
|
|
var c_oAscRecalcIndexTypes = {
|
|
RecalcIndexAdd: 1,
|
|
RecalcIndexRemove: 2
|
|
};
|
|
var c_oAscLockTypeElem = {
|
|
Range: 1,
|
|
Object: 2,
|
|
Sheet: 3
|
|
};
|
|
var c_oAscLockTypeElemSubType = {
|
|
DeleteColumns: 1,
|
|
InsertColumns: 2,
|
|
DeleteRows: 3,
|
|
InsertRows: 4,
|
|
ChangeProperties: 5
|
|
};
|
|
var c_oAscLockTypeElemPresentation = {
|
|
Object: 1,
|
|
Slide: 2,
|
|
Presentation: 3
|
|
};
|
|
function CRecalcIndexElement(recalcType, position, bIsSaveIndex) {
|
|
if (! (this instanceof CRecalcIndexElement)) {
|
|
return new CRecalcIndexElement(recalcType, position, bIsSaveIndex);
|
|
}
|
|
this._recalcType = recalcType;
|
|
this._position = position;
|
|
this._count = 1;
|
|
this.m_bIsSaveIndex = !!bIsSaveIndex;
|
|
return this;
|
|
}
|
|
CRecalcIndexElement.prototype = {
|
|
constructor: CRecalcIndexElement,
|
|
getLockOther: function (position, type) {
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? +1 : -1;
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && true === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && c_oAscLockTypes.kLockTypeMine === type && false === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getLockSaveOther: function (position, type) {
|
|
if (this.m_bIsSaveIndex) {
|
|
return position;
|
|
}
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? +1 : -1;
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && true === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && c_oAscLockTypes.kLockTypeMine === type && false === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getLockMe: function (position) {
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? -1 : +1;
|
|
if (position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
},
|
|
getLockMe2: function (position) {
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? -1 : +1;
|
|
if (true !== this.m_bIsSaveIndex || position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
}
|
|
};
|
|
function CRecalcIndex() {
|
|
if (! (this instanceof CRecalcIndex)) {
|
|
return new CRecalcIndex();
|
|
}
|
|
this._arrElements = [];
|
|
return this;
|
|
}
|
|
CRecalcIndex.prototype = {
|
|
constructor: CRecalcIndex,
|
|
add: function (recalcType, position, count, bIsSaveIndex) {
|
|
for (var i = 0; i < count; ++i) {
|
|
this._arrElements.push(new CRecalcIndexElement(recalcType, position, bIsSaveIndex));
|
|
}
|
|
},
|
|
clear: function () {
|
|
this._arrElements.length = 0;
|
|
},
|
|
getLockOther: function (position, type) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = 0; i < count; ++i) {
|
|
newPosition = this._arrElements[i].getLockOther(newPosition, type);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
},
|
|
getLockSaveOther: function (position, type) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = 0; i < count; ++i) {
|
|
newPosition = this._arrElements[i].getLockSaveOther(newPosition, type);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
},
|
|
getLockMe: function (position) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = count - 1; i >= 0; --i) {
|
|
newPosition = this._arrElements[i].getLockMe(newPosition);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
},
|
|
getLockMe2: function (position) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = count - 1; i >= 0; --i) {
|
|
newPosition = this._arrElements[i].getLockMe2(newPosition);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
}
|
|
};
|
|
exports.install = function (server, callbackFunction) {
|
|
var sockjs_opts = {
|
|
sockjs_url: "http://cdn.sockjs.org/sockjs-0.3.min.js"
|
|
},
|
|
sockjs_echo = sockjs.createServer(sockjs_opts),
|
|
connections = [],
|
|
messages = {},
|
|
objchanges = {},
|
|
indexuser = {},
|
|
locks = {},
|
|
arrsavelock = [],
|
|
dataHandler,
|
|
urlParse = new RegExp("^/doc/([0-9-.a-zA-Z_=]*)/c.+", "i"),
|
|
serverPort = 80;
|
|
sockjs_echo.on("connection", function (conn) {
|
|
if (null == conn) {
|
|
logger.error("null == conn");
|
|
return;
|
|
}
|
|
conn.on("data", function (message) {
|
|
try {
|
|
var data = JSON.parse(message);
|
|
dataHandler[data.type](conn, data);
|
|
} catch(e) {
|
|
logger.error("error receiving response:" + e);
|
|
}
|
|
});
|
|
conn.on("error", function () {
|
|
logger.error("On error");
|
|
});
|
|
conn.on("close", function () {
|
|
var connection = this,
|
|
docLock, userLocks, participants, reconected;
|
|
logger.info("Connection closed or timed out");
|
|
connections = _.reject(connections, function (el) {
|
|
return el.connection.id === connection.id;
|
|
});
|
|
reconected = _.any(connections, function (el) {
|
|
return el.connection.sessionId === connection.sessionId;
|
|
});
|
|
var state = (false == reconected) ? false : undefined;
|
|
participants = getParticipants(conn.docId);
|
|
var participantsMap = _.map(participants, function (conn) {
|
|
return {
|
|
id: conn.connection.userId,
|
|
username: conn.connection.userName
|
|
};
|
|
});
|
|
sendParticipantsState(participants, state, connection.userId, connection.userName, participantsMap);
|
|
if (!reconected) {
|
|
if (undefined != arrsavelock[conn.docId] && connection.userId == arrsavelock[conn.docId].user) {
|
|
if (null != arrsavelock[conn.docId].saveLockTimeOutId) {
|
|
clearTimeout(arrsavelock[conn.docId].saveLockTimeOutId);
|
|
}
|
|
arrsavelock[conn.docId] = undefined;
|
|
}
|
|
if (0 >= participants.length) {
|
|
if (dataBase) {
|
|
dataBase.remove("messages", {
|
|
docid: conn.docId
|
|
});
|
|
}
|
|
delete messages[conn.docId];
|
|
if (objchanges[conn.docId] && 0 < objchanges[conn.docId].length) {
|
|
sendChangesToServer(conn.serverHost, conn.serverPath, conn.docId);
|
|
}
|
|
if (dataBase) {
|
|
dataBase.remove("changes", {
|
|
docid: conn.docId
|
|
});
|
|
}
|
|
delete objchanges[conn.docId];
|
|
if (null != arrsavelock[conn.docId] && null != arrsavelock[conn.docId].saveLockTimeOutId) {
|
|
clearTimeout(arrsavelock[conn.docId].saveLockTimeOutId);
|
|
}
|
|
arrsavelock[conn.docId] = undefined;
|
|
}
|
|
docLock = locks[connection.docId];
|
|
if (docLock) {
|
|
userLocks = [];
|
|
if ("array" === typeOf(docLock)) {
|
|
for (var nIndex = 0; nIndex < docLock.length; ++nIndex) {
|
|
if (docLock[nIndex].sessionId === connection.sessionId) {
|
|
userLocks.push(docLock[nIndex]);
|
|
docLock.splice(nIndex, 1);
|
|
--nIndex;
|
|
}
|
|
}
|
|
} else {
|
|
for (var keyLockElem in docLock) {
|
|
if (docLock[keyLockElem].sessionId === connection.sessionId) {
|
|
userLocks.push(docLock[keyLockElem]);
|
|
delete docLock[keyLockElem];
|
|
}
|
|
}
|
|
}
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "releaselock",
|
|
locks: _.map(userLocks, function (e) {
|
|
return {
|
|
block: e.block,
|
|
user: e.user,
|
|
time: Date.now(),
|
|
changes: null
|
|
};
|
|
})
|
|
});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
function sendData(conn, data) {
|
|
conn.write(JSON.stringify(data));
|
|
}
|
|
function sendParticipantsState(participants, stateConnect, _userId, _userName, participantsMap) {
|
|
_.each(participants, function (participant) {
|
|
if (participant.connection.userId !== _userId) {
|
|
sendData(participant.connection, {
|
|
type: "participants",
|
|
participants: participantsMap
|
|
});
|
|
sendData(participant.connection, {
|
|
type: "connectstate",
|
|
state: stateConnect,
|
|
id: _userId,
|
|
username: _userName
|
|
});
|
|
}
|
|
});
|
|
}
|
|
function getParticipants(docId, exludeuserId) {
|
|
return _.filter(connections, function (el) {
|
|
return el.connection.docId === docId && el.connection.userId !== exludeuserId;
|
|
});
|
|
}
|
|
function sendChangesToServer(serverHost, serverPath, docId) {
|
|
if (!serverHost || !serverPath) {
|
|
return;
|
|
}
|
|
var options = {
|
|
host: serverHost,
|
|
port: serverPort,
|
|
path: serverPath,
|
|
method: "POST"
|
|
};
|
|
var req = http.request(options, function (res) {
|
|
res.setEncoding("utf8");
|
|
});
|
|
req.on("error", function (e) {
|
|
logger.warn("problem with request on server: " + e.message);
|
|
});
|
|
var sendData = JSON.stringify({
|
|
"id": docId,
|
|
"c": "cc"
|
|
});
|
|
req.write(sendData);
|
|
req.end();
|
|
}
|
|
function _recalcLockArray(userId, _locks, oRecalcIndexColumns, oRecalcIndexRows) {
|
|
if (null == _locks) {
|
|
return;
|
|
}
|
|
var count = _locks.length;
|
|
var element = null,
|
|
oRangeOrObjectId = null;
|
|
var i;
|
|
var sheetId = -1;
|
|
for (i = 0; i < count; ++i) {
|
|
if (userId === _locks[i].user) {
|
|
continue;
|
|
}
|
|
element = _locks[i].block;
|
|
if (c_oAscLockTypeElem.Range !== element["type"] || c_oAscLockTypeElemSubType.InsertColumns === element["subType"] || c_oAscLockTypeElemSubType.InsertRows === element["subType"]) {
|
|
continue;
|
|
}
|
|
sheetId = element["sheetId"];
|
|
oRangeOrObjectId = element["rangeOrObjectId"];
|
|
if (oRecalcIndexColumns.hasOwnProperty(sheetId)) {
|
|
oRangeOrObjectId["c1"] = oRecalcIndexColumns[sheetId].getLockMe2(oRangeOrObjectId["c1"]);
|
|
oRangeOrObjectId["c2"] = oRecalcIndexColumns[sheetId].getLockMe2(oRangeOrObjectId["c2"]);
|
|
}
|
|
if (oRecalcIndexRows.hasOwnProperty(sheetId)) {
|
|
oRangeOrObjectId["r1"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r1"]);
|
|
oRangeOrObjectId["r2"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r2"]);
|
|
}
|
|
}
|
|
}
|
|
function _addRecalcIndex(oRecalcIndex) {
|
|
var nIndex = 0;
|
|
var nRecalcType = c_oAscRecalcIndexTypes.RecalcIndexAdd;
|
|
var oRecalcIndexElement = null;
|
|
var oRecalcIndexResult = {};
|
|
for (var sheetId in oRecalcIndex) {
|
|
if (oRecalcIndex.hasOwnProperty(sheetId)) {
|
|
if (!oRecalcIndexResult.hasOwnProperty(sheetId)) {
|
|
oRecalcIndexResult[sheetId] = new CRecalcIndex();
|
|
}
|
|
for (; nIndex < oRecalcIndex[sheetId]._arrElements.length; ++nIndex) {
|
|
oRecalcIndexElement = oRecalcIndex[sheetId]._arrElements[nIndex];
|
|
if (true === oRecalcIndexElement.m_bIsSaveIndex) {
|
|
continue;
|
|
}
|
|
nRecalcType = (c_oAscRecalcIndexTypes.RecalcIndexAdd === oRecalcIndexElement._recalcType) ? c_oAscRecalcIndexTypes.RecalcIndexRemove : c_oAscRecalcIndexTypes.RecalcIndexAdd;
|
|
oRecalcIndexResult[sheetId].add(nRecalcType, oRecalcIndexElement._position, oRecalcIndexElement._count, true);
|
|
}
|
|
}
|
|
}
|
|
return oRecalcIndexResult;
|
|
}
|
|
function compareExcelBlock(newBlock, oldBlock) {
|
|
if (null !== newBlock.subType && null !== oldBlock.subType) {
|
|
return true;
|
|
}
|
|
if ((c_oAscLockTypeElemSubType.ChangeProperties === oldBlock.subType && c_oAscLockTypeElem.Sheet !== newBlock.type) || (c_oAscLockTypeElemSubType.ChangeProperties === newBlock.subType && c_oAscLockTypeElem.Sheet !== oldBlock.type)) {
|
|
return false;
|
|
}
|
|
var resultLock = false;
|
|
if (newBlock.type === c_oAscLockTypeElem.Range) {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Range) {
|
|
if (c_oAscLockTypeElemSubType.InsertRows === oldBlock.subType || c_oAscLockTypeElemSubType.InsertColumns === oldBlock.subType) {
|
|
resultLock = false;
|
|
} else {
|
|
if (isInterSection(newBlock.rangeOrObjectId, oldBlock.rangeOrObjectId)) {
|
|
resultLock = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Sheet) {
|
|
resultLock = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (newBlock.type === c_oAscLockTypeElem.Sheet) {
|
|
resultLock = true;
|
|
} else {
|
|
if (newBlock.type === c_oAscLockTypeElem.Object) {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Sheet) {
|
|
resultLock = true;
|
|
} else {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Object && oldBlock.rangeOrObjectId === newBlock.rangeOrObjectId) {
|
|
resultLock = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return resultLock;
|
|
}
|
|
function isInterSection(range1, range2) {
|
|
if (range2.c1 > range1.c2 || range2.c2 < range1.c1 || range2.r1 > range1.r2 || range2.r2 < range1.r1) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function typeOf(obj) {
|
|
if (obj === undefined) {
|
|
return "undefined";
|
|
}
|
|
if (obj === null) {
|
|
return "null";
|
|
}
|
|
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
|
|
}
|
|
function comparePresentationBlock(newBlock, oldBlock) {
|
|
var resultLock = false;
|
|
switch (newBlock.type) {
|
|
case c_oAscLockTypeElemPresentation.Presentation:
|
|
if (c_oAscLockTypeElemPresentation.Presentation === oldBlock.type) {
|
|
resultLock = newBlock.val === oldBlock.val;
|
|
}
|
|
break;
|
|
case c_oAscLockTypeElemPresentation.Slide:
|
|
if (c_oAscLockTypeElemPresentation.Slide === oldBlock.type) {
|
|
resultLock = newBlock.val === oldBlock.val;
|
|
} else {
|
|
if (c_oAscLockTypeElemPresentation.Object === oldBlock.type) {
|
|
resultLock = newBlock.val === oldBlock.slideId;
|
|
}
|
|
}
|
|
break;
|
|
case c_oAscLockTypeElemPresentation.Object:
|
|
if (c_oAscLockTypeElemPresentation.Slide === oldBlock.type) {
|
|
resultLock = newBlock.slideId === oldBlock.val;
|
|
} else {
|
|
if (c_oAscLockTypeElemPresentation.Object === oldBlock.type) {
|
|
resultLock = newBlock.objId === oldBlock.objId;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return resultLock;
|
|
}
|
|
dataHandler = (function () {
|
|
function auth(conn, data) {
|
|
if (data.token && data.user) {
|
|
var parsed = urlParse.exec(conn.url);
|
|
if (parsed.length > 1) {
|
|
conn.docId = parsed[1];
|
|
} else {}
|
|
if (!indexuser.hasOwnProperty(conn.docId)) {
|
|
indexuser[conn.docId] = 1;
|
|
} else {
|
|
indexuser[conn.docId] += 1;
|
|
}
|
|
conn.sessionState = 1;
|
|
conn.userId = data.user + indexuser[conn.docId];
|
|
conn.userName = data.username;
|
|
conn.serverHost = data.serverHost;
|
|
conn.serverPath = data.serverPath;
|
|
if (data.sessionId !== null && _.isString(data.sessionId) && data.sessionId !== "") {
|
|
logger.info("restored old session id=" + data.sessionId);
|
|
connections = _.reject(connections, function (el) {
|
|
return el.connection.sessionId === data.sessionId;
|
|
});
|
|
conn.sessionId = data.sessionId;
|
|
} else {
|
|
conn.sessionId = conn.id;
|
|
}
|
|
connections.push({
|
|
connection: conn
|
|
});
|
|
var participants = getParticipants(conn.docId);
|
|
var participantsMap = _.map(participants, function (conn) {
|
|
return {
|
|
id: conn.connection.userId,
|
|
username: conn.connection.userName
|
|
};
|
|
});
|
|
sendData(conn, {
|
|
type: "auth",
|
|
result: 1,
|
|
sessionId: conn.sessionId,
|
|
participants: participantsMap,
|
|
messages: messages[conn.docid],
|
|
locks: locks[conn.docId],
|
|
changes: objchanges[conn.docId],
|
|
indexuser: indexuser[conn.docId]
|
|
});
|
|
sendParticipantsState(participants, true, conn.userId, conn.userName, participantsMap);
|
|
}
|
|
}
|
|
function message(conn, data) {
|
|
var participants = getParticipants(conn.docId),
|
|
msg = {
|
|
docid: conn.docId,
|
|
message: data.message,
|
|
time: Date.now(),
|
|
user: conn.userId,
|
|
username: conn.userName
|
|
};
|
|
if (!messages.hasOwnProperty(conn.docId)) {
|
|
messages[conn.docId] = [msg];
|
|
} else {
|
|
messages[conn.docId].push(msg);
|
|
}
|
|
logger.info("database insert message: " + JSON.stringify(msg));
|
|
if (dataBase) {
|
|
dataBase.insert("messages", msg);
|
|
}
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "message",
|
|
messages: [msg]
|
|
});
|
|
});
|
|
}
|
|
function getlock(conn, data) {
|
|
var participants = getParticipants(conn.docId),
|
|
documentLocks;
|
|
if (!locks.hasOwnProperty(conn.docId)) {
|
|
locks[conn.docId] = {};
|
|
}
|
|
documentLocks = locks[conn.docId];
|
|
var arrayBlocks = data.block;
|
|
var isLock = false;
|
|
var i = 0;
|
|
var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
|
for (; i < lengthArray; ++i) {
|
|
logger.info("getLock id: " + arrayBlocks[i]);
|
|
if (documentLocks.hasOwnProperty(arrayBlocks[i]) && documentLocks[arrayBlocks[i]] !== null) {
|
|
isLock = true;
|
|
break;
|
|
}
|
|
}
|
|
if (0 === lengthArray) {
|
|
isLock = true;
|
|
}
|
|
if (!isLock) {
|
|
for (i = 0; i < lengthArray; ++i) {
|
|
documentLocks[arrayBlocks[i]] = {
|
|
time: Date.now(),
|
|
user: conn.userId,
|
|
block: arrayBlocks[i],
|
|
sessionId: conn.sessionId
|
|
};
|
|
}
|
|
}
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "getlock",
|
|
locks: locks[conn.docId]
|
|
});
|
|
});
|
|
}
|
|
function getlockrange(conn, data) {
|
|
var participants = getParticipants(conn.docId),
|
|
documentLocks,
|
|
documentLock;
|
|
if (!locks.hasOwnProperty(conn.docId)) {
|
|
locks[conn.docId] = [];
|
|
}
|
|
documentLocks = locks[conn.docId];
|
|
var arrayBlocks = data.block;
|
|
var isLock = false;
|
|
var isExistInArray = false;
|
|
var i = 0,
|
|
blockRange = null;
|
|
var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
|
for (; i < lengthArray && false === isLock; ++i) {
|
|
blockRange = arrayBlocks[i];
|
|
for (var keyLockInArray in documentLocks) {
|
|
if (true === isLock) {
|
|
break;
|
|
}
|
|
if (!documentLocks.hasOwnProperty(keyLockInArray)) {
|
|
continue;
|
|
}
|
|
documentLock = documentLocks[keyLockInArray];
|
|
if (documentLock.user === conn.userId && blockRange.sheetId === documentLock.block.sheetId && blockRange.type === c_oAscLockTypeElem.Object && documentLock.block.type === c_oAscLockTypeElem.Object && documentLock.block.rangeOrObjectId === blockRange.rangeOrObjectId) {
|
|
isExistInArray = true;
|
|
break;
|
|
}
|
|
if (c_oAscLockTypeElem.Sheet === blockRange.type && c_oAscLockTypeElem.Sheet === documentLock.block.type) {
|
|
if (documentLock.user === conn.userId) {
|
|
if (blockRange.sheetId === documentLock.block.sheetId) {
|
|
isExistInArray = true;
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
isLock = true;
|
|
break;
|
|
}
|
|
}
|
|
if (documentLock.user === conn.userId || !(documentLock.block) || blockRange.sheetId !== documentLock.block.sheetId) {
|
|
continue;
|
|
}
|
|
isLock = compareExcelBlock(blockRange, documentLock.block);
|
|
}
|
|
}
|
|
if (0 === lengthArray) {
|
|
isLock = true;
|
|
}
|
|
if (!isLock && !isExistInArray) {
|
|
for (i = 0; i < lengthArray; ++i) {
|
|
blockRange = arrayBlocks[i];
|
|
documentLocks.push({
|
|
time: Date.now(),
|
|
user: conn.userId,
|
|
block: blockRange,
|
|
sessionId: conn.sessionId
|
|
});
|
|
}
|
|
}
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "getlock",
|
|
locks: locks[conn.docId]
|
|
});
|
|
});
|
|
}
|
|
function getlockpresentation(conn, data) {
|
|
var participants = getParticipants(conn.docId),
|
|
documentLocks,
|
|
documentLock;
|
|
if (!locks.hasOwnProperty(conn.docId)) {
|
|
locks[conn.docId] = [];
|
|
}
|
|
documentLocks = locks[conn.docId];
|
|
var arrayBlocks = data.block;
|
|
var isLock = false;
|
|
var isExistInArray = false;
|
|
var i = 0,
|
|
blockRange = null;
|
|
var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
|
for (; i < lengthArray && false === isLock; ++i) {
|
|
blockRange = arrayBlocks[i];
|
|
for (var keyLockInArray in documentLocks) {
|
|
if (true === isLock) {
|
|
break;
|
|
}
|
|
if (!documentLocks.hasOwnProperty(keyLockInArray)) {
|
|
continue;
|
|
}
|
|
documentLock = documentLocks[keyLockInArray];
|
|
if (documentLock.user === conn.userId || !(documentLock.block)) {
|
|
continue;
|
|
}
|
|
isLock = comparePresentationBlock(blockRange, documentLock.block);
|
|
}
|
|
}
|
|
if (0 === lengthArray) {
|
|
isLock = true;
|
|
}
|
|
if (!isLock && !isExistInArray) {
|
|
for (i = 0; i < lengthArray; ++i) {
|
|
blockRange = arrayBlocks[i];
|
|
documentLocks.push({
|
|
time: Date.now(),
|
|
user: conn.userId,
|
|
block: blockRange,
|
|
sessionId: conn.sessionId
|
|
});
|
|
}
|
|
}
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "getlock",
|
|
locks: locks[conn.docId]
|
|
});
|
|
});
|
|
}
|
|
function savechanges(conn, data) {
|
|
var docLock, userLocks, participants;
|
|
if (data.endSaveChanges) {
|
|
docLock = locks[conn.docId];
|
|
if (docLock) {
|
|
if ("array" === typeOf(docLock)) {
|
|
userLocks = [];
|
|
for (var nIndex = 0; nIndex < docLock.length; ++nIndex) {
|
|
if (null !== docLock[nIndex] && docLock[nIndex].sessionId === conn.sessionId) {
|
|
userLocks.push(docLock[nIndex]);
|
|
docLock.splice(nIndex, 1);
|
|
--nIndex;
|
|
}
|
|
}
|
|
} else {
|
|
userLocks = _.filter(docLock, function (el) {
|
|
return el !== null && el.sessionId === conn.sessionId;
|
|
});
|
|
for (var i = 0; i < userLocks.length; i++) {
|
|
delete docLock[userLocks[i].block];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
userLocks = [];
|
|
}
|
|
var objchange = {
|
|
docid: conn.docId,
|
|
changes: data.changes,
|
|
time: Date.now(),
|
|
user: conn.userId
|
|
};
|
|
if (!objchanges.hasOwnProperty(conn.docId)) {
|
|
objchanges[conn.docId] = [objchange];
|
|
} else {
|
|
objchanges[conn.docId].push(objchange);
|
|
}
|
|
logger.info("database insert changes: " + JSON.stringify(objchange));
|
|
if (dataBase) {
|
|
dataBase.insert("changes", objchange);
|
|
}
|
|
if (!data.endSaveChanges) {
|
|
sendData(conn, {
|
|
type: "savePartChanges"
|
|
});
|
|
} else {
|
|
if (data.isExcel) {
|
|
var oElement = null;
|
|
var oRecalcIndexColumns = null,
|
|
oRecalcIndexRows = null;
|
|
var oChanges = JSON.parse(data.changes);
|
|
var nCount = oChanges.length;
|
|
var nIndexChanges = 0;
|
|
for (; nIndexChanges < nCount; ++nIndexChanges) {
|
|
oElement = oChanges[nIndexChanges];
|
|
if ("object" === typeof oElement) {
|
|
if ("0" === oElement["type"]) {
|
|
oRecalcIndexColumns = _addRecalcIndex(oElement["index"]);
|
|
} else {
|
|
if ("1" === oElement["type"]) {
|
|
oRecalcIndexRows = _addRecalcIndex(oElement["index"]);
|
|
}
|
|
}
|
|
}
|
|
if (null !== oRecalcIndexColumns && null !== oRecalcIndexRows) {
|
|
_recalcLockArray(conn.userId, locks[conn.docId], oRecalcIndexColumns, oRecalcIndexRows);
|
|
oRecalcIndexColumns = null;
|
|
oRecalcIndexRows = null;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
participants = getParticipants(conn.docId, conn.userId);
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "savechanges",
|
|
changes: data.changes,
|
|
locks: _.map(userLocks, function (e) {
|
|
return {
|
|
block: e.block,
|
|
user: e.user,
|
|
time: Date.now(),
|
|
changes: null
|
|
};
|
|
})
|
|
});
|
|
});
|
|
}
|
|
function issavelock(conn) {
|
|
var _docId = conn.docId;
|
|
var _userId = conn.userId;
|
|
var _time = Date.now();
|
|
var isSaveLock = (undefined === arrsavelock[_docId]) ? false : arrsavelock[_docId].savelock;
|
|
if (false === isSaveLock) {
|
|
arrsavelock[conn.docId] = {
|
|
docid: _docId,
|
|
savelock: true,
|
|
time: Date.now(),
|
|
user: conn.userId
|
|
};
|
|
var _tmpSaveLock = arrsavelock[_docId];
|
|
arrsavelock[conn.docId].saveLockTimeOutId = setTimeout(function () {
|
|
if (_tmpSaveLock && _userId == _tmpSaveLock.user && _time == _tmpSaveLock.time) {
|
|
arrsavelock[_docId] = undefined;
|
|
}
|
|
},
|
|
60000);
|
|
}
|
|
sendData(conn, {
|
|
type: "savelock",
|
|
savelock: isSaveLock
|
|
});
|
|
}
|
|
function unsavelock(conn) {
|
|
if (undefined != arrsavelock[conn.docId] && conn.userId != arrsavelock[conn.docId].user) {
|
|
return;
|
|
}
|
|
if (arrsavelock[conn.docId] && null != arrsavelock[conn.docId].saveLockTimeOutId) {
|
|
clearTimeout(arrsavelock[conn.docId].saveLockTimeOutId);
|
|
}
|
|
arrsavelock[conn.docId] = undefined;
|
|
sendData(conn, {
|
|
type: "unsavelock"
|
|
});
|
|
}
|
|
function getmessages(conn) {
|
|
sendData(conn, {
|
|
type: "message",
|
|
messages: messages[conn.docId]
|
|
});
|
|
}
|
|
function getusers(conn) {
|
|
var participants = getParticipants(conn.docId);
|
|
sendData(conn, {
|
|
type: "getusers",
|
|
participants: _.map(participants, function (conn) {
|
|
return {
|
|
id: conn.connection.userId,
|
|
username: conn.connection.userName
|
|
};
|
|
})
|
|
});
|
|
}
|
|
return {
|
|
auth: auth,
|
|
message: message,
|
|
getlock: getlock,
|
|
getlockrange: getlockrange,
|
|
getlockpresentation: getlockpresentation,
|
|
savechanges: savechanges,
|
|
issavelock: issavelock,
|
|
unsavelock: unsavelock,
|
|
getmessages: getmessages,
|
|
getusers: getusers
|
|
};
|
|
} ());
|
|
sockjs_echo.installHandlers(server, {
|
|
prefix: "/doc/[0-9-.a-zA-Z_=]*/c",
|
|
log: function (severity, message) {
|
|
logger.info(message);
|
|
}
|
|
});
|
|
var callbackLoadMessages = (function (arrayElements) {
|
|
if (null != arrayElements) {
|
|
messages = arrayElements;
|
|
if (dataBase) {
|
|
dataBase.remove("messages", {});
|
|
}
|
|
}
|
|
if (dataBase) {
|
|
dataBase.load("changes", callbackLoadChanges);
|
|
} else {
|
|
callbackLoadChanges(null);
|
|
}
|
|
});
|
|
var callbackLoadChanges = (function (arrayElements) {
|
|
if (null != arrayElements) {
|
|
if (dataBase) {
|
|
dataBase.remove("changes", {});
|
|
}
|
|
}
|
|
callbackFunction();
|
|
});
|
|
if (dataBase) {
|
|
dataBase.load("messages", callbackLoadMessages);
|
|
} else {
|
|
callbackLoadMessages(null);
|
|
}
|
|
}; |