diff options
Diffstat (limited to 'trunk/etherpad/src/etherpad/control/historycontrol.js')
-rw-r--r-- | trunk/etherpad/src/etherpad/control/historycontrol.js | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/trunk/etherpad/src/etherpad/control/historycontrol.js b/trunk/etherpad/src/etherpad/control/historycontrol.js new file mode 100644 index 0000000..a78cfad --- /dev/null +++ b/trunk/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]); +} |