diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.js index d4bda1110..fe9038721 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.js @@ -1,5 +1,5 @@ /** - * This code is mostly from the old Etherpad. Please help us to comment this code. + * 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 */ @@ -27,13 +27,12 @@ var Changeset = require('./Changeset'); var linestylefilter = require('./linestylefilter').linestylefilter; var colorutils = require('./colorutils').colorutils; var _ = require('./underscore'); - +require("./jquery.class"); // 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) +function loadBroadcastJS(tsclient, fireWhenAllScriptsAreLoaded, BroadcastSlider) { - var changesetLoader = undefined; - + var changesetLoader; // 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) { @@ -83,14 +82,14 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro var appLevelDisconnectReason = null; var padContents = { - currentRevision: clientVars.collab_client_vars.rev, - currentTime: clientVars.collab_client_vars.time, - currentLines: Changeset.splitTextLines(clientVars.collab_client_vars.initialAttributedText.text), + currentRevision: tsclient.clientVars.collab_client_vars.rev, + currentTime: tsclient.clientVars.collab_client_vars.time, + currentLines: Changeset.splitTextLines(tsclient.clientVars.collab_client_vars.initialAttributedText.text), currentDivs: null, // to be filled in once the dom loads - apool: (new AttribPool()).fromJsonable(clientVars.collab_client_vars.apool), + apool: (new AttribPool()).fromJsonable(tsclient.clientVars.collab_client_vars.apool), alines: Changeset.splitAttributionLines( - clientVars.collab_client_vars.initialAttributedText.attribs, clientVars.collab_client_vars.initialAttributedText.text), + tsclient.clientVars.collab_client_vars.initialAttributedText.attribs, tsclient.clientVars.collab_client_vars.initialAttributedText.text), // generates a jquery element containing HTML for a line lineToElement: function(line, aline) @@ -250,7 +249,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro */ function applyChangeset(changeset, revision, preventSliderMovement, timeDelta) - { + { // disable the next 'gotorevision' call handled by a timeslider update if (!preventSliderMovement) { @@ -274,12 +273,12 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro debugLog('Time Delta: ', timeDelta) updateTimer(); - + var authors = _.map(padContents.getActiveAuthors(), function(name) { return authorData[name]; }); - + BroadcastSlider.setAuthors(authors); } @@ -292,7 +291,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro str = '0' + str; return str; } - + var date = new Date(padContents.currentTime); var dateFormat = function() { @@ -307,15 +306,15 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro "month": month, "year": year, "hours": hours, - "minutes": minutes, + "minutes": minutes, "seconds": seconds })); } - - - - - + + + + + $('#timer').html(dateFormat()); var revisionDate = html10n.get("timeslider.saved", { "day": date.getDate(), @@ -338,7 +337,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro $('#revision_date').html(revisionDate) } - + updateTimer(); function goToRevision(newRevision) @@ -401,7 +400,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro changesetLoader.queueUp(start, 1, update); } - + var authors = _.map(padContents.getActiveAuthors(), function(name){ return authorData[name]; }); @@ -453,7 +452,8 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro var start = request.rev; var requestID = Math.floor(Math.random() * 100000); - sendSocketMsg("CHANGESET_REQ", { + //sendSocketMsg("CHANGESET_REQ", { + tsclient.sendMessage("CHANGESET_REQ", { "start": start, "granularity": granularity, "requestID": requestID @@ -461,21 +461,21 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro self.reqCallbacks[requestID] = callback; }, - handleSocketResponse: function(message) + handle_CHANGESET_REQ: function(data) { 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]; + var start = data.start; + var granularity = data.granularity; + var callback = self.reqCallbacks[data.requestID]; + delete self.reqCallbacks[data.requestID]; - self.handleResponse(message.data, start, granularity, callback); + self.handleResponse(data, start, granularity, callback); setTimeout(self.loadFromQueue, 10); }, handleResponse: function(data, start, granularity, callback) { - debugLog("response: ", data); + debugLog("handleResponse: ", data); var pool = (new AttribPool()).fromJsonable(data.apool); for (var i = 0; i < data.forwardsChangesets.length; i++) { @@ -489,56 +489,44 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro } if (callback) callback(start - 1, start + data.forwardsChangesets.length * granularity - 1); }, - handleMessageFromServer: function (obj) + handle_COLLABROOM: function (obj) { - debugLog("handleMessage:", arguments); - - if (obj.type == "COLLABROOM") + debugLog("handle_COLLABROOM:", arguments); + if (obj.type == "NEW_CHANGES") { - obj = obj.data; + debugLog(obj); + var changeset = Changeset.moveOpsToNewPool( + obj.changeset, (new AttribPool()).fromJsonable(obj.apool), padContents.apool); - if (obj.type == "NEW_CHANGES") - { - debugLog(obj); - var changeset = Changeset.moveOpsToNewPool( - obj.changeset, (new AttribPool()).fromJsonable(obj.apool), padContents.apool); + var changesetBack = Changeset.inverse( + obj.changeset, padContents.currentLines, padContents.alines, padContents.apool); - var changesetBack = Changeset.inverse( - obj.changeset, padContents.currentLines, padContents.alines, padContents.apool); + changesetBack = Changeset.moveOpsToNewPool( + changesetBack, (new AttribPool()).fromJsonable(obj.apool), padContents.apool); - var changesetBack = Changeset.moveOpsToNewPool( - 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); - - var authors = _.map(padContents.getActiveAuthors(), function(name) { - return authorData[name]; - }); - - BroadcastSlider.setAuthors(authors); - } - else if (obj.type == "NEW_SAVEDREV") - { - var savedRev = obj.savedRev; - BroadcastSlider.addSavedRevision(savedRev.revNum, savedRev); - } + loadedNewChangeset(changeset, changesetBack, obj.newRev - 1, obj.timeDelta); } - else if(obj.type == "CHANGESET_REQ") + else if (obj.type == "NEW_AUTHORDATA") { - changesetLoader.handleSocketResponse(obj); + var authorMap = {}; + authorMap[obj.author] = obj.data; + receiveAuthorData(authorMap); + + var authors = _.map(padContents.getActiveAuthors(), function(name) { + return authorData[name]; + }); + + BroadcastSlider.setAuthors(authors); } - else + else if (obj.type == "NEW_SAVEDREV") { - debugLog("Unknown message type: " + obj.type); + var savedRev = obj.savedRev; + BroadcastSlider.addSavedRevision(savedRev.revNum, savedRev); } } }; + tsclient.on("CHANGESET_REQ", changesetLoader.handle_CHANGESET_REQ); + tsclient.on("COLLABROOM", changesetLoader.handle_COLLABROOM); // to start upon window load, just push a function onto this array //window['onloadFuncts'].push(setUpSocket); @@ -570,7 +558,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro goToRevision.apply(goToRevision, arguments); } } - + BroadcastSlider.onSlider(goToRevisionIfEnabled); var dynamicCSS = makeCSSManager('dynamicsyntax'); @@ -581,7 +569,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro for (var author in newAuthorData) { var data = newAuthorData[author]; - var bgcolor = typeof data.colorId == "number" ? clientVars.colorPalette[data.colorId] : data.colorId; + var bgcolor = typeof data.colorId == "number" ? tsclient.clientVars.colorPalette[data.colorId] : data.colorId; if (bgcolor && dynamicCSS) { var selector = dynamicCSS.selectorStyle('.' + linestylefilter.getAuthorClassName(author)); @@ -592,7 +580,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro } } - receiveAuthorData(clientVars.collab_client_vars.historicalAuthorData); + receiveAuthorData(tsclient.clientVars.collab_client_vars.historicalAuthorData); return changesetLoader; } diff --git a/src/static/js/broadcast_revisions.js b/src/static/js/broadcast_revisions.js index 1980bdf30..a9c5f4ed0 100644 --- a/src/static/js/broadcast_revisions.js +++ b/src/static/js/broadcast_revisions.js @@ -1,5 +1,5 @@ /** - * This code is mostly from the old Etherpad. Please help us to comment this code. + * 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 */ @@ -23,7 +23,7 @@ // of the document. These revisions are connected together by various // changesets, or deltas, between any two revisions. -function loadBroadcastRevisionsJS() +function loadBroadcastRevisionsJS(clientVars) { function Revision(revNum) { @@ -44,9 +44,9 @@ function loadBroadcastRevisionsJS() this.changesets.push(changesetWrapper); this.changesets.sort(function(a, b) { - return (b.deltaRev - a.deltaRev) + return (b.deltaRev - a.deltaRev); }); - } + }; revisionInfo = {}; revisionInfo.addChangeset = function(fromIndex, toIndex, changeset, backChangeset, timeDelta) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 8179b7b5f..530a3b3f5 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -1,5 +1,5 @@ /** - * This code is mostly from the old Etherpad. Please help us to comment this code. + * 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 */ @@ -24,8 +24,9 @@ // Timeslider controller would probably be more appropriate. var _ = require('./underscore'); var padmodals = require('./pad_modals').padmodals; +require("./jquery.class"); -function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) +function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) { var BroadcastSlider; @@ -37,6 +38,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) var slidercallbacks = []; var savedRevisions = []; var sliderPlaying = false; + clientVars = _clientVars; function disableSelection(element) { @@ -56,7 +58,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) slidercallbacks[i](newval); } } - + var updateSliderElements = function() { for (var i = 0; i < savedRevisions.length; i++) @@ -65,7 +67,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) savedRevisions[i].css('left', (position * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)) - 1); } $("#ui-slider-handle").css('left', sliderPos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); - } + } var addSavedRevision = function(position, info) { @@ -167,7 +169,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) var height = $('#timeslider-top').height(); $('#editorcontainerbox').css({marginTop: height}); }, 600); - + function setAuthors(authors) { var authorsList = $("#authorsList"); @@ -181,7 +183,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) if (author.name) { if (numNamed !== 0) authorsList.append(', '); - + $('') .text(author.name || "unnamed") .css('background-color', authorColor) @@ -199,17 +201,17 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) if (numAnonymous > 0) { var anonymousAuthorString = html10n.get("timeslider.unnamedauthors", { num: numAnonymous }); - + if (numNamed !== 0){ authorsList.append(' + ' + anonymousAuthorString); } else { authorsList.append(anonymousAuthorString); } - + if(colorsAnonymous.length > 0){ authorsList.append(' ('); _.each(colorsAnonymous, function(color, i){ - if( i > 0 ) authorsList.append(' '); + if( i > 0 ) authorsList.append(' '); $(' ') .css('background-color', color) .addClass('author author-anonymous') @@ -217,13 +219,13 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) }); authorsList.append(')'); } - + } if (authors.length == 0) { authorsList.append(html10n.get("timeslider.toolbar.authorsList")); } - + fixPadHeight(); } @@ -281,7 +283,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) { disableSelection($("#playpause_button")[0]); disableSelection($("#timeslider")[0]); - + $(document).keyup(function(e) { var code = -1; @@ -326,7 +328,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) else if (code == 32) playpause(); }); - + $(window).resize(function() { updateSliderElements(); @@ -457,7 +459,7 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) if (clientVars) { $("#timeslider").show(); - + var startPos = clientVars.collab_client_vars.rev; if(window.location.hash.length > 1) { @@ -468,15 +470,15 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) setTimeout(function() { setSliderPosition(hashRev); }, 1); } } - + setSliderLength(clientVars.collab_client_vars.rev); setSliderPosition(clientVars.collab_client_vars.rev); - + _.each(clientVars.savedRevisions, function(revision) { addSavedRevision(revision.revNum, revision); }) - + } }); })(); diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index fd22c69a3..109b6a0ac 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -23,6 +23,7 @@ // These jQuery things should create local references, but for now `require()` // assigns to the global `$` and augments it with plugins. require('./jquery'); +require('./jquery.class'); JSON = require('./json2'); var createCookie = require('./pad_utils').createCookie; @@ -32,9 +33,165 @@ var hooks = require('./pluginfw/hooks'); var token, padId, export_links; -function init() { +$.Class("SocketClient", + { //statics + }, + { //instance + init: function (baseurl) { + this.baseurl = baseurl; + // connect to the server + console.log("[socket_client] connecting to:", this.baseurl); + this.socket = io.connect(this.baseurl, {resource: "socket.io"}); + // setup the socket callbacks: + _this = this; + + this.socket.on("connect", function(callback) { + _this.onConnect(callback); + }); + this.socket.on("disconnect", function(callback) { + _this.onDisconnect(callback); + }); + this.socket.on("message", function(message, callback) { + _this.onMessage(message, callback); + }); + }, + + onConnect: function(callback) { + console.log("[socket_client] > onConnect"); + }, + + onDisconnect: function(callback) { + console.log("[socket_client] > onDisconnect"); + }, + + onMessage: function(message, callback) { + console.log("[socket_client] > onMessage: ", message); + }, + + sendMessage: function(message, callback) { + this.socket.json.send(message); + if (callback) + callback(); + }, + } +); + +SocketClient("AuthenticatedSocketClient", + { //statics + }, + { //instance + init: function (baseurl, padID) { + + //make sure we have a token + this.token = readCookie("token"); + if(this.token == null) + { + this.token = "t." + randomString(); + createCookie("token", this.token, 60); + } + this.padID = padID; + this.sessionID = decodeURIComponent(readCookie("sessionID")); + this.password = readCookie("password"); + + this._super(baseurl); + }, + + sendMessage: function (type, data, callback) { + this.sessionID = decodeURIComponent(readCookie("sessionID")); + this.password = readCookie("password"); + var msg = { "component" : "pad", // FIXME: Remove this stupidity! + "type": type, + "data": data, + "padId": this.padID, + "token": this.token, + "sessionID": this.sessionID, + "password": this.password, + "protocolVersion": 2}; + this._super(msg, callback); + }, + + onMessage: function (message, callback) { + console.log("[authorized_client] > onMessage:", message); + if (message.accessStatus) + { //access denied? + //TODO raise some kind of error? + console.log("ACCESS ERROR!"); + } + this.dispatchMessage(message.type, message.data, callback); + }, + + dispatchMessage: function(type, data, callback) { + console.log("[authorized_client] > dispatchMessage('%s', %s)", type, data); + // first call local handlers + if ("handle_" + type in this) + this["handle_" + type](data, callback); + // then call registered handlers + if (type in this.handlers) + for(var h in this.handlers[type]) + { //TODO: maybe chain the handlers into some kind of chain-of-responsibility? + var handler = this.handlers[type][h]; + handler.handler.call(this, data, handler.context, callback); + } + }, + + on: function(type, callback, context) { + if (!(type in this.handlers)) + this.handlers[type] = []; + this.handlers[type].push({handler: callback, context: context}); + return this; + }, + + registerHandler: this.on, + unregisterHandler: function(type, context) { + if(type in this.handlers) + { + for(var h in this.handlers[type]) + if (this.handlers[type][h].context == context) + { + delete this.handlers[type][h]; + break; + } + } + }, + } +); + +AuthenticatedSocketClient("TimesliderClient", + { //statics + }, + { //instance + init: function (baseurl, padID) { + this._super(baseurl, padID); + this.handlers = {}; + }, + + onConnect: function (callback) { + this.sendMessage("CLIENT_READY", {}, function() { console.log("CLIENT_READY sent");}); + }, + + // ------------------------------------------ + // Handling events + handle_CLIENT_VARS: function(data, callback) { + console.log("[timeslider_client] handle_CLIENT_VARS: ", data); + this.clientVars = data; + }, + + handle_COLLABROOM: function(data, callback) { + console.log("[timeslider_client] handle_COLLABROOM: ", data); + }, + + handle_CHANGESET_REQ: function(data, callback) { + console.log("[timeslider_client] handle_COLLABROOM: ", data); + }, + } +); + +function init(baseURL) { $(document).ready(function () { + console.log("<<<<<<<<<<<<<<<<<<<<<<<<"); + console.log("HELLO"); + console.log("<<<<<<<<<<<<<<<<<<<<<<<<"); // start the custom js if (typeof customStart == "function") customStart(); @@ -47,7 +204,7 @@ function init() { //ensure we have a token token = readCookie("token"); - if(token == null) + if(token === null) { token = "t." + randomString(); createCookie("token", token, 60); @@ -55,12 +212,42 @@ function init() { var loc = document.location; //get the correct port - var port = loc.port == "" ? (loc.protocol == "https:" ? 443 : 80) : loc.port; + var port = loc.port === "" ? (loc.protocol == "https:" ? 443 : 80) : loc.port; //create the url var url = loc.protocol + "//" + loc.hostname + ":" + port + "/"; //find out in which subfolder we are - var resource = exports.baseURL.substring(1) + 'socket.io'; - + var resource = baseURL.substring(1) + 'socket.io'; + + console.log(url, baseURL, resource, padId); + var timesliderclient = new TimesliderClient(url, padId) + .on("CLIENT_VARS", function(data, context, callback) { + //load all script that doesn't work without the clientVars + BroadcastSlider = require('./broadcast_slider').loadBroadcastSliderJS(this.clientVars,fireWhenAllScriptsAreLoaded); + require('./broadcast_revisions').loadBroadcastRevisionsJS(this.clientVars); + changesetLoader = require('./broadcast').loadBroadcastJS(this, fireWhenAllScriptsAreLoaded, BroadcastSlider); + + //initialize export ui + require('./pad_impexp').padimpexp.init(); + + //change export urls when the slider moves + BroadcastSlider.onSlider(function(revno) + { + // export_links is a jQuery Array, so .each is allowed. + export_links.each(function() + { + this.setAttribute('href', this.href.replace( /(.+?)\/\w+\/(\d+\/)?export/ , '$1/' + padId + '/' + revno + '/export')); + }); + }); + + //fire all start functions of these scripts, formerly fired with window.load + for(var i=0;i < fireWhenAllScriptsAreLoaded.length;i++) + { + fireWhenAllScriptsAreLoaded[i](); + } + $("#ui-slider-handle").css('left', $("#ui-slider-bar").width() - 2); + }); + +/* //build up the socket io connection socket = io.connect(url, {resource: resource}); @@ -78,7 +265,7 @@ function init() { //route the incoming messages socket.on('message', function(message) { - if(window.console) console.log(message); + //if(window.console) console.log(message); if(message.type == "CLIENT_VARS") { @@ -86,14 +273,14 @@ function init() { } else if(message.accessStatus) { - $("body").html("