746 lines
22 KiB
JavaScript
746 lines
22 KiB
JavaScript
/**
|
|
* Copyright 2009 Google Inc.
|
|
*
|
|
* 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 global = this;
|
|
|
|
function loadBroadcastJS()
|
|
{
|
|
// 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)
|
|
{
|
|
Array.prototype.map = function (fun /*, thisp*/ )
|
|
{
|
|
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)
|
|
{
|
|
Array.prototype.forEach = function (fun /*, thisp*/ )
|
|
{
|
|
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)
|
|
{
|
|
Array.prototype.indexOf = function (elt /*, from*/ )
|
|
{
|
|
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
|
|
{
|
|
if(window.console) console.log.apply(console, arguments);
|
|
}
|
|
catch (e)
|
|
{
|
|
if(window.console) console.log("error printing: ", e);
|
|
}
|
|
}
|
|
|
|
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;
|
|
//var socket;
|
|
|
|
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
|
|
lineToElement: function (line, aline)
|
|
{
|
|
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);
|
|
},
|
|
|
|
applySpliceToDivs: function (start, numRemoved, newLines)
|
|
{
|
|
// 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
|
|
splice: function (start, numRemoved, newLinesVA)
|
|
{
|
|
var newLines = Array.prototype.slice.call(arguments, 2).map(
|
|
|
|
function (s)
|
|
{
|
|
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
|
|
get: function (i)
|
|
{
|
|
return this.currentLines[i];
|
|
},
|
|
// returns the number of lines in the document
|
|
length: function ()
|
|
{
|
|
return this.currentLines.length;
|
|
},
|
|
|
|
getActiveAuthors: function ()
|
|
{
|
|
var self = this;
|
|
var authors = [];
|
|
var seenNums = {};
|
|
var alines = self.alines;
|
|
for (var i = 0; i < alines.length; i++)
|
|
{
|
|
Changeset.eachAttribNumber(alines[i], function (n)
|
|
{
|
|
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)
|
|
{
|
|
return function ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
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();
|
|
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function (name)
|
|
{
|
|
return authorData[name];
|
|
}));
|
|
}
|
|
|
|
function updateTimer()
|
|
{
|
|
var zpad = function (str, length)
|
|
{
|
|
str = str + "";
|
|
while (str.length < length)
|
|
str = '0' + str;
|
|
return str;
|
|
}
|
|
|
|
var date = new Date(padContents.currentTime);
|
|
var dateFormat = function ()
|
|
{
|
|
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(""));
|
|
}
|
|
|
|
|
|
|
|
$('#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)
|
|
|
|
}
|
|
|
|
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
|
|
var update = function (start, end)
|
|
{
|
|
// 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);
|
|
}
|
|
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function (name)
|
|
{
|
|
return authorData[name];
|
|
}));
|
|
}
|
|
|
|
global.changesetLoader = {
|
|
running: false,
|
|
resolved: [],
|
|
requestQueue1: [],
|
|
requestQueue2: [],
|
|
requestQueue3: [],
|
|
reqCallbacks: [],
|
|
queueUp: function (revision, width, callback)
|
|
{
|
|
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);
|
|
}
|
|
},
|
|
loadFromQueue: function ()
|
|
{
|
|
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;
|
|
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, "requestID": requestID});
|
|
|
|
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)
|
|
{
|
|
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);
|
|
},
|
|
handleResponse: function (data, start, granularity, callback)
|
|
{
|
|
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);
|
|
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function (name)
|
|
{
|
|
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
|
|
}));
|
|
}
|
|
|
|
/*function setUpSocket()
|
|
{
|
|
// 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");
|
|
}
|
|
}*/
|
|
|
|
function setChannelState(newChannelState, moreInfo)
|
|
{
|
|
if (newChannelState != channelState)
|
|
{
|
|
channelState = newChannelState;
|
|
// callbacks.onChannelStateChange(channelState, moreInfo);
|
|
}
|
|
}
|
|
|
|
function abandonConnection(reason)
|
|
{
|
|
if (socket)
|
|
{
|
|
socket.onclosed = function ()
|
|
{};
|
|
socket.onhiccup = function ()
|
|
{};
|
|
socket.disconnect();
|
|
}
|
|
socket = null;
|
|
setChannelState("DISCONNECTED", reason);
|
|
}
|
|
|
|
/*window['onloadFuncts'] = [];
|
|
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 ()
|
|
fireWhenAllScriptsAreLoaded.push(function ()
|
|
{
|
|
// 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;
|
|
var goToRevisionIfEnabled = function ()
|
|
{
|
|
if (goToRevisionIfEnabledCount > 0)
|
|
{
|
|
goToRevisionIfEnabledCount--;
|
|
}
|
|
else
|
|
{
|
|
goToRevision.apply(goToRevision, arguments);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BroadcastSlider.onSlider(goToRevisionIfEnabled);
|
|
|
|
(function ()
|
|
{
|
|
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];
|
|
if ((typeof data.colorId) == 'number')
|
|
{
|
|
var bgcolor = clientVars.colorPalette[data.colorId];
|
|
if (bgcolor && dynamicCSS)
|
|
{
|
|
dynamicCSS.selectorStyle('.' + linestylefilter.getAuthorClassName(author)).backgroundColor = bgcolor;
|
|
}
|
|
}
|
|
authorData[author] = data;
|
|
}
|
|
}
|
|
|
|
receiveAuthorData(clientVars.historicalAuthorData);
|
|
}
|