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 ;
const _ = require ( './underscore' ) ;
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.
2020-11-21 19:37:57 +01:00
function loadBroadcastJS ( socket , sendSocketMsg , fireWhenAllScriptsAreLoaded , BroadcastSlider ) {
2020-11-23 19:24:19 +01:00
let changesetLoader = undefined ;
2011-06-20 12:44:04 +02:00
// Below Array#indexOf code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_indexof.htm
2020-11-23 19:24:19 +01:00
if ( ! Array . prototype . indexOf ) {
Array . prototype . indexOf = function ( elt /* , from*/ ) {
const len = this . length >>> 0 ;
2011-06-20 12:44:04 +02:00
2020-11-23 19:24:19 +01:00
let from = Number ( arguments [ 1 ] ) || 0 ;
2011-06-20 12:44:04 +02:00
from = ( from < 0 ) ? Math . ceil ( from ) : Math . floor ( from ) ;
if ( from < 0 ) from += len ;
2020-11-23 19:24:19 +01:00
for ( ; from < len ; from ++ ) {
2011-06-20 12:44:04 +02:00
if ( from in this && this [ from ] === elt ) return from ;
}
return - 1 ;
} ;
}
2020-11-21 19:37:57 +01:00
function debugLog ( ) {
2020-11-23 19:24:19 +01:00
try {
2011-07-07 19:59:34 +02:00
if ( window . console ) console . log . apply ( console , arguments ) ;
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-11-23 19:24:19 +01:00
// var socket;
const channelState = 'DISCONNECTED' ;
2011-06-20 12:44:04 +02:00
2020-11-23 19:24:19 +01:00
const appLevelDisconnectReason = null ;
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 ,
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-11-23 19:24:19 +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' ) ;
const emptyLine = ( line == '\n' ) ;
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-11-23 19:24:19 +01:00
applySpliceToDivs ( start , numRemoved , newLines ) {
2011-06-20 12:44:04 +02:00
// remove spliced-out lines from DOM
2020-11-23 19:24:19 +01:00
for ( var 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 = [ ] ;
for ( var 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-11-23 19:24:19 +01:00
for ( var i = 0 ; i < newDivs . length ; i ++ ) {
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
newDivs . unshift ( 0 ) ; // remove 0 elements
newDivs . unshift ( start ) ;
this . currentDivs . splice . apply ( this . currentDivs , newDivs ) ;
return this ;
} ,
// splice the lines
2020-11-23 19:24:19 +01:00
splice ( start , numRemoved , newLinesVA ) {
const newLines = _ . map ( Array . prototype . slice . call ( arguments , 2 ) , ( s ) => s ) ;
2011-06-20 12:44:04 +02:00
// apply this splice to the divs
this . applySpliceToDivs ( start , numRemoved , newLines ) ;
// call currentLines.splice, to keep the currentLines array up to date
newLines . unshift ( numRemoved ) ;
newLines . unshift ( start ) ;
this . currentLines . splice . apply ( this . currentLines , arguments ) ;
} ,
// 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 self = this ;
const authors = [ ] ;
const seenNums = { } ;
const alines = self . alines ;
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-11-23 19:24:19 +01:00
if ( self . apool . getAttribKey ( n ) == 'author' ) {
const a = self . apool . getAttribValue ( n ) ;
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-11-21 19:37:57 +01:00
function callCatchingErrors ( catcher , func ) {
2020-11-23 19:24:19 +01:00
try {
2011-06-20 12:44:04 +02:00
wrapRecordingErrors ( catcher , func ) ( ) ;
2020-11-23 19:24:19 +01:00
} catch ( e ) { /* absorb*/
2011-06-20 12:44:04 +02:00
}
}
2020-11-21 19:37:57 +01:00
function wrapRecordingErrors ( catcher , func ) {
2020-11-23 19:24:19 +01:00
return function ( ) {
try {
2011-06-20 12:44:04 +02:00
return func . apply ( this , Array . prototype . slice . call ( arguments ) ) ;
2020-11-23 19:24:19 +01:00
} catch ( e ) {
2011-06-20 12:44:04 +02:00
// caughtErrors.push(e);
// caughtErrorCatchers.push(catcher);
// caughtErrorTimes.push(+new Date());
// console.dir({catcher: catcher, e: e});
debugLog ( e ) ; // TODO(kroo): added temporary, to catch errors
throw e ;
}
} ;
}
2020-11-21 19:37:57 +01:00
function loadedNewChangeset ( changesetForward , changesetBackward , revision , timeDelta ) {
2020-11-23 19:24:19 +01:00
const broadcasting = ( BroadcastSlider . getSliderPosition ( ) == revisionInfo . latest ) ;
2011-06-20 12:44:04 +02:00
revisionInfo . addChangeset ( revision , revision + 1 , changesetForward , changesetBackward , timeDelta ) ;
BroadcastSlider . setSliderLength ( revisionInfo . latest ) ;
if ( broadcasting ) applyChangeset ( changesetForward , revision + 1 , false , timeDelta ) ;
}
2020-11-23 19:24:19 +01:00
/ *
2011-06-20 12:44:04 +02:00
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 .
* /
2020-11-21 19:37:57 +01:00
function 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-11-23 19:24:19 +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-10-21 11:05:58 +02: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
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 ) ;
2011-06-20 12:44:04 +02:00
}
2020-11-21 19:37:57 +01:00
function updateTimer ( ) {
2020-11-23 19:24:19 +01:00
const zpad = function ( str , length ) {
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 ) ;
const dateFormat = function ( ) {
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 ) ;
2011-06-20 12:44:04 +02: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-11-21 19:37:57 +01:00
function goToRevision ( newRevision ) {
2011-06-20 12:44:04 +02:00
padContents . targetRevision = newRevision ;
2020-11-23 19:24:19 +01:00
const self = this ;
const path = 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-11-23 19:24:19 +01:00
if ( path . status == 'complete' ) {
2011-06-20 12:44:04 +02:00
var cs = path . changesets ;
var changeset = cs [ 0 ] ;
var timeDelta = path . times [ 0 ] ;
2020-11-23 19:24:19 +01:00
for ( var 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-11-23 19:24:19 +01:00
} else if ( path . status == 'partial' ) {
const sliderLocation = padContents . currentRevision ;
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-11-23 19:24:19 +01:00
const update = function ( start , end ) {
// 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...
var cs = path . changesets ;
var changeset = cs [ 0 ] ;
var timeDelta = path . times [ 0 ] ;
2020-11-23 19:24:19 +01:00
for ( var 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 ) ;
2011-06-20 12:44:04 +02:00
}
2019-04-16 00:34:29 +02:00
2014-08-09 18:52:44 +02:00
function loadChangesetsForRevision ( revision , callback ) {
2020-11-23 19:24:19 +01:00
if ( BroadcastSlider . getSliderLength ( ) > 10000 ) {
2014-08-09 18:52:44 +02:00
var start = ( Math . floor ( ( revision ) / 10000 ) * 10000 ) ; // revision 0 to 10
changesetLoader . queueUp ( start , 100 ) ;
}
2020-11-23 19:24:19 +01:00
if ( BroadcastSlider . getSliderLength ( ) > 1000 ) {
2014-08-09 18:52:44 +02:00
var start = ( Math . floor ( ( revision ) / 1000 ) * 1000 ) ; // (start from -1, go to 19) + 1
changesetLoader . queueUp ( start , 10 ) ;
}
start = ( Math . floor ( ( revision ) / 100 ) * 100 ) ;
changesetLoader . queueUp ( start , 1 , callback ) ;
}
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 ;
// if(changesetLoader.requestQueue.indexOf(revision) != -1)
// return; // already in the queue.
2020-11-23 19:24:19 +01:00
if ( changesetLoader . resolved . indexOf ( ` ${ revision } _ ${ width } ` ) != - 1 ) return ; // already loaded from the server
changesetLoader . resolved . push ( ` ${ revision } _ ${ width } ` ) ;
2011-06-20 12:44:04 +02:00
2020-11-23 19:24:19 +01:00
const requestQueue = width == 1 ? changesetLoader . requestQueue3 : width == 10 ? changesetLoader . requestQueue2 : changesetLoader . requestQueue1 ;
2011-06-20 12:44:04 +02:00
requestQueue . push (
2020-11-23 19:24:19 +01:00
{
rev : revision ,
res : width ,
callback ,
} ) ;
if ( ! changesetLoader . running ) {
2011-06-20 12:44:04 +02:00
changesetLoader . running = true ;
setTimeout ( changesetLoader . loadFromQueue , 10 ) ;
}
} ,
2020-11-23 19:24:19 +01:00
loadFromQueue ( ) {
const self = changesetLoader ;
const requestQueue = self . requestQueue1 . length > 0 ? self . requestQueue1 : self . requestQueue2 . length > 0 ? self . requestQueue2 : self . requestQueue3 . length > 0 ? self . requestQueue3 : null ;
2011-06-20 12:44:04 +02:00
2020-11-23 19:24:19 +01:00
if ( ! requestQueue ) {
2011-06-20 12:44:04 +02:00
self . running = false ;
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
} ) ;
2011-06-20 16:37:41 +02:00
self . reqCallbacks [ requestID ] = callback ;
} ,
2020-11-23 19:24:19 +01:00
handleSocketResponse ( message ) {
const self = changesetLoader ;
2011-07-07 19:59:34 +02:00
2020-11-23 19:24:19 +01:00
const start = message . data . start ;
const granularity = message . data . granularity ;
const callback = self . reqCallbacks [ message . data . requestID ] ;
2011-07-07 19:59:34 +02:00
delete self . reqCallbacks [ message . data . requestID ] ;
self . handleResponse ( message . data , start , granularity , callback ) ;
setTimeout ( self . loadFromQueue , 10 ) ;
2011-06-20 12:44:04 +02:00
} ,
2020-11-23 19:24:19 +01:00
handleResponse ( data , start , granularity , callback ) {
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);
const forwardcs = Changeset . moveOpsToNewPool ( data . forwardsChangesets [ i ] , pool , padContents . apool ) ;
const backwardcs = Changeset . moveOpsToNewPool ( data . backwardsChangesets [ i ] , pool , padContents . apool ) ;
2011-06-20 12:44:04 +02:00
revisionInfo . addChangeset ( astart , aend , forwardcs , backwardcs , data . timeDeltas [ i ] ) ;
}
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 ) {
if ( obj . type == 'COLLABROOM' ) {
2012-04-23 13:29:27 +02:00
obj = obj . data ;
2020-11-23 19:24:19 +01:00
if ( obj . type == 'NEW_CHANGES' ) {
const changeset = Changeset . moveOpsToNewPool (
obj . changeset , ( new AttribPool ( ) ) . fromJsonable ( obj . apool ) , padContents . apool ) ;
2012-04-23 13:29:27 +02:00
var 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
var 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-11-23 19:24:19 +01:00
} else if ( obj . type == 'NEW_AUTHORDATA' ) {
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-11-23 19:24:19 +01:00
} else if ( obj . type == 'NEW_SAVEDREV' ) {
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 } ) ;
} else if ( obj . type == 'CHANGESET_REQ' ) {
2012-04-23 13:29:27 +02:00
changesetLoader . 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
var goToRevisionIfEnabledCount = 0 ;
2020-11-23 19:24:19 +01:00
const goToRevisionIfEnabled = function ( ) {
if ( goToRevisionIfEnabledCount > 0 ) {
2012-04-23 14:47:07 +02:00
goToRevisionIfEnabledCount -- ;
2020-11-23 19:24:19 +01:00
} else {
2012-04-23 14:47:07 +02:00
goToRevision . apply ( goToRevision , arguments ) ;
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' ) ;
2011-06-20 12:44:04 +02:00
var authorData = { } ;
2020-11-21 19:37:57 +01:00
function receiveAuthorData ( newAuthorData ) {
2020-11-23 19:24:19 +01:00
for ( const author in newAuthorData ) {
const data = newAuthorData [ author ] ;
const bgcolor = typeof data . colorId === 'number' ? clientVars . colorPalette [ data . colorId ] : data . colorId ;
if ( bgcolor && dynamicCSS ) {
const selector = dynamicCSS . selectorStyle ( ` . ${ linestylefilter . getAuthorClassName ( author ) } ` ) ;
selector . backgroundColor = bgcolor ;
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 ;
}
}
2012-04-23 12:52:30 +02:00
receiveAuthorData ( clientVars . collab _client _vars . historicalAuthorData ) ;
2012-01-27 06:20:26 +01:00
return changesetLoader ;
2020-07-20 00:46:58 +02:00
2020-11-23 19:24:19 +01:00
function goToLineNumber ( lineNumber ) {
2020-07-20 00:46:58 +02:00
// Sets the Y scrolling of the browser to go to this line
2020-11-23 19:24:19 +01:00
const line = $ ( '#innerdocbody' ) . find ( ` div:nth-child( ${ lineNumber + 1 } ) ` ) ;
const newY = $ ( line ) [ 0 ] . offsetTop ;
const ecb = document . getElementById ( 'editorcontainerbox' ) ;
2020-07-20 15:14:02 +02:00
// Chrome 55 - 59 bugfix
2020-11-23 19:24:19 +01:00
if ( ecb . scrollTo ) {
2020-07-20 10:49:40 +02:00
ecb . scrollTo ( { top : newY , behavior : 'smooth' } ) ;
2020-11-23 19:24:19 +01:00
} else {
2020-07-20 15:14:02 +02:00
$ ( '#editorcontainerbox' ) . scrollTop ( newY ) ;
2020-07-20 10:49:40 +02:00
}
2020-07-20 00:46:58 +02:00
}
2011-06-20 12:44:04 +02:00
}
2012-01-16 02:23:48 +01:00
exports . loadBroadcastJS = loadBroadcastJS ;