summaryrefslogtreecommitdiffstats
path: root/etherpad/src/etherpad/control/historycontrol.js
diff options
context:
space:
mode:
Diffstat (limited to 'etherpad/src/etherpad/control/historycontrol.js')
-rw-r--r--etherpad/src/etherpad/control/historycontrol.js226
1 files changed, 226 insertions, 0 deletions
diff --git a/etherpad/src/etherpad/control/historycontrol.js b/etherpad/src/etherpad/control/historycontrol.js
new file mode 100644
index 0000000..a78cfad
--- /dev/null
+++ b/etherpad/src/etherpad/control/historycontrol.js
@@ -0,0 +1,226 @@
+/**
+ * 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.
+ */
+
+import("fastJSON");
+import("etherpad.utils.render404");
+import("etherpad.pad.model");
+import("etherpad.collab.collab_server");
+import("etherpad.collab.ace.easysync2.*");
+import("jsutils.eachProperty");
+
+function _urlCache() {
+ if (!appjet.cache.historyUrlCache) {
+ appjet.cache.historyUrlCache = {};
+ }
+ return appjet.cache.historyUrlCache;
+}
+
+function _replyWithJSONAndCache(obj) {
+ obj.apiversion = _VERSION;
+ var output = fastJSON.stringify(obj);
+ _urlCache()[request.path] = output;
+ response.write(output);
+ response.stop();
+}
+
+function _replyWithJSON(obj) {
+ obj.apiversion = _VERSION;
+ response.write(fastJSON.stringify(obj));
+ response.stop();
+}
+
+function _error(msg, num) {
+ _replyWithJSON({error: String(msg), errornum: num});
+}
+
+var _VERSION = 1;
+
+var _ERROR_REVISION_NUMBER_TOO_LARGE = 14;
+
+function _do_text(padId, r) {
+ if (! padId) render404();
+ model.accessPadGlobal(padId, function(pad) {
+ if (! pad.exists()) {
+ render404();
+ }
+ if (r > pad.getHeadRevisionNumber()) {
+ _error("Revision number too large", _ERROR_REVISION_NUMBER_TOO_LARGE);
+ }
+ var text = pad.getInternalRevisionText(r);
+ text = _censorText(text);
+ _replyWithJSONAndCache({ text: text });
+ });
+}
+
+function _do_stat(padId) {
+ var obj = {};
+ if (! padId) {
+ obj.exists = false;
+ }
+ else {
+ model.accessPadGlobal(padId, function(pad) {
+ if (! pad.exists()) {
+ obj.exists = false;
+ }
+ else {
+ obj.exists = true;
+ obj.latestRev = pad.getHeadRevisionNumber();
+ }
+ });
+ }
+ _replyWithJSON(obj);
+}
+
+function _censorText(text) {
+ // may not change length of text
+ return text.replace(/(http:\/\/pad.spline.inf.fu-berlin.de\/)(\w+)/g, function(url, u1, u2) {
+ return u1 + u2.replace(/\w/g, '-');
+ });
+}
+
+function _do_changes(padId, first, last) {
+ if (! padId) render404();
+
+ var charPool = [];
+ var changeList = [];
+
+ function charPoolText(txt) {
+ charPool.push(txt);
+ return _encodeVarInt(txt.length);
+ }
+
+ model.accessPadGlobal(padId, function(pad) {
+
+ if (first > pad.getHeadRevisionNumber() || last > pad.getHeadRevisionNumber()) {
+ _error("Revision number too large", _ERROR_REVISION_NUMBER_TOO_LARGE);
+ }
+
+ var curAText = Changeset.makeAText("\n");
+ if (first > 0) {
+ curAText = pad.getInternalRevisionAText(first - 1);
+ }
+ curAText.text = _censorText(curAText.text);
+ var lastTimestamp = null;
+ for(var r=first;r<=last;r++) {
+ var binRev = [];
+ var timestamp = +pad.getRevisionDate(r);
+ binRev.push(_encodeTimeStamp(timestamp, lastTimestamp));
+ lastTimestamp = timestamp;
+ binRev.push(_encodeVarInt(1)); // fake author
+
+ var c = pad.getRevisionChangeset(r);
+ var splices = Changeset.toSplices(c);
+ splices.forEach(function (splice) {
+ var startChar = splice[0];
+ var endChar = splice[1];
+ var newText = splice[2];
+ oldText = curAText.text.substring(startChar, endChar);
+
+ if (oldText.length == 0) {
+ binRev.push('+');
+ binRev.push(_encodeVarInt(startChar));
+ binRev.push(charPoolText(newText));
+ }
+ else if (newText.length == 0) {
+ binRev.push('-');
+ binRev.push(_encodeVarInt(startChar));
+ binRev.push(charPoolText(oldText));
+ }
+ else {
+ binRev.push('*');
+ binRev.push(_encodeVarInt(startChar));
+ binRev.push(charPoolText(oldText));
+ binRev.push(charPoolText(newText));
+ }
+ });
+ changeList.push(binRev.join(''));
+
+ curAText = Changeset.applyToAText(c, curAText, pad.pool());
+ }
+
+ _replyWithJSONAndCache({charPool: charPool.join(''), changes: changeList.join(',')});
+
+ });
+}
+
+function render_history(padOpaqueRef, rest) {
+ if (_urlCache()[request.path]) {
+ response.write(_urlCache()[request.path]);
+ response.stop();
+ return true;
+ }
+ var padId;
+ if (padOpaqueRef == "CSi1xgbFXl" || padOpaqueRef == "13sentences") {
+ // made-up, hard-coded opaque ref, should be a table for these
+ padId = "jbg5HwzUX8";
+ }
+ else if (padOpaqueRef == "dO1j7Zf34z" || padOpaqueRef == "foundervisa") {
+ // made-up, hard-coded opaque ref, should be a table for these
+ padId = "3hS7kQyDXG";
+ }
+ else {
+ padId = null;
+ }
+ var regexResult;
+ if ((regexResult = /^stat$/.exec(rest))) {
+ _do_stat(padId);
+ }
+ else if ((regexResult = /^text\/(\d+)$/.exec(rest))) {
+ var r = Number(regexResult[1]);
+ _do_text(padId, r);
+ }
+ else if ((regexResult = /^changes\/(\d+)-(\d+)$/.exec(rest))) {
+ _do_changes(padId, Number(regexResult[1]), Number(regexResult[2]));
+ }
+ else {
+ return false;
+ }
+}
+
+function _encodeVarInt(num) {
+ var n = +num;
+ if (isNaN(n)) {
+ throw new Error("Can't encode non-number "+num);
+ }
+ var chars = [];
+ var done = false;
+ while (! done) {
+ if (n < 32) done = true;
+ var nd = (n % 32);
+ if (chars.length > 0) {
+ // non-first, will become non-last digit
+ nd = (nd | 32);
+ }
+ chars.push(_BASE64_DIGITS[nd]);
+ n = Math.floor(n / 32)
+ }
+ return chars.reverse().join('');
+}
+var _BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
+
+function _encodeTimeStamp(tMillis, baseMillis) {
+ var t = Math.floor(tMillis/1000);
+ var base = Math.floor(baseMillis/1000);
+ var absolute = ["+", t];
+ var resultPair = absolute;
+ if (((typeof base) == "number") && base <= t) {
+ var relative = ["", t - base];
+ if (relative[1] < absolute[1]) {
+ resultPair = relative;
+ }
+ }
+ return resultPair[0] + _encodeVarInt(resultPair[1]);
+}