2011-12-04 16:33:56 +01:00
/ * *
* This code is mostly from the old Etherpad . Please help us to comment this code .
* 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 .
* /
2012-01-17 12:51:00 +01:00
var makeCSSManager = require ( '/cssmanager' ) . makeCSSManager ;
2012-01-17 12:46:15 +01:00
var domline = require ( '/domline' ) . domline ;
2012-01-17 12:43:10 +01:00
var AttribPool = require ( '/AttributePoolFactory' ) . createAttributePool ;
var Changeset = require ( '/Changeset' ) ;
2012-01-17 12:48:36 +01:00
var linestylefilter = require ( '/linestylefilter' ) . linestylefilter ;
2012-02-06 16:00:42 +01:00
var colorutils = require ( '/colorutils' ) . colorutils ;
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.
function loadBroadcastJS ( socket , sendSocketMsg , fireWhenAllScriptsAreLoaded , BroadcastSlider )
2011-06-20 12:44:04 +02:00
{
2012-01-27 06:20:26 +01:00
var changesetLoader = undefined ;
2011-06-20 12:44:04 +02:00
// just in case... (todo: this must be somewhere else in the client code.)
// Below Array#map code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_map.htm
if ( ! Array . prototype . map )
{
2011-07-07 19:59:34 +02:00
Array . prototype . map = function ( fun /*, thisp*/ )
2011-06-20 12:44:04 +02:00
{
var len = this . length >>> 0 ;
if ( typeof fun != "function" ) throw new TypeError ( ) ;
var res = new Array ( len ) ;
var thisp = arguments [ 1 ] ;
for ( var i = 0 ; i < len ; i ++ )
{
if ( i in this ) res [ i ] = fun . call ( thisp , this [ i ] , i , this ) ;
}
return res ;
} ;
}
// Below Array#forEach code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_foreach.htm
if ( ! Array . prototype . forEach )
{
2011-07-07 19:59:34 +02:00
Array . prototype . forEach = function ( fun /*, thisp*/ )
2011-06-20 12:44:04 +02:00
{
var len = this . length >>> 0 ;
if ( typeof fun != "function" ) throw new TypeError ( ) ;
var thisp = arguments [ 1 ] ;
for ( var i = 0 ; i < len ; i ++ )
{
if ( i in this ) fun . call ( thisp , this [ i ] , i , this ) ;
}
} ;
}
// Below Array#indexOf code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_indexof.htm
if ( ! Array . prototype . indexOf )
{
2011-07-07 19:59:34 +02:00
Array . prototype . indexOf = function ( elt /*, from*/ )
2011-06-20 12:44:04 +02:00
{
var len = this . length >>> 0 ;
var from = Number ( arguments [ 1 ] ) || 0 ;
from = ( from < 0 ) ? Math . ceil ( from ) : Math . floor ( from ) ;
if ( from < 0 ) from += len ;
for ( ; from < len ; from ++ )
{
if ( from in this && this [ from ] === elt ) return from ;
}
return - 1 ;
} ;
}
function debugLog ( )
{
try
{
2011-07-07 19:59:34 +02:00
if ( window . console ) console . log . apply ( console , arguments ) ;
2011-06-20 12:44:04 +02:00
}
catch ( e )
{
2011-07-07 19:59:34 +02:00
if ( window . console ) console . log ( "error printing: " , e ) ;
2011-06-20 12:44:04 +02:00
}
}
function randomString ( )
{
return "_" + Math . floor ( Math . random ( ) * 1000000 ) ;
}
// for IE
if ( $ . browser . msie )
{
try
{
document . execCommand ( "BackgroundImageCache" , false , true ) ;
}
catch ( e )
{ }
}
var userId = "hiddenUser" + randomString ( ) ;
var socketId ;
2011-06-20 16:37:41 +02:00
//var socket;
2011-06-20 12:44:04 +02:00
var channelState = "DISCONNECTED" ;
var appLevelDisconnectReason = null ;
var padContents = {
currentRevision : clientVars . revNum ,
currentTime : clientVars . currentTime ,
currentLines : Changeset . splitTextLines ( clientVars . initialStyledContents . atext . text ) ,
currentDivs : null ,
// to be filled in once the dom loads
apool : ( new AttribPool ( ) ) . fromJsonable ( clientVars . initialStyledContents . apool ) ,
alines : Changeset . splitAttributionLines (
clientVars . initialStyledContents . atext . attribs , clientVars . initialStyledContents . atext . text ) ,
// generates a jquery element containing HTML for a line
2011-07-07 19:59:34 +02:00
lineToElement : function ( line , aline )
2011-06-20 12:44:04 +02:00
{
var element = document . createElement ( "div" ) ;
var emptyLine = ( line == '\n' ) ;
var domInfo = domline . createDomLine ( ! emptyLine , true ) ;
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 ) ;
} ,
2011-07-07 19:59:34 +02:00
applySpliceToDivs : function ( start , numRemoved , newLines )
2011-06-20 12:44:04 +02:00
{
// remove spliced-out lines from DOM
for ( var i = start ; i < start + numRemoved && i < this . currentDivs . length ; i ++ )
{
debugLog ( "removing" , this . currentDivs [ i ] . attr ( 'id' ) ) ;
this . currentDivs [ i ] . remove ( ) ;
}
// remove spliced-out line divs from currentDivs array
this . currentDivs . splice ( start , numRemoved ) ;
var newDivs = [ ] ;
for ( var i = 0 ; i < newLines . length ; i ++ )
{
newDivs . push ( this . lineToElement ( newLines [ i ] , this . alines [ start + i ] ) ) ;
}
// grab the div just before the first one
var startDiv = this . currentDivs [ start - 1 ] || null ;
// insert the div elements into the correct place, in the correct order
for ( var i = 0 ; i < newDivs . length ; i ++ )
{
if ( startDiv )
{
startDiv . after ( newDivs [ i ] ) ;
}
else
{
$ ( "#padcontent" ) . prepend ( newDivs [ i ] ) ;
}
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
2011-07-07 19:59:34 +02:00
splice : function ( start , numRemoved , newLinesVA )
2011-06-20 12:44:04 +02:00
{
var newLines = Array . prototype . slice . call ( arguments , 2 ) . map (
2011-07-07 19:59:34 +02:00
function ( s )
2011-06-20 12:44:04 +02:00
{
return s ;
} ) ;
// 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
2011-07-07 19:59:34 +02:00
get : function ( i )
2011-06-20 12:44:04 +02:00
{
return this . currentLines [ i ] ;
} ,
// returns the number of lines in the document
2011-07-07 19:59:34 +02:00
length : function ( )
2011-06-20 12:44:04 +02:00
{
return this . currentLines . length ;
} ,
2011-07-07 19:59:34 +02:00
getActiveAuthors : function ( )
2011-06-20 12:44:04 +02:00
{
var self = this ;
var authors = [ ] ;
var seenNums = { } ;
var alines = self . alines ;
for ( var i = 0 ; i < alines . length ; i ++ )
{
2011-07-07 19:59:34 +02:00
Changeset . eachAttribNumber ( alines [ i ] , function ( n )
2011-06-20 12:44:04 +02:00
{
if ( ! seenNums [ n ] )
{
seenNums [ n ] = true ;
if ( self . apool . getAttribKey ( n ) == 'author' )
{
var a = self . apool . getAttribValue ( n ) ;
if ( a )
{
authors . push ( a ) ;
}
}
}
} ) ;
}
authors . sort ( ) ;
return authors ;
}
} ;
function callCatchingErrors ( catcher , func )
{
try
{
wrapRecordingErrors ( catcher , func ) ( ) ;
}
catch ( e )
{ /*absorb*/
}
}
function wrapRecordingErrors ( catcher , func )
{
2011-07-07 19:59:34 +02:00
return function ( )
2011-06-20 12:44:04 +02:00
{
try
{
return func . apply ( this , Array . prototype . slice . call ( arguments ) ) ;
}
catch ( e )
{
// 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 ;
}
} ;
}
function loadedNewChangeset ( changesetForward , changesetBackward , revision , timeDelta )
{
var broadcasting = ( BroadcastSlider . getSliderPosition ( ) == revisionInfo . latest ) ;
debugLog ( "broadcasting:" , broadcasting , BroadcastSlider . getSliderPosition ( ) , revisionInfo . latest , revision ) ;
revisionInfo . addChangeset ( revision , revision + 1 , changesetForward , changesetBackward , timeDelta ) ;
BroadcastSlider . setSliderLength ( revisionInfo . latest ) ;
if ( broadcasting ) applyChangeset ( changesetForward , revision + 1 , false , timeDelta ) ;
}
2011-07-07 19:59:34 +02: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 .
* /
function applyChangeset ( changeset , revision , preventSliderMovement , timeDelta )
{
// disable the next 'gotorevision' call handled by a timeslider update
if ( ! preventSliderMovement )
{
goToRevisionIfEnabledCount ++ ;
BroadcastSlider . setSliderPosition ( revision ) ;
}
try
{
// must mutate attribution lines before text lines
Changeset . mutateAttributionLines ( changeset , padContents . alines , padContents . apool ) ;
}
catch ( e )
{
debugLog ( e ) ;
}
Changeset . mutateTextLines ( changeset , padContents ) ;
padContents . currentRevision = revision ;
padContents . currentTime += timeDelta * 1000 ;
debugLog ( 'Time Delta: ' , timeDelta )
updateTimer ( ) ;
2011-07-07 19:59:34 +02:00
BroadcastSlider . setAuthors ( padContents . getActiveAuthors ( ) . map ( function ( name )
2011-06-20 12:44:04 +02:00
{
return authorData [ name ] ;
} ) ) ;
}
function updateTimer ( )
{
2011-07-07 19:59:34 +02:00
var zpad = function ( str , length )
2011-06-20 12:44:04 +02:00
{
str = str + "" ;
while ( str . length < length )
str = '0' + str ;
return str ;
}
2011-07-07 19:59:34 +02:00
2011-06-20 12:44:04 +02:00
var date = new Date ( padContents . currentTime ) ;
2011-07-07 19:59:34 +02:00
var dateFormat = function ( )
2011-06-20 12:44:04 +02:00
{
var month = zpad ( date . getMonth ( ) + 1 , 2 ) ;
var day = zpad ( date . getDate ( ) , 2 ) ;
var year = ( date . getFullYear ( ) ) ;
var hours = zpad ( date . getHours ( ) , 2 ) ;
var minutes = zpad ( date . getMinutes ( ) , 2 ) ;
var seconds = zpad ( date . getSeconds ( ) , 2 ) ;
return ( [ month , '/' , day , '/' , year , ' ' , hours , ':' , minutes , ':' , seconds ] . join ( "" ) ) ;
}
2011-07-07 19:59:34 +02:00
2011-06-20 12:44:04 +02:00
$ ( '#timer' ) . html ( dateFormat ( ) ) ;
var revisionDate = [ "Saved" , [ "Jan" , "Feb" , "March" , "April" , "May" , "June" , "July" , "Aug" , "Sept" , "Oct" , "Nov" , "Dec" ] [ date . getMonth ( ) ] , date . getDate ( ) + "," , date . getFullYear ( ) ] . join ( " " )
$ ( '#revision_date' ) . html ( revisionDate )
}
2011-07-25 18:26:56 +02:00
updateTimer ( ) ;
2011-06-20 12:44:04 +02:00
function goToRevision ( newRevision )
{
padContents . targetRevision = newRevision ;
var self = this ;
var path = revisionInfo . getPath ( padContents . currentRevision , newRevision ) ;
debugLog ( 'newRev: ' , padContents . currentRevision , path ) ;
if ( path . status == 'complete' )
{
var cs = path . changesets ;
debugLog ( "status: complete, changesets: " , cs , "path:" , path ) ;
var changeset = cs [ 0 ] ;
var timeDelta = path . times [ 0 ] ;
for ( var i = 1 ; i < cs . length ; i ++ )
{
changeset = Changeset . compose ( changeset , cs [ i ] , padContents . apool ) ;
timeDelta += path . times [ i ] ;
}
if ( changeset ) applyChangeset ( changeset , path . rev , true , timeDelta ) ;
}
else if ( path . status == "partial" )
{
debugLog ( 'partial' ) ;
var sliderLocation = padContents . currentRevision ;
// callback is called after changeset information is pulled from server
// this may never get called, if the changeset has already been loaded
2011-07-07 19:59:34 +02:00
var update = function ( start , end )
2011-06-20 12:44:04 +02:00
{
// if we've called goToRevision in the time since, don't goToRevision
goToRevision ( padContents . targetRevision ) ;
} ;
// do our best with what we have...
var cs = path . changesets ;
var changeset = cs [ 0 ] ;
var timeDelta = path . times [ 0 ] ;
for ( var i = 1 ; i < cs . length ; i ++ )
{
changeset = Changeset . compose ( changeset , cs [ i ] , padContents . apool ) ;
timeDelta += path . times [ i ] ;
}
if ( changeset ) applyChangeset ( changeset , path . rev , true , timeDelta ) ;
if ( BroadcastSlider . getSliderLength ( ) > 10000 )
{
var start = ( Math . floor ( ( newRevision ) / 10000 ) * 10000 ) ; // revision 0 to 10
changesetLoader . queueUp ( start , 100 ) ;
}
if ( BroadcastSlider . getSliderLength ( ) > 1000 )
{
var start = ( Math . floor ( ( newRevision ) / 1000 ) * 1000 ) ; // (start from -1, go to 19) + 1
changesetLoader . queueUp ( start , 10 ) ;
}
start = ( Math . floor ( ( newRevision ) / 100 ) * 100 ) ;
changesetLoader . queueUp ( start , 1 , update ) ;
}
2011-07-07 19:59:34 +02:00
BroadcastSlider . setAuthors ( padContents . getActiveAuthors ( ) . map ( function ( name )
2011-06-20 12:44:04 +02:00
{
return authorData [ name ] ;
} ) ) ;
}
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 : [ ] ,
2011-07-07 19:59:34 +02:00
queueUp : function ( 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.
if ( changesetLoader . resolved . indexOf ( revision + "_" + width ) != - 1 ) return ; // already loaded from the server
changesetLoader . resolved . push ( revision + "_" + width ) ;
var requestQueue = width == 1 ? changesetLoader . requestQueue3 : width == 10 ? changesetLoader . requestQueue2 : changesetLoader . requestQueue1 ;
requestQueue . push (
{
'rev' : revision ,
'res' : width ,
'callback' : callback
} ) ;
if ( ! changesetLoader . running )
{
changesetLoader . running = true ;
setTimeout ( changesetLoader . loadFromQueue , 10 ) ;
}
} ,
2011-07-07 19:59:34 +02:00
loadFromQueue : function ( )
2011-06-20 12:44:04 +02:00
{
var self = changesetLoader ;
var requestQueue = self . requestQueue1 . length > 0 ? self . requestQueue1 : self . requestQueue2 . length > 0 ? self . requestQueue2 : self . requestQueue3 . length > 0 ? self . requestQueue3 : null ;
if ( ! requestQueue )
{
self . running = false ;
return ;
}
var request = requestQueue . pop ( ) ;
var granularity = request . res ;
var callback = request . callback ;
var start = request . rev ;
2011-07-07 19:59:34 +02:00
var requestID = Math . floor ( Math . random ( ) * 100000 ) ;
/ * v a r m s g = { " c o m p o n e n t " : " t i m e s l i d e r " ,
2011-06-20 16:37:41 +02:00
"type" : "CHANGESET_REQ" ,
"padId" : padId ,
"token" : token ,
"protocolVersion" : 2 ,
"data"
{
"start" : start ,
"granularity" : granularity
} } ;
socket . send ( msg ) ; * /
2011-07-07 19:59:34 +02:00
sendSocketMsg ( "CHANGESET_REQ" , {
"start" : start ,
"granularity" : granularity ,
"requestID" : requestID
} ) ;
2011-06-20 16:37:41 +02:00
self . reqCallbacks [ requestID ] = callback ;
2011-07-07 19:59:34 +02:00
/ * d e b u g L o g ( " l o a d i n g i n g r e v i s i o n " , s t a r t , " t h r o u g h a j a x " ) ;
2011-06-20 12:44:04 +02:00
$ . 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
2011-06-20 16:37:41 +02:00
} ) ; * /
} ,
2011-07-07 19:59:34 +02:00
handleSocketResponse : function ( message )
{
var self = changesetLoader ;
var start = message . data . start ;
var granularity = message . data . granularity ;
var callback = self . reqCallbacks [ message . data . requestID ] ;
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
} ,
2011-07-07 19:59:34 +02:00
handleResponse : function ( data , start , granularity , callback )
2011-06-20 12:44:04 +02:00
{
debugLog ( "response: " , data ) ;
var pool = ( new AttribPool ( ) ) . fromJsonable ( data . apool ) ;
for ( var i = 0 ; i < data . forwardsChangesets . length ; i ++ )
{
var astart = start + i * granularity - 1 ; // rev -1 is a blank single line
var aend = start + ( i + 1 ) * granularity - 1 ; // totalRevs is the most recent revision
if ( aend > data . actualEndNum - 1 ) aend = data . actualEndNum - 1 ;
debugLog ( "adding changeset:" , astart , aend ) ;
var forwardcs = Changeset . moveOpsToNewPool ( data . forwardsChangesets [ i ] , pool , padContents . apool ) ;
var backwardcs = Changeset . moveOpsToNewPool ( data . backwardsChangesets [ i ] , pool , padContents . apool ) ;
revisionInfo . addChangeset ( astart , aend , forwardcs , backwardcs , data . timeDeltas [ i ] ) ;
}
if ( callback ) callback ( start - 1 , start + data . forwardsChangesets . length * granularity - 1 ) ;
}
} ;
function handleMessageFromServer ( )
{
debugLog ( "handleMessage:" , arguments ) ;
var obj = arguments [ 0 ] [ 'data' ] ;
var expectedType = "COLLABROOM" ;
obj = JSON . parse ( obj ) ;
if ( obj [ 'type' ] == expectedType )
{
obj = obj [ 'data' ] ;
if ( obj [ 'type' ] == "NEW_CHANGES" )
{
debugLog ( obj ) ;
var changeset = Changeset . moveOpsToNewPool (
obj . changeset , ( new AttribPool ( ) ) . fromJsonable ( obj . apool ) , padContents . apool ) ;
var changesetBack = Changeset . moveOpsToNewPool (
obj . changesetBack , ( new AttribPool ( ) ) . fromJsonable ( obj . apool ) , padContents . apool ) ;
loadedNewChangeset ( changeset , changesetBack , obj . newRev - 1 , obj . timeDelta ) ;
}
else if ( obj [ 'type' ] == "NEW_AUTHORDATA" )
{
var authorMap = { } ;
authorMap [ obj . author ] = obj . data ;
receiveAuthorData ( authorMap ) ;
2011-07-07 19:59:34 +02:00
BroadcastSlider . setAuthors ( padContents . getActiveAuthors ( ) . map ( function ( name )
2011-06-20 12:44:04 +02:00
{
return authorData [ name ] ;
} ) ) ;
}
else if ( obj [ 'type' ] == "NEW_SAVEDREV" )
{
var savedRev = obj . savedRev ;
BroadcastSlider . addSavedRevision ( savedRev . revNum , savedRev ) ;
}
}
else
{
debugLog ( "incorrect message type: " + obj [ 'type' ] + ", expected " + expectedType ) ;
}
}
function handleSocketClosed ( params )
{
debugLog ( "socket closed!" , params ) ;
socket = null ;
BroadcastSlider . showReconnectUI ( ) ;
// var reason = appLevelDisconnectReason || params.reason;
// var shouldReconnect = params.reconnect;
// if (shouldReconnect) {
// // determine if this is a tight reconnect loop due to weird connectivity problems
// // reconnectTimes.push(+new Date());
// var TOO_MANY_RECONNECTS = 8;
// var TOO_SHORT_A_TIME_MS = 10000;
// if (reconnectTimes.length >= TOO_MANY_RECONNECTS &&
// ((+new Date()) - reconnectTimes[reconnectTimes.length-TOO_MANY_RECONNECTS]) <
// TOO_SHORT_A_TIME_MS) {
// setChannelState("DISCONNECTED", "looping");
// }
// else {
// setChannelState("RECONNECTING", reason);
// setUpSocket();
// }
// }
// else {
// BroadcastSlider.showReconnectUI();
// setChannelState("DISCONNECTED", reason);
// }
}
function sendMessage ( msg )
{
socket . postMessage ( JSON . stringify (
{
type : "COLLABROOM" ,
data : msg
} ) ) ;
}
2011-07-07 19:59:34 +02:00
/ * f u n c t i o n s e t U p S o c k e t ( )
2011-06-20 12:44:04 +02:00
{
// required for Comet
if ( ( ! $ . browser . msie ) && ( ! ( $ . browser . mozilla && $ . browser . version . indexOf ( "1.8." ) == 0 ) ) )
{
document . domain = document . domain ; // for comet
}
var success = false ;
callCatchingErrors ( "setUpSocket" , function ( )
{
appLevelDisconnectReason = null ;
socketId = String ( Math . floor ( Math . random ( ) * 1e12 ) ) ;
socket = new WebSocket ( socketId ) ;
socket . onmessage = wrapRecordingErrors ( "socket.onmessage" , handleMessageFromServer ) ;
socket . onclosed = wrapRecordingErrors ( "socket.onclosed" , handleSocketClosed ) ;
socket . onopen = wrapRecordingErrors ( "socket.onopen" , function ( )
{
setChannelState ( "CONNECTED" ) ;
var msg = {
type : "CLIENT_READY" ,
roomType : 'padview' ,
roomName : 'padview/' + clientVars . viewId ,
data : {
lastRev : clientVars . revNum ,
userInfo : {
userId : userId
}
}
} ;
sendMessage ( msg ) ;
} ) ;
// socket.onhiccup = wrapRecordingErrors("socket.onhiccup", handleCometHiccup);
// socket.onlogmessage = function(x) {debugLog(x); };
socket . connect ( ) ;
success = true ;
} ) ;
if ( success )
{
//initialStartConnectTime = +new Date();
}
else
{
abandonConnection ( "initsocketfail" ) ;
}
2011-06-20 16:37:41 +02:00
} * /
2011-06-20 12:44:04 +02:00
function setChannelState ( newChannelState , moreInfo )
{
if ( newChannelState != channelState )
{
channelState = newChannelState ;
// callbacks.onChannelStateChange(channelState, moreInfo);
}
}
function abandonConnection ( reason )
{
if ( socket )
{
2011-07-07 19:59:34 +02:00
socket . onclosed = function ( )
2011-06-20 12:44:04 +02:00
{ } ;
2011-07-07 19:59:34 +02:00
socket . onhiccup = function ( )
2011-06-20 12:44:04 +02:00
{ } ;
socket . disconnect ( ) ;
}
socket = null ;
setChannelState ( "DISCONNECTED" , reason ) ;
}
2011-07-07 19:59:34 +02:00
/ * w i n d o w [ ' o n l o a d F u n c t s ' ] = [ ] ;
2011-06-20 12:44:04 +02:00
window . onload = function ( )
{
window [ 'isloaded' ] = true ;
window [ 'onloadFuncts' ] . forEach ( function ( funct )
{
funct ( ) ;
} ) ;
} ; * /
// to start upon window load, just push a function onto this array
//window['onloadFuncts'].push(setUpSocket);
//window['onloadFuncts'].push(function ()
2011-07-07 19:59:34 +02:00
fireWhenAllScriptsAreLoaded . push ( function ( )
2011-06-20 12:44:04 +02:00
{
// set up the currentDivs and DOM
padContents . currentDivs = [ ] ;
$ ( "#padcontent" ) . html ( "" ) ;
for ( var i = 0 ; i < padContents . currentLines . length ; i ++ )
{
var div = padContents . lineToElement ( padContents . currentLines [ i ] , padContents . alines [ i ] ) ;
padContents . currentDivs . push ( div ) ;
$ ( "#padcontent" ) . append ( div ) ;
}
debugLog ( padContents . currentDivs ) ;
} ) ;
// this is necessary to keep infinite loops of events firing,
// since goToRevision changes the slider position
var goToRevisionIfEnabledCount = 0 ;
2011-07-07 19:59:34 +02:00
var goToRevisionIfEnabled = function ( )
2011-06-20 12:44:04 +02:00
{
if ( goToRevisionIfEnabledCount > 0 )
{
goToRevisionIfEnabledCount -- ;
}
else
{
goToRevision . apply ( goToRevision , arguments ) ;
}
}
2011-07-07 19:59:34 +02:00
2011-06-20 12:44:04 +02:00
BroadcastSlider . onSlider ( goToRevisionIfEnabled ) ;
2011-07-07 19:59:34 +02:00
( function ( )
2011-06-20 12:44:04 +02:00
{
for ( var i = 0 ; i < clientVars . initialChangesets . length ; i ++ )
{
var csgroup = clientVars . initialChangesets [ i ] ;
var start = clientVars . initialChangesets [ i ] . start ;
var granularity = clientVars . initialChangesets [ i ] . granularity ;
debugLog ( "loading changest on startup: " , start , granularity , csgroup ) ;
changesetLoader . handleResponse ( csgroup , start , granularity , null ) ;
}
} ) ( ) ;
var dynamicCSS = makeCSSManager ( 'dynamicsyntax' ) ;
var authorData = { } ;
function receiveAuthorData ( newAuthorData )
{
for ( var author in newAuthorData )
{
var data = newAuthorData [ author ] ;
2011-08-21 19:53:30 +02:00
var bgcolor = typeof data . colorId == "number" ? clientVars . colorPalette [ data . colorId ] : data . colorId ;
2011-08-20 19:22:10 +02:00
if ( bgcolor && dynamicCSS )
2011-06-20 12:44:04 +02:00
{
2012-02-06 16:00:42 +01:00
var 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 ;
}
}
receiveAuthorData ( clientVars . historicalAuthorData ) ;
2012-01-27 06:20:26 +01:00
return changesetLoader ;
2011-06-20 12:44:04 +02:00
}
2012-01-16 02:23:48 +01:00
exports . loadBroadcastJS = loadBroadcastJS ;