Merge branch 'johnyma22'
Conflicts: node/utils/Minify.js src/static/js/pad.js src/static/js/pad_docbar.js src/static/js/pad_editbar.js src/static/js/pad_savedrevs.js static/css/timeslider.css static/pad.html
This commit is contained in:
commit
33c53e61c2
|
@ -15,6 +15,11 @@ var padManager = require("./PadManager");
|
|||
var padMessageHandler = require("../handler/PadMessageHandler");
|
||||
var readOnlyManager = require("./ReadOnlyManager");
|
||||
var crypto = require("crypto");
|
||||
var randomString = require("../utils/randomstring");
|
||||
|
||||
//serialization/deserialization attributes
|
||||
var attributeBlackList = ["id"];
|
||||
var jsonableList = ["pool"];
|
||||
|
||||
/**
|
||||
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
|
||||
|
@ -34,7 +39,7 @@ var Pad = function Pad(id) {
|
|||
this.publicStatus = false;
|
||||
this.passwordHash = null;
|
||||
this.id = id;
|
||||
|
||||
this.savedRevisions = [];
|
||||
};
|
||||
|
||||
exports.Pad = Pad;
|
||||
|
@ -75,15 +80,28 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
|
|||
newRevData.meta.atext = this.atext;
|
||||
}
|
||||
|
||||
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
||||
db.set("pad:"+this.id, {atext: this.atext,
|
||||
pool: this.pool.toJsonable(),
|
||||
head: this.head,
|
||||
chatHead: this.chatHead,
|
||||
publicStatus: this.publicStatus,
|
||||
passwordHash: this.passwordHash});
|
||||
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
||||
this.saveToDatabase();
|
||||
};
|
||||
|
||||
//save all attributes to the database
|
||||
Pad.prototype.saveToDatabase = function saveToDatabase(){
|
||||
var dbObject = {};
|
||||
|
||||
for(var attr in this){
|
||||
if(typeof this[attr] === "function") continue;
|
||||
if(attributeBlackList.indexOf(attr) !== -1) continue;
|
||||
|
||||
dbObject[attr] = this[attr];
|
||||
|
||||
if(jsonableList.indexOf(attr) !== -1){
|
||||
dbObject[attr] = dbObject[attr].toJsonable();
|
||||
}
|
||||
}
|
||||
|
||||
db.set("pad:"+this.id, dbObject);
|
||||
}
|
||||
|
||||
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
|
||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
||||
};
|
||||
|
@ -200,11 +218,10 @@ Pad.prototype.setText = function setText(newText) {
|
|||
};
|
||||
|
||||
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||
this.chatHead++;
|
||||
//save the chat entry in the database
|
||||
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
||||
//save the new chat head
|
||||
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
||||
this.chatHead++;
|
||||
//save the chat entry in the database
|
||||
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
||||
this.saveToDatabase();
|
||||
};
|
||||
|
||||
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
||||
|
@ -324,27 +341,14 @@ Pad.prototype.init = function init(text, callback) {
|
|||
//if this pad exists, load it
|
||||
if(value != null)
|
||||
{
|
||||
_this.head = value.head;
|
||||
_this.atext = value.atext;
|
||||
_this.pool = _this.pool.fromJsonable(value.pool);
|
||||
|
||||
//ensure we have a local chatHead variable
|
||||
if(value.chatHead != null)
|
||||
_this.chatHead = value.chatHead;
|
||||
else
|
||||
_this.chatHead = -1;
|
||||
|
||||
//ensure we have a local publicStatus variable
|
||||
if(value.publicStatus != null)
|
||||
_this.publicStatus = value.publicStatus;
|
||||
else
|
||||
_this.publicStatus = false;
|
||||
|
||||
//ensure we have a local passwordHash variable
|
||||
if(value.passwordHash != null)
|
||||
_this.passwordHash = value.passwordHash;
|
||||
else
|
||||
_this.passwordHash = null;
|
||||
//copy all attr. To a transfrom via fromJsonable if necassary
|
||||
for(var attr in value){
|
||||
if(jsonableList.indexOf(attr) !== -1){
|
||||
_this[attr] = _this[attr].fromJsonable(value[attr]);
|
||||
} else {
|
||||
_this[attr] = value[attr];
|
||||
}
|
||||
}
|
||||
}
|
||||
//this pad doesn't exist, so create it
|
||||
else
|
||||
|
@ -452,12 +456,12 @@ Pad.prototype.remove = function remove(callback) {
|
|||
//set in db
|
||||
Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
|
||||
this.publicStatus = publicStatus;
|
||||
db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
|
||||
this.saveToDatabase();
|
||||
};
|
||||
|
||||
Pad.prototype.setPassword = function setPassword(password) {
|
||||
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
||||
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
||||
this.saveToDatabase();
|
||||
};
|
||||
|
||||
Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
|
||||
|
@ -468,6 +472,31 @@ Pad.prototype.isPasswordProtected = function isPasswordProtected() {
|
|||
return this.passwordHash != null;
|
||||
};
|
||||
|
||||
Pad.prototype.addSavedRevision = function addSavedRevision(revNum, savedById, label) {
|
||||
//if this revision is already saved, return silently
|
||||
for(var i in this.savedRevisions){
|
||||
if(this.savedRevisions.revNum === revNum){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//build the saved revision object
|
||||
var savedRevision = {};
|
||||
savedRevision.revNum = revNum;
|
||||
savedRevision.savedById = savedById;
|
||||
savedRevision.label = label || "Revision " + revNum;
|
||||
savedRevision.timestamp = new Date().getTime();
|
||||
savedRevision.id = randomString(10);
|
||||
|
||||
//save this new saved revision
|
||||
this.savedRevisions.push(savedRevision);
|
||||
this.saveToDatabase();
|
||||
};
|
||||
|
||||
Pad.prototype.getSavedRevisions = function getSavedRevisions() {
|
||||
return this.savedRevisions;
|
||||
};
|
||||
|
||||
/* Crypto helper methods */
|
||||
|
||||
function hash(password, salt)
|
||||
|
|
|
@ -190,6 +190,11 @@ exports.handleMessage = function(client, message)
|
|||
{
|
||||
handleChatMessage(client, message);
|
||||
}
|
||||
else if(message.type == "COLLABROOM" &&
|
||||
message.data.type == "SAVE_REVISION")
|
||||
{
|
||||
handleSaveRevisionMessage(client, message);
|
||||
}
|
||||
else if(message.type == "COLLABROOM" &&
|
||||
message.data.type == "CLIENT_MESSAGE" &&
|
||||
message.data.payload.type == "suggestUserName")
|
||||
|
@ -203,6 +208,23 @@ exports.handleMessage = function(client, message)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a save revision message
|
||||
* @param client the client that send this message
|
||||
* @param message the message from the client
|
||||
*/
|
||||
function handleSaveRevisionMessage(client, message){
|
||||
var padId = session2pad[client.id];
|
||||
var userId = sessioninfos[client.id].author;
|
||||
|
||||
padManager.getPad(padId, function(err, pad)
|
||||
{
|
||||
if(ERR(err)) return;
|
||||
|
||||
pad.addSavedRevision(pad.head, userId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a Chat Message
|
||||
* @param client the client that send this message
|
||||
|
|
|
@ -166,6 +166,7 @@ function createTimesliderClientVars (padId, callback)
|
|||
hooks: [],
|
||||
initialStyledContents: {}
|
||||
};
|
||||
|
||||
var pad;
|
||||
var initialChangesets = [];
|
||||
|
||||
|
@ -180,6 +181,12 @@ function createTimesliderClientVars (padId, callback)
|
|||
callback();
|
||||
});
|
||||
},
|
||||
//get all saved revisions and add them
|
||||
function(callback)
|
||||
{
|
||||
clientVars.savedRevisions = pad.getSavedRevisions();
|
||||
callback();
|
||||
},
|
||||
//get all authors and add them to
|
||||
function(callback)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Generates a random String with the given length. Is needed to generate the Author, Group, readonly, session Ids
|
||||
*/
|
||||
var randomString = function randomString(len)
|
||||
{
|
||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
var randomstring = '';
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars.substring(rnum, rnum + 1);
|
||||
}
|
||||
return randomstring;
|
||||
};
|
||||
|
||||
module.exports = randomString;
|
|
@ -1036,6 +1036,9 @@ margin-top: 1px;
|
|||
background-position: 0px -183px;
|
||||
display: inline-block;
|
||||
}
|
||||
.buttonicon-savedRevision {
|
||||
background-position: 0px -493px
|
||||
}
|
||||
|
||||
#usericon
|
||||
{
|
||||
|
@ -1269,4 +1272,4 @@ input[type=checkbox] {
|
|||
#online_count {
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,8 +71,11 @@
|
|||
#padmain {top:30px;}
|
||||
#editbarright {float:right;}
|
||||
#returnbutton {color:#222; font-size:16px; line-height:29px; margin-top:0; padding-right:6px;}
|
||||
#importexport {top:118px;}
|
||||
#importexport .popup {width:185px;}
|
||||
#importexport{
|
||||
top:118px;
|
||||
width:185px;
|
||||
}
|
||||
|
||||
/* lists */
|
||||
.list-bullet2, .list-indent2, .list-number2 {margin-left:3em;}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 8.1 KiB |
|
@ -42,7 +42,7 @@ var padeditbar = require('./pad_editbar').padeditbar;
|
|||
var padeditor = require('./pad_editor').padeditor;
|
||||
var padimpexp = require('./pad_impexp').padimpexp;
|
||||
var padmodals = require('./pad_modals').padmodals;
|
||||
var padsavedrevs = require('./pad_savedrevs').padsavedrevs;
|
||||
var padsavedrevs = require('./pad_savedrevs');
|
||||
var paduserlist = require('./pad_userlist').paduserlist;
|
||||
var padutils = require('./pad_utils').padutils;
|
||||
|
||||
|
@ -52,6 +52,48 @@ var randomString = require('./pad_utils').randomString;
|
|||
|
||||
var hooks = require('./pluginfw/hooks');
|
||||
|
||||
function createCookie(name, value, days, path)
|
||||
{
|
||||
if (days)
|
||||
{
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
var expires = "; expires=" + date.toGMTString();
|
||||
}
|
||||
else var expires = "";
|
||||
|
||||
if(!path)
|
||||
path = "/";
|
||||
|
||||
document.cookie = name + "=" + value + expires + "; path=" + path;
|
||||
}
|
||||
|
||||
function readCookie(name)
|
||||
{
|
||||
var nameEQ = name + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++)
|
||||
{
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function randomString()
|
||||
{
|
||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
var string_length = 20;
|
||||
var randomstring = '';
|
||||
for (var i = 0; i < string_length; i++)
|
||||
{
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars.substring(rnum, rnum + 1);
|
||||
}
|
||||
return "t." + randomstring;
|
||||
}
|
||||
|
||||
function getParams()
|
||||
{
|
||||
var params = getUrlVars()
|
||||
|
@ -459,7 +501,7 @@ var pad = {
|
|||
guestPolicy: pad.padOptions.guestPolicy
|
||||
}, this);
|
||||
padimpexp.init(this);
|
||||
padsavedrevs.init(clientVars.initialRevisionList, this);
|
||||
padsavedrevs.init(this);
|
||||
|
||||
padeditor.init(postAceInit, pad.padOptions.view || {}, this);
|
||||
|
||||
|
|
|
@ -449,7 +449,7 @@ var paddocbar = (function()
|
|||
handleResizePage: function()
|
||||
{
|
||||
// Side-step circular reference. This should be injected.
|
||||
var padsavedrevs = require('./pad_savedrevs').padsavedrevs;
|
||||
var padsavedrevs = require('./pad_savedrevs');
|
||||
padsavedrevs.handleResizePage();
|
||||
},
|
||||
hideLaterIfNoOtherInteraction: function()
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
var padutils = require('./pad_utils').padutils;
|
||||
var padeditor = require('./pad_editor').padeditor;
|
||||
var padsavedrevs = require('./pad_savedrevs').padsavedrevs;
|
||||
var padsavedrevs = require('./pad_savedrevs');
|
||||
|
||||
function indexOf(array, value) {
|
||||
for (var i = 0, ii = array.length; i < ii; i++) {
|
||||
|
@ -131,7 +131,7 @@ var padeditbar = (function()
|
|||
{
|
||||
self.toogleDropDown("importexport");
|
||||
}
|
||||
else if (cmd == 'save')
|
||||
else if (cmd == 'savedRevision')
|
||||
{
|
||||
padsavedrevs.saveNow();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
* Copyright 2012 Peter 'Pita' Martischka
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -20,507 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var padutils = require('./pad_utils').padutils;
|
||||
var paddocbar = require('./pad_docbar').paddocbar;
|
||||
var pad;
|
||||
|
||||
var padsavedrevs = (function()
|
||||
{
|
||||
exports.saveNow = function(){
|
||||
pad.collabClient.sendMessage({"type": "SAVE_REVISION"});
|
||||
alert("This revision is now marked as a saved revision");
|
||||
}
|
||||
|
||||
function reversedCopy(L)
|
||||
{
|
||||
var L2 = L.slice();
|
||||
L2.reverse();
|
||||
return L2;
|
||||
}
|
||||
|
||||
function makeRevisionBox(revisionInfo, rnum)
|
||||
{
|
||||
var box = $('<div class="srouterbox">' + '<div class="srinnerbox">' + '<a href="javascript:void(0)" class="srname"><!-- --></a>' + '<div class="sractions"><a class="srview" href="javascript:void(0)" target="_blank">view</a> | <a class="srrestore" href="javascript:void(0)">restore</a></div>' + '<div class="srtime"><!-- --></div>' + '<div class="srauthor"><!-- --></div>' + '<img class="srtwirly" src="static/img/misc/status-ball.gif">' + '</div></div>');
|
||||
setBoxLabel(box, revisionInfo.label);
|
||||
setBoxTimestamp(box, revisionInfo.timestamp);
|
||||
box.find(".srauthor").html("by " + padutils.escapeHtml(revisionInfo.savedBy));
|
||||
var viewLink = '/ep/pad/view/' + pad.getPadId() + '/' + revisionInfo.id;
|
||||
box.find(".srview").attr('href', viewLink);
|
||||
var restoreLink = 'javascript:void(require('+JSON.stringify(module.id)+').padsavedrevs.restoreRevision(' + JSON.stringify(rnum) + ');';
|
||||
box.find(".srrestore").attr('href', restoreLink);
|
||||
box.find(".srname").click(function(evt)
|
||||
{
|
||||
editRevisionLabel(rnum, box);
|
||||
});
|
||||
return box;
|
||||
}
|
||||
|
||||
function setBoxLabel(box, label)
|
||||
{
|
||||
box.find(".srname").html(padutils.escapeHtml(label)).attr('title', label);
|
||||
}
|
||||
|
||||
function setBoxTimestamp(box, timestamp)
|
||||
{
|
||||
box.find(".srtime").html(padutils.escapeHtml(
|
||||
padutils.timediff(new Date(timestamp))));
|
||||
}
|
||||
|
||||
function getNthBox(n)
|
||||
{
|
||||
return $("#savedrevisions .srouterbox").eq(n);
|
||||
}
|
||||
|
||||
function editRevisionLabel(rnum, box)
|
||||
{
|
||||
var input = $('<input type="text" class="srnameedit"/>');
|
||||
box.find(".srnameedit").remove(); // just in case
|
||||
var label = box.find(".srname");
|
||||
input.width(label.width());
|
||||
input.height(label.height());
|
||||
input.css('top', label.position().top);
|
||||
input.css('left', label.position().left);
|
||||
label.after(input);
|
||||
label.css('opacity', 0);
|
||||
|
||||
function endEdit()
|
||||
{
|
||||
input.remove();
|
||||
label.css('opacity', 1);
|
||||
}
|
||||
var rev = currentRevisionList[rnum];
|
||||
var oldLabel = rev.label;
|
||||
input.blur(function()
|
||||
{
|
||||
var newLabel = input.val();
|
||||
if (newLabel && newLabel != oldLabel)
|
||||
{
|
||||
relabelRevision(rnum, newLabel);
|
||||
}
|
||||
endEdit();
|
||||
});
|
||||
input.val(rev.label).focus().select();
|
||||
padutils.bindEnterAndEscape(input, function onEnter()
|
||||
{
|
||||
input.blur();
|
||||
}, function onEscape()
|
||||
{
|
||||
input.val('').blur();
|
||||
});
|
||||
}
|
||||
|
||||
function relabelRevision(rnum, newLabel)
|
||||
{
|
||||
var rev = currentRevisionList[rnum];
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/saverevisionlabel',
|
||||
data: {
|
||||
userId: pad.getUserId(),
|
||||
padId: pad.getPadId(),
|
||||
revId: rev.id,
|
||||
newLabel: newLabel
|
||||
},
|
||||
success: success,
|
||||
error: error
|
||||
});
|
||||
|
||||
function success(text)
|
||||
{
|
||||
var newRevisionList = JSON.parse(text);
|
||||
self.newRevisionList(newRevisionList);
|
||||
pad.sendClientMessage(
|
||||
{
|
||||
type: 'revisionLabel',
|
||||
revisionList: reversedCopy(currentRevisionList),
|
||||
savedBy: pad.getUserName(),
|
||||
newLabel: newLabel
|
||||
});
|
||||
}
|
||||
|
||||
function error(e)
|
||||
{
|
||||
alert("Oops! There was an error saving that revision label. Please try again later.");
|
||||
}
|
||||
}
|
||||
|
||||
var currentRevisionList = [];
|
||||
|
||||
function setRevisionList(newRevisionList, noAnimation)
|
||||
{
|
||||
// deals with changed labels and new added revisions
|
||||
for (var i = 0; i < currentRevisionList.length; i++)
|
||||
{
|
||||
var a = currentRevisionList[i];
|
||||
var b = newRevisionList[i];
|
||||
if (b.label != a.label)
|
||||
{
|
||||
setBoxLabel(getNthBox(i), b.label);
|
||||
}
|
||||
}
|
||||
for (var j = currentRevisionList.length; j < newRevisionList.length; j++)
|
||||
{
|
||||
var newBox = makeRevisionBox(newRevisionList[j], j);
|
||||
$("#savedrevs-scrollinner").append(newBox);
|
||||
newBox.css('left', j * REVISION_BOX_WIDTH);
|
||||
}
|
||||
var newOnes = (newRevisionList.length > currentRevisionList.length);
|
||||
currentRevisionList = newRevisionList;
|
||||
if (newOnes)
|
||||
{
|
||||
setDesiredScroll(getMaxScroll());
|
||||
if (noAnimation)
|
||||
{
|
||||
setScroll(desiredScroll);
|
||||
}
|
||||
|
||||
if (!noAnimation)
|
||||
{
|
||||
var nameOfLast = currentRevisionList[currentRevisionList.length - 1].label;
|
||||
displaySavedTip(nameOfLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function refreshRevisionList()
|
||||
{
|
||||
for (var i = 0; i < currentRevisionList.length; i++)
|
||||
{
|
||||
var r = currentRevisionList[i];
|
||||
var box = getNthBox(i);
|
||||
setBoxTimestamp(box, r.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
var savedTipAnimator = padutils.makeShowHideAnimator(function(state)
|
||||
{
|
||||
if (state == -1)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 0).css('display', 'block');
|
||||
}
|
||||
else if (state == 0)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 1);
|
||||
}
|
||||
else if (state == 1)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 0).css('display', 'none');
|
||||
}
|
||||
else if (state < 0)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 1);
|
||||
}
|
||||
else if (state > 0)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 1 - state);
|
||||
}
|
||||
}, false, 25, 300);
|
||||
|
||||
function displaySavedTip(text)
|
||||
{
|
||||
$("#revision-notifier .name").html(padutils.escapeHtml(text));
|
||||
savedTipAnimator.show();
|
||||
padutils.cancelActions("hide-revision-notifier");
|
||||
var hideLater = padutils.getCancellableAction("hide-revision-notifier", function()
|
||||
{
|
||||
savedTipAnimator.hide();
|
||||
});
|
||||
window.setTimeout(hideLater, 3000);
|
||||
}
|
||||
|
||||
var REVISION_BOX_WIDTH = 120;
|
||||
var curScroll = 0; // distance between left of revisions and right of view
|
||||
var desiredScroll = 0;
|
||||
|
||||
function getScrollWidth()
|
||||
{
|
||||
return REVISION_BOX_WIDTH * currentRevisionList.length;
|
||||
}
|
||||
|
||||
function getViewportWidth()
|
||||
{
|
||||
return $("#savedrevs-scrollouter").width();
|
||||
}
|
||||
|
||||
function getMinScroll()
|
||||
{
|
||||
return Math.min(getViewportWidth(), getScrollWidth());
|
||||
}
|
||||
|
||||
function getMaxScroll()
|
||||
{
|
||||
return getScrollWidth();
|
||||
}
|
||||
|
||||
function setScroll(newScroll)
|
||||
{
|
||||
curScroll = newScroll;
|
||||
$("#savedrevs-scrollinner").css('right', newScroll);
|
||||
updateScrollArrows();
|
||||
}
|
||||
|
||||
function setDesiredScroll(newDesiredScroll, dontUpdate)
|
||||
{
|
||||
desiredScroll = Math.min(getMaxScroll(), Math.max(getMinScroll(), newDesiredScroll));
|
||||
if (!dontUpdate)
|
||||
{
|
||||
updateScroll();
|
||||
}
|
||||
}
|
||||
|
||||
function updateScroll()
|
||||
{
|
||||
updateScrollArrows();
|
||||
scrollAnimator.scheduleAnimation();
|
||||
}
|
||||
|
||||
function updateScrollArrows()
|
||||
{
|
||||
$("#savedrevs-scrollleft").toggleClass("disabledscrollleft", desiredScroll <= getMinScroll());
|
||||
$("#savedrevs-scrollright").toggleClass("disabledscrollright", desiredScroll >= getMaxScroll());
|
||||
}
|
||||
var scrollAnimator = padutils.makeAnimationScheduler(function()
|
||||
{
|
||||
setDesiredScroll(desiredScroll, true); // re-clamp
|
||||
if (Math.abs(desiredScroll - curScroll) < 1)
|
||||
{
|
||||
setScroll(desiredScroll);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
setScroll(curScroll + (desiredScroll - curScroll) * 0.5);
|
||||
return true;
|
||||
}
|
||||
}, 50, 2);
|
||||
|
||||
var isSaving = false;
|
||||
|
||||
function setIsSaving(v)
|
||||
{
|
||||
isSaving = v;
|
||||
rerenderButton();
|
||||
}
|
||||
|
||||
function haveReachedRevLimit()
|
||||
{
|
||||
var mv = pad.getPrivilege('maxRevisions');
|
||||
return (!(mv < 0 || mv > currentRevisionList.length));
|
||||
}
|
||||
|
||||
function rerenderButton()
|
||||
{
|
||||
if (isSaving || (!pad.isFullyConnected()) || haveReachedRevLimit())
|
||||
{
|
||||
$("#savedrevs-savenow").css('opacity', 0.75);
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#savedrevs-savenow").css('opacity', 1);
|
||||
}
|
||||
}
|
||||
|
||||
var scrollRepeatTimer = null;
|
||||
var scrollStartTime = 0;
|
||||
|
||||
function setScrollRepeatTimer(dir)
|
||||
{
|
||||
clearScrollRepeatTimer();
|
||||
scrollStartTime = +new Date;
|
||||
scrollRepeatTimer = window.setTimeout(function f()
|
||||
{
|
||||
if (!scrollRepeatTimer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.scroll(dir);
|
||||
var scrollTime = (+new Date) - scrollStartTime;
|
||||
var delay = (scrollTime > 2000 ? 50 : 300);
|
||||
scrollRepeatTimer = window.setTimeout(f, delay);
|
||||
}, 300);
|
||||
$(document).bind('mouseup', clearScrollRepeatTimer);
|
||||
}
|
||||
|
||||
function clearScrollRepeatTimer()
|
||||
{
|
||||
if (scrollRepeatTimer)
|
||||
{
|
||||
window.clearTimeout(scrollRepeatTimer);
|
||||
scrollRepeatTimer = null;
|
||||
}
|
||||
$(document).unbind('mouseup', clearScrollRepeatTimer);
|
||||
}
|
||||
|
||||
var pad = undefined;
|
||||
var self = {
|
||||
init: function(initialRevisions, _pad)
|
||||
{
|
||||
pad = _pad;
|
||||
self.newRevisionList(initialRevisions, true);
|
||||
|
||||
$("#savedrevs-savenow").click(function()
|
||||
{
|
||||
self.saveNow();
|
||||
});
|
||||
$("#savedrevs-scrollleft").mousedown(function()
|
||||
{
|
||||
self.scroll('left');
|
||||
setScrollRepeatTimer('left');
|
||||
});
|
||||
$("#savedrevs-scrollright").mousedown(function()
|
||||
{
|
||||
self.scroll('right');
|
||||
setScrollRepeatTimer('right');
|
||||
});
|
||||
$("#savedrevs-close").click(function()
|
||||
{
|
||||
paddocbar.setShownPanel(null);
|
||||
});
|
||||
|
||||
// update "saved n minutes ago" times
|
||||
window.setInterval(function()
|
||||
{
|
||||
refreshRevisionList();
|
||||
}, 60 * 1000);
|
||||
},
|
||||
restoreRevision: function(rnum)
|
||||
{
|
||||
var rev = currentRevisionList[rnum];
|
||||
var warning = ("Restoring this revision will overwrite the current" + " text of the pad. " + "Are you sure you want to continue?");
|
||||
var hidePanel = paddocbar.hideLaterIfNoOtherInteraction();
|
||||
var box = getNthBox(rnum);
|
||||
if (confirm(warning))
|
||||
{
|
||||
box.find(".srtwirly").show();
|
||||
$.ajax(
|
||||
{
|
||||
type: 'get',
|
||||
url: '/ep/pad/getrevisionatext',
|
||||
data: {
|
||||
padId: pad.getPadId(),
|
||||
revId: rev.id
|
||||
},
|
||||
success: success,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
|
||||
function success(resultJson)
|
||||
{
|
||||
untwirl();
|
||||
var result = JSON.parse(resultJson);
|
||||
padeditor.restoreRevisionText(result);
|
||||
window.setTimeout(function()
|
||||
{
|
||||
hidePanel();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function error(e)
|
||||
{
|
||||
untwirl();
|
||||
alert("Oops! There was an error retreiving the text (revNum= " + rev.revNum + "; padId=" + pad.getPadId());
|
||||
}
|
||||
|
||||
function untwirl()
|
||||
{
|
||||
box.find(".srtwirly").hide();
|
||||
}
|
||||
},
|
||||
showReachedLimit: function()
|
||||
{
|
||||
alert("Sorry, you do not have privileges to save more than " + pad.getPrivilege('maxRevisions') + " revisions.");
|
||||
},
|
||||
newRevisionList: function(lst, noAnimation)
|
||||
{
|
||||
// server gives us list with newest first;
|
||||
// we want chronological order
|
||||
var L = reversedCopy(lst);
|
||||
setRevisionList(L, noAnimation);
|
||||
rerenderButton();
|
||||
},
|
||||
saveNow: function()
|
||||
{
|
||||
if (isSaving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!pad.isFullyConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (haveReachedRevLimit())
|
||||
{
|
||||
self.showReachedLimit();
|
||||
return;
|
||||
}
|
||||
setIsSaving(true);
|
||||
var savedBy = pad.getUserName() || "unnamed";
|
||||
pad.callWhenNotCommitting(submitSave);
|
||||
|
||||
function submitSave()
|
||||
{
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/saverevision',
|
||||
data: {
|
||||
padId: pad.getPadId(),
|
||||
savedBy: savedBy,
|
||||
savedById: pad.getUserId(),
|
||||
revNum: pad.getCollabRevisionNumber()
|
||||
},
|
||||
success: success,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
|
||||
function success(text)
|
||||
{
|
||||
setIsSaving(false);
|
||||
var newRevisionList = JSON.parse(text);
|
||||
self.newRevisionList(newRevisionList);
|
||||
pad.sendClientMessage(
|
||||
{
|
||||
type: 'newRevisionList',
|
||||
revisionList: newRevisionList,
|
||||
savedBy: savedBy
|
||||
});
|
||||
}
|
||||
|
||||
function error(e)
|
||||
{
|
||||
setIsSaving(false);
|
||||
alert("Oops! The server failed to save the revision. Please try again later.");
|
||||
}
|
||||
},
|
||||
handleResizePage: function()
|
||||
{
|
||||
updateScrollArrows();
|
||||
},
|
||||
handleIsFullyConnected: function(isConnected)
|
||||
{
|
||||
rerenderButton();
|
||||
},
|
||||
scroll: function(dir)
|
||||
{
|
||||
var minScroll = getMinScroll();
|
||||
var maxScroll = getMaxScroll();
|
||||
if (dir == 'left')
|
||||
{
|
||||
if (desiredScroll > minScroll)
|
||||
{
|
||||
var n = Math.floor((desiredScroll - 1 - minScroll) / REVISION_BOX_WIDTH);
|
||||
setDesiredScroll(Math.max(0, n) * REVISION_BOX_WIDTH + minScroll);
|
||||
}
|
||||
}
|
||||
else if (dir == 'right')
|
||||
{
|
||||
if (desiredScroll < maxScroll)
|
||||
{
|
||||
var n = Math.floor((maxScroll - desiredScroll - 1) / REVISION_BOX_WIDTH);
|
||||
setDesiredScroll(maxScroll - Math.max(0, n) * REVISION_BOX_WIDTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}());
|
||||
|
||||
exports.padsavedrevs = padsavedrevs;
|
||||
exports.init = function(_pad){
|
||||
pad = _pad;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,11 @@
|
|||
</ul>
|
||||
<ul class="menu_right">
|
||||
<% e.begin_block("editbarMenuRight"); %>
|
||||
<li onClick="window.pad&&pad.editbarClick('savedRevision');return false;">
|
||||
<a id="settingslink" title="Mark this revision as a saved revision">
|
||||
<div class="buttonicon buttonicon-savedRevision"></div>
|
||||
</a>
|
||||
</li>
|
||||
<li id="settingslink" onClick="window.pad&&pad.editbarClick('settings');return false;">
|
||||
<a class="buttonicon buttonicon-settings" id="settingslink" title="Settings of this pad"></a>
|
||||
</li>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
Loading…
Reference in New Issue