aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/etherpad/src/static/js/pad_savedrevs.js
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/etherpad/src/static/js/pad_savedrevs.js')
-rw-r--r--trunk/etherpad/src/static/js/pad_savedrevs.js408
1 files changed, 408 insertions, 0 deletions
diff --git a/trunk/etherpad/src/static/js/pad_savedrevs.js b/trunk/etherpad/src/static/js/pad_savedrevs.js
new file mode 100644
index 0000000..ec06a1f
--- /dev/null
+++ b/trunk/etherpad/src/static/js/pad_savedrevs.js
@@ -0,0 +1,408 @@
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var padsavedrevs = (function() {
+
+ 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 padsavedrevs.restoreRevision('+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 self = {
+ init: function(initialRevisions) {
+ 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;
+}()); \ No newline at end of file