@@ -11,11 +11,16 @@ var shortId = require('shortid');
1111var Sequelize = require ( "sequelize" ) ;
1212var async = require ( 'async' ) ;
1313var moment = require ( 'moment' ) ;
14+ var DiffMatchPatch = require ( 'diff-match-patch' ) ;
15+ var dmp = new DiffMatchPatch ( ) ;
1416
1517// core
1618var config = require ( "../config.js" ) ;
1719var logger = require ( "../logger.js" ) ;
1820
21+ //ot
22+ var ot = require ( "../ot/index.js" ) ;
23+
1924// permission types
2025var permissionTypes = [ "freely" , "editable" , "locked" , "private" ] ;
2126
@@ -115,6 +120,7 @@ module.exports = function (sequelize, DataTypes) {
115120 var fsModifiedTime = moment ( fs . statSync ( filePath ) . mtime ) ;
116121 var dbModifiedTime = moment ( note . lastchangeAt || note . createdAt ) ;
117122 var body = fs . readFileSync ( filePath , 'utf8' ) ;
123+ var contentLength = body . length ;
118124 var title = Note . parseNoteTitle ( body ) ;
119125 body = LZString . compressToBase64 ( body ) ;
120126 title = LZString . compressToBase64 ( title ) ;
@@ -126,7 +132,20 @@ module.exports = function (sequelize, DataTypes) {
126132 } ) . then ( function ( note ) {
127133 sequelize . models . Revision . saveNoteRevision ( note , function ( err , revision ) {
128134 if ( err ) return _callback ( err , null ) ;
129- return callback ( null , note . id ) ;
135+ // update authorship on after making revision of docs
136+ var patch = dmp . patch_fromText ( LZString . decompressFromBase64 ( revision . patch ) ) ;
137+ var operations = Note . transformPatchToOperations ( patch , contentLength ) ;
138+ var authorship = note . authorship ? JSON . parse ( LZString . decompressFromBase64 ( note . authorship ) ) : [ ] ;
139+ for ( var i = 0 ; i < operations . length ; i ++ ) {
140+ authorship = Note . updateAuthorshipByOperation ( operations [ i ] , null , authorship ) ;
141+ }
142+ note . update ( {
143+ authorship : authorship
144+ } ) . then ( function ( note ) {
145+ return callback ( null , note . id ) ;
146+ } ) . catch ( function ( err ) {
147+ return _callback ( err , null ) ;
148+ } ) ;
130149 } ) ;
131150 } ) . catch ( function ( err ) {
132151 return _callback ( err , null ) ;
@@ -247,6 +266,162 @@ module.exports = function (sequelize, DataTypes) {
247266 _meta . slideOptions = meta . slideOptions ;
248267 }
249268 return _meta ;
269+ } ,
270+ updateAuthorshipByOperation : function ( operation , userId , authorships ) {
271+ var index = 0 ;
272+ var timestamp = Date . now ( ) ;
273+ for ( var i = 0 ; i < operation . length ; i ++ ) {
274+ var op = operation [ i ] ;
275+ if ( ot . TextOperation . isRetain ( op ) ) {
276+ index += op ;
277+ } else if ( ot . TextOperation . isInsert ( op ) ) {
278+ var opStart = index ;
279+ var opEnd = index + op . length ;
280+ var inserted = false ;
281+ // authorship format: [userId, startPos, endPos, createdAt, updatedAt]
282+ if ( authorships . length <= 0 ) authorships . push ( [ userId , opStart , opEnd , timestamp , timestamp ] ) ;
283+ else {
284+ for ( var j = 0 ; j < authorships . length ; j ++ ) {
285+ var authorship = authorships [ j ] ;
286+ if ( ! inserted ) {
287+ var nextAuthorship = authorships [ j + 1 ] || - 1 ;
288+ if ( nextAuthorship != - 1 && nextAuthorship [ 1 ] >= opEnd || j >= authorships . length - 1 ) {
289+ if ( authorship [ 1 ] < opStart && authorship [ 2 ] > opStart ) {
290+ // divide
291+ var postLength = authorship [ 2 ] - opStart ;
292+ authorship [ 2 ] = opStart ;
293+ authorship [ 4 ] = timestamp ;
294+ authorships . splice ( j + 1 , 0 , [ userId , opStart , opEnd , timestamp , timestamp ] ) ;
295+ authorships . splice ( j + 2 , 0 , [ authorship [ 0 ] , opEnd , opEnd + postLength , authorship [ 3 ] , timestamp ] ) ;
296+ j += 2 ;
297+ inserted = true ;
298+ } else if ( authorship [ 1 ] >= opStart ) {
299+ authorships . splice ( j , 0 , [ userId , opStart , opEnd , timestamp , timestamp ] ) ;
300+ j += 1 ;
301+ inserted = true ;
302+ } else if ( authorship [ 2 ] <= opStart ) {
303+ authorships . splice ( j + 1 , 0 , [ userId , opStart , opEnd , timestamp , timestamp ] ) ;
304+ j += 1 ;
305+ inserted = true ;
306+ }
307+ }
308+ }
309+ if ( authorship [ 1 ] >= opStart ) {
310+ authorship [ 1 ] += op . length ;
311+ authorship [ 2 ] += op . length ;
312+ }
313+ }
314+ }
315+ index += op . length ;
316+ } else if ( ot . TextOperation . isDelete ( op ) ) {
317+ var opStart = index ;
318+ var opEnd = index - op ;
319+ if ( operation . length == 1 ) {
320+ authorships = [ ] ;
321+ } else if ( authorships . length > 0 ) {
322+ for ( var j = 0 ; j < authorships . length ; j ++ ) {
323+ var authorship = authorships [ j ] ;
324+ if ( authorship [ 1 ] >= opStart && authorship [ 1 ] <= opEnd && authorship [ 2 ] >= opStart && authorship [ 2 ] <= opEnd ) {
325+ authorships . splice ( j , 1 ) ;
326+ j -= 1 ;
327+ } else if ( authorship [ 1 ] < opStart && authorship [ 1 ] < opEnd && authorship [ 2 ] > opStart && authorship [ 2 ] > opEnd ) {
328+ authorship [ 2 ] += op ;
329+ authorship [ 4 ] = timestamp ;
330+ } else if ( authorship [ 2 ] >= opStart && authorship [ 2 ] <= opEnd ) {
331+ authorship [ 2 ] = opStart ;
332+ authorship [ 4 ] = timestamp ;
333+ } else if ( authorship [ 1 ] >= opStart && authorship [ 1 ] <= opEnd ) {
334+ authorship [ 1 ] = opEnd ;
335+ authorship [ 4 ] = timestamp ;
336+ }
337+ if ( authorship [ 1 ] >= opEnd ) {
338+ authorship [ 1 ] += op ;
339+ authorship [ 2 ] += op ;
340+ }
341+ }
342+ }
343+ index += op ;
344+ }
345+ }
346+ // merge
347+ for ( var j = 0 ; j < authorships . length ; j ++ ) {
348+ var authorship = authorships [ j ] ;
349+ for ( var k = j + 1 ; k < authorships . length ; k ++ ) {
350+ var nextAuthorship = authorships [ k ] ;
351+ if ( nextAuthorship && authorship [ 0 ] === nextAuthorship [ 0 ] && authorship [ 2 ] === nextAuthorship [ 1 ] ) {
352+ var minTimestamp = Math . min ( authorship [ 3 ] , nextAuthorship [ 3 ] ) ;
353+ var maxTimestamp = Math . max ( authorship [ 3 ] , nextAuthorship [ 3 ] ) ;
354+ authorships . splice ( j , 1 , [ authorship [ 0 ] , authorship [ 1 ] , nextAuthorship [ 2 ] , minTimestamp , maxTimestamp ] ) ;
355+ authorships . splice ( k , 1 ) ;
356+ j -= 1 ;
357+ break ;
358+ }
359+ }
360+ }
361+ // clear
362+ for ( var j = 0 ; j < authorships . length ; j ++ ) {
363+ var authorship = authorships [ j ] ;
364+ if ( ! authorship [ 0 ] ) {
365+ authorships . splice ( j , 1 ) ;
366+ j -= 1 ;
367+ }
368+ }
369+ return authorships ;
370+ } ,
371+ transformPatchToOperations : function ( patch , contentLength ) {
372+ var operations = [ ] ;
373+ if ( patch . length > 0 ) {
374+ // calculate original content length
375+ for ( var j = patch . length - 1 ; j >= 0 ; j -- ) {
376+ var p = patch [ j ] ;
377+ for ( var i = 0 ; i < p . diffs . length ; i ++ ) {
378+ var diff = p . diffs [ i ] ;
379+ switch ( diff [ 0 ] ) {
380+ case 1 : // insert
381+ contentLength -= diff [ 1 ] . length ;
382+ break ;
383+ case - 1 : // delete
384+ contentLength += diff [ 1 ] . length ;
385+ break ;
386+ }
387+ }
388+ }
389+ // generate operations
390+ var bias = 0 ;
391+ var lengthBias = 0 ;
392+ for ( var j = 0 ; j < patch . length ; j ++ ) {
393+ var operation = [ ] ;
394+ var p = patch [ j ] ;
395+ var currIndex = p . start1 ;
396+ var currLength = contentLength - bias ;
397+ for ( var i = 0 ; i < p . diffs . length ; i ++ ) {
398+ var diff = p . diffs [ i ] ;
399+ switch ( diff [ 0 ] ) {
400+ case 0 : // retain
401+ if ( i == 0 ) // first
402+ operation . push ( currIndex + diff [ 1 ] . length ) ;
403+ else if ( i != p . diffs . length - 1 ) // mid
404+ operation . push ( diff [ 1 ] . length ) ;
405+ else // last
406+ operation . push ( currLength + lengthBias - currIndex ) ;
407+ currIndex += diff [ 1 ] . length ;
408+ break ;
409+ case 1 : // insert
410+ operation . push ( diff [ 1 ] ) ;
411+ lengthBias += diff [ 1 ] . length ;
412+ currIndex += diff [ 1 ] . length ;
413+ break ;
414+ case - 1 : // delete
415+ operation . push ( - diff [ 1 ] . length ) ;
416+ bias += diff [ 1 ] . length ;
417+ currIndex += diff [ 1 ] . length ;
418+ break ;
419+ }
420+ }
421+ operations . push ( operation ) ;
422+ }
423+ }
424+ return operations ;
250425 }
251426 } ,
252427 hooks : {
0 commit comments