2020-12-15 18:37:15 +01:00
|
|
|
'use strict';
|
|
|
|
|
2011-12-04 16:33:56 +01:00
|
|
|
/**
|
2019-04-16 00:34:29 +02:00
|
|
|
* This code is mostly from the old Etherpad. Please help us to comment this code.
|
2011-12-04 16:33:56 +01:00
|
|
|
* This helps other people to understand this code better and helps them to improve it.
|
|
|
|
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
|
|
|
*/
|
|
|
|
|
2011-06-20 12:44:04 +02:00
|
|
|
/**
|
|
|
|
* Copyright 2009 Google Inc.
|
2011-07-07 19:59:34 +02:00
|
|
|
*
|
2011-06-20 12:44:04 +02:00
|
|
|
* 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
|
2011-07-07 19:59:34 +02:00
|
|
|
*
|
2011-06-20 12:44:04 +02:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2011-07-07 19:59:34 +02:00
|
|
|
*
|
2011-06-20 12:44:04 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const makeCSSManager = require('./cssmanager').makeCSSManager;
|
|
|
|
const domline = require('./domline').domline;
|
|
|
|
const AttribPool = require('./AttributePool');
|
|
|
|
const Changeset = require('./Changeset');
|
|
|
|
const linestylefilter = require('./linestylefilter').linestylefilter;
|
|
|
|
const colorutils = require('./colorutils').colorutils;
|
2021-02-21 16:07:39 +01:00
|
|
|
const _ = require('./vendors/underscore');
|
2020-11-23 19:24:19 +01:00
|
|
|
const hooks = require('./pluginfw/hooks');
|
2012-01-16 05:16:11 +01:00
|
|
|
|
2012-01-27 06:20:26 +01:00
|
|
|
// These parameters were global, now they are injected. A reference to the
|
|
|
|
// Timeslider controller would probably be more appropriate.
|
2021-02-17 17:24:47 +01:00
|
|
|
const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider) => {
|
2020-12-15 18:37:15 +01:00
|
|
|
let goToRevisionIfEnabledCount = 0;
|
2020-11-23 19:24:19 +01:00
|
|
|
let changesetLoader = undefined;
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const debugLog = (...args) => {
|
2020-11-23 19:24:19 +01:00
|
|
|
try {
|
2020-12-15 18:37:15 +01:00
|
|
|
if (window.console) console.log(...args);
|
2020-11-23 19:24:19 +01:00
|
|
|
} catch (e) {
|
|
|
|
if (window.console) console.log('error printing: ', e);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
2020-12-15 18:37:15 +01:00
|
|
|
};
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const padContents = {
|
2012-04-23 12:52:30 +02:00
|
|
|
currentRevision: clientVars.collab_client_vars.rev,
|
|
|
|
currentTime: clientVars.collab_client_vars.time,
|
2020-12-15 18:37:15 +01:00
|
|
|
currentLines:
|
|
|
|
Changeset.splitTextLines(clientVars.collab_client_vars.initialAttributedText.text),
|
2011-06-20 12:44:04 +02:00
|
|
|
currentDivs: null,
|
|
|
|
// to be filled in once the dom loads
|
2012-04-23 12:52:30 +02:00
|
|
|
apool: (new AttribPool()).fromJsonable(clientVars.collab_client_vars.apool),
|
2011-06-20 12:44:04 +02:00
|
|
|
alines: Changeset.splitAttributionLines(
|
2020-12-15 18:37:15 +01:00
|
|
|
clientVars.collab_client_vars.initialAttributedText.attribs,
|
|
|
|
clientVars.collab_client_vars.initialAttributedText.text),
|
2011-06-20 12:44:04 +02:00
|
|
|
|
|
|
|
// generates a jquery element containing HTML for a line
|
2020-11-23 19:24:19 +01:00
|
|
|
lineToElement(line, aline) {
|
|
|
|
const element = document.createElement('div');
|
2020-12-15 18:37:15 +01:00
|
|
|
const emptyLine = (line === '\n');
|
2020-11-23 19:24:19 +01:00
|
|
|
const domInfo = domline.createDomLine(!emptyLine, true);
|
2011-06-20 12:44:04 +02:00
|
|
|
linestylefilter.populateDomLine(line, aline, this.apool, domInfo);
|
|
|
|
domInfo.prepareForAdd();
|
|
|
|
element.className = domInfo.node.className;
|
|
|
|
element.innerHTML = domInfo.node.innerHTML;
|
|
|
|
element.id = Math.random();
|
|
|
|
return $(element);
|
|
|
|
},
|
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
// splice the lines
|
|
|
|
splice(start, numRemoved, ...newLines) {
|
2011-06-20 12:44:04 +02:00
|
|
|
// remove spliced-out lines from DOM
|
2020-12-15 18:37:15 +01:00
|
|
|
for (let i = start; i < start + numRemoved && i < this.currentDivs.length; i++) {
|
2011-06-20 12:44:04 +02:00
|
|
|
this.currentDivs[i].remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove spliced-out line divs from currentDivs array
|
|
|
|
this.currentDivs.splice(start, numRemoved);
|
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const newDivs = [];
|
2020-12-15 18:37:15 +01:00
|
|
|
for (let i = 0; i < newLines.length; i++) {
|
2011-06-20 12:44:04 +02:00
|
|
|
newDivs.push(this.lineToElement(newLines[i], this.alines[start + i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// grab the div just before the first one
|
2020-11-23 19:24:19 +01:00
|
|
|
let startDiv = this.currentDivs[start - 1] || null;
|
2011-06-20 12:44:04 +02:00
|
|
|
|
|
|
|
// insert the div elements into the correct place, in the correct order
|
2020-12-15 18:37:15 +01:00
|
|
|
for (let i = 0; i < newDivs.length; i++) {
|
2020-11-23 19:24:19 +01:00
|
|
|
if (startDiv) {
|
2011-06-20 12:44:04 +02:00
|
|
|
startDiv.after(newDivs[i]);
|
2020-11-23 19:24:19 +01:00
|
|
|
} else {
|
|
|
|
$('#innerdocbody').prepend(newDivs[i]);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
|
|
|
startDiv = newDivs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert new divs into currentDivs array
|
2020-12-15 18:37:15 +01:00
|
|
|
this.currentDivs.splice(start, 0, ...newDivs);
|
2011-06-20 12:44:04 +02:00
|
|
|
|
|
|
|
// call currentLines.splice, to keep the currentLines array up to date
|
2020-12-15 18:37:15 +01:00
|
|
|
this.currentLines.splice(start, numRemoved, ...newLines);
|
2011-06-20 12:44:04 +02:00
|
|
|
},
|
|
|
|
// returns the contents of the specified line I
|
2020-11-23 19:24:19 +01:00
|
|
|
get(i) {
|
2011-06-20 12:44:04 +02:00
|
|
|
return this.currentLines[i];
|
|
|
|
},
|
|
|
|
// returns the number of lines in the document
|
2020-11-23 19:24:19 +01:00
|
|
|
length() {
|
2011-06-20 12:44:04 +02:00
|
|
|
return this.currentLines.length;
|
|
|
|
},
|
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
getActiveAuthors() {
|
|
|
|
const authors = [];
|
|
|
|
const seenNums = {};
|
2020-12-15 18:37:15 +01:00
|
|
|
const alines = this.alines;
|
2020-11-23 19:24:19 +01:00
|
|
|
for (let i = 0; i < alines.length; i++) {
|
|
|
|
Changeset.eachAttribNumber(alines[i], (n) => {
|
|
|
|
if (!seenNums[n]) {
|
2011-06-20 12:44:04 +02:00
|
|
|
seenNums[n] = true;
|
2020-12-15 18:37:15 +01:00
|
|
|
if (this.apool.getAttribKey(n) === 'author') {
|
|
|
|
const a = this.apool.getAttribValue(n);
|
2020-11-23 19:24:19 +01:00
|
|
|
if (a) {
|
2011-06-20 12:44:04 +02:00
|
|
|
authors.push(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
authors.sort();
|
|
|
|
return authors;
|
2020-11-23 19:24:19 +01:00
|
|
|
},
|
2011-06-20 12:44:04 +02:00
|
|
|
};
|
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const applyChangeset = (changeset, revision, preventSliderMovement, timeDelta) => {
|
2011-06-20 12:44:04 +02:00
|
|
|
// disable the next 'gotorevision' call handled by a timeslider update
|
2020-11-23 19:24:19 +01:00
|
|
|
if (!preventSliderMovement) {
|
2011-06-20 12:44:04 +02:00
|
|
|
goToRevisionIfEnabledCount++;
|
|
|
|
BroadcastSlider.setSliderPosition(revision);
|
|
|
|
}
|
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const oldAlines = padContents.alines.slice();
|
|
|
|
try {
|
2011-06-20 12:44:04 +02:00
|
|
|
// must mutate attribution lines before text lines
|
|
|
|
Changeset.mutateAttributionLines(changeset, padContents.alines, padContents.apool);
|
2020-11-23 19:24:19 +01:00
|
|
|
} catch (e) {
|
2011-06-20 12:44:04 +02:00
|
|
|
debugLog(e);
|
|
|
|
}
|
|
|
|
|
2020-10-21 11:05:58 +02:00
|
|
|
// scroll to the area that is changed before the lines are mutated
|
2020-12-15 18:37:15 +01:00
|
|
|
if ($('#options-followContents').is(':checked') ||
|
|
|
|
$('#options-followContents').prop('checked')) {
|
2020-10-21 11:05:58 +02:00
|
|
|
// get the index of the first line that has mutated attributes
|
|
|
|
// the last line in `oldAlines` should always equal to "|1+1", ie newline without attributes
|
|
|
|
// so it should be safe to assume this line has changed attributes when inserting content at
|
|
|
|
// the bottom of a pad
|
|
|
|
let lineChanged;
|
2020-11-23 19:24:19 +01:00
|
|
|
_.some(oldAlines, (line, index) => {
|
|
|
|
if (line !== padContents.alines[index]) {
|
2020-10-21 11:05:58 +02:00
|
|
|
lineChanged = index;
|
|
|
|
return true; // break
|
|
|
|
}
|
2020-11-23 19:24:19 +01:00
|
|
|
});
|
2020-12-15 18:37:15 +01:00
|
|
|
// deal with someone is the author of a line and changes one character,
|
|
|
|
// so the alines won't change
|
2020-11-23 19:24:19 +01:00
|
|
|
if (lineChanged === undefined) {
|
2020-10-21 11:05:58 +02:00
|
|
|
lineChanged = Changeset.opIterator(Changeset.unpack(changeset).ops).next().lines;
|
2020-10-10 17:57:22 +02:00
|
|
|
}
|
2020-10-21 11:05:58 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const goToLineNumber = (lineNumber) => {
|
|
|
|
// Sets the Y scrolling of the browser to go to this line
|
|
|
|
const line = $('#innerdocbody').find(`div:nth-child(${lineNumber + 1})`);
|
|
|
|
const newY = $(line)[0].offsetTop;
|
|
|
|
const ecb = document.getElementById('editorcontainerbox');
|
|
|
|
// Chrome 55 - 59 bugfix
|
|
|
|
if (ecb.scrollTo) {
|
2020-12-21 23:34:13 +01:00
|
|
|
ecb.scrollTo({top: newY, behavior: 'auto'});
|
2020-12-15 18:37:15 +01:00
|
|
|
} else {
|
|
|
|
$('#editorcontainerbox').scrollTop(newY);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-10-21 11:05:58 +02:00
|
|
|
goToLineNumber(lineChanged);
|
2020-07-20 00:46:58 +02:00
|
|
|
}
|
|
|
|
|
2011-06-20 12:44:04 +02:00
|
|
|
Changeset.mutateTextLines(changeset, padContents);
|
|
|
|
padContents.currentRevision = revision;
|
2013-01-10 01:01:38 +01:00
|
|
|
padContents.currentTime += timeDelta * 1000;
|
|
|
|
|
2011-06-20 12:44:04 +02:00
|
|
|
updateTimer();
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const authors = _.map(padContents.getActiveAuthors(), (name) => authorData[name]);
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2012-02-19 15:47:20 +01:00
|
|
|
BroadcastSlider.setAuthors(authors);
|
2020-12-15 18:37:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const loadedNewChangeset = (changesetForward, changesetBackward, revision, timeDelta) => {
|
|
|
|
const revisionInfo = window.revisionInfo;
|
|
|
|
const broadcasting = (BroadcastSlider.getSliderPosition() === revisionInfo.latest);
|
|
|
|
revisionInfo.addChangeset(
|
|
|
|
revision, revision + 1, changesetForward, changesetBackward, timeDelta);
|
|
|
|
BroadcastSlider.setSliderLength(revisionInfo.latest);
|
|
|
|
if (broadcasting) applyChangeset(changesetForward, revision + 1, false, timeDelta);
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
At this point, we must be certain that the changeset really does map from
|
|
|
|
the current revision to the specified revision. Any mistakes here will
|
|
|
|
cause the whole slider to get out of sync.
|
|
|
|
*/
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const updateTimer = () => {
|
|
|
|
const zpad = (str, length) => {
|
2020-11-23 19:24:19 +01:00
|
|
|
str = `${str}`;
|
|
|
|
while (str.length < length) str = `0${str}`;
|
|
|
|
return str;
|
|
|
|
};
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const date = new Date(padContents.currentTime);
|
2020-12-15 18:37:15 +01:00
|
|
|
const dateFormat = () => {
|
2020-11-23 19:24:19 +01:00
|
|
|
const month = zpad(date.getMonth() + 1, 2);
|
|
|
|
const day = zpad(date.getDate(), 2);
|
|
|
|
const year = (date.getFullYear());
|
|
|
|
const hours = zpad(date.getHours(), 2);
|
|
|
|
const minutes = zpad(date.getMinutes(), 2);
|
|
|
|
const seconds = zpad(date.getSeconds(), 2);
|
|
|
|
return (html10n.get('timeslider.dateformat', {
|
|
|
|
day,
|
|
|
|
month,
|
|
|
|
year,
|
|
|
|
hours,
|
|
|
|
minutes,
|
|
|
|
seconds,
|
|
|
|
}));
|
|
|
|
};
|
2019-04-16 00:34:29 +02:00
|
|
|
|
|
|
|
|
2011-06-20 12:44:04 +02:00
|
|
|
$('#timer').html(dateFormat());
|
2020-11-23 19:24:19 +01:00
|
|
|
const revisionDate = html10n.get('timeslider.saved', {
|
|
|
|
day: date.getDate(),
|
|
|
|
month: [
|
|
|
|
html10n.get('timeslider.month.january'),
|
|
|
|
html10n.get('timeslider.month.february'),
|
|
|
|
html10n.get('timeslider.month.march'),
|
|
|
|
html10n.get('timeslider.month.april'),
|
|
|
|
html10n.get('timeslider.month.may'),
|
|
|
|
html10n.get('timeslider.month.june'),
|
|
|
|
html10n.get('timeslider.month.july'),
|
|
|
|
html10n.get('timeslider.month.august'),
|
|
|
|
html10n.get('timeslider.month.september'),
|
|
|
|
html10n.get('timeslider.month.october'),
|
|
|
|
html10n.get('timeslider.month.november'),
|
|
|
|
html10n.get('timeslider.month.december'),
|
|
|
|
][date.getMonth()],
|
|
|
|
year: date.getFullYear(),
|
2012-11-23 06:20:09 +01:00
|
|
|
});
|
2020-11-23 19:24:19 +01:00
|
|
|
$('#revision_date').html(revisionDate);
|
2020-12-15 18:37:15 +01:00
|
|
|
};
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2011-07-25 18:26:56 +02:00
|
|
|
updateTimer();
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const goToRevision = (newRevision) => {
|
2011-06-20 12:44:04 +02:00
|
|
|
padContents.targetRevision = newRevision;
|
2020-12-15 18:37:15 +01:00
|
|
|
const path = window.revisionInfo.getPath(padContents.currentRevision, newRevision);
|
2020-03-24 15:53:12 +01:00
|
|
|
|
|
|
|
hooks.aCallAll('goToRevisionEvent', {
|
2020-11-23 19:24:19 +01:00
|
|
|
rev: newRevision,
|
2020-03-24 15:53:12 +01:00
|
|
|
});
|
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
if (path.status === 'complete') {
|
|
|
|
const cs = path.changesets;
|
|
|
|
let changeset = cs[0];
|
|
|
|
let timeDelta = path.times[0];
|
|
|
|
for (let i = 1; i < cs.length; i++) {
|
2011-06-20 12:44:04 +02:00
|
|
|
changeset = Changeset.compose(changeset, cs[i], padContents.apool);
|
|
|
|
timeDelta += path.times[i];
|
|
|
|
}
|
|
|
|
if (changeset) applyChangeset(changeset, path.rev, true, timeDelta);
|
2020-12-15 18:37:15 +01:00
|
|
|
} else if (path.status === 'partial') {
|
2011-06-20 12:44:04 +02:00
|
|
|
// callback is called after changeset information is pulled from server
|
|
|
|
// this may never get called, if the changeset has already been loaded
|
2020-12-15 18:37:15 +01:00
|
|
|
const update = (start, end) => {
|
2020-11-23 19:24:19 +01:00
|
|
|
// if we've called goToRevision in the time since, don't goToRevision
|
|
|
|
goToRevision(padContents.targetRevision);
|
|
|
|
};
|
2011-06-20 12:44:04 +02:00
|
|
|
|
|
|
|
// do our best with what we have...
|
2020-12-15 18:37:15 +01:00
|
|
|
const cs = path.changesets;
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
let changeset = cs[0];
|
|
|
|
let timeDelta = path.times[0];
|
|
|
|
for (let i = 1; i < cs.length; i++) {
|
2011-06-20 12:44:04 +02:00
|
|
|
changeset = Changeset.compose(changeset, cs[i], padContents.apool);
|
|
|
|
timeDelta += path.times[i];
|
|
|
|
}
|
|
|
|
if (changeset) applyChangeset(changeset, path.rev, true, timeDelta);
|
|
|
|
|
2014-08-09 18:52:44 +02:00
|
|
|
// Loading changeset history for new revision
|
|
|
|
loadChangesetsForRevision(newRevision, update);
|
|
|
|
// Loading changeset history for old revision (to make diff between old and new revision)
|
|
|
|
loadChangesetsForRevision(padContents.currentRevision - 1);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const authors = _.map(padContents.getActiveAuthors(), (name) => authorData[name]);
|
2012-02-19 15:47:20 +01:00
|
|
|
BroadcastSlider.setAuthors(authors);
|
2020-12-15 18:37:15 +01:00
|
|
|
};
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const loadChangesetsForRevision = (revision, callback) => {
|
2020-11-23 19:24:19 +01:00
|
|
|
if (BroadcastSlider.getSliderLength() > 10000) {
|
2020-12-15 18:37:15 +01:00
|
|
|
const start = (Math.floor((revision) / 10000) * 10000); // revision 0 to 10
|
2014-08-09 18:52:44 +02:00
|
|
|
changesetLoader.queueUp(start, 100);
|
|
|
|
}
|
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
if (BroadcastSlider.getSliderLength() > 1000) {
|
2020-12-15 18:37:15 +01:00
|
|
|
const start = (Math.floor((revision) / 1000) * 1000); // (start from -1, go to 19) + 1
|
2014-08-09 18:52:44 +02:00
|
|
|
changesetLoader.queueUp(start, 10);
|
|
|
|
}
|
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const start = (Math.floor((revision) / 100) * 100);
|
2014-08-09 18:52:44 +02:00
|
|
|
|
|
|
|
changesetLoader.queueUp(start, 1, callback);
|
2020-12-15 18:37:15 +01:00
|
|
|
};
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2012-01-27 06:20:26 +01:00
|
|
|
changesetLoader = {
|
2011-06-20 12:44:04 +02:00
|
|
|
running: false,
|
|
|
|
resolved: [],
|
|
|
|
requestQueue1: [],
|
|
|
|
requestQueue2: [],
|
|
|
|
requestQueue3: [],
|
2011-06-20 16:37:41 +02:00
|
|
|
reqCallbacks: [],
|
2020-11-23 19:24:19 +01:00
|
|
|
queueUp(revision, width, callback) {
|
2011-06-20 12:44:04 +02:00
|
|
|
if (revision < 0) revision = 0;
|
2020-12-15 18:37:15 +01:00
|
|
|
// if(this.requestQueue.indexOf(revision) != -1)
|
2011-06-20 12:44:04 +02:00
|
|
|
// return; // already in the queue.
|
2020-12-15 18:37:15 +01:00
|
|
|
if (this.resolved.indexOf(`${revision}_${width}`) !== -1) {
|
|
|
|
// already loaded from the server
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.resolved.push(`${revision}_${width}`);
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const requestQueue =
|
|
|
|
width === 1 ? this.requestQueue3
|
|
|
|
: width === 10 ? this.requestQueue2
|
|
|
|
: this.requestQueue1;
|
2011-06-20 12:44:04 +02:00
|
|
|
requestQueue.push(
|
2020-11-23 19:24:19 +01:00
|
|
|
{
|
|
|
|
rev: revision,
|
|
|
|
res: width,
|
|
|
|
callback,
|
|
|
|
});
|
2020-12-15 18:37:15 +01:00
|
|
|
if (!this.running) {
|
|
|
|
this.running = true;
|
|
|
|
setTimeout(() => this.loadFromQueue(), 10);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
|
|
|
},
|
2020-11-23 19:24:19 +01:00
|
|
|
loadFromQueue() {
|
2020-12-15 18:37:15 +01:00
|
|
|
const requestQueue =
|
|
|
|
this.requestQueue1.length > 0 ? this.requestQueue1
|
|
|
|
: this.requestQueue2.length > 0 ? this.requestQueue2
|
|
|
|
: this.requestQueue3.length > 0 ? this.requestQueue3
|
|
|
|
: null;
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
if (!requestQueue) {
|
2020-12-15 18:37:15 +01:00
|
|
|
this.running = false;
|
2011-06-20 12:44:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const request = requestQueue.pop();
|
|
|
|
const granularity = request.res;
|
|
|
|
const callback = request.callback;
|
|
|
|
const start = request.rev;
|
|
|
|
const requestID = Math.floor(Math.random() * 100000);
|
2011-07-07 19:59:34 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
sendSocketMsg('CHANGESET_REQ', {
|
|
|
|
start,
|
|
|
|
granularity,
|
|
|
|
requestID,
|
2011-07-07 19:59:34 +02:00
|
|
|
});
|
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
this.reqCallbacks[requestID] = callback;
|
2011-06-20 16:37:41 +02:00
|
|
|
},
|
2020-11-23 19:24:19 +01:00
|
|
|
handleSocketResponse(message) {
|
|
|
|
const start = message.data.start;
|
|
|
|
const granularity = message.data.granularity;
|
2020-12-15 18:37:15 +01:00
|
|
|
const callback = this.reqCallbacks[message.data.requestID];
|
|
|
|
delete this.reqCallbacks[message.data.requestID];
|
2011-07-07 19:59:34 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
this.handleResponse(message.data, start, granularity, callback);
|
|
|
|
setTimeout(() => this.loadFromQueue(), 10);
|
2011-06-20 12:44:04 +02:00
|
|
|
},
|
2020-12-15 18:37:15 +01:00
|
|
|
handleResponse: (data, start, granularity, callback) => {
|
2020-11-23 19:24:19 +01:00
|
|
|
const pool = (new AttribPool()).fromJsonable(data.apool);
|
|
|
|
for (let i = 0; i < data.forwardsChangesets.length; i++) {
|
|
|
|
const astart = start + i * granularity - 1; // rev -1 is a blank single line
|
|
|
|
let aend = start + (i + 1) * granularity - 1; // totalRevs is the most recent revision
|
2011-06-20 12:44:04 +02:00
|
|
|
if (aend > data.actualEndNum - 1) aend = data.actualEndNum - 1;
|
2020-11-23 19:24:19 +01:00
|
|
|
// debugLog("adding changeset:", astart, aend);
|
2020-12-15 18:37:15 +01:00
|
|
|
const forwardcs =
|
|
|
|
Changeset.moveOpsToNewPool(data.forwardsChangesets[i], pool, padContents.apool);
|
|
|
|
const backwardcs =
|
|
|
|
Changeset.moveOpsToNewPool(data.backwardsChangesets[i], pool, padContents.apool);
|
|
|
|
window.revisionInfo.addChangeset(astart, aend, forwardcs, backwardcs, data.timeDeltas[i]);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
|
|
|
if (callback) callback(start - 1, start + data.forwardsChangesets.length * granularity - 1);
|
2012-04-23 13:29:27 +02:00
|
|
|
},
|
2020-11-23 19:24:19 +01:00
|
|
|
handleMessageFromServer(obj) {
|
2020-12-15 18:37:15 +01:00
|
|
|
if (obj.type === 'COLLABROOM') {
|
2012-04-23 13:29:27 +02:00
|
|
|
obj = obj.data;
|
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
if (obj.type === 'NEW_CHANGES') {
|
2020-11-23 19:24:19 +01:00
|
|
|
const changeset = Changeset.moveOpsToNewPool(
|
|
|
|
obj.changeset, (new AttribPool()).fromJsonable(obj.apool), padContents.apool);
|
2012-04-23 13:29:27 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
let changesetBack = Changeset.inverse(
|
2020-11-23 19:24:19 +01:00
|
|
|
obj.changeset, padContents.currentLines, padContents.alines, padContents.apool);
|
2012-04-23 13:29:27 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
changesetBack = Changeset.moveOpsToNewPool(
|
2020-11-23 19:24:19 +01:00
|
|
|
changesetBack, (new AttribPool()).fromJsonable(obj.apool), padContents.apool);
|
2012-04-23 13:29:27 +02:00
|
|
|
|
|
|
|
loadedNewChangeset(changeset, changesetBack, obj.newRev - 1, obj.timeDelta);
|
2020-12-15 18:37:15 +01:00
|
|
|
} else if (obj.type === 'NEW_AUTHORDATA') {
|
2020-11-23 19:24:19 +01:00
|
|
|
const authorMap = {};
|
2012-04-23 13:29:27 +02:00
|
|
|
authorMap[obj.author] = obj.data;
|
|
|
|
receiveAuthorData(authorMap);
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const authors = _.map(padContents.getActiveAuthors(), (name) => authorData[name]);
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2012-04-23 13:29:27 +02:00
|
|
|
BroadcastSlider.setAuthors(authors);
|
2020-12-15 18:37:15 +01:00
|
|
|
} else if (obj.type === 'NEW_SAVEDREV') {
|
2020-11-23 19:24:19 +01:00
|
|
|
const savedRev = obj.savedRev;
|
2012-04-23 13:29:27 +02:00
|
|
|
BroadcastSlider.addSavedRevision(savedRev.revNum, savedRev);
|
|
|
|
}
|
2020-11-23 19:24:19 +01:00
|
|
|
hooks.callAll(`handleClientTimesliderMessage_${obj.type}`, {payload: obj});
|
2020-12-15 18:37:15 +01:00
|
|
|
} else if (obj.type === 'CHANGESET_REQ') {
|
|
|
|
this.handleSocketResponse(obj);
|
2020-11-23 19:24:19 +01:00
|
|
|
} else {
|
|
|
|
debugLog(`Unknown message type: ${obj.type}`);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
2020-11-23 19:24:19 +01:00
|
|
|
},
|
2012-04-23 13:29:27 +02:00
|
|
|
};
|
2011-06-20 12:44:04 +02:00
|
|
|
|
|
|
|
// to start upon window load, just push a function onto this array
|
2020-11-23 19:24:19 +01:00
|
|
|
// window['onloadFuncts'].push(setUpSocket);
|
|
|
|
// window['onloadFuncts'].push(function ()
|
|
|
|
fireWhenAllScriptsAreLoaded.push(() => {
|
2011-06-20 12:44:04 +02:00
|
|
|
// set up the currentDivs and DOM
|
|
|
|
padContents.currentDivs = [];
|
2020-11-23 19:24:19 +01:00
|
|
|
$('#innerdocbody').html('');
|
|
|
|
for (let i = 0; i < padContents.currentLines.length; i++) {
|
|
|
|
const div = padContents.lineToElement(padContents.currentLines[i], padContents.alines[i]);
|
2011-06-20 12:44:04 +02:00
|
|
|
padContents.currentDivs.push(div);
|
2020-11-23 19:24:19 +01:00
|
|
|
$('#innerdocbody').append(div);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// this is necessary to keep infinite loops of events firing,
|
|
|
|
// since goToRevision changes the slider position
|
2020-12-15 18:37:15 +01:00
|
|
|
const goToRevisionIfEnabled = (...args) => {
|
2020-11-23 19:24:19 +01:00
|
|
|
if (goToRevisionIfEnabledCount > 0) {
|
2012-04-23 14:47:07 +02:00
|
|
|
goToRevisionIfEnabledCount--;
|
2020-11-23 19:24:19 +01:00
|
|
|
} else {
|
2020-12-15 18:37:15 +01:00
|
|
|
goToRevision(...args);
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
2020-11-23 19:24:19 +01:00
|
|
|
};
|
2019-04-16 00:34:29 +02:00
|
|
|
|
2012-04-23 14:47:07 +02:00
|
|
|
BroadcastSlider.onSlider(goToRevisionIfEnabled);
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-11-23 19:24:19 +01:00
|
|
|
const dynamicCSS = makeCSSManager('dynamicsyntax');
|
2020-12-15 18:37:15 +01:00
|
|
|
const authorData = {};
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2020-12-15 18:37:15 +01:00
|
|
|
const receiveAuthorData = (newAuthorData) => {
|
|
|
|
for (const [author, data] of Object.entries(newAuthorData)) {
|
|
|
|
const bgcolor = typeof data.colorId === 'number'
|
|
|
|
? clientVars.colorPalette[data.colorId] : data.colorId;
|
2020-11-23 19:24:19 +01:00
|
|
|
if (bgcolor && dynamicCSS) {
|
|
|
|
const selector = dynamicCSS.selectorStyle(`.${linestylefilter.getAuthorClassName(author)}`);
|
|
|
|
selector.backgroundColor = bgcolor;
|
2020-12-15 18:37:15 +01:00
|
|
|
selector.color = (colorutils.luminosity(colorutils.css2triple(bgcolor)) < 0.5)
|
|
|
|
? '#ffffff' : '#000000'; // see ace2_inner.js for the other part
|
2011-06-20 12:44:04 +02:00
|
|
|
}
|
|
|
|
authorData[author] = data;
|
|
|
|
}
|
2020-12-15 18:37:15 +01:00
|
|
|
};
|
2011-06-20 12:44:04 +02:00
|
|
|
|
2012-04-23 12:52:30 +02:00
|
|
|
receiveAuthorData(clientVars.collab_client_vars.historicalAuthorData);
|
2012-01-27 06:20:26 +01:00
|
|
|
|
|
|
|
return changesetLoader;
|
2021-02-17 17:24:47 +01:00
|
|
|
};
|
2012-01-16 02:23:48 +01:00
|
|
|
|
|
|
|
exports.loadBroadcastJS = loadBroadcastJS;
|