mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-02-01 03:12:42 +01:00
Unified timeslider and pad editing protocol / component
This commit is contained in:
parent
d08f3ff5ee
commit
914d79ad20
8 changed files with 359 additions and 578 deletions
|
@ -203,6 +203,10 @@ exports.handleMessage = function(client, message)
|
|||
{
|
||||
handleSuggestUserName(client, message);
|
||||
}
|
||||
else if(message.type == "CHANGESET_REQ")
|
||||
{
|
||||
handleChangesetRequest(client, message);
|
||||
}
|
||||
//if the message type is unknown, throw an exception
|
||||
else
|
||||
{
|
||||
|
@ -210,6 +214,7 @@ exports.handleMessage = function(client, message)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles a save revision message
|
||||
* @param client the client that send this message
|
||||
|
@ -661,6 +666,7 @@ function handleClientReady(client, message)
|
|||
var authorColorId;
|
||||
var pad;
|
||||
var historicalAuthorData = {};
|
||||
var currentTime;
|
||||
var readOnlyId;
|
||||
var chatMessages;
|
||||
|
||||
|
@ -735,6 +741,16 @@ function handleClientReady(client, message)
|
|||
var authors = pad.getAllAuthors();
|
||||
|
||||
async.parallel([
|
||||
//get timestamp of latest revission needed for timeslider
|
||||
function(callback)
|
||||
{
|
||||
pad.getRevisionDate(pad.getHeadRevisionNumber(), function(err, date)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
currentTime = date;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get all author data out of the database
|
||||
function(callback)
|
||||
{
|
||||
|
@ -761,7 +777,6 @@ function handleClientReady(client, message)
|
|||
}
|
||||
], callback);
|
||||
|
||||
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
|
@ -813,7 +828,8 @@ function handleClientReady(client, message)
|
|||
"historicalAuthorData": historicalAuthorData,
|
||||
"apool": apool,
|
||||
"rev": pad.getHeadRevisionNumber(),
|
||||
"globalPadId": message.padId
|
||||
"globalPadId": message.padId,
|
||||
"time": currentTime,
|
||||
},
|
||||
"colorPalette": ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#d97979", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#7991d9", "#a979d9", "#d979c1", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd", "#4c9c82", "#12d1ad", "#2d8e80", "#7485c3", "#a091c7", "#3185ab", "#6818b4", "#e6e76d", "#a42c64", "#f386e5", "#4ecc0c", "#c0c236", "#693224", "#b5de6a", "#9b88fd", "#358f9b", "#496d2f", "#e267fe", "#d23056", "#1a1a64", "#5aa335", "#d722bb", "#86dc6c", "#b5a714", "#955b6a", "#9f2985", "#4b81c8", "#3d6a5b", "#434e16", "#d16084", "#af6a0e", "#8c8bd8"],
|
||||
"clientIp": "127.0.0.1",
|
||||
|
@ -837,7 +853,9 @@ function handleClientReady(client, message)
|
|||
"plugins": {
|
||||
"plugins": plugins.plugins,
|
||||
"parts": plugins.parts,
|
||||
}
|
||||
},
|
||||
"initialChangesets": [] // FIXME: REMOVE THIS SHIT
|
||||
|
||||
}
|
||||
|
||||
//Add a username to the clientVars if one avaiable
|
||||
|
@ -858,7 +876,7 @@ function handleClientReady(client, message)
|
|||
else
|
||||
{
|
||||
//Send the clientVars to the Client
|
||||
client.json.send(clientVars);
|
||||
client.json.send({type: "CLIENT_VARS", data: clientVars});
|
||||
//Save the revision in sessioninfos
|
||||
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
||||
}
|
||||
|
@ -961,3 +979,325 @@ function handleClientReady(client, message)
|
|||
ERR(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request for a rough changeset, the timeslider client needs it
|
||||
*/
|
||||
function handleChangesetRequest(client, message)
|
||||
{
|
||||
//check if all ok
|
||||
if(message.data == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no data!");
|
||||
return;
|
||||
}
|
||||
if(message.padId == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||
return;
|
||||
}
|
||||
if(message.data.granularity == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
||||
return;
|
||||
}
|
||||
if(message.data.start == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no start!");
|
||||
return;
|
||||
}
|
||||
if(message.data.requestID == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
||||
return;
|
||||
}
|
||||
|
||||
var granularity = message.data.granularity;
|
||||
var start = message.data.start;
|
||||
var end = start + (100 * granularity);
|
||||
var padId = message.padId;
|
||||
|
||||
//build the requested rough changesets and send them back
|
||||
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
|
||||
{
|
||||
ERR(err);
|
||||
|
||||
var data = changesetInfo;
|
||||
data.requestID = message.data.requestID;
|
||||
|
||||
client.json.send({type: "CHANGESET_REQ", data: data});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to rebuild the getChangestInfo function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L144
|
||||
*/
|
||||
function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||
{
|
||||
var forwardsChangesets = [];
|
||||
var backwardsChangesets = [];
|
||||
var timeDeltas = [];
|
||||
var apool = new AttributePool();
|
||||
var pad;
|
||||
var composedChangesets = {};
|
||||
var revisionDate = [];
|
||||
var lines;
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//calculate the last full endnum
|
||||
var lastRev = pad.getHeadRevisionNumber();
|
||||
if (endNum > lastRev+1) {
|
||||
endNum = lastRev+1;
|
||||
}
|
||||
endNum = Math.floor(endNum / granularity)*granularity;
|
||||
|
||||
var compositesChangesetNeeded = [];
|
||||
var revTimesNeeded = [];
|
||||
|
||||
//figure out which composite Changeset and revTimes we need, to load them in bulk
|
||||
var compositeStart = startNum;
|
||||
while (compositeStart < endNum)
|
||||
{
|
||||
var compositeEnd = compositeStart + granularity;
|
||||
|
||||
//add the composite Changeset we needed
|
||||
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
|
||||
|
||||
//add the t1 time we need
|
||||
revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1);
|
||||
//add the t2 time we need
|
||||
revTimesNeeded.push(compositeEnd - 1);
|
||||
|
||||
compositeStart += granularity;
|
||||
}
|
||||
|
||||
//get all needed db values parallel
|
||||
async.parallel([
|
||||
function(callback)
|
||||
{
|
||||
//get all needed composite Changesets
|
||||
async.forEach(compositesChangesetNeeded, function(item, callback)
|
||||
{
|
||||
composePadChangesets(padId, item.start, item.end, function(err, changeset)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
composedChangesets[item.start + "/" + item.end] = changeset;
|
||||
callback();
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//get all needed revision Dates
|
||||
async.forEach(revTimesNeeded, function(revNum, callback)
|
||||
{
|
||||
pad.getRevisionDate(revNum, function(err, revDate)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
revisionDate[revNum] = Math.floor(revDate/1000);
|
||||
callback();
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
//get the lines
|
||||
function(callback)
|
||||
{
|
||||
getPadLines(padId, startNum-1, function(err, _lines)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
lines = _lines;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
},
|
||||
//doesn't know what happens here excatly :/
|
||||
function(callback)
|
||||
{
|
||||
var compositeStart = startNum;
|
||||
|
||||
while (compositeStart < endNum)
|
||||
{
|
||||
if (compositeStart + granularity > endNum)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var compositeEnd = compositeStart + granularity;
|
||||
|
||||
var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
|
||||
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
|
||||
|
||||
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
|
||||
Changeset.mutateTextLines(forwards, lines.textlines);
|
||||
|
||||
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
|
||||
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
|
||||
|
||||
var t1, t2;
|
||||
if (compositeStart == 0)
|
||||
{
|
||||
t1 = revisionDate[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
t1 = revisionDate[compositeStart - 1];
|
||||
}
|
||||
|
||||
t2 = revisionDate[compositeEnd - 1];
|
||||
|
||||
timeDeltas.push(t2 - t1);
|
||||
forwardsChangesets.push(forwards2);
|
||||
backwardsChangesets.push(backwards2);
|
||||
|
||||
compositeStart += granularity;
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
callback(null, {forwardsChangesets: forwardsChangesets,
|
||||
backwardsChangesets: backwardsChangesets,
|
||||
apool: apool.toJsonable(),
|
||||
actualEndNum: endNum,
|
||||
timeDeltas: timeDeltas,
|
||||
start: startNum,
|
||||
granularity: granularity });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rebuild the getPadLines function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
||||
*/
|
||||
function getPadLines(padId, revNum, callback)
|
||||
{
|
||||
var atext;
|
||||
var result = {};
|
||||
var pad;
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get the atext
|
||||
function(callback)
|
||||
{
|
||||
if(revNum >= 0)
|
||||
{
|
||||
pad.getInternalRevisionAText(revNum, function(err, _atext)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
atext = _atext;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
atext = Changeset.makeAText("\n");
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
result.textlines = Changeset.splitTextLines(atext.text);
|
||||
result.alines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
callback(null);
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rebuild the composePadChangeset function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241
|
||||
*/
|
||||
function composePadChangesets(padId, startNum, endNum, callback)
|
||||
{
|
||||
var pad;
|
||||
var changesets = [];
|
||||
var changeset;
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//fetch all changesets we need
|
||||
function(callback)
|
||||
{
|
||||
var changesetsNeeded=[];
|
||||
|
||||
//create a array for all changesets, we will
|
||||
//replace the values with the changeset later
|
||||
for(var r=startNum;r<endNum;r++)
|
||||
{
|
||||
changesetsNeeded.push(r);
|
||||
}
|
||||
|
||||
//get all changesets
|
||||
async.forEach(changesetsNeeded, function(revNum,callback)
|
||||
{
|
||||
pad.getRevisionChangeset(revNum, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
changesets[revNum] = value;
|
||||
callback();
|
||||
});
|
||||
},callback);
|
||||
},
|
||||
//compose Changesets
|
||||
function(callback)
|
||||
{
|
||||
changeset = changesets[startNum];
|
||||
var pool = pad.apool();
|
||||
|
||||
for(var r=startNum+1;r<endNum;r++)
|
||||
{
|
||||
var cs = changesets[r];
|
||||
changeset = Changeset.compose(changeset, cs, pool);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
}
|
||||
],
|
||||
//return err and changeset
|
||||
function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, changeset);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,534 +0,0 @@
|
|||
/**
|
||||
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
var ERR = require("async-stacktrace");
|
||||
var async = require("async");
|
||||
var padManager = require("../db/PadManager");
|
||||
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
|
||||
var settings = require('../utils/Settings');
|
||||
var authorManager = require("../db/AuthorManager");
|
||||
var log4js = require('log4js');
|
||||
var messageLogger = log4js.getLogger("message");
|
||||
|
||||
/**
|
||||
* Saves the Socket class we need to send and recieve data from the client
|
||||
*/
|
||||
var socketio;
|
||||
|
||||
/**
|
||||
* This Method is called by server.js to tell the message handler on which socket it should send
|
||||
* @param socket_io The Socket
|
||||
*/
|
||||
exports.setSocketIO = function(socket_io)
|
||||
{
|
||||
socketio=socket_io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the connection of a new user
|
||||
* @param client the new client
|
||||
*/
|
||||
exports.handleConnect = function(client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the disconnection of a user
|
||||
* @param client the client that leaves
|
||||
*/
|
||||
exports.handleDisconnect = function(client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a message from a user
|
||||
* @param client the client that send this message
|
||||
* @param message the message from the client
|
||||
*/
|
||||
exports.handleMessage = function(client, message)
|
||||
{
|
||||
//Check what type of message we get and delegate to the other methodes
|
||||
if(message.type == "CLIENT_READY")
|
||||
{
|
||||
handleClientReady(client, message);
|
||||
}
|
||||
else if(message.type == "CHANGESET_REQ")
|
||||
{
|
||||
handleChangesetRequest(client, message);
|
||||
}
|
||||
//if the message type is unkown, throw an exception
|
||||
else
|
||||
{
|
||||
messageLogger.warn("Dropped message, unknown Message Type: '" + message.type + "'");
|
||||
}
|
||||
}
|
||||
|
||||
function handleClientReady(client, message)
|
||||
{
|
||||
if(message.padId == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||
return;
|
||||
}
|
||||
|
||||
//send the timeslider client the clientVars, with this values its able to start
|
||||
createTimesliderClientVars (message.padId, function(err, clientVars)
|
||||
{
|
||||
ERR(err);
|
||||
|
||||
client.json.send({type: "CLIENT_VARS", data: clientVars});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request for a rough changeset, the timeslider client needs it
|
||||
*/
|
||||
function handleChangesetRequest(client, message)
|
||||
{
|
||||
//check if all ok
|
||||
if(message.data == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no data!");
|
||||
return;
|
||||
}
|
||||
if(message.padId == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||
return;
|
||||
}
|
||||
if(message.data.granularity == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
||||
return;
|
||||
}
|
||||
if(message.data.start == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no start!");
|
||||
return;
|
||||
}
|
||||
if(message.data.requestID == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
||||
return;
|
||||
}
|
||||
|
||||
var granularity = message.data.granularity;
|
||||
var start = message.data.start;
|
||||
var end = start + (100 * granularity);
|
||||
var padId = message.padId;
|
||||
|
||||
//build the requested rough changesets and send them back
|
||||
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
|
||||
{
|
||||
ERR(err);
|
||||
|
||||
var data = changesetInfo;
|
||||
data.requestID = message.data.requestID;
|
||||
|
||||
client.json.send({type: "CHANGESET_REQ", data: data});
|
||||
});
|
||||
}
|
||||
|
||||
function createTimesliderClientVars (padId, callback)
|
||||
{
|
||||
var clientVars = {
|
||||
viewId: padId,
|
||||
colorPalette: ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#d97979", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#7991d9", "#a979d9", "#d979c1", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd"],
|
||||
savedRevisions: [],
|
||||
padIdForUrl: padId,
|
||||
fullWidth: false,
|
||||
disableRightBar: false,
|
||||
initialChangesets: [],
|
||||
abiwordAvailable: settings.abiwordAvailable(),
|
||||
hooks: [],
|
||||
initialStyledContents: {}
|
||||
};
|
||||
|
||||
var pad;
|
||||
var initialChangesets = [];
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get all saved revisions and add them
|
||||
function(callback)
|
||||
{
|
||||
clientVars.savedRevisions = pad.getSavedRevisions();
|
||||
callback();
|
||||
},
|
||||
//get all authors and add them to
|
||||
function(callback)
|
||||
{
|
||||
var historicalAuthorData = {};
|
||||
//get all authors out of the attribut pool
|
||||
var authors = pad.getAllAuthors();
|
||||
|
||||
//get all author data out of the database
|
||||
async.forEach(authors, function(authorId, callback)
|
||||
{
|
||||
authorManager.getAuthor(authorId, function(err, author)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
historicalAuthorData[authorId] = author;
|
||||
callback();
|
||||
});
|
||||
}, function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
//add historicalAuthorData to the clientVars and continue
|
||||
clientVars.historicalAuthorData = historicalAuthorData;
|
||||
clientVars.initialStyledContents.historicalAuthorData = historicalAuthorData;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get the timestamp of the last revision
|
||||
function(callback)
|
||||
{
|
||||
pad.getRevisionDate(pad.getHeadRevisionNumber(), function(err, date)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
clientVars.currentTime = date;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//get the head revision Number
|
||||
var lastRev = pad.getHeadRevisionNumber();
|
||||
|
||||
//add the revNum to the client Vars
|
||||
clientVars.revNum = lastRev;
|
||||
clientVars.totalRevs = lastRev;
|
||||
|
||||
var atext = Changeset.cloneAText(pad.atext);
|
||||
var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);
|
||||
var apool = attribsForWire.pool.toJsonable();
|
||||
atext.attribs = attribsForWire.translated;
|
||||
|
||||
clientVars.initialStyledContents.apool = apool;
|
||||
clientVars.initialStyledContents.atext = atext;
|
||||
|
||||
var granularities = [100, 10, 1];
|
||||
|
||||
//get the latest rough changesets
|
||||
async.forEach(granularities, function(granularity, callback)
|
||||
{
|
||||
var topGranularity = granularity*10;
|
||||
|
||||
getChangesetInfo(padId, Math.floor(lastRev / topGranularity)*topGranularity,
|
||||
Math.floor(lastRev / topGranularity)*topGranularity+topGranularity, granularity,
|
||||
function(err, changeset)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
clientVars.initialChangesets.push(changeset);
|
||||
callback();
|
||||
});
|
||||
}, callback);
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, clientVars);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rebuild the getChangestInfo function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L144
|
||||
*/
|
||||
function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||
{
|
||||
var forwardsChangesets = [];
|
||||
var backwardsChangesets = [];
|
||||
var timeDeltas = [];
|
||||
var apool = new AttributePool();
|
||||
var pad;
|
||||
var composedChangesets = {};
|
||||
var revisionDate = [];
|
||||
var lines;
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//calculate the last full endnum
|
||||
var lastRev = pad.getHeadRevisionNumber();
|
||||
if (endNum > lastRev+1) {
|
||||
endNum = lastRev+1;
|
||||
}
|
||||
endNum = Math.floor(endNum / granularity)*granularity;
|
||||
|
||||
var compositesChangesetNeeded = [];
|
||||
var revTimesNeeded = [];
|
||||
|
||||
//figure out which composite Changeset and revTimes we need, to load them in bulk
|
||||
var compositeStart = startNum;
|
||||
while (compositeStart < endNum)
|
||||
{
|
||||
var compositeEnd = compositeStart + granularity;
|
||||
|
||||
//add the composite Changeset we needed
|
||||
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
|
||||
|
||||
//add the t1 time we need
|
||||
revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1);
|
||||
//add the t2 time we need
|
||||
revTimesNeeded.push(compositeEnd - 1);
|
||||
|
||||
compositeStart += granularity;
|
||||
}
|
||||
|
||||
//get all needed db values parallel
|
||||
async.parallel([
|
||||
function(callback)
|
||||
{
|
||||
//get all needed composite Changesets
|
||||
async.forEach(compositesChangesetNeeded, function(item, callback)
|
||||
{
|
||||
composePadChangesets(padId, item.start, item.end, function(err, changeset)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
composedChangesets[item.start + "/" + item.end] = changeset;
|
||||
callback();
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//get all needed revision Dates
|
||||
async.forEach(revTimesNeeded, function(revNum, callback)
|
||||
{
|
||||
pad.getRevisionDate(revNum, function(err, revDate)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
revisionDate[revNum] = Math.floor(revDate/1000);
|
||||
callback();
|
||||
});
|
||||
}, callback);
|
||||
},
|
||||
//get the lines
|
||||
function(callback)
|
||||
{
|
||||
getPadLines(padId, startNum-1, function(err, _lines)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
lines = _lines;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
},
|
||||
//doesn't know what happens here excatly :/
|
||||
function(callback)
|
||||
{
|
||||
var compositeStart = startNum;
|
||||
|
||||
while (compositeStart < endNum)
|
||||
{
|
||||
if (compositeStart + granularity > endNum)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var compositeEnd = compositeStart + granularity;
|
||||
|
||||
var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
|
||||
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
|
||||
|
||||
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
|
||||
Changeset.mutateTextLines(forwards, lines.textlines);
|
||||
|
||||
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
|
||||
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
|
||||
|
||||
var t1, t2;
|
||||
if (compositeStart == 0)
|
||||
{
|
||||
t1 = revisionDate[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
t1 = revisionDate[compositeStart - 1];
|
||||
}
|
||||
|
||||
t2 = revisionDate[compositeEnd - 1];
|
||||
|
||||
timeDeltas.push(t2 - t1);
|
||||
forwardsChangesets.push(forwards2);
|
||||
backwardsChangesets.push(backwards2);
|
||||
|
||||
compositeStart += granularity;
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
callback(null, {forwardsChangesets: forwardsChangesets,
|
||||
backwardsChangesets: backwardsChangesets,
|
||||
apool: apool.toJsonable(),
|
||||
actualEndNum: endNum,
|
||||
timeDeltas: timeDeltas,
|
||||
start: startNum,
|
||||
granularity: granularity });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rebuild the getPadLines function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
||||
*/
|
||||
function getPadLines(padId, revNum, callback)
|
||||
{
|
||||
var atext;
|
||||
var result = {};
|
||||
var pad;
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get the atext
|
||||
function(callback)
|
||||
{
|
||||
if(revNum >= 0)
|
||||
{
|
||||
pad.getInternalRevisionAText(revNum, function(err, _atext)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
atext = _atext;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
atext = Changeset.makeAText("\n");
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
result.textlines = Changeset.splitTextLines(atext.text);
|
||||
result.alines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
callback(null);
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rebuild the composePadChangeset function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241
|
||||
*/
|
||||
function composePadChangesets(padId, startNum, endNum, callback)
|
||||
{
|
||||
var pad;
|
||||
var changesets = [];
|
||||
var changeset;
|
||||
|
||||
async.series([
|
||||
//get the pad from the database
|
||||
function(callback)
|
||||
{
|
||||
padManager.getPad(padId, function(err, _pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
pad = _pad;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//fetch all changesets we need
|
||||
function(callback)
|
||||
{
|
||||
var changesetsNeeded=[];
|
||||
|
||||
//create a array for all changesets, we will
|
||||
//replace the values with the changeset later
|
||||
for(var r=startNum;r<endNum;r++)
|
||||
{
|
||||
changesetsNeeded.push(r);
|
||||
}
|
||||
|
||||
//get all changesets
|
||||
async.forEach(changesetsNeeded, function(revNum,callback)
|
||||
{
|
||||
pad.getRevisionChangeset(revNum, function(err, value)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
changesets[revNum] = value;
|
||||
callback();
|
||||
});
|
||||
},callback);
|
||||
},
|
||||
//compose Changesets
|
||||
function(callback)
|
||||
{
|
||||
changeset = changesets[startNum];
|
||||
var pool = pad.apool();
|
||||
|
||||
for(var r=startNum+1;r<endNum;r++)
|
||||
{
|
||||
var cs = changesets[r];
|
||||
changeset = Changeset.compose(changeset, cs, pool);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
}
|
||||
],
|
||||
//return err and changeset
|
||||
function(err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, changeset);
|
||||
});
|
||||
}
|
|
@ -5,7 +5,6 @@ var socketIORouter = require("../../handler/SocketIORouter");
|
|||
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
|
||||
|
||||
var padMessageHandler = require("../../handler/PadMessageHandler");
|
||||
var timesliderMessageHandler = require("../../handler/TimesliderMessageHandler");
|
||||
|
||||
var connect = require('connect');
|
||||
|
||||
|
@ -59,7 +58,6 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
//Initalize the Socket.IO Router
|
||||
socketIORouter.setSocketIO(io);
|
||||
socketIORouter.addComponent("pad", padMessageHandler);
|
||||
socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
||||
|
||||
hooks.callAll("socketio", {"app": args.app, "io": io});
|
||||
}
|
||||
|
|
|
@ -84,14 +84,14 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
|||
var appLevelDisconnectReason = null;
|
||||
|
||||
var padContents = {
|
||||
currentRevision: clientVars.revNum,
|
||||
currentTime: clientVars.currentTime,
|
||||
currentLines: Changeset.splitTextLines(clientVars.initialStyledContents.atext.text),
|
||||
currentRevision: clientVars.collab_client_vars.rev,
|
||||
currentTime: clientVars.collab_client_vars.time,
|
||||
currentLines: Changeset.splitTextLines(clientVars.collab_client_vars.initialAttributedText.text),
|
||||
currentDivs: null,
|
||||
// to be filled in once the dom loads
|
||||
apool: (new AttribPool()).fromJsonable(clientVars.initialStyledContents.apool),
|
||||
apool: (new AttribPool()).fromJsonable(clientVars.collab_client_vars.apool),
|
||||
alines: Changeset.splitAttributionLines(
|
||||
clientVars.initialStyledContents.atext.attribs, clientVars.initialStyledContents.atext.text),
|
||||
clientVars.collab_client_vars.initialAttributedText.attribs, clientVars.collab_client_vars.initialAttributedText.text),
|
||||
|
||||
// generates a jquery element containing HTML for a line
|
||||
lineToElement: function(line, aline)
|
||||
|
@ -432,19 +432,6 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
|||
var start = request.rev;
|
||||
var requestID = Math.floor(Math.random() * 100000);
|
||||
|
||||
/*var msg = { "component" : "timeslider",
|
||||
"type":"CHANGESET_REQ",
|
||||
"padId": padId,
|
||||
"token": token,
|
||||
"protocolVersion": 2,
|
||||
"data"
|
||||
{
|
||||
"start": start,
|
||||
"granularity": granularity
|
||||
}};
|
||||
|
||||
socket.send(msg);*/
|
||||
|
||||
sendSocketMsg("CHANGESET_REQ", {
|
||||
"start": start,
|
||||
"granularity": granularity,
|
||||
|
@ -452,19 +439,6 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
|||
});
|
||||
|
||||
self.reqCallbacks[requestID] = callback;
|
||||
|
||||
/*debugLog("loadinging revision", start, "through ajax");
|
||||
$.getJSON("/ep/pad/changes/" + clientVars.padIdForUrl + "?s=" + start + "&g=" + granularity, function (data, textStatus)
|
||||
{
|
||||
if (textStatus !== "success")
|
||||
{
|
||||
console.log(textStatus);
|
||||
BroadcastSlider.showReconnectUI();
|
||||
}
|
||||
self.handleResponse(data, start, granularity, callback);
|
||||
|
||||
setTimeout(self.loadFromQueue, 10); // load the next ajax function
|
||||
});*/
|
||||
},
|
||||
handleSocketResponse: function(message)
|
||||
{
|
||||
|
@ -493,7 +467,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
|||
revisionInfo.addChangeset(astart, aend, forwardcs, backwardcs, data.timeDeltas[i]);
|
||||
}
|
||||
if (callback) callback(start - 1, start + data.forwardsChangesets.length * granularity - 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function handleMessageFromServer()
|
||||
|
@ -686,7 +660,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
|||
}
|
||||
}
|
||||
|
||||
receiveAuthorData(clientVars.historicalAuthorData);
|
||||
receiveAuthorData(clientVars.collab_client_vars.historicalAuthorData);
|
||||
|
||||
return changesetLoader;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ function loadBroadcastRevisionsJS()
|
|||
endRevision.addChangeset(fromIndex, backChangeset, -1 * timeDelta);
|
||||
}
|
||||
|
||||
revisionInfo.latest = clientVars.totalRevs || -1;
|
||||
revisionInfo.latest = clientVars.collab_client_vars.rev || -1;
|
||||
|
||||
revisionInfo.createNew = function(index)
|
||||
{
|
||||
|
|
|
@ -481,8 +481,8 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
|||
}
|
||||
|
||||
$("#timeslider").show();
|
||||
setSliderLength(clientVars.totalRevs);
|
||||
setSliderPosition(clientVars.revNum);
|
||||
setSliderLength(clientVars.collab_client_vars.rev);
|
||||
setSliderPosition(clientVars.collab_client_vars.rev);
|
||||
|
||||
_.each(clientVars.savedRevisions, function(revision)
|
||||
{
|
||||
|
|
|
@ -316,7 +316,7 @@ function handshake()
|
|||
receivedClientVars = true;
|
||||
|
||||
//set some client vars
|
||||
clientVars = obj;
|
||||
clientVars = obj.data;
|
||||
clientVars.userAgent = "Anonymous";
|
||||
clientVars.collab_client_vars.clientAgent = "Anonymous";
|
||||
|
||||
|
|
|
@ -87,6 +87,9 @@ function init() {
|
|||
else if(message.accessStatus)
|
||||
{
|
||||
$("body").html("<h2>You have no permission to access this pad</h2>")
|
||||
} else {
|
||||
console.warn("Unknown message type: " + message.type);
|
||||
console.warn(["XXX", message]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -107,7 +110,7 @@ function sendSocketMsg(type, data)
|
|||
var sessionID = readCookie("sessionID");
|
||||
var password = readCookie("password");
|
||||
|
||||
var msg = { "component" : "timeslider",
|
||||
var msg = { "component" : "pad", // FIXME: Remove this stupidity!
|
||||
"type": type,
|
||||
"data": data,
|
||||
"padId": padId,
|
||||
|
|
Loading…
Reference in a new issue