mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-06-17 19:54:50 -04:00
prepare to async: trivial reformatting
This change is only cosmetic. Its aim is do make it easier to understand the async changes that are going to be merged later on. It was extracted from the original work from Ray Bellis. To verify that nothing has changed, you can run the following command on each file touched by this commit: npm install uglify-es diff --unified <(uglify-js --beautify bracketize <BEFORE.js>) <(uglify-js --beautify bracketize <AFTER.js>) This is a complete script that does the same automatically (works from a mercurial clone): ```bash #!/usr/bin/env bash set -eu REVISION=<THIS_REVISION> PARENT_REV=$(hg identify --rev "${REVISION}" --template '{p1rev}') FILE_LIST=$(hg status --no-status --change ${REVISION}) UGLIFYJS="node_modules/uglify-es/bin/uglifyjs" for FILE_NAME in ${FILE_LIST[@]}; do echo "Checking ${FILE_NAME}" diff --unified \ <("${UGLIFYJS}" --beautify bracketize <(hg cat --rev "${PARENT_REV}" "${FILE_NAME}")) \ <("${UGLIFYJS}" --beautify bracketize <(hg cat --rev "${REVISION}" "${FILE_NAME}")) done ```
This commit is contained in:
parent
cc23bd18a4
commit
9497ee734f
33 changed files with 2706 additions and 2943 deletions
|
@ -30,40 +30,32 @@ function getPadTXT(pad, revNum, callback)
|
|||
var atext = pad.atext;
|
||||
var html;
|
||||
async.waterfall([
|
||||
|
||||
// fetch revision atext
|
||||
function(callback) {
|
||||
if (revNum != undefined) {
|
||||
pad.getInternalRevisionAText(revNum, function(err, revisionAtext) {
|
||||
if (ERR(err, callback)) return;
|
||||
|
||||
|
||||
function (callback)
|
||||
{
|
||||
if (revNum != undefined)
|
||||
{
|
||||
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
atext = revisionAtext;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
|
||||
// convert atext to html
|
||||
|
||||
|
||||
function (callback)
|
||||
{
|
||||
html = getTXTFromAtext(pad, atext); // only this line is different to the HTML function
|
||||
function(callback) {
|
||||
// only this line is different to the HTML function
|
||||
html = getTXTFromAtext(pad, atext);
|
||||
callback(null);
|
||||
}],
|
||||
|
||||
// run final callback
|
||||
function(err) {
|
||||
if (ERR(err, callback)) return;
|
||||
|
||||
|
||||
function (err)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, html);
|
||||
});
|
||||
}
|
||||
|
@ -80,17 +72,14 @@ function getTXTFromAtext(pad, atext, authorColors)
|
|||
var anumMap = {};
|
||||
var css = "";
|
||||
|
||||
props.forEach(function (propName, i)
|
||||
{
|
||||
props.forEach(function(propName, i) {
|
||||
var propTrueNum = apool.putAttrib([propName, true], true);
|
||||
if (propTrueNum >= 0)
|
||||
{
|
||||
if (propTrueNum >= 0) {
|
||||
anumMap[propTrueNum] = i;
|
||||
}
|
||||
});
|
||||
|
||||
function getLineTXT(text, attribs)
|
||||
{
|
||||
function getLineTXT(text, attribs) {
|
||||
var propVals = [false, false, false];
|
||||
var ENTER = 1;
|
||||
var STAY = 2;
|
||||
|
@ -106,94 +95,77 @@ function getTXTFromAtext(pad, atext, authorColors)
|
|||
|
||||
var idx = 0;
|
||||
|
||||
function processNextChars(numChars)
|
||||
{
|
||||
if (numChars <= 0)
|
||||
{
|
||||
function processNextChars(numChars) {
|
||||
if (numChars <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
||||
idx += numChars;
|
||||
|
||||
while (iter.hasNext())
|
||||
{
|
||||
while (iter.hasNext()) {
|
||||
var o = iter.next();
|
||||
var propChanged = false;
|
||||
Changeset.eachAttribNumber(o.attribs, function (a)
|
||||
{
|
||||
if (a in anumMap)
|
||||
{
|
||||
|
||||
Changeset.eachAttribNumber(o.attribs, function(a) {
|
||||
if (a in anumMap) {
|
||||
var i = anumMap[a]; // i = 0 => bold, etc.
|
||||
if (!propVals[i])
|
||||
{
|
||||
|
||||
if (!propVals[i]) {
|
||||
propVals[i] = ENTER;
|
||||
propChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
propVals[i] = STAY;
|
||||
}
|
||||
}
|
||||
});
|
||||
for (var i = 0; i < propVals.length; i++)
|
||||
{
|
||||
if (propVals[i] === true)
|
||||
{
|
||||
|
||||
for (var i = 0; i < propVals.length; i++) {
|
||||
if (propVals[i] === true) {
|
||||
propVals[i] = LEAVE;
|
||||
propChanged = true;
|
||||
}
|
||||
else if (propVals[i] === STAY)
|
||||
{
|
||||
propVals[i] = true; // set it back
|
||||
} else if (propVals[i] === STAY) {
|
||||
// set it back
|
||||
propVals[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// now each member of propVal is in {false,LEAVE,ENTER,true}
|
||||
// according to what happens at start of span
|
||||
if (propChanged)
|
||||
{
|
||||
if (propChanged) {
|
||||
// leaving bold (e.g.) also leaves italics, etc.
|
||||
var left = false;
|
||||
for (var i = 0; i < propVals.length; i++)
|
||||
{
|
||||
|
||||
for (var i = 0; i < propVals.length; i++) {
|
||||
var v = propVals[i];
|
||||
if (!left)
|
||||
{
|
||||
if (v === LEAVE)
|
||||
{
|
||||
|
||||
if (!left) {
|
||||
if (v === LEAVE) {
|
||||
left = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v === true)
|
||||
{
|
||||
propVals[i] = STAY; // tag will be closed and re-opened
|
||||
} else {
|
||||
if (v === true) {
|
||||
// tag will be closed and re-opened
|
||||
propVals[i] = STAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tags2close = [];
|
||||
|
||||
for (var i = propVals.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (propVals[i] === LEAVE)
|
||||
{
|
||||
for (var i = propVals.length - 1; i >= 0; i--) {
|
||||
if (propVals[i] === LEAVE) {
|
||||
//emitCloseTag(i);
|
||||
tags2close.push(i);
|
||||
propVals[i] = false;
|
||||
}
|
||||
else if (propVals[i] === STAY)
|
||||
{
|
||||
} else if (propVals[i] === STAY) {
|
||||
//emitCloseTag(i);
|
||||
tags2close.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < propVals.length; i++)
|
||||
{
|
||||
if (propVals[i] === ENTER || propVals[i] === STAY)
|
||||
{
|
||||
for (var i = 0; i < propVals.length; i++) {
|
||||
if (propVals[i] === ENTER || propVals[i] === STAY) {
|
||||
propVals[i] = true;
|
||||
}
|
||||
}
|
||||
|
@ -201,9 +173,9 @@ function getTXTFromAtext(pad, atext, authorColors)
|
|||
} // end if (propChanged)
|
||||
|
||||
var chars = o.chars;
|
||||
if (o.lines)
|
||||
{
|
||||
chars--; // exclude newline at end of line, if present
|
||||
if (o.lines) {
|
||||
// exclude newline at end of line, if present
|
||||
chars--;
|
||||
}
|
||||
|
||||
var s = taker.take(chars);
|
||||
|
@ -220,19 +192,19 @@ function getTXTFromAtext(pad, atext, authorColors)
|
|||
} // end iteration over spans in line
|
||||
|
||||
var tags2close = [];
|
||||
for (var i = propVals.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (propVals[i])
|
||||
{
|
||||
for (var i = propVals.length - 1; i >= 0; i--) {
|
||||
if (propVals[i]) {
|
||||
tags2close.push(i);
|
||||
propVals[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // end processNextChars
|
||||
|
||||
processNextChars(text.length - idx);
|
||||
return(assem.toString());
|
||||
} // end getLineHTML
|
||||
|
||||
var pieces = [css];
|
||||
|
||||
// Need to deal with constraints imposed on HTML lists; can
|
||||
|
@ -242,41 +214,44 @@ function getTXTFromAtext(pad, atext, authorColors)
|
|||
// so we want to do something reasonable there. We also
|
||||
// want to deal gracefully with blank lines.
|
||||
// => keeps track of the parents level of indentation
|
||||
for (var i = 0; i < textLines.length; i++)
|
||||
{
|
||||
for (var i = 0; i < textLines.length; i++) {
|
||||
var line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
var lineContent = getLineTXT(line.text, line.aline);
|
||||
if(line.listTypeName == "bullet"){
|
||||
|
||||
if (line.listTypeName == "bullet") {
|
||||
lineContent = "* " + lineContent; // add a bullet
|
||||
}
|
||||
if(line.listLevel > 0){
|
||||
for (var j = line.listLevel - 1; j >= 0; j--){
|
||||
|
||||
if (line.listLevel > 0) {
|
||||
for (var j = line.listLevel - 1; j >= 0; j--) {
|
||||
pieces.push('\t');
|
||||
}
|
||||
if(line.listTypeName == "number"){
|
||||
|
||||
if (line.listTypeName == "number") {
|
||||
pieces.push(line.listLevel + ". ");
|
||||
// This is bad because it doesn't truly reflect what the user
|
||||
// sees because browsers do magic on nested <ol><li>s
|
||||
}
|
||||
|
||||
pieces.push(lineContent, '\n');
|
||||
}else{
|
||||
} else {
|
||||
pieces.push(lineContent, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
return pieces.join('');
|
||||
}
|
||||
|
||||
exports.getTXTFromAtext = getTXTFromAtext;
|
||||
|
||||
exports.getPadTXTDocument = function (padId, revNum, callback)
|
||||
exports.getPadTXTDocument = function(padId, revNum, callback)
|
||||
{
|
||||
padManager.getPad(padId, function (err, pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
padManager.getPad(padId, function(err, pad) {
|
||||
if (ERR(err, callback)) return;
|
||||
|
||||
getPadTXT(pad, revNum, function(err, html) {
|
||||
if (ERR(err, callback)) return;
|
||||
|
||||
getPadTXT(pad, revNum, function (err, html)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
callback(null, html);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,57 +18,56 @@ var log4js = require('log4js');
|
|||
var async = require("async");
|
||||
var db = require("../db/DB").db;
|
||||
|
||||
exports.setPadRaw = function(padId, records, callback){
|
||||
exports.setPadRaw = function(padId, records, callback)
|
||||
{
|
||||
records = JSON.parse(records);
|
||||
|
||||
async.eachSeries(Object.keys(records), function(key, cb){
|
||||
var value = records[key]
|
||||
async.eachSeries(Object.keys(records), function(key, cb) {
|
||||
var value = records[key];
|
||||
|
||||
if(!value){
|
||||
if (!value) {
|
||||
return setImmediate(cb);
|
||||
}
|
||||
|
||||
// Author data
|
||||
if(value.padIDs){
|
||||
// rewrite author pad ids
|
||||
if (value.padIDs) {
|
||||
// Author data - rewrite author pad ids
|
||||
value.padIDs[padId] = 1;
|
||||
var newKey = key;
|
||||
|
||||
// Does this author already exist?
|
||||
db.get(key, function(err, author){
|
||||
if(author){
|
||||
// Yes, add the padID to the author..
|
||||
if( Object.prototype.toString.call(author) === '[object Array]'){
|
||||
db.get(key, function(err, author) {
|
||||
if (author) {
|
||||
// Yes, add the padID to the author
|
||||
if (Object.prototype.toString.call(author) === '[object Array]') {
|
||||
author.padIDs.push(padId);
|
||||
}
|
||||
value = author;
|
||||
}else{
|
||||
} else {
|
||||
// No, create a new array with the author info in
|
||||
value.padIDs = [padId];
|
||||
}
|
||||
});
|
||||
|
||||
// Not author data, probably pad data
|
||||
}else{
|
||||
// we can split it to look to see if its pad data
|
||||
} else {
|
||||
// Not author data, probably pad data
|
||||
// we can split it to look to see if it's pad data
|
||||
var oldPadId = key.split(":");
|
||||
|
||||
// we know its pad data..
|
||||
if(oldPadId[0] === "pad"){
|
||||
|
||||
// we know it's pad data
|
||||
if (oldPadId[0] === "pad") {
|
||||
// so set the new pad id for the author
|
||||
oldPadId[1] = padId;
|
||||
|
||||
|
||||
// and create the value
|
||||
var newKey = oldPadId.join(":"); // create the new key
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Write the value to the server
|
||||
db.set(newKey, value);
|
||||
|
||||
setImmediate(cb);
|
||||
}, function(){
|
||||
},
|
||||
function() {
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -36,19 +36,22 @@ function setPadHTML(pad, html, callback)
|
|||
// Convert a dom tree into a list of lines and attribute liens
|
||||
// using the content collector object
|
||||
var cc = contentcollector.makeContentCollector(true, null, pad.pool);
|
||||
try{ // we use a try here because if the HTML is bad it will blow up
|
||||
try {
|
||||
// we use a try here because if the HTML is bad it will blow up
|
||||
cc.collectContent(doc);
|
||||
}catch(e){
|
||||
} catch(e) {
|
||||
apiLogger.warn("HTML was not properly formed", e);
|
||||
return callback(e); // We don't process the HTML because it was bad..
|
||||
|
||||
// don't process the HTML because it was bad
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
var result = cc.finish();
|
||||
|
||||
apiLogger.debug('Lines:');
|
||||
|
||||
var i;
|
||||
for (i = 0; i < result.lines.length; i += 1)
|
||||
{
|
||||
for (i = 0; i < result.lines.length; i += 1) {
|
||||
apiLogger.debug('Line ' + (i + 1) + ' text: ' + result.lines[i]);
|
||||
apiLogger.debug('Line ' + (i + 1) + ' attributes: ' + result.lineAttribs[i]);
|
||||
}
|
||||
|
@ -59,18 +62,15 @@ function setPadHTML(pad, html, callback)
|
|||
apiLogger.debug(newText);
|
||||
var newAttribs = result.lineAttribs.join('|1+1') + '|1+1';
|
||||
|
||||
function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ )
|
||||
{
|
||||
function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ ) {
|
||||
var attribsIter = Changeset.opIterator(attribs);
|
||||
var textIndex = 0;
|
||||
var newTextStart = 0;
|
||||
var newTextEnd = newText.length;
|
||||
while (attribsIter.hasNext())
|
||||
{
|
||||
while (attribsIter.hasNext()) {
|
||||
var op = attribsIter.next();
|
||||
var nextIndex = textIndex + op.chars;
|
||||
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd))
|
||||
{
|
||||
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
|
||||
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
|
||||
}
|
||||
textIndex = nextIndex;
|
||||
|
@ -81,13 +81,13 @@ function setPadHTML(pad, html, callback)
|
|||
var builder = Changeset.builder(1);
|
||||
|
||||
// assemble each line into the builder
|
||||
eachAttribRun(newAttribs, function(start, end, attribs)
|
||||
{
|
||||
eachAttribRun(newAttribs, function(start, end, attribs) {
|
||||
builder.insert(newText.substring(start, end), attribs);
|
||||
});
|
||||
|
||||
// the changeset is ready!
|
||||
var theChangeset = builder.toString();
|
||||
|
||||
apiLogger.debug('The changeset: ' + theChangeset);
|
||||
pad.setText("\n");
|
||||
pad.appendRevision(theChangeset);
|
||||
|
|
|
@ -1,336 +1,348 @@
|
|||
var Changeset = require("../../static/js/Changeset");
|
||||
var async = require("async");
|
||||
var exportHtml = require('./ExportHtml');
|
||||
|
||||
function PadDiff (pad, fromRev, toRev){
|
||||
//check parameters
|
||||
if(!pad || !pad.id || !pad.atext || !pad.pool)
|
||||
{
|
||||
|
||||
function PadDiff (pad, fromRev, toRev) {
|
||||
// check parameters
|
||||
if (!pad || !pad.id || !pad.atext || !pad.pool) {
|
||||
throw new Error('Invalid pad');
|
||||
}
|
||||
|
||||
|
||||
var range = pad.getValidRevisionRange(fromRev, toRev);
|
||||
if(!range) { throw new Error('Invalid revision range.' +
|
||||
if (!range) {
|
||||
throw new Error('Invalid revision range.' +
|
||||
' startRev: ' + fromRev +
|
||||
' endRev: ' + toRev); }
|
||||
|
||||
' endRev: ' + toRev);
|
||||
}
|
||||
|
||||
this._pad = pad;
|
||||
this._fromRev = range.startRev;
|
||||
this._toRev = range.endRev;
|
||||
this._html = null;
|
||||
this._authors = [];
|
||||
}
|
||||
|
||||
PadDiff.prototype._isClearAuthorship = function(changeset){
|
||||
//unpack
|
||||
|
||||
PadDiff.prototype._isClearAuthorship = function(changeset) {
|
||||
// unpack
|
||||
var unpacked = Changeset.unpack(changeset);
|
||||
|
||||
//check if there is nothing in the charBank
|
||||
if(unpacked.charBank !== "")
|
||||
|
||||
// check if there is nothing in the charBank
|
||||
if (unpacked.charBank !== "") {
|
||||
return false;
|
||||
|
||||
//check if oldLength == newLength
|
||||
if(unpacked.oldLen !== unpacked.newLen)
|
||||
}
|
||||
|
||||
// check if oldLength == newLength
|
||||
if (unpacked.oldLen !== unpacked.newLen) {
|
||||
return false;
|
||||
|
||||
//lets iterator over the operators
|
||||
}
|
||||
|
||||
// lets iterator over the operators
|
||||
var iterator = Changeset.opIterator(unpacked.ops);
|
||||
|
||||
//get the first operator, this should be a clear operator
|
||||
|
||||
// get the first operator, this should be a clear operator
|
||||
var clearOperator = iterator.next();
|
||||
|
||||
//check if there is only one operator
|
||||
if(iterator.hasNext() === true)
|
||||
|
||||
// check if there is only one operator
|
||||
if (iterator.hasNext() === true) {
|
||||
return false;
|
||||
|
||||
//check if this operator doesn't change text
|
||||
if(clearOperator.opcode !== "=")
|
||||
}
|
||||
|
||||
// check if this operator doesn't change text
|
||||
if (clearOperator.opcode !== "=") {
|
||||
return false;
|
||||
|
||||
//check that this operator applys to the complete text
|
||||
//if the text ends with a new line, its exactly one character less, else it has the same length
|
||||
if(clearOperator.chars !== unpacked.oldLen-1 && clearOperator.chars !== unpacked.oldLen)
|
||||
}
|
||||
|
||||
// check that this operator applys to the complete text
|
||||
// if the text ends with a new line, its exactly one character less, else it has the same length
|
||||
if (clearOperator.chars !== unpacked.oldLen-1 && clearOperator.chars !== unpacked.oldLen) {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
var attributes = [];
|
||||
Changeset.eachAttribNumber(changeset, function(attrNum){
|
||||
Changeset.eachAttribNumber(changeset, function(attrNum) {
|
||||
attributes.push(attrNum);
|
||||
});
|
||||
|
||||
//check that this changeset uses only one attribute
|
||||
if(attributes.length !== 1)
|
||||
|
||||
// check that this changeset uses only one attribute
|
||||
if (attributes.length !== 1) {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
var appliedAttribute = this._pad.pool.getAttrib(attributes[0]);
|
||||
|
||||
//check if the applied attribute is an anonymous author attribute
|
||||
if(appliedAttribute[0] !== "author" || appliedAttribute[1] !== "")
|
||||
|
||||
// check if the applied attribute is an anonymous author attribute
|
||||
if (appliedAttribute[0] !== "author" || appliedAttribute[1] !== "") {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
PadDiff.prototype._createClearAuthorship = function(rev, callback){
|
||||
|
||||
PadDiff.prototype._createClearAuthorship = function(rev, callback) {
|
||||
var self = this;
|
||||
this._pad.getInternalRevisionAText(rev, function(err, atext){
|
||||
if(err){
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
//build clearAuthorship changeset
|
||||
this._pad.getInternalRevisionAText(rev, function(err, atext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// build clearAuthorship changeset
|
||||
var builder = Changeset.builder(atext.text.length);
|
||||
builder.keepText(atext.text, [['author','']], self._pad.pool);
|
||||
var changeset = builder.toString();
|
||||
|
||||
|
||||
callback(null, changeset);
|
||||
});
|
||||
};
|
||||
|
||||
PadDiff.prototype._createClearStartAtext = function(rev, callback){
|
||||
|
||||
PadDiff.prototype._createClearStartAtext = function(rev, callback) {
|
||||
var self = this;
|
||||
|
||||
//get the atext of this revision
|
||||
this._pad.getInternalRevisionAText(rev, function(err, atext){
|
||||
if(err){
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
//create the clearAuthorship changeset
|
||||
self._createClearAuthorship(rev, function(err, changeset){
|
||||
if(err){
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
try {
|
||||
//apply the clearAuthorship changeset
|
||||
var newAText = Changeset.applyToAText(changeset, atext, self._pad.pool);
|
||||
} catch(err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, newAText);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) {
|
||||
var self = this;
|
||||
|
||||
//find out which revisions we need
|
||||
var revisions = [];
|
||||
for(var i=startRev;i<(startRev+count) && i<=this._pad.head;i++){
|
||||
revisions.push(i);
|
||||
}
|
||||
|
||||
var changesets = [], authors = [];
|
||||
|
||||
//get all needed revisions
|
||||
async.forEach(revisions, function(rev, callback){
|
||||
self._pad.getRevision(rev, function(err, revision){
|
||||
if(err){
|
||||
|
||||
// get the atext of this revision
|
||||
this._pad.getInternalRevisionAText(rev, function(err, atext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// create the clearAuthorship changeset
|
||||
self._createClearAuthorship(rev, function(err, changeset) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// apply the clearAuthorship changeset
|
||||
var newAText = Changeset.applyToAText(changeset, atext, self._pad.pool);
|
||||
} catch(err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, newAText);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) {
|
||||
var self = this;
|
||||
|
||||
// find out which revisions we need
|
||||
var revisions = [];
|
||||
for (var i = startRev; i < (startRev + count) && i <= this._pad.head; i++) {
|
||||
revisions.push(i);
|
||||
}
|
||||
|
||||
var changesets = [], authors = [];
|
||||
|
||||
// get all needed revisions
|
||||
async.forEach(revisions, function(rev, callback) {
|
||||
self._pad.getRevision(rev, function(err, revision) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var arrayNum = rev-startRev;
|
||||
|
||||
|
||||
changesets[arrayNum] = revision.changeset;
|
||||
authors[arrayNum] = revision.meta.author;
|
||||
|
||||
|
||||
callback();
|
||||
});
|
||||
}, function(err){
|
||||
},
|
||||
function(err) {
|
||||
callback(err, changesets, authors);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
PadDiff.prototype._addAuthors = function(authors) {
|
||||
var self = this;
|
||||
//add to array if not in the array
|
||||
authors.forEach(function(author){
|
||||
if(self._authors.indexOf(author) == -1){
|
||||
|
||||
// add to array if not in the array
|
||||
authors.forEach(function(author) {
|
||||
if (self._authors.indexOf(author) == -1) {
|
||||
self._authors.push(author);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
PadDiff.prototype._createDiffAtext = function(callback) {
|
||||
var self = this;
|
||||
var bulkSize = 100;
|
||||
|
||||
//get the cleaned startAText
|
||||
self._createClearStartAtext(self._fromRev, function(err, atext){
|
||||
if(err) { return callback(err); }
|
||||
|
||||
|
||||
// get the cleaned startAText
|
||||
self._createClearStartAtext(self._fromRev, function(err, atext) {
|
||||
if (err) { return callback(err); }
|
||||
|
||||
var superChangeset = null;
|
||||
|
||||
|
||||
var rev = self._fromRev + 1;
|
||||
|
||||
//async while loop
|
||||
|
||||
// async while loop
|
||||
async.whilst(
|
||||
//loop condition
|
||||
// loop condition
|
||||
function () { return rev <= self._toRev; },
|
||||
|
||||
//loop body
|
||||
|
||||
// loop body
|
||||
function (callback) {
|
||||
//get the bulk
|
||||
self._getChangesetsInBulk(rev,bulkSize,function(err, changesets, authors){
|
||||
// get the bulk
|
||||
self._getChangesetsInBulk(rev,bulkSize,function(err, changesets, authors) {
|
||||
var addedAuthors = [];
|
||||
|
||||
//run trough all changesets
|
||||
for(var i=0;i<changesets.length && (rev+i)<=self._toRev;i++){
|
||||
|
||||
// run trough all changesets
|
||||
for (var i = 0; i < changesets.length && (rev + i) <= self._toRev; i++) {
|
||||
var changeset = changesets[i];
|
||||
|
||||
//skip clearAuthorship Changesets
|
||||
if(self._isClearAuthorship(changeset)){
|
||||
|
||||
// skip clearAuthorship Changesets
|
||||
if (self._isClearAuthorship(changeset)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
changeset = self._extendChangesetWithAuthor(changeset, authors[i], self._pad.pool);
|
||||
|
||||
//add this author to the authorarray
|
||||
|
||||
// add this author to the authorarray
|
||||
addedAuthors.push(authors[i]);
|
||||
|
||||
//compose it with the superChangset
|
||||
if(superChangeset === null){
|
||||
|
||||
// compose it with the superChangset
|
||||
if (superChangeset === null) {
|
||||
superChangeset = changeset;
|
||||
} else {
|
||||
} else {
|
||||
superChangeset = Changeset.composeWithDeletions(superChangeset, changeset, self._pad.pool);
|
||||
}
|
||||
}
|
||||
|
||||
//add the authors to the PadDiff authorArray
|
||||
|
||||
// add the authors to the PadDiff authorArray
|
||||
self._addAuthors(addedAuthors);
|
||||
|
||||
//lets continue with the next bulk
|
||||
|
||||
// lets continue with the next bulk
|
||||
rev += bulkSize;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
||||
//after the loop has ended
|
||||
|
||||
// after the loop has ended
|
||||
function (err) {
|
||||
//if there are only clearAuthorship changesets, we don't get a superChangeset, so we can skip this step
|
||||
if(superChangeset){
|
||||
// if there are only clearAuthorship changesets, we don't get a superChangeset, so we can skip this step
|
||||
if (superChangeset) {
|
||||
var deletionChangeset = self._createDeletionChangeset(superChangeset,atext,self._pad.pool);
|
||||
|
||||
|
||||
try {
|
||||
//apply the superChangeset, which includes all addings
|
||||
atext = Changeset.applyToAText(superChangeset,atext,self._pad.pool);
|
||||
//apply the deletionChangeset, which adds a deletions
|
||||
atext = Changeset.applyToAText(deletionChangeset,atext,self._pad.pool);
|
||||
// apply the superChangeset, which includes all addings
|
||||
atext = Changeset.applyToAText(superChangeset, atext, self._pad.pool);
|
||||
// apply the deletionChangeset, which adds a deletions
|
||||
atext = Changeset.applyToAText(deletionChangeset, atext, self._pad.pool);
|
||||
} catch(err) {
|
||||
return callback(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
callback(err, atext);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
PadDiff.prototype.getHtml = function(callback){
|
||||
//cache the html
|
||||
if(this._html != null){
|
||||
|
||||
PadDiff.prototype.getHtml = function(callback) {
|
||||
// cache the html
|
||||
if (this._html != null) {
|
||||
return callback(null, this._html);
|
||||
}
|
||||
|
||||
|
||||
var self = this;
|
||||
var atext, html, authorColors;
|
||||
|
||||
|
||||
async.series([
|
||||
//get the diff atext
|
||||
function(callback){
|
||||
self._createDiffAtext(function(err, _atext){
|
||||
if(err){
|
||||
// get the diff atext
|
||||
function(callback) {
|
||||
self._createDiffAtext(function(err, _atext) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
atext = _atext;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//get the authorColor table
|
||||
function(callback){
|
||||
self._pad.getAllAuthorColors(function(err, _authorColors){
|
||||
if(err){
|
||||
|
||||
// get the authorColor table
|
||||
function(callback) {
|
||||
self._pad.getAllAuthorColors(function(err, _authorColors) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
authorColors = _authorColors;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//convert the atext to html
|
||||
function(callback){
|
||||
|
||||
// convert the atext to html
|
||||
function(callback) {
|
||||
html = exportHtml.getHTMLFromAtext(self._pad, atext, authorColors);
|
||||
self._html = html;
|
||||
callback();
|
||||
}
|
||||
], function(err){
|
||||
],
|
||||
function(err) {
|
||||
callback(err, html);
|
||||
});
|
||||
};
|
||||
|
||||
PadDiff.prototype.getAuthors = function(callback){
|
||||
|
||||
PadDiff.prototype.getAuthors = function(callback) {
|
||||
var self = this;
|
||||
|
||||
//check if html was already produced, if not produce it, this generates the author array at the same time
|
||||
if(self._html == null){
|
||||
self.getHtml(function(err){
|
||||
if(err){
|
||||
|
||||
// check if html was already produced, if not produce it, this generates the author array at the same time
|
||||
if (self._html == null) {
|
||||
self.getHtml(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
callback(null, self._authors);
|
||||
});
|
||||
} else {
|
||||
callback(null, self._authors);
|
||||
}
|
||||
};
|
||||
|
||||
PadDiff.prototype._extendChangesetWithAuthor = function(changeset, author, apool) {
|
||||
//unpack
|
||||
var unpacked = Changeset.unpack(changeset);
|
||||
|
||||
|
||||
PadDiff.prototype._extendChangesetWithAuthor = function(changeset, author, apool) {
|
||||
// unpack
|
||||
var unpacked = Changeset.unpack(changeset);
|
||||
|
||||
var iterator = Changeset.opIterator(unpacked.ops);
|
||||
var assem = Changeset.opAssembler();
|
||||
|
||||
//create deleted attribs
|
||||
|
||||
// create deleted attribs
|
||||
var authorAttrib = apool.putAttrib(["author", author || ""]);
|
||||
var deletedAttrib = apool.putAttrib(["removed", true]);
|
||||
var attribs = "*" + Changeset.numToString(authorAttrib) + "*" + Changeset.numToString(deletedAttrib);
|
||||
|
||||
//iteratore over the operators of the changeset
|
||||
while(iterator.hasNext()){
|
||||
|
||||
// iteratore over the operators of the changeset
|
||||
while(iterator.hasNext()) {
|
||||
var operator = iterator.next();
|
||||
|
||||
//this is a delete operator, extend it with the author
|
||||
if(operator.opcode === "-"){
|
||||
|
||||
if (operator.opcode === "-") {
|
||||
// this is a delete operator, extend it with the author
|
||||
operator.attribs = attribs;
|
||||
}
|
||||
//this is operator changes only attributes, let's mark which author did that
|
||||
else if(operator.opcode === "=" && operator.attribs){
|
||||
} else if (operator.opcode === "=" && operator.attribs) {
|
||||
// this is operator changes only attributes, let's mark which author did that
|
||||
operator.attribs+="*"+Changeset.numToString(authorAttrib);
|
||||
}
|
||||
|
||||
//append the new operator to our assembler
|
||||
|
||||
// append the new operator to our assembler
|
||||
assem.append(operator);
|
||||
}
|
||||
|
||||
//return the modified changeset
|
||||
|
||||
// return the modified changeset
|
||||
return Changeset.pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank);
|
||||
};
|
||||
|
||||
//this method is 80% like Changeset.inverse. I just changed so instead of reverting, it adds deletions and attribute changes to to the atext.
|
||||
|
||||
// this method is 80% like Changeset.inverse. I just changed so instead of reverting, it adds deletions and attribute changes to to the atext.
|
||||
PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
||||
var lines = Changeset.splitTextLines(startAText.text);
|
||||
var alines = Changeset.splitAttributionLines(startAText.attribs, startAText.text);
|
||||
|
||||
|
||||
// lines and alines are what the exports is meant to apply to.
|
||||
// They may be arrays or objects with .get(i) and .length methods.
|
||||
// They include final newlines on lines.
|
||||
|
||||
|
||||
function lines_get(idx) {
|
||||
if (lines.get) {
|
||||
return lines.get(idx);
|
||||
|
@ -338,7 +350,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
return lines[idx];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function alines_get(idx) {
|
||||
if (alines.get) {
|
||||
return alines.get(idx);
|
||||
|
@ -346,19 +358,19 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
return alines[idx];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var curLine = 0;
|
||||
var curChar = 0;
|
||||
var curLineOpIter = null;
|
||||
var curLineOpIterLine;
|
||||
var curLineNextOp = Changeset.newOp('+');
|
||||
|
||||
|
||||
var unpacked = Changeset.unpack(cs);
|
||||
var csIter = Changeset.opIterator(unpacked.ops);
|
||||
var builder = Changeset.builder(unpacked.newLen);
|
||||
|
||||
|
||||
function consumeAttribRuns(numChars, func /*(len, attribs, endsLine)*/ ) {
|
||||
|
||||
|
||||
if ((!curLineOpIter) || (curLineOpIterLine != curLine)) {
|
||||
// create curLineOpIter and advance it to curChar
|
||||
curLineOpIter = Changeset.opIterator(alines_get(curLine));
|
||||
|
@ -375,7 +387,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while (numChars > 0) {
|
||||
if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) {
|
||||
curLine++;
|
||||
|
@ -384,22 +396,25 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
curLineNextOp.chars = 0;
|
||||
curLineOpIter = Changeset.opIterator(alines_get(curLine));
|
||||
}
|
||||
|
||||
if (!curLineNextOp.chars) {
|
||||
curLineOpIter.next(curLineNextOp);
|
||||
}
|
||||
|
||||
var charsToUse = Math.min(numChars, curLineNextOp.chars);
|
||||
|
||||
func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0);
|
||||
numChars -= charsToUse;
|
||||
curLineNextOp.chars -= charsToUse;
|
||||
curChar += charsToUse;
|
||||
}
|
||||
|
||||
|
||||
if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) {
|
||||
curLine++;
|
||||
curChar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function skip(N, L) {
|
||||
if (L) {
|
||||
curLine += L;
|
||||
|
@ -412,27 +427,29 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function nextText(numChars) {
|
||||
var len = 0;
|
||||
var assem = Changeset.stringAssembler();
|
||||
var firstString = lines_get(curLine).substring(curChar);
|
||||
len += firstString.length;
|
||||
assem.append(firstString);
|
||||
|
||||
|
||||
var lineNum = curLine + 1;
|
||||
|
||||
while (len < numChars) {
|
||||
var nextString = lines_get(lineNum);
|
||||
len += nextString.length;
|
||||
assem.append(nextString);
|
||||
lineNum++;
|
||||
}
|
||||
|
||||
|
||||
return assem.toString().substring(0, numChars);
|
||||
}
|
||||
|
||||
|
||||
function cachedStrFunc(func) {
|
||||
var cache = {};
|
||||
|
||||
return function (s) {
|
||||
if (!cache[s]) {
|
||||
cache[s] = func(s);
|
||||
|
@ -440,57 +457,59 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
return cache[s];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var attribKeys = [];
|
||||
var attribValues = [];
|
||||
|
||||
//iterate over all operators of this changeset
|
||||
|
||||
// iterate over all operators of this changeset
|
||||
while (csIter.hasNext()) {
|
||||
var csOp = csIter.next();
|
||||
|
||||
if (csOp.opcode == '=') {
|
||||
|
||||
if (csOp.opcode == '=') {
|
||||
var textBank = nextText(csOp.chars);
|
||||
|
||||
|
||||
// decide if this equal operator is an attribution change or not. We can see this by checkinf if attribs is set.
|
||||
// If the text this operator applies to is only a star, than this is a false positive and should be ignored
|
||||
if (csOp.attribs && textBank != "*") {
|
||||
var deletedAttrib = apool.putAttrib(["removed", true]);
|
||||
var authorAttrib = apool.putAttrib(["author", ""]);
|
||||
|
||||
|
||||
attribKeys.length = 0;
|
||||
attribValues.length = 0;
|
||||
Changeset.eachAttribNumber(csOp.attribs, function (n) {
|
||||
attribKeys.push(apool.getAttribKey(n));
|
||||
attribValues.push(apool.getAttribValue(n));
|
||||
|
||||
if(apool.getAttribKey(n) === "author"){
|
||||
|
||||
if (apool.getAttribKey(n) === "author") {
|
||||
authorAttrib = n;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var undoBackToAttribs = cachedStrFunc(function (attribs) {
|
||||
var backAttribs = [];
|
||||
for (var i = 0; i < attribKeys.length; i++) {
|
||||
var appliedKey = attribKeys[i];
|
||||
var appliedValue = attribValues[i];
|
||||
var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, apool);
|
||||
|
||||
if (appliedValue != oldValue) {
|
||||
backAttribs.push([appliedKey, oldValue]);
|
||||
}
|
||||
}
|
||||
|
||||
return Changeset.makeAttribsString('=', backAttribs, apool);
|
||||
});
|
||||
|
||||
|
||||
var oldAttribsAddition = "*" + Changeset.numToString(deletedAttrib) + "*" + Changeset.numToString(authorAttrib);
|
||||
|
||||
|
||||
var textLeftToProcess = textBank;
|
||||
|
||||
while(textLeftToProcess.length > 0){
|
||||
//process till the next line break or process only one line break
|
||||
|
||||
while(textLeftToProcess.length > 0) {
|
||||
// process till the next line break or process only one line break
|
||||
var lengthToProcess = textLeftToProcess.indexOf("\n");
|
||||
var lineBreak = false;
|
||||
switch(lengthToProcess){
|
||||
case -1:
|
||||
switch(lengthToProcess) {
|
||||
case -1:
|
||||
lengthToProcess=textLeftToProcess.length;
|
||||
break;
|
||||
case 0:
|
||||
|
@ -498,27 +517,28 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
lengthToProcess=1;
|
||||
break;
|
||||
}
|
||||
|
||||
//get the text we want to procceed in this step
|
||||
|
||||
// get the text we want to procceed in this step
|
||||
var processText = textLeftToProcess.substr(0, lengthToProcess);
|
||||
|
||||
textLeftToProcess = textLeftToProcess.substr(lengthToProcess);
|
||||
|
||||
if(lineBreak){
|
||||
builder.keep(1, 1); //just skip linebreaks, don't do a insert + keep for a linebreak
|
||||
|
||||
//consume the attributes of this linebreak
|
||||
consumeAttribRuns(1, function(){});
|
||||
|
||||
if (lineBreak) {
|
||||
builder.keep(1, 1); // just skip linebreaks, don't do a insert + keep for a linebreak
|
||||
|
||||
// consume the attributes of this linebreak
|
||||
consumeAttribRuns(1, function() {});
|
||||
} else {
|
||||
//add the old text via an insert, but add a deletion attribute + the author attribute of the author who deleted it
|
||||
// add the old text via an insert, but add a deletion attribute + the author attribute of the author who deleted it
|
||||
var textBankIndex = 0;
|
||||
consumeAttribRuns(lengthToProcess, function (len, attribs, endsLine) {
|
||||
//get the old attributes back
|
||||
// get the old attributes back
|
||||
var attribs = (undoBackToAttribs(attribs) || "") + oldAttribsAddition;
|
||||
|
||||
|
||||
builder.insert(processText.substr(textBankIndex, len), attribs);
|
||||
textBankIndex += len;
|
||||
});
|
||||
|
||||
|
||||
builder.keep(lengthToProcess, 0);
|
||||
}
|
||||
}
|
||||
|
@ -531,16 +551,16 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
|
|||
} else if (csOp.opcode == '-') {
|
||||
var textBank = nextText(csOp.chars);
|
||||
var textBankIndex = 0;
|
||||
|
||||
|
||||
consumeAttribRuns(csOp.chars, function (len, attribs, endsLine) {
|
||||
builder.insert(textBank.substr(textBankIndex, len), attribs + csOp.attribs);
|
||||
textBankIndex += len;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Changeset.checkRep(builder.toString());
|
||||
};
|
||||
|
||||
//export the constructor
|
||||
|
||||
// export the constructor
|
||||
module.exports = PadDiff;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue