From e55ef0fe95a588a17987716579cd703b2e123140 Mon Sep 17 00:00:00 2001 From: s1341 Date: Sat, 23 Nov 2013 19:39:52 +0200 Subject: [PATCH 01/50] Introduce timeslider object This commit introduces a new TimesliderClient object which will eventually handle all communication with the server in a sane fashion. At the moment, minimal work has been done to wire the existing code into this object. The current 'on("MESSAGE_TYPE")' model will change so that the timeslider object is actually the one managing all the information, instead of delegating it all out to callbacks. This current version still works just as the stock version did. --- src/static/js/broadcast.js | 130 +++++++-------- src/static/js/broadcast_revisions.js | 8 +- src/static/js/broadcast_slider.js | 36 ++-- src/static/js/timeslider.js | 241 ++++++++++++++++++++++----- src/templates/timeslider.html | 9 +- 5 files changed, 284 insertions(+), 140 deletions(-) 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("

You have no permission to access this pad

") + $("body").html("

You have no permission to access this pad

"); } else { changesetLoader.handleMessageFromServer(message); } }); - +*/ //get all the export links - export_links = $('#export > .exportlink') + export_links = $('#export > .exportlink'); if(document.referrer.length > 0 && document.referrer.substring(document.referrer.lastIndexOf("/")-1,document.referrer.lastIndexOf("/")) === "p") { $("#returnbutton").attr("href", document.referrer); @@ -106,7 +293,7 @@ function init() { window.location.reload(); }); - exports.socket = socket; // make the socket available + //exports.socket = socket; // make the socket available exports.BroadcastSlider = BroadcastSlider; // Make the slider available hooks.aCallAll("postTimesliderInit"); @@ -128,42 +315,10 @@ function sendSocketMsg(type, data) "password": password, "protocolVersion": 2}; + socket.json.send(msg); } var fireWhenAllScriptsAreLoaded = []; - -var changesetLoader; -function handleClientVars(message) -{ - //save the client Vars - clientVars = message.data; - - //load all script that doesn't work without the clientVars - BroadcastSlider = require('./broadcast_slider').loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded); - require('./broadcast_revisions').loadBroadcastRevisionsJS(); - changesetLoader = require('./broadcast').loadBroadcastJS(socket, sendSocketMsg, 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); -} - -exports.baseURL = ''; exports.init = init; diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index 2e00b8c2a..c40563ec8 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -226,17 +226,16 @@ } var plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); - var socket = require('ep_etherpad-lite/static/js/timeslider').socket; - BroadcastSlider = require('ep_etherpad-lite/static/js/timeslider').BroadcastSlider; + var timeslider = require('ep_etherpad-lite/static/js/timeslider') + var socket = timeslider.socket; + BroadcastSlider = timeslider.BroadcastSlider; plugins.baseURL = baseURL; plugins.update(function () { var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); hooks.plugins = plugins; - var timeslider = require('ep_etherpad-lite/static/js/timeslider') - timeslider.baseURL = baseURL; - timeslider.init(); + timeslider.init(baseURL); /* TODO: These globals shouldn't exist. */ padeditbar = require('ep_etherpad-lite/static/js/pad_editbar').padeditbar; From 95310dafd4952f00db938c0181c2a75e2b33ad5f Mon Sep 17 00:00:00 2001 From: s1341 Date: Sat, 23 Nov 2013 19:53:10 +0200 Subject: [PATCH 02/50] handlers dict needs to be in AuthorizedClient; cleanup --- src/static/js/timeslider.js | 55 ++----------------------------------- 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index 109b6a0ac..95be76d7a 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -84,7 +84,7 @@ SocketClient("AuthenticatedSocketClient", //make sure we have a token this.token = readCookie("token"); - if(this.token == null) + if(this.token === null) { this.token = "t." + randomString(); createCookie("token", this.token, 60); @@ -92,6 +92,7 @@ SocketClient("AuthenticatedSocketClient", this.padID = padID; this.sessionID = decodeURIComponent(readCookie("sessionID")); this.password = readCookie("password"); + this.handlers = {}; this._super(baseurl); }, @@ -162,7 +163,6 @@ AuthenticatedSocketClient("TimesliderClient", { //instance init: function (baseurl, padID) { this._super(baseurl, padID); - this.handlers = {}; }, onConnect: function (callback) { @@ -247,38 +247,6 @@ function init(baseURL) { $("#ui-slider-handle").css('left', $("#ui-slider-bar").width() - 2); }); -/* - //build up the socket io connection - socket = io.connect(url, {resource: resource}); - - //send the ready message once we're connected - socket.on('connect', function() - { - sendSocketMsg("CLIENT_READY", {}); - }); - - socket.on('disconnect', function() - { - BroadcastSlider.showReconnectUI(); - }); - - //route the incoming messages - socket.on('message', function(message) - { - //if(window.console) console.log(message); - - if(message.type == "CLIENT_VARS") - { - handleClientVars(message); - } - else if(message.accessStatus) - { - $("body").html("

You have no permission to access this pad

"); - } else { - changesetLoader.handleMessageFromServer(message); - } - }); -*/ //get all the export links export_links = $('#export > .exportlink'); @@ -300,25 +268,6 @@ function init(baseURL) { }); } -//sends a message over the socket -function sendSocketMsg(type, data) -{ - var sessionID = decodeURIComponent(readCookie("sessionID")); - var password = readCookie("password"); - - var msg = { "component" : "pad", // FIXME: Remove this stupidity! - "type": type, - "data": data, - "padId": padId, - "token": token, - "sessionID": sessionID, - "password": password, - "protocolVersion": 2}; - - - socket.json.send(msg); -} - var fireWhenAllScriptsAreLoaded = []; exports.init = init; From e6a426de4a00ffb40630d49ab92859b7cb1ea3d7 Mon Sep 17 00:00:00 2001 From: s1341 Date: Sun, 24 Nov 2013 20:14:21 +0200 Subject: [PATCH 03/50] Initial UI classes, with working partial rendering The slider handle and stars render correctly. Not yet wired up to real events. --- src/static/css/timeslider.css | 47 ++++---- src/static/js/broadcast_slider.js | 176 +++++++++++++++++++++++++++--- src/static/js/timeslider.js | 4 +- src/templates/timeslider.html | 15 +-- 4 files changed, 198 insertions(+), 44 deletions(-) diff --git a/src/static/css/timeslider.css b/src/static/css/timeslider.css index b3c201847..712a5ff6b 100644 --- a/src/static/css/timeslider.css +++ b/src/static/css/timeslider.css @@ -1,3 +1,29 @@ +/* + * slider handles (SliderHandle) + */ +#timeslider .slider-handle { + position: absolute; +} + +#timeslider .star { + background-image: url(../../static/img/star.png); + cursor: pointer; + height: 16px; + top: 40px; + width: 15px; +} +#timeslider .handle { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + background-image: url(../../static/img/crushed_current_location.png); + cursor: pointer; + height: 61px; + left: 0; + top: 0; + width: 13px; +} + #editorcontainerbox { overflow: auto; top: 40px; @@ -46,18 +72,7 @@ top: 1px; width: 100%; } -#ui-slider-handle { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - background-image: url(../../static/img/crushed_current_location.png); - cursor: pointer; - height: 61px; - left: 0; - position: absolute; - top: 0; - width: 13px; -} + #ui-slider-bar { -webkit-user-select: none; -moz-user-select: none; @@ -121,14 +136,6 @@ top: 20px; width: 30px; } -#timeslider .star { - background-image: url(../../static/img/star.png); - cursor: pointer; - height: 16px; - position: absolute; - top: 40px; - width: 15px; -} #timeslider #timer { color: #fff; font-family: Arial, sans-serif; diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 530a3b3f5..c73b786a0 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -26,19 +26,163 @@ var _ = require('./underscore'); var padmodals = require('./pad_modals').padmodals; require("./jquery.class"); -function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) +/** + * A class for anything which can be hung off of the slider bar. + * I.e. this is for the handle, or saved-revisions (stars). + * TODO: + * - handle mousedowns/clicks + * - handle drags + */ +$.Class("SliderHandleUI", + {//statics + }, + {//instance + /** + * Construct the SliderHandle. + * @param {SliderUI} slider The slider from which this handle will be hung. + * @param {Number} position The initial position for this handle. + */ + init: function (slider, position, type) { + console.log("New SliderHandle(%d, %s)", position, type); + this.slider = slider; + this.position = position; + this.events = {}; + //create the element: + this.element = $("
"); + this.element.addClass(type); + //handle events + this.element.mouseup(this._dispatchEvent); + this.element.mousedown(this._dispatchEvent); + }, + on: function (eventname, callback, context) { + if (!(eventname in this.events)) + this.events[eventname] = []; + this.events[eventname].push({handler:callback, context: context}); + }, + _dispatchEvent: function (evt) { + if (eventname in this.events) + for (var i in this.events[eventname]) + { + e = this.events[eventname][i]; + e.handler(evt, e.context); + } + }, + render: function () { + this.element.css('left', this.position * this.slider.getTickSize()); + }, + setPosition: function (position) { + this.position = position; + this.render(); + } + } +); + +SliderHandleUI("SliderSavedRevisionUI", + {//statics + }, + {//instance + init: function(slider, revision) { + this._super(slider, revision.revNum, 'star'); + this.revision = revision; + }, + } +); + +$.Class("SliderUI", + {//statics + }, + {//instance + init: function (timeslider, root_element) { + this.timeslider = timeslider; + console.log("New SliderUI, head_revision = %d", this.timeslider.head_revision); + // parse the various elements we need: + this.elements = {}; + this.loadElements(root_element); + + this.handles=[]; + this.main_handle = this.attachHandle(new SliderHandleUI(this, this.timeslider.head_revision, 'handle')); + + this.loadSavedRevisions(); + + this.render(); + + //events: + _this = this; + $(window).resize(function() {_this.render.call(_this)}); + + this.elements['slider-bar'].mousedown(function(evt) { + var revision = Math.floor((evt.clientX-$(this).offset().left) / _this.getTickSize()); + _this.goToRevision(revision); + }); + }, + loadElements: function (root_element) { + this.elements['root'] = root_element; + //this.elements['slider-handle'] = root_element.first("#ui-slider-handle"); + this.elements['slider-bar'] = root_element.find("#ui-slider-bar"); + this.elements['slider'] = root_element.find("#timeslider-slider"); + this.elements['button-left'] = root_element.find("#leftstep"); + this.elements['button-right'] = root_element.find("#rightstep"); + this.elements['button-play'] = root_element.find("#playpause_button"); + this.elements['timestamp'] = root_element.find("#timer"); + this.elements['revision-label'] = root_element.find("#revision_label"); + this.elements['revision-date'] = root_element.find("#revision_date"); + this.elements['authors'] = root_element.first("#authorsList"); + }, + loadSavedRevisions: function () { + for (var r in this.timeslider.savedRevisions) { + var rev = this.timeslider.savedRevisions[r]; + this.attachHandle(new SliderSavedRevisionUI(this, rev)); + }; + }, + render: function () { + for(var h in this.handles) + this.handles[h].render(); + }, + attachHandle: function (handle) { + this.handles.push(handle); + this.elements['slider'].append(handle.element); + return handle; + }, + getTickSize: function () { + return (this.elements['slider-bar'].width()) / (this.timeslider.head_revision * 1.0); + }, + goToRevision: function (revNum) { + //TODO: this should actually do an async jump to revision (with all the server fetching + //and changeset rendering that that implies), and perform the setPosition in a callback. + if (revNum <= 0 || revNum > this.timeslider.head_revision) + revNum = this.timeslider.latest_revision; + console.log("GO TO REVISION", revNum) + this.main_handle.setPosition(revNum); + }, + + + } +); + +function loadBroadcastSliderJS(tsclient, fireWhenAllScriptsAreLoaded) { var BroadcastSlider; (function() { // wrap this code in its own namespace + + tsui = new SliderUI(tsclient, $("#timeslider-top")); + + // if there was a revision specified in the 'location.hash', jump to it. + if (window.location.hash.length > 1) { + var rev = Number(window.location.hash.substr(1)); + if(!isNaN(rev)) + tsui.goToRevision(rev); + } + + var sliderLength = 1000; var sliderPos = 0; var sliderActive = false; var slidercallbacks = []; var savedRevisions = []; var sliderPlaying = false; - clientVars = _clientVars; + clientVars = tsclient.clientVars; function disableSelection(element) { @@ -64,9 +208,10 @@ function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) for (var i = 0; i < savedRevisions.length; i++) { var position = parseInt(savedRevisions[i].attr('pos')); - savedRevisions[i].css('left', (position * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)) - 1); + //tsui._setElementPosition(savedRevisions[i], position); } - $("#ui-slider-handle").css('left', sliderPos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); + //tsui._setElementPosition(tsui.elements['slider-handle'], sliderPos); + } var addSavedRevision = function(position, info) @@ -76,7 +221,7 @@ function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) newSavedRevision.attr('pos', position); newSavedRevision.css('position', 'absolute'); - newSavedRevision.css('left', (position * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)) - 1); + //tsui._setElementPosition(newSavedRevision, position); $("#timeslider-slider").append(newSavedRevision); newSavedRevision.mouseup(function(evt) { @@ -329,18 +474,20 @@ function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) }); - $(window).resize(function() + /*$(window).resize(function() { + tsui.render(); updateSliderElements(); }); + */ - $("#ui-slider-bar").mousedown(function(evt) + /*$("#ui-slider-bar").mousedown(function(evt) { setSliderPosition(Math.floor((evt.clientX - $("#ui-slider-bar").offset().left) * sliderLength / 742)); $("#ui-slider-handle").css('left', (evt.clientX - $("#ui-slider-bar").offset().left)); $("#ui-slider-handle").trigger(evt); }); - + */ // Slider dragging $("#ui-slider-handle").mousedown(function(evt) { @@ -461,7 +608,7 @@ function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) $("#timeslider").show(); var startPos = clientVars.collab_client_vars.rev; - if(window.location.hash.length > 1) + /*if(window.location.hash.length > 1) { var hashRev = Number(window.location.hash.substr(1)); if(!isNaN(hashRev)) @@ -470,14 +617,15 @@ function loadBroadcastSliderJS(_clientVars, fireWhenAllScriptsAreLoaded) setTimeout(function() { setSliderPosition(hashRev); }, 1); } } - - setSliderLength(clientVars.collab_client_vars.rev); - setSliderPosition(clientVars.collab_client_vars.rev); - - _.each(clientVars.savedRevisions, function(revision) + */ + //setSliderLength(clientVars.collab_client_vars.rev); + //setSliderPosition(clientVars.collab_client_vars.rev); + /* + _.eacheach(clientVars.savedRevisions, function(revision) { addSavedRevision(revision.revNum, revision); }) + */ } }); diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index 95be76d7a..fb6353043 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -174,6 +174,8 @@ AuthenticatedSocketClient("TimesliderClient", handle_CLIENT_VARS: function(data, callback) { console.log("[timeslider_client] handle_CLIENT_VARS: ", data); this.clientVars = data; + this.current_revision = this.head_revision = this.clientVars.collab_client_vars.rev; + this.savedRevisions = this.clientVars.savedRevisions; }, handle_COLLABROOM: function(data, callback) { @@ -222,7 +224,7 @@ function init(baseURL) { 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); + BroadcastSlider = require('./broadcast_slider').loadBroadcastSliderJS(this,fireWhenAllScriptsAreLoaded); require('./broadcast_revisions').loadBroadcastRevisionsJS(this.clientVars); changesetLoader = require('./broadcast').loadBroadcastJS(this, fireWhenAllScriptsAreLoaded, BroadcastSlider); diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index c40563ec8..4b0423df9 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -52,7 +52,7 @@
<% e.begin_block("timesliderTop"); %>
- -
@@ -90,7 +89,7 @@ <% e.end_block(); %>
-
+

@@ -207,14 +206,13 @@