summaryrefslogtreecommitdiffstats
path: root/trunk/etherpad/src/static/js/broadcast.js
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/etherpad/src/static/js/broadcast.js')
-rw-r--r--trunk/etherpad/src/static/js/broadcast.js607
1 files changed, 0 insertions, 607 deletions
diff --git a/trunk/etherpad/src/static/js/broadcast.js b/trunk/etherpad/src/static/js/broadcast.js
deleted file mode 100644
index 9fa8141..0000000
--- a/trunk/etherpad/src/static/js/broadcast.js
+++ /dev/null
@@ -1,607 +0,0 @@
-/**
- * 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.
- */
-
-// just in case... (todo: this must be somewhere else in the client code.)
-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;
- };
-}
-
-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);
- }
- };
-}
-
-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 {
- // console.log.apply(console, arguments);
- } catch (e) {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];}));
-}
-
-var changesetLoader = {
- running: false,
- resolved: [],
- requestQueue1: [],
- requestQueue2: [],
- requestQueue3: [],
- 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;
- 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
- }
- );
- },
- 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() {
- // 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);