2021-10-12 16:50:13 +02:00
/ *
THIS IS A GENERATED / BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
* /
'use strict' ;
var obsidian = require ( 'obsidian' ) ;
/ * ! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Copyright ( c ) Microsoft Corporation .
Permission to use , copy , modify , and / or distribute this software for any
purpose with or without fee is hereby granted .
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT ,
INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE , DATA OR PROFITS , WHETHER IN AN ACTION OF CONTRACT , NEGLIGENCE OR
OTHER TORTIOUS ACTION , ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
function _ _awaiter ( thisArg , _arguments , P , generator ) {
function adopt ( value ) { return value instanceof P ? value : new P ( function ( resolve ) { resolve ( value ) ; } ) ; }
return new ( P || ( P = Promise ) ) ( function ( resolve , reject ) {
function fulfilled ( value ) { try { step ( generator . next ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function rejected ( value ) { try { step ( generator [ "throw" ] ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function step ( result ) { result . done ? resolve ( result . value ) : adopt ( result . value ) . then ( fulfilled , rejected ) ; }
step ( ( generator = generator . apply ( thisArg , _arguments || [ ] ) ) . next ( ) ) ;
} ) ;
}
var e = { 105 : e => { e . exports = function ( e ) { return function ( ) { var t = arguments . length ; if ( t ) { for ( var n = [ ] , r = - 1 ; ++ r < t ; ) n [ r ] = arguments [ r ] ; return e . call ( this , n ) } return e . call ( this , [ ] ) } } ; } , 187 : e => { var t , n = "object" == typeof Reflect ? Reflect : null , r = n && "function" == typeof n . apply ? n . apply : function ( e , t , n ) { return Function . prototype . apply . call ( e , t , n ) } ; t = n && "function" == typeof n . ownKeys ? n . ownKeys : Object . getOwnPropertySymbols ? function ( e ) { return Object . getOwnPropertyNames ( e ) . concat ( Object . getOwnPropertySymbols ( e ) ) } : function ( e ) { return Object . getOwnPropertyNames ( e ) } ; var o = Number . isNaN || function ( e ) { return e != e } ; function i ( ) { i . init . call ( this ) ; } e . exports = i , e . exports . once = function ( e , t ) { return new Promise ( ( function ( n , r ) { function o ( n ) { e . removeListener ( t , i ) , r ( n ) ; } function i ( ) { "function" == typeof e . removeListener && e . removeListener ( "error" , o ) , n ( [ ] . slice . call ( arguments ) ) ; } v ( e , t , i , { once : ! 0 } ) , "error" !== t && function ( e , t , n ) { "function" == typeof e . on && v ( e , "error" , t , { once : ! 0 } ) ; } ( e , o ) ; } ) ) } , i . EventEmitter = i , i . prototype . _events = void 0 , i . prototype . _eventsCount = 0 , i . prototype . _maxListeners = void 0 ; var s = 10 ; function a ( e ) { if ( "function" != typeof e ) throw new TypeError ( 'The "listener" argument must be of type Function. Received type ' + typeof e ) } function u ( e ) { return void 0 === e . _maxListeners ? i . defaultMaxListeners : e . _maxListeners } function c ( e , t , n , r ) { var o , i , s , c ; if ( a ( n ) , void 0 === ( i = e . _events ) ? ( i = e . _events = Object . create ( null ) , e . _eventsCount = 0 ) : ( void 0 !== i . newListener && ( e . emit ( "newListener" , t , n . listener ? n . listener : n ) , i = e . _events ) , s = i [ t ] ) , void 0 === s ) s = i [ t ] = n , ++ e . _eventsCount ; else if ( "function" == typeof s ? s = i [ t ] = r ? [ n , s ] : [ s , n ] : r ? s . unshift ( n ) : s . push ( n ) , ( o = u ( e ) ) > 0 && s . length > o && ! s . warned ) { s . warned = ! 0 ; var f = new Error ( "Possible EventEmitter memory leak detected. " + s . length + " " + String ( t ) + " listeners added. Use emitter.setMaxListeners() to increase limit" ) ; f . name = "MaxListenersExceededWarning" , f . emitter = e , f . type = t , f . count = s . length , c = f , console && console . warn && console . warn ( c ) ; } return e } function f ( ) { if ( ! this . fired ) return this . target . removeListener ( this . type , this . wrapFn ) , this . fired = ! 0 , 0 === arguments . length ? this . listener . call ( this . target ) : this . listener . apply ( this . target , arguments ) } function l ( e , t , n ) { var r = { fired : ! 1 , wrapFn : void 0 , target : e , type : t , listener : n } , o = f . bind ( r ) ; return o . listener = n , r . wrapFn = o , o } function d ( e , t , n ) { var r = e . _events ; if ( void 0 === r ) return [ ] ; var o = r [ t ] ; return void 0 === o ? [ ] : "function" == typeof o ? n ? [ o . listener || o ] : [ o ] : n ? function ( e ) { for ( var t = new Array ( e . length ) , n = 0 ; n < t . length ; ++ n ) t [ n ] = e [ n ] . listener || e [ n ] ; return t } ( o ) : p ( o , o . length ) } function h ( e ) { var t = this . _events ; if ( void 0 !== t ) { var n = t [ e ] ; if ( "function" == typeof n ) return 1 ; if ( void 0 !== n ) return n . length } return 0 } function p ( e , t ) { for ( var n = new Array ( t ) , r = 0 ; r < t ; ++ r ) n [ r ] = e [ r ] ; return n } function v ( e , t , n , r ) { if ( "function" == typeof e . on ) r . once ? e . once ( t , n ) : e . on ( t , n ) ; else { if ( "function" != typeof e . addEventListener ) throw new TypeError ( 'The "emitter" argument must be of type EventEmitter. Received type ' + typeof e ) ; e . addEventListener ( t , ( function o ( i ) { r . once && e . removeEventListener ( t , o ) , n ( i ) ; } ) ) ; } } Object . defineProperty ( i , "defaultMaxListeners" , { enumerable : ! 0 , get : function ( ) { return s } , set : function ( e ) { if ( "number" != typeof e || e < 0 || o ( e ) ) throw new RangeError ( 'The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + e + "." ) ; s = e ; } } ) , i . init = function ( ) { void 0 !== this . _events && this . _events !== Object . getPrototypeOf ( this ) . _events || ( this . _events = Object . create ( null ) , this . _eventsCount = 0 ) , this . _maxListeners = this . _maxListeners || void 0 ; } , i . prototype . setMaxListeners = function ( e ) { if ( "number" != typeof e || e < 0 || o ( e ) ) throw new RangeError ( 'The value of "n" is out of range. It must be a non-negative number. Received ' + e + "." ) ; return this . _maxListeners = e , this } , i . prototype . getMaxListeners = function ( ) { return u ( this ) } , i . prototype . emit = function ( e ) { for ( var t = [ ] , n = 1 ; n < arguments . length ; n ++ ) t . push ( arguments [ n ] ) ; var o = "error" === e , i = this . _events ; if ( void 0 !== i ) o = o && void 0 === i . error ; else if ( ! o ) return ! 1 ; if ( o ) { var s ; if ( t . length > 0 && ( s = t [ 0 ] ) , s instanceof Error ) throw s ; var a = new Error ( "Unhandled error." + ( s ? " (" + s . message + ")" : "" ) ) ; throw a . context = s , a } var u = i [ e ] ; if ( void 0 === u ) re
function createCommonjsModule ( fn ) {
var module = { exports : { } } ;
return fn ( module , module . exports ) , module . exports ;
}
/ * *
* Diff Match and Patch
* Copyright 2018 The diff - match - patch Authors .
* https : //github.com/google/diff-match-patch
*
* 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 diffMatchPatch = createCommonjsModule ( function ( module ) {
/ * *
* @ fileoverview Computes the difference between two texts to create a patch .
* Applies the patch onto another text , allowing for errors .
* @ author fraser @ google . com ( Neil Fraser )
* /
/ * *
* Class containing the diff , match and patch methods .
* @ constructor
* /
var diff _match _patch = function ( ) {
// Defaults.
// Redefine these in your program to override the defaults.
// Number of seconds to map a diff before giving up (0 for infinity).
this . Diff _Timeout = 1.0 ;
// Cost of an empty edit operation in terms of edit characters.
this . Diff _EditCost = 4 ;
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
this . Match _Threshold = 0.5 ;
// How far to search for a match (0 = exact location, 1000+ = broad match).
// A match this many characters away from the expected location will add
// 1.0 to the score (0.0 is a perfect match).
this . Match _Distance = 1000 ;
// When deleting a large block of text (over ~64 characters), how close do
// the contents have to be to match the expected contents. (0.0 = perfection,
// 1.0 = very loose). Note that Match_Threshold controls how closely the
// end points of a delete need to match.
this . Patch _DeleteThreshold = 0.5 ;
// Chunk size for context length.
this . Patch _Margin = 4 ;
// The number of bits in an int.
this . Match _MaxBits = 32 ;
} ;
// DIFF FUNCTIONS
/ * *
* The data structure representing a diff is an array of tuples :
* [ [ DIFF _DELETE , 'Hello' ] , [ DIFF _INSERT , 'Goodbye' ] , [ DIFF _EQUAL , ' world.' ] ]
* which means : delete 'Hello' , add 'Goodbye' and keep ' world.'
* /
var DIFF _DELETE = - 1 ;
var DIFF _INSERT = 1 ;
var DIFF _EQUAL = 0 ;
/ * *
* Class representing one diff tuple .
* ~ Attempts to look like a two - element array ( which is what this used to be ) . ~
* Constructor returns an actual two - element array , to allow destructing @ JackuB
* See https : //github.com/JackuB/diff-match-patch/issues/14 for details
* @ param { number } op Operation , one of : DIFF _DELETE , DIFF _INSERT , DIFF _EQUAL .
* @ param { string } text Text to be deleted , inserted , or retained .
* @ constructor
* /
diff _match _patch . Diff = function ( op , text ) {
return [ op , text ] ;
} ;
/ * *
* Find the differences between two texts . Simplifies the problem by stripping
* any common prefix or suffix off the texts before diffing .
* @ param { string } text1 Old string to be diffed .
* @ param { string } text2 New string to be diffed .
* @ param { boolean = } opt _checklines Optional speedup flag . If present and false ,
* then don ' t run a line - level diff first to identify the changed areas .
* Defaults to true , which does a faster , slightly less optimal diff .
* @ param { number = } opt _deadline Optional time when the diff should be complete
* by . Used internally for recursive calls . Users should set DiffTimeout
* instead .
* @ return { ! Array . < ! diff _match _patch . Diff > } Array of diff tuples .
* /
diff _match _patch . prototype . diff _main = function ( text1 , text2 , opt _checklines ,
opt _deadline ) {
// Set a deadline by which time the diff must be complete.
if ( typeof opt _deadline == 'undefined' ) {
if ( this . Diff _Timeout <= 0 ) {
opt _deadline = Number . MAX _VALUE ;
} else {
opt _deadline = ( new Date ) . getTime ( ) + this . Diff _Timeout * 1000 ;
}
}
var deadline = opt _deadline ;
// Check for null inputs.
if ( text1 == null || text2 == null ) {
throw new Error ( 'Null input. (diff_main)' ) ;
}
// Check for equality (speedup).
if ( text1 == text2 ) {
if ( text1 ) {
return [ new diff _match _patch . Diff ( DIFF _EQUAL , text1 ) ] ;
}
return [ ] ;
}
if ( typeof opt _checklines == 'undefined' ) {
opt _checklines = true ;
}
var checklines = opt _checklines ;
// Trim off common prefix (speedup).
var commonlength = this . diff _commonPrefix ( text1 , text2 ) ;
var commonprefix = text1 . substring ( 0 , commonlength ) ;
text1 = text1 . substring ( commonlength ) ;
text2 = text2 . substring ( commonlength ) ;
// Trim off common suffix (speedup).
commonlength = this . diff _commonSuffix ( text1 , text2 ) ;
var commonsuffix = text1 . substring ( text1 . length - commonlength ) ;
text1 = text1 . substring ( 0 , text1 . length - commonlength ) ;
text2 = text2 . substring ( 0 , text2 . length - commonlength ) ;
// Compute the diff on the middle block.
var diffs = this . diff _compute _ ( text1 , text2 , checklines , deadline ) ;
// Restore the prefix and suffix.
if ( commonprefix ) {
diffs . unshift ( new diff _match _patch . Diff ( DIFF _EQUAL , commonprefix ) ) ;
}
if ( commonsuffix ) {
diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , commonsuffix ) ) ;
}
this . diff _cleanupMerge ( diffs ) ;
return diffs ;
} ;
/ * *
* Find the differences between two texts . Assumes that the texts do not
* have any common prefix or suffix .
* @ param { string } text1 Old string to be diffed .
* @ param { string } text2 New string to be diffed .
* @ param { boolean } checklines Speedup flag . If false , then don ' t run a
* line - level diff first to identify the changed areas .
* If true , then run a faster , slightly less optimal diff .
* @ param { number } deadline Time when the diff should be complete by .
* @ return { ! Array . < ! diff _match _patch . Diff > } Array of diff tuples .
* @ private
* /
diff _match _patch . prototype . diff _compute _ = function ( text1 , text2 , checklines ,
deadline ) {
var diffs ;
if ( ! text1 ) {
// Just add some text (speedup).
return [ new diff _match _patch . Diff ( DIFF _INSERT , text2 ) ] ;
}
if ( ! text2 ) {
// Just delete some text (speedup).
return [ new diff _match _patch . Diff ( DIFF _DELETE , text1 ) ] ;
}
var longtext = text1 . length > text2 . length ? text1 : text2 ;
var shorttext = text1 . length > text2 . length ? text2 : text1 ;
var i = longtext . indexOf ( shorttext ) ;
if ( i != - 1 ) {
// Shorter text is inside the longer text (speedup).
diffs = [ new diff _match _patch . Diff ( DIFF _INSERT , longtext . substring ( 0 , i ) ) ,
new diff _match _patch . Diff ( DIFF _EQUAL , shorttext ) ,
new diff _match _patch . Diff ( DIFF _INSERT ,
longtext . substring ( i + shorttext . length ) ) ] ;
// Swap insertions for deletions if diff is reversed.
if ( text1 . length > text2 . length ) {
diffs [ 0 ] [ 0 ] = diffs [ 2 ] [ 0 ] = DIFF _DELETE ;
}
return diffs ;
}
if ( shorttext . length == 1 ) {
// Single character string.
// After the previous speedup, the character can't be an equality.
return [ new diff _match _patch . Diff ( DIFF _DELETE , text1 ) ,
new diff _match _patch . Diff ( DIFF _INSERT , text2 ) ] ;
}
// Check to see if the problem can be split in two.
var hm = this . diff _halfMatch _ ( text1 , text2 ) ;
if ( hm ) {
// A half-match was found, sort out the return data.
var text1 _a = hm [ 0 ] ;
var text1 _b = hm [ 1 ] ;
var text2 _a = hm [ 2 ] ;
var text2 _b = hm [ 3 ] ;
var mid _common = hm [ 4 ] ;
// Send both pairs off for separate processing.
var diffs _a = this . diff _main ( text1 _a , text2 _a , checklines , deadline ) ;
var diffs _b = this . diff _main ( text1 _b , text2 _b , checklines , deadline ) ;
// Merge the results.
return diffs _a . concat ( [ new diff _match _patch . Diff ( DIFF _EQUAL , mid _common ) ] ,
diffs _b ) ;
}
if ( checklines && text1 . length > 100 && text2 . length > 100 ) {
return this . diff _lineMode _ ( text1 , text2 , deadline ) ;
}
return this . diff _bisect _ ( text1 , text2 , deadline ) ;
} ;
/ * *
* Do a quick line - level diff on both strings , then rediff the parts for
* greater accuracy .
* This speedup can produce non - minimal diffs .
* @ param { string } text1 Old string to be diffed .
* @ param { string } text2 New string to be diffed .
* @ param { number } deadline Time when the diff should be complete by .
* @ return { ! Array . < ! diff _match _patch . Diff > } Array of diff tuples .
* @ private
* /
diff _match _patch . prototype . diff _lineMode _ = function ( text1 , text2 , deadline ) {
// Scan the text on a line-by-line basis first.
var a = this . diff _linesToChars _ ( text1 , text2 ) ;
text1 = a . chars1 ;
text2 = a . chars2 ;
var linearray = a . lineArray ;
var diffs = this . diff _main ( text1 , text2 , false , deadline ) ;
// Convert the diff back to original text.
this . diff _charsToLines _ ( diffs , linearray ) ;
// Eliminate freak matches (e.g. blank lines)
this . diff _cleanupSemantic ( diffs ) ;
// Rediff any replacement blocks, this time character-by-character.
// Add a dummy entry at the end.
diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , '' ) ) ;
var pointer = 0 ;
var count _delete = 0 ;
var count _insert = 0 ;
var text _delete = '' ;
var text _insert = '' ;
while ( pointer < diffs . length ) {
switch ( diffs [ pointer ] [ 0 ] ) {
case DIFF _INSERT :
count _insert ++ ;
text _insert += diffs [ pointer ] [ 1 ] ;
break ;
case DIFF _DELETE :
count _delete ++ ;
text _delete += diffs [ pointer ] [ 1 ] ;
break ;
case DIFF _EQUAL :
// Upon reaching an equality, check for prior redundancies.
if ( count _delete >= 1 && count _insert >= 1 ) {
// Delete the offending records and add the merged ones.
diffs . splice ( pointer - count _delete - count _insert ,
count _delete + count _insert ) ;
pointer = pointer - count _delete - count _insert ;
var subDiff =
this . diff _main ( text _delete , text _insert , false , deadline ) ;
for ( var j = subDiff . length - 1 ; j >= 0 ; j -- ) {
diffs . splice ( pointer , 0 , subDiff [ j ] ) ;
}
pointer = pointer + subDiff . length ;
}
count _insert = 0 ;
count _delete = 0 ;
text _delete = '' ;
text _insert = '' ;
break ;
}
pointer ++ ;
}
diffs . pop ( ) ; // Remove the dummy entry at the end.
return diffs ;
} ;
/ * *
* Find the 'middle snake' of a diff , split the problem in two
* and return the recursively constructed diff .
* See Myers 1986 paper : An O ( ND ) Difference Algorithm and Its Variations .
* @ param { string } text1 Old string to be diffed .
* @ param { string } text2 New string to be diffed .
* @ param { number } deadline Time at which to bail if not yet complete .
* @ return { ! Array . < ! diff _match _patch . Diff > } Array of diff tuples .
* @ private
* /
diff _match _patch . prototype . diff _bisect _ = function ( text1 , text2 , deadline ) {
// Cache the text lengths to prevent multiple calls.
var text1 _length = text1 . length ;
var text2 _length = text2 . length ;
var max _d = Math . ceil ( ( text1 _length + text2 _length ) / 2 ) ;
var v _offset = max _d ;
var v _length = 2 * max _d ;
var v1 = new Array ( v _length ) ;
var v2 = new Array ( v _length ) ;
// Setting all elements to -1 is faster in Chrome & Firefox than mixing
// integers and undefined.
for ( var x = 0 ; x < v _length ; x ++ ) {
v1 [ x ] = - 1 ;
v2 [ x ] = - 1 ;
}
v1 [ v _offset + 1 ] = 0 ;
v2 [ v _offset + 1 ] = 0 ;
var delta = text1 _length - text2 _length ;
// If the total number of characters is odd, then the front path will collide
// with the reverse path.
var front = ( delta % 2 != 0 ) ;
// Offsets for start and end of k loop.
// Prevents mapping of space beyond the grid.
var k1start = 0 ;
var k1end = 0 ;
var k2start = 0 ;
var k2end = 0 ;
for ( var d = 0 ; d < max _d ; d ++ ) {
// Bail out if deadline is reached.
if ( ( new Date ( ) ) . getTime ( ) > deadline ) {
break ;
}
// Walk the front path one step.
for ( var k1 = - d + k1start ; k1 <= d - k1end ; k1 += 2 ) {
var k1 _offset = v _offset + k1 ;
var x1 ;
if ( k1 == - d || ( k1 != d && v1 [ k1 _offset - 1 ] < v1 [ k1 _offset + 1 ] ) ) {
x1 = v1 [ k1 _offset + 1 ] ;
} else {
x1 = v1 [ k1 _offset - 1 ] + 1 ;
}
var y1 = x1 - k1 ;
while ( x1 < text1 _length && y1 < text2 _length &&
text1 . charAt ( x1 ) == text2 . charAt ( y1 ) ) {
x1 ++ ;
y1 ++ ;
}
v1 [ k1 _offset ] = x1 ;
if ( x1 > text1 _length ) {
// Ran off the right of the graph.
k1end += 2 ;
} else if ( y1 > text2 _length ) {
// Ran off the bottom of the graph.
k1start += 2 ;
} else if ( front ) {
var k2 _offset = v _offset + delta - k1 ;
if ( k2 _offset >= 0 && k2 _offset < v _length && v2 [ k2 _offset ] != - 1 ) {
// Mirror x2 onto top-left coordinate system.
var x2 = text1 _length - v2 [ k2 _offset ] ;
if ( x1 >= x2 ) {
// Overlap detected.
return this . diff _bisectSplit _ ( text1 , text2 , x1 , y1 , deadline ) ;
}
}
}
}
// Walk the reverse path one step.
for ( var k2 = - d + k2start ; k2 <= d - k2end ; k2 += 2 ) {
var k2 _offset = v _offset + k2 ;
var x2 ;
if ( k2 == - d || ( k2 != d && v2 [ k2 _offset - 1 ] < v2 [ k2 _offset + 1 ] ) ) {
x2 = v2 [ k2 _offset + 1 ] ;
} else {
x2 = v2 [ k2 _offset - 1 ] + 1 ;
}
var y2 = x2 - k2 ;
while ( x2 < text1 _length && y2 < text2 _length &&
text1 . charAt ( text1 _length - x2 - 1 ) ==
text2 . charAt ( text2 _length - y2 - 1 ) ) {
x2 ++ ;
y2 ++ ;
}
v2 [ k2 _offset ] = x2 ;
if ( x2 > text1 _length ) {
// Ran off the left of the graph.
k2end += 2 ;
} else if ( y2 > text2 _length ) {
// Ran off the top of the graph.
k2start += 2 ;
} else if ( ! front ) {
var k1 _offset = v _offset + delta - k2 ;
if ( k1 _offset >= 0 && k1 _offset < v _length && v1 [ k1 _offset ] != - 1 ) {
var x1 = v1 [ k1 _offset ] ;
var y1 = v _offset + x1 - k1 _offset ;
// Mirror x2 onto top-left coordinate system.
x2 = text1 _length - x2 ;
if ( x1 >= x2 ) {
// Overlap detected.
return this . diff _bisectSplit _ ( text1 , text2 , x1 , y1 , deadline ) ;
}
}
}
}
}
// Diff took too long and hit the deadline or
// number of diffs equals number of characters, no commonality at all.
return [ new diff _match _patch . Diff ( DIFF _DELETE , text1 ) ,
new diff _match _patch . Diff ( DIFF _INSERT , text2 ) ] ;
} ;
/ * *
* Given the location of the 'middle snake' , split the diff in two parts
* and recurse .
* @ param { string } text1 Old string to be diffed .
* @ param { string } text2 New string to be diffed .
* @ param { number } x Index of split point in text1 .
* @ param { number } y Index of split point in text2 .
* @ param { number } deadline Time at which to bail if not yet complete .
* @ return { ! Array . < ! diff _match _patch . Diff > } Array of diff tuples .
* @ private
* /
diff _match _patch . prototype . diff _bisectSplit _ = function ( text1 , text2 , x , y ,
deadline ) {
var text1a = text1 . substring ( 0 , x ) ;
var text2a = text2 . substring ( 0 , y ) ;
var text1b = text1 . substring ( x ) ;
var text2b = text2 . substring ( y ) ;
// Compute both diffs serially.
var diffs = this . diff _main ( text1a , text2a , false , deadline ) ;
var diffsb = this . diff _main ( text1b , text2b , false , deadline ) ;
return diffs . concat ( diffsb ) ;
} ;
/ * *
* Split two texts into an array of strings . Reduce the texts to a string of
* hashes where each Unicode character represents one line .
* @ param { string } text1 First string .
* @ param { string } text2 Second string .
* @ return { { chars1 : string , chars2 : string , lineArray : ! Array . < string > } }
* An object containing the encoded text1 , the encoded text2 and
* the array of unique strings .
* The zeroth element of the array of unique strings is intentionally blank .
* @ private
* /
diff _match _patch . prototype . diff _linesToChars _ = function ( text1 , text2 ) {
var lineArray = [ ] ; // e.g. lineArray[4] == 'Hello\n'
var lineHash = { } ; // e.g. lineHash['Hello\n'] == 4
// '\x00' is a valid character, but various debuggers don't like it.
// So we'll insert a junk entry to avoid generating a null character.
lineArray [ 0 ] = '' ;
/ * *
* Split a text into an array of strings . Reduce the texts to a string of
* hashes where each Unicode character represents one line .
* Modifies linearray and linehash through being a closure .
* @ param { string } text String to encode .
* @ return { string } Encoded string .
* @ private
* /
function diff _linesToCharsMunge _ ( text ) {
var chars = '' ;
// Walk the text, pulling out a substring for each line.
// text.split('\n') would would temporarily double our memory footprint.
// Modifying text would create many large strings to garbage collect.
var lineStart = 0 ;
var lineEnd = - 1 ;
// Keeping our own length variable is faster than looking it up.
var lineArrayLength = lineArray . length ;
while ( lineEnd < text . length - 1 ) {
lineEnd = text . indexOf ( '\n' , lineStart ) ;
if ( lineEnd == - 1 ) {
lineEnd = text . length - 1 ;
}
var line = text . substring ( lineStart , lineEnd + 1 ) ;
if ( lineHash . hasOwnProperty ? lineHash . hasOwnProperty ( line ) :
( lineHash [ line ] !== undefined ) ) {
chars += String . fromCharCode ( lineHash [ line ] ) ;
} else {
if ( lineArrayLength == maxLines ) {
// Bail out at 65535 because
// String.fromCharCode(65536) == String.fromCharCode(0)
line = text . substring ( lineStart ) ;
lineEnd = text . length ;
}
chars += String . fromCharCode ( lineArrayLength ) ;
lineHash [ line ] = lineArrayLength ;
lineArray [ lineArrayLength ++ ] = line ;
}
lineStart = lineEnd + 1 ;
}
return chars ;
}
// Allocate 2/3rds of the space for text1, the rest for text2.
var maxLines = 40000 ;
var chars1 = diff _linesToCharsMunge _ ( text1 ) ;
maxLines = 65535 ;
var chars2 = diff _linesToCharsMunge _ ( text2 ) ;
return { chars1 : chars1 , chars2 : chars2 , lineArray : lineArray } ;
} ;
/ * *
* Rehydrate the text in a diff from a string of line hashes to real lines of
* text .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ param { ! Array . < string > } lineArray Array of unique strings .
* @ private
* /
diff _match _patch . prototype . diff _charsToLines _ = function ( diffs , lineArray ) {
for ( var i = 0 ; i < diffs . length ; i ++ ) {
var chars = diffs [ i ] [ 1 ] ;
var text = [ ] ;
for ( var j = 0 ; j < chars . length ; j ++ ) {
text [ j ] = lineArray [ chars . charCodeAt ( j ) ] ;
}
diffs [ i ] [ 1 ] = text . join ( '' ) ;
}
} ;
/ * *
* Determine the common prefix of two strings .
* @ param { string } text1 First string .
* @ param { string } text2 Second string .
* @ return { number } The number of characters common to the start of each
* string .
* /
diff _match _patch . prototype . diff _commonPrefix = function ( text1 , text2 ) {
// Quick check for common null cases.
if ( ! text1 || ! text2 || text1 . charAt ( 0 ) != text2 . charAt ( 0 ) ) {
return 0 ;
}
// Binary search.
// Performance analysis: https://neil.fraser.name/news/2007/10/09/
var pointermin = 0 ;
var pointermax = Math . min ( text1 . length , text2 . length ) ;
var pointermid = pointermax ;
var pointerstart = 0 ;
while ( pointermin < pointermid ) {
if ( text1 . substring ( pointerstart , pointermid ) ==
text2 . substring ( pointerstart , pointermid ) ) {
pointermin = pointermid ;
pointerstart = pointermin ;
} else {
pointermax = pointermid ;
}
pointermid = Math . floor ( ( pointermax - pointermin ) / 2 + pointermin ) ;
}
return pointermid ;
} ;
/ * *
* Determine the common suffix of two strings .
* @ param { string } text1 First string .
* @ param { string } text2 Second string .
* @ return { number } The number of characters common to the end of each string .
* /
diff _match _patch . prototype . diff _commonSuffix = function ( text1 , text2 ) {
// Quick check for common null cases.
if ( ! text1 || ! text2 ||
text1 . charAt ( text1 . length - 1 ) != text2 . charAt ( text2 . length - 1 ) ) {
return 0 ;
}
// Binary search.
// Performance analysis: https://neil.fraser.name/news/2007/10/09/
var pointermin = 0 ;
var pointermax = Math . min ( text1 . length , text2 . length ) ;
var pointermid = pointermax ;
var pointerend = 0 ;
while ( pointermin < pointermid ) {
if ( text1 . substring ( text1 . length - pointermid , text1 . length - pointerend ) ==
text2 . substring ( text2 . length - pointermid , text2 . length - pointerend ) ) {
pointermin = pointermid ;
pointerend = pointermin ;
} else {
pointermax = pointermid ;
}
pointermid = Math . floor ( ( pointermax - pointermin ) / 2 + pointermin ) ;
}
return pointermid ;
} ;
/ * *
* Determine if the suffix of one string is the prefix of another .
* @ param { string } text1 First string .
* @ param { string } text2 Second string .
* @ return { number } The number of characters common to the end of the first
* string and the start of the second string .
* @ private
* /
diff _match _patch . prototype . diff _commonOverlap _ = function ( text1 , text2 ) {
// Cache the text lengths to prevent multiple calls.
var text1 _length = text1 . length ;
var text2 _length = text2 . length ;
// Eliminate the null case.
if ( text1 _length == 0 || text2 _length == 0 ) {
return 0 ;
}
// Truncate the longer string.
if ( text1 _length > text2 _length ) {
text1 = text1 . substring ( text1 _length - text2 _length ) ;
} else if ( text1 _length < text2 _length ) {
text2 = text2 . substring ( 0 , text1 _length ) ;
}
var text _length = Math . min ( text1 _length , text2 _length ) ;
// Quick check for the worst case.
if ( text1 == text2 ) {
return text _length ;
}
// Start by looking for a single character match
// and increase length until no match is found.
// Performance analysis: https://neil.fraser.name/news/2010/11/04/
var best = 0 ;
var length = 1 ;
while ( true ) {
var pattern = text1 . substring ( text _length - length ) ;
var found = text2 . indexOf ( pattern ) ;
if ( found == - 1 ) {
return best ;
}
length += found ;
if ( found == 0 || text1 . substring ( text _length - length ) ==
text2 . substring ( 0 , length ) ) {
best = length ;
length ++ ;
}
}
} ;
/ * *
* Do the two texts share a substring which is at least half the length of the
* longer text ?
* This speedup can produce non - minimal diffs .
* @ param { string } text1 First string .
* @ param { string } text2 Second string .
* @ return { Array . < string > } Five element Array , containing the prefix of
* text1 , the suffix of text1 , the prefix of text2 , the suffix of
* text2 and the common middle . Or null if there was no match .
* @ private
* /
diff _match _patch . prototype . diff _halfMatch _ = function ( text1 , text2 ) {
if ( this . Diff _Timeout <= 0 ) {
// Don't risk returning a non-optimal diff if we have unlimited time.
return null ;
}
var longtext = text1 . length > text2 . length ? text1 : text2 ;
var shorttext = text1 . length > text2 . length ? text2 : text1 ;
if ( longtext . length < 4 || shorttext . length * 2 < longtext . length ) {
return null ; // Pointless.
}
var dmp = this ; // 'this' becomes 'window' in a closure.
/ * *
* Does a substring of shorttext exist within longtext such that the substring
* is at least half the length of longtext ?
* Closure , but does not reference any external variables .
* @ param { string } longtext Longer string .
* @ param { string } shorttext Shorter string .
* @ param { number } i Start index of quarter length substring within longtext .
* @ return { Array . < string > } Five element Array , containing the prefix of
* longtext , the suffix of longtext , the prefix of shorttext , the suffix
* of shorttext and the common middle . Or null if there was no match .
* @ private
* /
function diff _halfMatchI _ ( longtext , shorttext , i ) {
// Start with a 1/4 length substring at position i as a seed.
var seed = longtext . substring ( i , i + Math . floor ( longtext . length / 4 ) ) ;
var j = - 1 ;
var best _common = '' ;
var best _longtext _a , best _longtext _b , best _shorttext _a , best _shorttext _b ;
while ( ( j = shorttext . indexOf ( seed , j + 1 ) ) != - 1 ) {
var prefixLength = dmp . diff _commonPrefix ( longtext . substring ( i ) ,
shorttext . substring ( j ) ) ;
var suffixLength = dmp . diff _commonSuffix ( longtext . substring ( 0 , i ) ,
shorttext . substring ( 0 , j ) ) ;
if ( best _common . length < suffixLength + prefixLength ) {
best _common = shorttext . substring ( j - suffixLength , j ) +
shorttext . substring ( j , j + prefixLength ) ;
best _longtext _a = longtext . substring ( 0 , i - suffixLength ) ;
best _longtext _b = longtext . substring ( i + prefixLength ) ;
best _shorttext _a = shorttext . substring ( 0 , j - suffixLength ) ;
best _shorttext _b = shorttext . substring ( j + prefixLength ) ;
}
}
if ( best _common . length * 2 >= longtext . length ) {
return [ best _longtext _a , best _longtext _b ,
best _shorttext _a , best _shorttext _b , best _common ] ;
} else {
return null ;
}
}
// First check if the second quarter is the seed for a half-match.
var hm1 = diff _halfMatchI _ ( longtext , shorttext ,
Math . ceil ( longtext . length / 4 ) ) ;
// Check again based on the third quarter.
var hm2 = diff _halfMatchI _ ( longtext , shorttext ,
Math . ceil ( longtext . length / 2 ) ) ;
var hm ;
if ( ! hm1 && ! hm2 ) {
return null ;
} else if ( ! hm2 ) {
hm = hm1 ;
} else if ( ! hm1 ) {
hm = hm2 ;
} else {
// Both matched. Select the longest.
hm = hm1 [ 4 ] . length > hm2 [ 4 ] . length ? hm1 : hm2 ;
}
// A half-match was found, sort out the return data.
var text1 _a , text1 _b , text2 _a , text2 _b ;
if ( text1 . length > text2 . length ) {
text1 _a = hm [ 0 ] ;
text1 _b = hm [ 1 ] ;
text2 _a = hm [ 2 ] ;
text2 _b = hm [ 3 ] ;
} else {
text2 _a = hm [ 0 ] ;
text2 _b = hm [ 1 ] ;
text1 _a = hm [ 2 ] ;
text1 _b = hm [ 3 ] ;
}
var mid _common = hm [ 4 ] ;
return [ text1 _a , text1 _b , text2 _a , text2 _b , mid _common ] ;
} ;
/ * *
* Reduce the number of edits by eliminating semantically trivial equalities .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* /
diff _match _patch . prototype . diff _cleanupSemantic = function ( diffs ) {
var changes = false ;
var equalities = [ ] ; // Stack of indices where equalities are found.
var equalitiesLength = 0 ; // Keeping our own length var is faster in JS.
/** @type {?string} */
var lastEquality = null ;
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
var pointer = 0 ; // Index of current position.
// Number of characters that changed prior to the equality.
var length _insertions1 = 0 ;
var length _deletions1 = 0 ;
// Number of characters that changed after the equality.
var length _insertions2 = 0 ;
var length _deletions2 = 0 ;
while ( pointer < diffs . length ) {
if ( diffs [ pointer ] [ 0 ] == DIFF _EQUAL ) { // Equality found.
equalities [ equalitiesLength ++ ] = pointer ;
length _insertions1 = length _insertions2 ;
length _deletions1 = length _deletions2 ;
length _insertions2 = 0 ;
length _deletions2 = 0 ;
lastEquality = diffs [ pointer ] [ 1 ] ;
} else { // An insertion or deletion.
if ( diffs [ pointer ] [ 0 ] == DIFF _INSERT ) {
length _insertions2 += diffs [ pointer ] [ 1 ] . length ;
} else {
length _deletions2 += diffs [ pointer ] [ 1 ] . length ;
}
// Eliminate an equality that is smaller or equal to the edits on both
// sides of it.
if ( lastEquality && ( lastEquality . length <=
Math . max ( length _insertions1 , length _deletions1 ) ) &&
( lastEquality . length <= Math . max ( length _insertions2 ,
length _deletions2 ) ) ) {
// Duplicate record.
diffs . splice ( equalities [ equalitiesLength - 1 ] , 0 ,
new diff _match _patch . Diff ( DIFF _DELETE , lastEquality ) ) ;
// Change second copy to insert.
diffs [ equalities [ equalitiesLength - 1 ] + 1 ] [ 0 ] = DIFF _INSERT ;
// Throw away the equality we just deleted.
equalitiesLength -- ;
// Throw away the previous equality (it needs to be reevaluated).
equalitiesLength -- ;
pointer = equalitiesLength > 0 ? equalities [ equalitiesLength - 1 ] : - 1 ;
length _insertions1 = 0 ; // Reset the counters.
length _deletions1 = 0 ;
length _insertions2 = 0 ;
length _deletions2 = 0 ;
lastEquality = null ;
changes = true ;
}
}
pointer ++ ;
}
// Normalize the diff.
if ( changes ) {
this . diff _cleanupMerge ( diffs ) ;
}
this . diff _cleanupSemanticLossless ( diffs ) ;
// Find any overlaps between deletions and insertions.
// e.g: <del>abcxxx</del><ins>xxxdef</ins>
// -> <del>abc</del>xxx<ins>def</ins>
// e.g: <del>xxxabc</del><ins>defxxx</ins>
// -> <ins>def</ins>xxx<del>abc</del>
// Only extract an overlap if it is as big as the edit ahead or behind it.
pointer = 1 ;
while ( pointer < diffs . length ) {
if ( diffs [ pointer - 1 ] [ 0 ] == DIFF _DELETE &&
diffs [ pointer ] [ 0 ] == DIFF _INSERT ) {
var deletion = diffs [ pointer - 1 ] [ 1 ] ;
var insertion = diffs [ pointer ] [ 1 ] ;
var overlap _length1 = this . diff _commonOverlap _ ( deletion , insertion ) ;
var overlap _length2 = this . diff _commonOverlap _ ( insertion , deletion ) ;
if ( overlap _length1 >= overlap _length2 ) {
if ( overlap _length1 >= deletion . length / 2 ||
overlap _length1 >= insertion . length / 2 ) {
// Overlap found. Insert an equality and trim the surrounding edits.
diffs . splice ( pointer , 0 , new diff _match _patch . Diff ( DIFF _EQUAL ,
insertion . substring ( 0 , overlap _length1 ) ) ) ;
diffs [ pointer - 1 ] [ 1 ] =
deletion . substring ( 0 , deletion . length - overlap _length1 ) ;
diffs [ pointer + 1 ] [ 1 ] = insertion . substring ( overlap _length1 ) ;
pointer ++ ;
}
} else {
if ( overlap _length2 >= deletion . length / 2 ||
overlap _length2 >= insertion . length / 2 ) {
// Reverse overlap found.
// Insert an equality and swap and trim the surrounding edits.
diffs . splice ( pointer , 0 , new diff _match _patch . Diff ( DIFF _EQUAL ,
deletion . substring ( 0 , overlap _length2 ) ) ) ;
diffs [ pointer - 1 ] [ 0 ] = DIFF _INSERT ;
diffs [ pointer - 1 ] [ 1 ] =
insertion . substring ( 0 , insertion . length - overlap _length2 ) ;
diffs [ pointer + 1 ] [ 0 ] = DIFF _DELETE ;
diffs [ pointer + 1 ] [ 1 ] =
deletion . substring ( overlap _length2 ) ;
pointer ++ ;
}
}
pointer ++ ;
}
pointer ++ ;
}
} ;
/ * *
* Look for single edits surrounded on both sides by equalities
* which can be shifted sideways to align the edit to a word boundary .
* e . g : The c < ins > at c < / i n s > a m e . - > T h e < i n s > c a t < / i n s > c a m e .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* /
diff _match _patch . prototype . diff _cleanupSemanticLossless = function ( diffs ) {
/ * *
* Given two strings , compute a score representing whether the internal
* boundary falls on logical boundaries .
* Scores range from 6 ( best ) to 0 ( worst ) .
* Closure , but does not reference any external variables .
* @ param { string } one First string .
* @ param { string } two Second string .
* @ return { number } The score .
* @ private
* /
function diff _cleanupSemanticScore _ ( one , two ) {
if ( ! one || ! two ) {
// Edges are the best.
return 6 ;
}
// Each port of this function behaves slightly differently due to
// subtle differences in each language's definition of things like
// 'whitespace'. Since this function's purpose is largely cosmetic,
// the choice has been made to use each language's native features
// rather than force total conformity.
var char1 = one . charAt ( one . length - 1 ) ;
var char2 = two . charAt ( 0 ) ;
var nonAlphaNumeric1 = char1 . match ( diff _match _patch . nonAlphaNumericRegex _ ) ;
var nonAlphaNumeric2 = char2 . match ( diff _match _patch . nonAlphaNumericRegex _ ) ;
var whitespace1 = nonAlphaNumeric1 &&
char1 . match ( diff _match _patch . whitespaceRegex _ ) ;
var whitespace2 = nonAlphaNumeric2 &&
char2 . match ( diff _match _patch . whitespaceRegex _ ) ;
var lineBreak1 = whitespace1 &&
char1 . match ( diff _match _patch . linebreakRegex _ ) ;
var lineBreak2 = whitespace2 &&
char2 . match ( diff _match _patch . linebreakRegex _ ) ;
var blankLine1 = lineBreak1 &&
one . match ( diff _match _patch . blanklineEndRegex _ ) ;
var blankLine2 = lineBreak2 &&
two . match ( diff _match _patch . blanklineStartRegex _ ) ;
if ( blankLine1 || blankLine2 ) {
// Five points for blank lines.
return 5 ;
} else if ( lineBreak1 || lineBreak2 ) {
// Four points for line breaks.
return 4 ;
} else if ( nonAlphaNumeric1 && ! whitespace1 && whitespace2 ) {
// Three points for end of sentences.
return 3 ;
} else if ( whitespace1 || whitespace2 ) {
// Two points for whitespace.
return 2 ;
} else if ( nonAlphaNumeric1 || nonAlphaNumeric2 ) {
// One point for non-alphanumeric.
return 1 ;
}
return 0 ;
}
var pointer = 1 ;
// Intentionally ignore the first and last element (don't need checking).
while ( pointer < diffs . length - 1 ) {
if ( diffs [ pointer - 1 ] [ 0 ] == DIFF _EQUAL &&
diffs [ pointer + 1 ] [ 0 ] == DIFF _EQUAL ) {
// This is a single edit surrounded by equalities.
var equality1 = diffs [ pointer - 1 ] [ 1 ] ;
var edit = diffs [ pointer ] [ 1 ] ;
var equality2 = diffs [ pointer + 1 ] [ 1 ] ;
// First, shift the edit as far left as possible.
var commonOffset = this . diff _commonSuffix ( equality1 , edit ) ;
if ( commonOffset ) {
var commonString = edit . substring ( edit . length - commonOffset ) ;
equality1 = equality1 . substring ( 0 , equality1 . length - commonOffset ) ;
edit = commonString + edit . substring ( 0 , edit . length - commonOffset ) ;
equality2 = commonString + equality2 ;
}
// Second, step character by character right, looking for the best fit.
var bestEquality1 = equality1 ;
var bestEdit = edit ;
var bestEquality2 = equality2 ;
var bestScore = diff _cleanupSemanticScore _ ( equality1 , edit ) +
diff _cleanupSemanticScore _ ( edit , equality2 ) ;
while ( edit . charAt ( 0 ) === equality2 . charAt ( 0 ) ) {
equality1 += edit . charAt ( 0 ) ;
edit = edit . substring ( 1 ) + equality2 . charAt ( 0 ) ;
equality2 = equality2 . substring ( 1 ) ;
var score = diff _cleanupSemanticScore _ ( equality1 , edit ) +
diff _cleanupSemanticScore _ ( edit , equality2 ) ;
// The >= encourages trailing rather than leading whitespace on edits.
if ( score >= bestScore ) {
bestScore = score ;
bestEquality1 = equality1 ;
bestEdit = edit ;
bestEquality2 = equality2 ;
}
}
if ( diffs [ pointer - 1 ] [ 1 ] != bestEquality1 ) {
// We have an improvement, save it back to the diff.
if ( bestEquality1 ) {
diffs [ pointer - 1 ] [ 1 ] = bestEquality1 ;
} else {
diffs . splice ( pointer - 1 , 1 ) ;
pointer -- ;
}
diffs [ pointer ] [ 1 ] = bestEdit ;
if ( bestEquality2 ) {
diffs [ pointer + 1 ] [ 1 ] = bestEquality2 ;
} else {
diffs . splice ( pointer + 1 , 1 ) ;
pointer -- ;
}
}
}
pointer ++ ;
}
} ;
// Define some regex patterns for matching boundaries.
diff _match _patch . nonAlphaNumericRegex _ = /[^a-zA-Z0-9]/ ;
diff _match _patch . whitespaceRegex _ = /\s/ ;
diff _match _patch . linebreakRegex _ = /[\r\n]/ ;
diff _match _patch . blanklineEndRegex _ = /\n\r?\n$/ ;
diff _match _patch . blanklineStartRegex _ = /^\r?\n\r?\n/ ;
/ * *
* Reduce the number of edits by eliminating operationally trivial equalities .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* /
diff _match _patch . prototype . diff _cleanupEfficiency = function ( diffs ) {
var changes = false ;
var equalities = [ ] ; // Stack of indices where equalities are found.
var equalitiesLength = 0 ; // Keeping our own length var is faster in JS.
/** @type {?string} */
var lastEquality = null ;
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
var pointer = 0 ; // Index of current position.
// Is there an insertion operation before the last equality.
var pre _ins = false ;
// Is there a deletion operation before the last equality.
var pre _del = false ;
// Is there an insertion operation after the last equality.
var post _ins = false ;
// Is there a deletion operation after the last equality.
var post _del = false ;
while ( pointer < diffs . length ) {
if ( diffs [ pointer ] [ 0 ] == DIFF _EQUAL ) { // Equality found.
if ( diffs [ pointer ] [ 1 ] . length < this . Diff _EditCost &&
( post _ins || post _del ) ) {
// Candidate found.
equalities [ equalitiesLength ++ ] = pointer ;
pre _ins = post _ins ;
pre _del = post _del ;
lastEquality = diffs [ pointer ] [ 1 ] ;
} else {
// Not a candidate, and can never become one.
equalitiesLength = 0 ;
lastEquality = null ;
}
post _ins = post _del = false ;
} else { // An insertion or deletion.
if ( diffs [ pointer ] [ 0 ] == DIFF _DELETE ) {
post _del = true ;
} else {
post _ins = true ;
}
/ *
* Five types to be split :
* < ins > A < / i n s > < d e l > B < / d e l > X Y < i n s > C < / i n s > < d e l > D < / d e l >
* < ins > A < / i n s > X < i n s > C < / i n s > < d e l > D < / d e l >
* < ins > A < / i n s > < d e l > B < / d e l > X < i n s > C < / i n s >
* < ins > A < / d e l > X < i n s > C < / i n s > < d e l > D < / d e l >
* < ins > A < / i n s > < d e l > B < / d e l > X < d e l > C < / d e l >
* /
if ( lastEquality && ( ( pre _ins && pre _del && post _ins && post _del ) ||
( ( lastEquality . length < this . Diff _EditCost / 2 ) &&
( pre _ins + pre _del + post _ins + post _del ) == 3 ) ) ) {
// Duplicate record.
diffs . splice ( equalities [ equalitiesLength - 1 ] , 0 ,
new diff _match _patch . Diff ( DIFF _DELETE , lastEquality ) ) ;
// Change second copy to insert.
diffs [ equalities [ equalitiesLength - 1 ] + 1 ] [ 0 ] = DIFF _INSERT ;
equalitiesLength -- ; // Throw away the equality we just deleted;
lastEquality = null ;
if ( pre _ins && pre _del ) {
// No changes made which could affect previous entry, keep going.
post _ins = post _del = true ;
equalitiesLength = 0 ;
} else {
equalitiesLength -- ; // Throw away the previous equality.
pointer = equalitiesLength > 0 ?
equalities [ equalitiesLength - 1 ] : - 1 ;
post _ins = post _del = false ;
}
changes = true ;
}
}
pointer ++ ;
}
if ( changes ) {
this . diff _cleanupMerge ( diffs ) ;
}
} ;
/ * *
* Reorder and merge like edit sections . Merge equalities .
* Any edit section can move as long as it doesn ' t cross an equality .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* /
diff _match _patch . prototype . diff _cleanupMerge = function ( diffs ) {
// Add a dummy entry at the end.
diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , '' ) ) ;
var pointer = 0 ;
var count _delete = 0 ;
var count _insert = 0 ;
var text _delete = '' ;
var text _insert = '' ;
var commonlength ;
while ( pointer < diffs . length ) {
switch ( diffs [ pointer ] [ 0 ] ) {
case DIFF _INSERT :
count _insert ++ ;
text _insert += diffs [ pointer ] [ 1 ] ;
pointer ++ ;
break ;
case DIFF _DELETE :
count _delete ++ ;
text _delete += diffs [ pointer ] [ 1 ] ;
pointer ++ ;
break ;
case DIFF _EQUAL :
// Upon reaching an equality, check for prior redundancies.
if ( count _delete + count _insert > 1 ) {
if ( count _delete !== 0 && count _insert !== 0 ) {
// Factor out any common prefixies.
commonlength = this . diff _commonPrefix ( text _insert , text _delete ) ;
if ( commonlength !== 0 ) {
if ( ( pointer - count _delete - count _insert ) > 0 &&
diffs [ pointer - count _delete - count _insert - 1 ] [ 0 ] ==
DIFF _EQUAL ) {
diffs [ pointer - count _delete - count _insert - 1 ] [ 1 ] +=
text _insert . substring ( 0 , commonlength ) ;
} else {
diffs . splice ( 0 , 0 , new diff _match _patch . Diff ( DIFF _EQUAL ,
text _insert . substring ( 0 , commonlength ) ) ) ;
pointer ++ ;
}
text _insert = text _insert . substring ( commonlength ) ;
text _delete = text _delete . substring ( commonlength ) ;
}
// Factor out any common suffixies.
commonlength = this . diff _commonSuffix ( text _insert , text _delete ) ;
if ( commonlength !== 0 ) {
diffs [ pointer ] [ 1 ] = text _insert . substring ( text _insert . length -
commonlength ) + diffs [ pointer ] [ 1 ] ;
text _insert = text _insert . substring ( 0 , text _insert . length -
commonlength ) ;
text _delete = text _delete . substring ( 0 , text _delete . length -
commonlength ) ;
}
}
// Delete the offending records and add the merged ones.
pointer -= count _delete + count _insert ;
diffs . splice ( pointer , count _delete + count _insert ) ;
if ( text _delete . length ) {
diffs . splice ( pointer , 0 ,
new diff _match _patch . Diff ( DIFF _DELETE , text _delete ) ) ;
pointer ++ ;
}
if ( text _insert . length ) {
diffs . splice ( pointer , 0 ,
new diff _match _patch . Diff ( DIFF _INSERT , text _insert ) ) ;
pointer ++ ;
}
pointer ++ ;
} else if ( pointer !== 0 && diffs [ pointer - 1 ] [ 0 ] == DIFF _EQUAL ) {
// Merge this equality with the previous one.
diffs [ pointer - 1 ] [ 1 ] += diffs [ pointer ] [ 1 ] ;
diffs . splice ( pointer , 1 ) ;
} else {
pointer ++ ;
}
count _insert = 0 ;
count _delete = 0 ;
text _delete = '' ;
text _insert = '' ;
break ;
}
}
if ( diffs [ diffs . length - 1 ] [ 1 ] === '' ) {
diffs . pop ( ) ; // Remove the dummy entry at the end.
}
// Second pass: look for single edits surrounded on both sides by equalities
// which can be shifted sideways to eliminate an equality.
// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
var changes = false ;
pointer = 1 ;
// Intentionally ignore the first and last element (don't need checking).
while ( pointer < diffs . length - 1 ) {
if ( diffs [ pointer - 1 ] [ 0 ] == DIFF _EQUAL &&
diffs [ pointer + 1 ] [ 0 ] == DIFF _EQUAL ) {
// This is a single edit surrounded by equalities.
if ( diffs [ pointer ] [ 1 ] . substring ( diffs [ pointer ] [ 1 ] . length -
diffs [ pointer - 1 ] [ 1 ] . length ) == diffs [ pointer - 1 ] [ 1 ] ) {
// Shift the edit over the previous equality.
diffs [ pointer ] [ 1 ] = diffs [ pointer - 1 ] [ 1 ] +
diffs [ pointer ] [ 1 ] . substring ( 0 , diffs [ pointer ] [ 1 ] . length -
diffs [ pointer - 1 ] [ 1 ] . length ) ;
diffs [ pointer + 1 ] [ 1 ] = diffs [ pointer - 1 ] [ 1 ] + diffs [ pointer + 1 ] [ 1 ] ;
diffs . splice ( pointer - 1 , 1 ) ;
changes = true ;
} else if ( diffs [ pointer ] [ 1 ] . substring ( 0 , diffs [ pointer + 1 ] [ 1 ] . length ) ==
diffs [ pointer + 1 ] [ 1 ] ) {
// Shift the edit over the next equality.
diffs [ pointer - 1 ] [ 1 ] += diffs [ pointer + 1 ] [ 1 ] ;
diffs [ pointer ] [ 1 ] =
diffs [ pointer ] [ 1 ] . substring ( diffs [ pointer + 1 ] [ 1 ] . length ) +
diffs [ pointer + 1 ] [ 1 ] ;
diffs . splice ( pointer + 1 , 1 ) ;
changes = true ;
}
}
pointer ++ ;
}
// If shifts were made, the diff needs reordering and another shift sweep.
if ( changes ) {
this . diff _cleanupMerge ( diffs ) ;
}
} ;
/ * *
* loc is a location in text1 , compute and return the equivalent location in
* text2 .
* e . g . 'The cat' vs 'The big cat' , 1 - > 1 , 5 - > 8
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ param { number } loc Location within text1 .
* @ return { number } Location within text2 .
* /
diff _match _patch . prototype . diff _xIndex = function ( diffs , loc ) {
var chars1 = 0 ;
var chars2 = 0 ;
var last _chars1 = 0 ;
var last _chars2 = 0 ;
var x ;
for ( x = 0 ; x < diffs . length ; x ++ ) {
if ( diffs [ x ] [ 0 ] !== DIFF _INSERT ) { // Equality or deletion.
chars1 += diffs [ x ] [ 1 ] . length ;
}
if ( diffs [ x ] [ 0 ] !== DIFF _DELETE ) { // Equality or insertion.
chars2 += diffs [ x ] [ 1 ] . length ;
}
if ( chars1 > loc ) { // Overshot the location.
break ;
}
last _chars1 = chars1 ;
last _chars2 = chars2 ;
}
// Was the location was deleted?
if ( diffs . length != x && diffs [ x ] [ 0 ] === DIFF _DELETE ) {
return last _chars2 ;
}
// Add the remaining character length.
return last _chars2 + ( loc - last _chars1 ) ;
} ;
/ * *
* Convert a diff array into a pretty HTML report .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ return { string } HTML representation .
* /
diff _match _patch . prototype . diff _prettyHtml = function ( diffs ) {
var html = [ ] ;
var pattern _amp = /&/g ;
var pattern _lt = /</g ;
var pattern _gt = />/g ;
var pattern _para = /\n/g ;
for ( var x = 0 ; x < diffs . length ; x ++ ) {
var op = diffs [ x ] [ 0 ] ; // Operation (insert, delete, equal)
var data = diffs [ x ] [ 1 ] ; // Text of change.
var text = data . replace ( pattern _amp , '&' ) . replace ( pattern _lt , '<' )
. replace ( pattern _gt , '>' ) . replace ( pattern _para , '¶<br>' ) ;
switch ( op ) {
case DIFF _INSERT :
html [ x ] = '<ins style="background:#e6ffe6;">' + text + '</ins>' ;
break ;
case DIFF _DELETE :
html [ x ] = '<del style="background:#ffe6e6;">' + text + '</del>' ;
break ;
case DIFF _EQUAL :
html [ x ] = '<span>' + text + '</span>' ;
break ;
}
}
return html . join ( '' ) ;
} ;
/ * *
* Compute and return the source text ( all equalities and deletions ) .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ return { string } Source text .
* /
diff _match _patch . prototype . diff _text1 = function ( diffs ) {
var text = [ ] ;
for ( var x = 0 ; x < diffs . length ; x ++ ) {
if ( diffs [ x ] [ 0 ] !== DIFF _INSERT ) {
text [ x ] = diffs [ x ] [ 1 ] ;
}
}
return text . join ( '' ) ;
} ;
/ * *
* Compute and return the destination text ( all equalities and insertions ) .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ return { string } Destination text .
* /
diff _match _patch . prototype . diff _text2 = function ( diffs ) {
var text = [ ] ;
for ( var x = 0 ; x < diffs . length ; x ++ ) {
if ( diffs [ x ] [ 0 ] !== DIFF _DELETE ) {
text [ x ] = diffs [ x ] [ 1 ] ;
}
}
return text . join ( '' ) ;
} ;
/ * *
* Compute the Levenshtein distance ; the number of inserted , deleted or
* substituted characters .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ return { number } Number of changes .
* /
diff _match _patch . prototype . diff _levenshtein = function ( diffs ) {
var levenshtein = 0 ;
var insertions = 0 ;
var deletions = 0 ;
for ( var x = 0 ; x < diffs . length ; x ++ ) {
var op = diffs [ x ] [ 0 ] ;
var data = diffs [ x ] [ 1 ] ;
switch ( op ) {
case DIFF _INSERT :
insertions += data . length ;
break ;
case DIFF _DELETE :
deletions += data . length ;
break ;
case DIFF _EQUAL :
// A deletion and an insertion is one substitution.
levenshtein += Math . max ( insertions , deletions ) ;
insertions = 0 ;
deletions = 0 ;
break ;
}
}
levenshtein += Math . max ( insertions , deletions ) ;
return levenshtein ;
} ;
/ * *
* Crush the diff into an encoded string which describes the operations
* required to transform text1 into text2 .
* E . g . = 3 \ t - 2 \ t + ing - > Keep 3 chars , delete 2 chars , insert 'ing' .
* Operations are tab - separated . Inserted text is escaped using % xx notation .
* @ param { ! Array . < ! diff _match _patch . Diff > } diffs Array of diff tuples .
* @ return { string } Delta text .
* /
diff _match _patch . prototype . diff _toDelta = function ( diffs ) {
var text = [ ] ;
for ( var x = 0 ; x < diffs . length ; x ++ ) {
switch ( diffs [ x ] [ 0 ] ) {
case DIFF _INSERT :
text [ x ] = '+' + encodeURI ( diffs [ x ] [ 1 ] ) ;
break ;
case DIFF _DELETE :
text [ x ] = '-' + diffs [ x ] [ 1 ] . length ;
break ;
case DIFF _EQUAL :
text [ x ] = '=' + diffs [ x ] [ 1 ] . length ;
break ;
}
}
return text . join ( '\t' ) . replace ( /%20/g , ' ' ) ;
} ;
/ * *
* Given the original text1 , and an encoded string which describes the
* operations required to transform text1 into text2 , compute the full diff .
* @ param { string } text1 Source string for the diff .
* @ param { string } delta Delta text .
* @ return { ! Array . < ! diff _match _patch . Diff > } Array of diff tuples .
* @ throws { ! Error } If invalid input .
* /
diff _match _patch . prototype . diff _fromDelta = function ( text1 , delta ) {
var diffs = [ ] ;
var diffsLength = 0 ; // Keeping our own length var is faster in JS.
var pointer = 0 ; // Cursor in text1
var tokens = delta . split ( /\t/g ) ;
for ( var x = 0 ; x < tokens . length ; x ++ ) {
// Each token begins with a one character parameter which specifies the
// operation of this token (delete, insert, equality).
var param = tokens [ x ] . substring ( 1 ) ;
switch ( tokens [ x ] . charAt ( 0 ) ) {
case '+' :
try {
diffs [ diffsLength ++ ] =
new diff _match _patch . Diff ( DIFF _INSERT , decodeURI ( param ) ) ;
} catch ( ex ) {
// Malformed URI sequence.
throw new Error ( 'Illegal escape in diff_fromDelta: ' + param ) ;
}
break ;
case '-' :
// Fall through.
case '=' :
var n = parseInt ( param , 10 ) ;
if ( isNaN ( n ) || n < 0 ) {
throw new Error ( 'Invalid number in diff_fromDelta: ' + param ) ;
}
var text = text1 . substring ( pointer , pointer += n ) ;
if ( tokens [ x ] . charAt ( 0 ) == '=' ) {
diffs [ diffsLength ++ ] = new diff _match _patch . Diff ( DIFF _EQUAL , text ) ;
} else {
diffs [ diffsLength ++ ] = new diff _match _patch . Diff ( DIFF _DELETE , text ) ;
}
break ;
default :
// Blank tokens are ok (from a trailing \t).
// Anything else is an error.
if ( tokens [ x ] ) {
throw new Error ( 'Invalid diff operation in diff_fromDelta: ' +
tokens [ x ] ) ;
}
}
}
if ( pointer != text1 . length ) {
throw new Error ( 'Delta length (' + pointer +
') does not equal source text length (' + text1 . length + ').' ) ;
}
return diffs ;
} ;
// MATCH FUNCTIONS
/ * *
* Locate the best instance of 'pattern' in 'text' near 'loc' .
* @ param { string } text The text to search .
* @ param { string } pattern The pattern to search for .
* @ param { number } loc The location to search around .
* @ return { number } Best match index or - 1.
* /
diff _match _patch . prototype . match _main = function ( text , pattern , loc ) {
// Check for null inputs.
if ( text == null || pattern == null || loc == null ) {
throw new Error ( 'Null input. (match_main)' ) ;
}
loc = Math . max ( 0 , Math . min ( loc , text . length ) ) ;
if ( text == pattern ) {
// Shortcut (potentially not guaranteed by the algorithm)
return 0 ;
} else if ( ! text . length ) {
// Nothing to match.
return - 1 ;
} else if ( text . substring ( loc , loc + pattern . length ) == pattern ) {
// Perfect match at the perfect spot! (Includes case of null pattern)
return loc ;
} else {
// Do a fuzzy compare.
return this . match _bitap _ ( text , pattern , loc ) ;
}
} ;
/ * *
* Locate the best instance of 'pattern' in 'text' near 'loc' using the
* Bitap algorithm .
* @ param { string } text The text to search .
* @ param { string } pattern The pattern to search for .
* @ param { number } loc The location to search around .
* @ return { number } Best match index or - 1.
* @ private
* /
diff _match _patch . prototype . match _bitap _ = function ( text , pattern , loc ) {
if ( pattern . length > this . Match _MaxBits ) {
throw new Error ( 'Pattern too long for this browser.' ) ;
}
// Initialise the alphabet.
var s = this . match _alphabet _ ( pattern ) ;
var dmp = this ; // 'this' becomes 'window' in a closure.
/ * *
* Compute and return the score for a match with e errors and x location .
* Accesses loc and pattern through being a closure .
* @ param { number } e Number of errors in match .
* @ param { number } x Location of match .
* @ return { number } Overall score for match ( 0.0 = good , 1.0 = bad ) .
* @ private
* /
function match _bitapScore _ ( e , x ) {
var accuracy = e / pattern . length ;
var proximity = Math . abs ( loc - x ) ;
if ( ! dmp . Match _Distance ) {
// Dodge divide by zero error.
return proximity ? 1.0 : accuracy ;
}
return accuracy + ( proximity / dmp . Match _Distance ) ;
}
// Highest score beyond which we give up.
var score _threshold = this . Match _Threshold ;
// Is there a nearby exact match? (speedup)
var best _loc = text . indexOf ( pattern , loc ) ;
if ( best _loc != - 1 ) {
score _threshold = Math . min ( match _bitapScore _ ( 0 , best _loc ) , score _threshold ) ;
// What about in the other direction? (speedup)
best _loc = text . lastIndexOf ( pattern , loc + pattern . length ) ;
if ( best _loc != - 1 ) {
score _threshold =
Math . min ( match _bitapScore _ ( 0 , best _loc ) , score _threshold ) ;
}
}
// Initialise the bit arrays.
var matchmask = 1 << ( pattern . length - 1 ) ;
best _loc = - 1 ;
var bin _min , bin _mid ;
var bin _max = pattern . length + text . length ;
var last _rd ;
for ( var d = 0 ; d < pattern . length ; d ++ ) {
// Scan for the best match; each iteration allows for one more error.
// Run a binary search to determine how far from 'loc' we can stray at this
// error level.
bin _min = 0 ;
bin _mid = bin _max ;
while ( bin _min < bin _mid ) {
if ( match _bitapScore _ ( d , loc + bin _mid ) <= score _threshold ) {
bin _min = bin _mid ;
} else {
bin _max = bin _mid ;
}
bin _mid = Math . floor ( ( bin _max - bin _min ) / 2 + bin _min ) ;
}
// Use the result from this iteration as the maximum for the next.
bin _max = bin _mid ;
var start = Math . max ( 1 , loc - bin _mid + 1 ) ;
var finish = Math . min ( loc + bin _mid , text . length ) + pattern . length ;
var rd = Array ( finish + 2 ) ;
rd [ finish + 1 ] = ( 1 << d ) - 1 ;
for ( var j = finish ; j >= start ; j -- ) {
// The alphabet (s) is a sparse hash, so the following line generates
// warnings.
var charMatch = s [ text . charAt ( j - 1 ) ] ;
if ( d === 0 ) { // First pass: exact match.
rd [ j ] = ( ( rd [ j + 1 ] << 1 ) | 1 ) & charMatch ;
} else { // Subsequent passes: fuzzy match.
rd [ j ] = ( ( ( rd [ j + 1 ] << 1 ) | 1 ) & charMatch ) |
( ( ( last _rd [ j + 1 ] | last _rd [ j ] ) << 1 ) | 1 ) |
last _rd [ j + 1 ] ;
}
if ( rd [ j ] & matchmask ) {
var score = match _bitapScore _ ( d , j - 1 ) ;
// This match will almost certainly be better than any existing match.
// But check anyway.
if ( score <= score _threshold ) {
// Told you so.
score _threshold = score ;
best _loc = j - 1 ;
if ( best _loc > loc ) {
// When passing loc, don't exceed our current distance from loc.
start = Math . max ( 1 , 2 * loc - best _loc ) ;
} else {
// Already passed loc, downhill from here on in.
break ;
}
}
}
}
// No hope for a (better) match at greater error levels.
if ( match _bitapScore _ ( d + 1 , loc ) > score _threshold ) {
break ;
}
last _rd = rd ;
}
return best _loc ;
} ;
/ * *
* Initialise the alphabet for the Bitap algorithm .
* @ param { string } pattern The text to encode .
* @ return { ! Object } Hash of character locations .
* @ private
* /
diff _match _patch . prototype . match _alphabet _ = function ( pattern ) {
var s = { } ;
for ( var i = 0 ; i < pattern . length ; i ++ ) {
s [ pattern . charAt ( i ) ] = 0 ;
}
for ( var i = 0 ; i < pattern . length ; i ++ ) {
s [ pattern . charAt ( i ) ] |= 1 << ( pattern . length - i - 1 ) ;
}
return s ;
} ;
// PATCH FUNCTIONS
/ * *
* Increase the context until it is unique ,
* but don ' t let the pattern expand beyond Match _MaxBits .
* @ param { ! diff _match _patch . patch _obj } patch The patch to grow .
* @ param { string } text Source text .
* @ private
* /
diff _match _patch . prototype . patch _addContext _ = function ( patch , text ) {
if ( text . length == 0 ) {
return ;
}
if ( patch . start2 === null ) {
throw Error ( 'patch not initialized' ) ;
}
var pattern = text . substring ( patch . start2 , patch . start2 + patch . length1 ) ;
var padding = 0 ;
// Look for the first and last matches of pattern in text. If two different
// matches are found, increase the pattern length.
while ( text . indexOf ( pattern ) != text . lastIndexOf ( pattern ) &&
pattern . length < this . Match _MaxBits - this . Patch _Margin -
this . Patch _Margin ) {
padding += this . Patch _Margin ;
pattern = text . substring ( patch . start2 - padding ,
patch . start2 + patch . length1 + padding ) ;
}
// Add one chunk for good luck.
padding += this . Patch _Margin ;
// Add the prefix.
var prefix = text . substring ( patch . start2 - padding , patch . start2 ) ;
if ( prefix ) {
patch . diffs . unshift ( new diff _match _patch . Diff ( DIFF _EQUAL , prefix ) ) ;
}
// Add the suffix.
var suffix = text . substring ( patch . start2 + patch . length1 ,
patch . start2 + patch . length1 + padding ) ;
if ( suffix ) {
patch . diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , suffix ) ) ;
}
// Roll back the start points.
patch . start1 -= prefix . length ;
patch . start2 -= prefix . length ;
// Extend the lengths.
patch . length1 += prefix . length + suffix . length ;
patch . length2 += prefix . length + suffix . length ;
} ;
/ * *
* Compute a list of patches to turn text1 into text2 .
* Use diffs if provided , otherwise compute it ourselves .
* There are four ways to call this function , depending on what data is
* available to the caller :
* Method 1 :
* a = text1 , b = text2
* Method 2 :
* a = diffs
* Method 3 ( optimal ) :
* a = text1 , b = diffs
* Method 4 ( deprecated , use method 3 ) :
* a = text1 , b = text2 , c = diffs
*
* @ param { string | ! Array . < ! diff _match _patch . Diff > } a text1 ( methods 1 , 3 , 4 ) or
* Array of diff tuples for text1 to text2 ( method 2 ) .
* @ param { string | ! Array . < ! diff _match _patch . Diff >= } opt _b text2 ( methods 1 , 4 ) or
* Array of diff tuples for text1 to text2 ( method 3 ) or undefined ( method 2 ) .
* @ param { string | ! Array . < ! diff _match _patch . Diff >= } opt _c Array of diff tuples
* for text1 to text2 ( method 4 ) or undefined ( methods 1 , 2 , 3 ) .
* @ return { ! Array . < ! diff _match _patch . patch _obj > } Array of Patch objects .
* /
diff _match _patch . prototype . patch _make = function ( a , opt _b , opt _c ) {
var text1 , diffs ;
if ( typeof a == 'string' && typeof opt _b == 'string' &&
typeof opt _c == 'undefined' ) {
// Method 1: text1, text2
// Compute diffs from text1 and text2.
text1 = /** @type {string} */ ( a ) ;
diffs = this . diff _main ( text1 , /** @type {string} */ ( opt _b ) , true ) ;
if ( diffs . length > 2 ) {
this . diff _cleanupSemantic ( diffs ) ;
this . diff _cleanupEfficiency ( diffs ) ;
}
} else if ( a && typeof a == 'object' && typeof opt _b == 'undefined' &&
typeof opt _c == 'undefined' ) {
// Method 2: diffs
// Compute text1 from diffs.
diffs = /** @type {!Array.<!diff_match_patch.Diff>} */ ( a ) ;
text1 = this . diff _text1 ( diffs ) ;
} else if ( typeof a == 'string' && opt _b && typeof opt _b == 'object' &&
typeof opt _c == 'undefined' ) {
// Method 3: text1, diffs
text1 = /** @type {string} */ ( a ) ;
diffs = /** @type {!Array.<!diff_match_patch.Diff>} */ ( opt _b ) ;
} else if ( typeof a == 'string' && typeof opt _b == 'string' &&
opt _c && typeof opt _c == 'object' ) {
// Method 4: text1, text2, diffs
// text2 is not used.
text1 = /** @type {string} */ ( a ) ;
diffs = /** @type {!Array.<!diff_match_patch.Diff>} */ ( opt _c ) ;
} else {
throw new Error ( 'Unknown call format to patch_make.' ) ;
}
if ( diffs . length === 0 ) {
return [ ] ; // Get rid of the null case.
}
var patches = [ ] ;
var patch = new diff _match _patch . patch _obj ( ) ;
var patchDiffLength = 0 ; // Keeping our own length var is faster in JS.
var char _count1 = 0 ; // Number of characters into the text1 string.
var char _count2 = 0 ; // Number of characters into the text2 string.
// Start with text1 (prepatch_text) and apply the diffs until we arrive at
// text2 (postpatch_text). We recreate the patches one by one to determine
// context info.
var prepatch _text = text1 ;
var postpatch _text = text1 ;
for ( var x = 0 ; x < diffs . length ; x ++ ) {
var diff _type = diffs [ x ] [ 0 ] ;
var diff _text = diffs [ x ] [ 1 ] ;
if ( ! patchDiffLength && diff _type !== DIFF _EQUAL ) {
// A new patch starts here.
patch . start1 = char _count1 ;
patch . start2 = char _count2 ;
}
switch ( diff _type ) {
case DIFF _INSERT :
patch . diffs [ patchDiffLength ++ ] = diffs [ x ] ;
patch . length2 += diff _text . length ;
postpatch _text = postpatch _text . substring ( 0 , char _count2 ) + diff _text +
postpatch _text . substring ( char _count2 ) ;
break ;
case DIFF _DELETE :
patch . length1 += diff _text . length ;
patch . diffs [ patchDiffLength ++ ] = diffs [ x ] ;
postpatch _text = postpatch _text . substring ( 0 , char _count2 ) +
postpatch _text . substring ( char _count2 +
diff _text . length ) ;
break ;
case DIFF _EQUAL :
if ( diff _text . length <= 2 * this . Patch _Margin &&
patchDiffLength && diffs . length != x + 1 ) {
// Small equality inside a patch.
patch . diffs [ patchDiffLength ++ ] = diffs [ x ] ;
patch . length1 += diff _text . length ;
patch . length2 += diff _text . length ;
} else if ( diff _text . length >= 2 * this . Patch _Margin ) {
// Time for a new patch.
if ( patchDiffLength ) {
this . patch _addContext _ ( patch , prepatch _text ) ;
patches . push ( patch ) ;
patch = new diff _match _patch . patch _obj ( ) ;
patchDiffLength = 0 ;
// Unlike Unidiff, our patch lists have a rolling context.
// https://github.com/google/diff-match-patch/wiki/Unidiff
// Update prepatch text & pos to reflect the application of the
// just completed patch.
prepatch _text = postpatch _text ;
char _count1 = char _count2 ;
}
}
break ;
}
// Update the current character count.
if ( diff _type !== DIFF _INSERT ) {
char _count1 += diff _text . length ;
}
if ( diff _type !== DIFF _DELETE ) {
char _count2 += diff _text . length ;
}
}
// Pick up the leftover patch if not empty.
if ( patchDiffLength ) {
this . patch _addContext _ ( patch , prepatch _text ) ;
patches . push ( patch ) ;
}
return patches ;
} ;
/ * *
* Given an array of patches , return another array that is identical .
* @ param { ! Array . < ! diff _match _patch . patch _obj > } patches Array of Patch objects .
* @ return { ! Array . < ! diff _match _patch . patch _obj > } Array of Patch objects .
* /
diff _match _patch . prototype . patch _deepCopy = function ( patches ) {
// Making deep copies is hard in JavaScript.
var patchesCopy = [ ] ;
for ( var x = 0 ; x < patches . length ; x ++ ) {
var patch = patches [ x ] ;
var patchCopy = new diff _match _patch . patch _obj ( ) ;
patchCopy . diffs = [ ] ;
for ( var y = 0 ; y < patch . diffs . length ; y ++ ) {
patchCopy . diffs [ y ] =
new diff _match _patch . Diff ( patch . diffs [ y ] [ 0 ] , patch . diffs [ y ] [ 1 ] ) ;
}
patchCopy . start1 = patch . start1 ;
patchCopy . start2 = patch . start2 ;
patchCopy . length1 = patch . length1 ;
patchCopy . length2 = patch . length2 ;
patchesCopy [ x ] = patchCopy ;
}
return patchesCopy ;
} ;
/ * *
* Merge a set of patches onto the text . Return a patched text , as well
* as a list of true / false values indicating which patches were applied .
* @ param { ! Array . < ! diff _match _patch . patch _obj > } patches Array of Patch objects .
* @ param { string } text Old text .
* @ return { ! Array . < string | ! Array . < boolean >> } Two element Array , containing the
* new text and an array of boolean values .
* /
diff _match _patch . prototype . patch _apply = function ( patches , text ) {
if ( patches . length == 0 ) {
return [ text , [ ] ] ;
}
// Deep copy the patches so that no changes are made to originals.
patches = this . patch _deepCopy ( patches ) ;
var nullPadding = this . patch _addPadding ( patches ) ;
text = nullPadding + text + nullPadding ;
this . patch _splitMax ( patches ) ;
// delta keeps track of the offset between the expected and actual location
// of the previous patch. If there are patches expected at positions 10 and
// 20, but the first patch was found at 12, delta is 2 and the second patch
// has an effective expected position of 22.
var delta = 0 ;
var results = [ ] ;
for ( var x = 0 ; x < patches . length ; x ++ ) {
var expected _loc = patches [ x ] . start2 + delta ;
var text1 = this . diff _text1 ( patches [ x ] . diffs ) ;
var start _loc ;
var end _loc = - 1 ;
if ( text1 . length > this . Match _MaxBits ) {
// patch_splitMax will only provide an oversized pattern in the case of
// a monster delete.
start _loc = this . match _main ( text , text1 . substring ( 0 , this . Match _MaxBits ) ,
expected _loc ) ;
if ( start _loc != - 1 ) {
end _loc = this . match _main ( text ,
text1 . substring ( text1 . length - this . Match _MaxBits ) ,
expected _loc + text1 . length - this . Match _MaxBits ) ;
if ( end _loc == - 1 || start _loc >= end _loc ) {
// Can't find valid trailing context. Drop this patch.
start _loc = - 1 ;
}
}
} else {
start _loc = this . match _main ( text , text1 , expected _loc ) ;
}
if ( start _loc == - 1 ) {
// No match found. :(
results [ x ] = false ;
// Subtract the delta for this failed patch from subsequent patches.
delta -= patches [ x ] . length2 - patches [ x ] . length1 ;
} else {
// Found a match. :)
results [ x ] = true ;
delta = start _loc - expected _loc ;
var text2 ;
if ( end _loc == - 1 ) {
text2 = text . substring ( start _loc , start _loc + text1 . length ) ;
} else {
text2 = text . substring ( start _loc , end _loc + this . Match _MaxBits ) ;
}
if ( text1 == text2 ) {
// Perfect match, just shove the replacement text in.
text = text . substring ( 0 , start _loc ) +
this . diff _text2 ( patches [ x ] . diffs ) +
text . substring ( start _loc + text1 . length ) ;
} else {
// Imperfect match. Run a diff to get a framework of equivalent
// indices.
var diffs = this . diff _main ( text1 , text2 , false ) ;
if ( text1 . length > this . Match _MaxBits &&
this . diff _levenshtein ( diffs ) / text1 . length >
this . Patch _DeleteThreshold ) {
// The end points match, but the content is unacceptably bad.
results [ x ] = false ;
} else {
this . diff _cleanupSemanticLossless ( diffs ) ;
var index1 = 0 ;
var index2 ;
for ( var y = 0 ; y < patches [ x ] . diffs . length ; y ++ ) {
var mod = patches [ x ] . diffs [ y ] ;
if ( mod [ 0 ] !== DIFF _EQUAL ) {
index2 = this . diff _xIndex ( diffs , index1 ) ;
}
if ( mod [ 0 ] === DIFF _INSERT ) { // Insertion
text = text . substring ( 0 , start _loc + index2 ) + mod [ 1 ] +
text . substring ( start _loc + index2 ) ;
} else if ( mod [ 0 ] === DIFF _DELETE ) { // Deletion
text = text . substring ( 0 , start _loc + index2 ) +
text . substring ( start _loc + this . diff _xIndex ( diffs ,
index1 + mod [ 1 ] . length ) ) ;
}
if ( mod [ 0 ] !== DIFF _DELETE ) {
index1 += mod [ 1 ] . length ;
}
}
}
}
}
}
// Strip the padding off.
text = text . substring ( nullPadding . length , text . length - nullPadding . length ) ;
return [ text , results ] ;
} ;
/ * *
* Add some padding on text start and end so that edges can match something .
* Intended to be called only from within patch _apply .
* @ param { ! Array . < ! diff _match _patch . patch _obj > } patches Array of Patch objects .
* @ return { string } The padding string added to each side .
* /
diff _match _patch . prototype . patch _addPadding = function ( patches ) {
var paddingLength = this . Patch _Margin ;
var nullPadding = '' ;
for ( var x = 1 ; x <= paddingLength ; x ++ ) {
nullPadding += String . fromCharCode ( x ) ;
}
// Bump all the patches forward.
for ( var x = 0 ; x < patches . length ; x ++ ) {
patches [ x ] . start1 += paddingLength ;
patches [ x ] . start2 += paddingLength ;
}
// Add some padding on start of first diff.
var patch = patches [ 0 ] ;
var diffs = patch . diffs ;
if ( diffs . length == 0 || diffs [ 0 ] [ 0 ] != DIFF _EQUAL ) {
// Add nullPadding equality.
diffs . unshift ( new diff _match _patch . Diff ( DIFF _EQUAL , nullPadding ) ) ;
patch . start1 -= paddingLength ; // Should be 0.
patch . start2 -= paddingLength ; // Should be 0.
patch . length1 += paddingLength ;
patch . length2 += paddingLength ;
} else if ( paddingLength > diffs [ 0 ] [ 1 ] . length ) {
// Grow first equality.
var extraLength = paddingLength - diffs [ 0 ] [ 1 ] . length ;
diffs [ 0 ] [ 1 ] = nullPadding . substring ( diffs [ 0 ] [ 1 ] . length ) + diffs [ 0 ] [ 1 ] ;
patch . start1 -= extraLength ;
patch . start2 -= extraLength ;
patch . length1 += extraLength ;
patch . length2 += extraLength ;
}
// Add some padding on end of last diff.
patch = patches [ patches . length - 1 ] ;
diffs = patch . diffs ;
if ( diffs . length == 0 || diffs [ diffs . length - 1 ] [ 0 ] != DIFF _EQUAL ) {
// Add nullPadding equality.
diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , nullPadding ) ) ;
patch . length1 += paddingLength ;
patch . length2 += paddingLength ;
} else if ( paddingLength > diffs [ diffs . length - 1 ] [ 1 ] . length ) {
// Grow last equality.
var extraLength = paddingLength - diffs [ diffs . length - 1 ] [ 1 ] . length ;
diffs [ diffs . length - 1 ] [ 1 ] += nullPadding . substring ( 0 , extraLength ) ;
patch . length1 += extraLength ;
patch . length2 += extraLength ;
}
return nullPadding ;
} ;
/ * *
* Look through the patches and break up any which are longer than the maximum
* limit of the match algorithm .
* Intended to be called only from within patch _apply .
* @ param { ! Array . < ! diff _match _patch . patch _obj > } patches Array of Patch objects .
* /
diff _match _patch . prototype . patch _splitMax = function ( patches ) {
var patch _size = this . Match _MaxBits ;
for ( var x = 0 ; x < patches . length ; x ++ ) {
if ( patches [ x ] . length1 <= patch _size ) {
continue ;
}
var bigpatch = patches [ x ] ;
// Remove the big old patch.
patches . splice ( x -- , 1 ) ;
var start1 = bigpatch . start1 ;
var start2 = bigpatch . start2 ;
var precontext = '' ;
while ( bigpatch . diffs . length !== 0 ) {
// Create one of several smaller patches.
var patch = new diff _match _patch . patch _obj ( ) ;
var empty = true ;
patch . start1 = start1 - precontext . length ;
patch . start2 = start2 - precontext . length ;
if ( precontext !== '' ) {
patch . length1 = patch . length2 = precontext . length ;
patch . diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , precontext ) ) ;
}
while ( bigpatch . diffs . length !== 0 &&
patch . length1 < patch _size - this . Patch _Margin ) {
var diff _type = bigpatch . diffs [ 0 ] [ 0 ] ;
var diff _text = bigpatch . diffs [ 0 ] [ 1 ] ;
if ( diff _type === DIFF _INSERT ) {
// Insertions are harmless.
patch . length2 += diff _text . length ;
start2 += diff _text . length ;
patch . diffs . push ( bigpatch . diffs . shift ( ) ) ;
empty = false ;
} else if ( diff _type === DIFF _DELETE && patch . diffs . length == 1 &&
patch . diffs [ 0 ] [ 0 ] == DIFF _EQUAL &&
diff _text . length > 2 * patch _size ) {
// This is a large deletion. Let it pass in one chunk.
patch . length1 += diff _text . length ;
start1 += diff _text . length ;
empty = false ;
patch . diffs . push ( new diff _match _patch . Diff ( diff _type , diff _text ) ) ;
bigpatch . diffs . shift ( ) ;
} else {
// Deletion or equality. Only take as much as we can stomach.
diff _text = diff _text . substring ( 0 ,
patch _size - patch . length1 - this . Patch _Margin ) ;
patch . length1 += diff _text . length ;
start1 += diff _text . length ;
if ( diff _type === DIFF _EQUAL ) {
patch . length2 += diff _text . length ;
start2 += diff _text . length ;
} else {
empty = false ;
}
patch . diffs . push ( new diff _match _patch . Diff ( diff _type , diff _text ) ) ;
if ( diff _text == bigpatch . diffs [ 0 ] [ 1 ] ) {
bigpatch . diffs . shift ( ) ;
} else {
bigpatch . diffs [ 0 ] [ 1 ] =
bigpatch . diffs [ 0 ] [ 1 ] . substring ( diff _text . length ) ;
}
}
}
// Compute the head context for the next patch.
precontext = this . diff _text2 ( patch . diffs ) ;
precontext =
precontext . substring ( precontext . length - this . Patch _Margin ) ;
// Append the end context for this patch.
var postcontext = this . diff _text1 ( bigpatch . diffs )
. substring ( 0 , this . Patch _Margin ) ;
if ( postcontext !== '' ) {
patch . length1 += postcontext . length ;
patch . length2 += postcontext . length ;
if ( patch . diffs . length !== 0 &&
patch . diffs [ patch . diffs . length - 1 ] [ 0 ] === DIFF _EQUAL ) {
patch . diffs [ patch . diffs . length - 1 ] [ 1 ] += postcontext ;
} else {
patch . diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , postcontext ) ) ;
}
}
if ( ! empty ) {
patches . splice ( ++ x , 0 , patch ) ;
}
}
}
} ;
/ * *
* Take a list of patches and return a textual representation .
* @ param { ! Array . < ! diff _match _patch . patch _obj > } patches Array of Patch objects .
* @ return { string } Text representation of patches .
* /
diff _match _patch . prototype . patch _toText = function ( patches ) {
var text = [ ] ;
for ( var x = 0 ; x < patches . length ; x ++ ) {
text [ x ] = patches [ x ] ;
}
return text . join ( '' ) ;
} ;
/ * *
* Parse a textual representation of patches and return a list of Patch objects .
* @ param { string } textline Text representation of patches .
* @ return { ! Array . < ! diff _match _patch . patch _obj > } Array of Patch objects .
* @ throws { ! Error } If invalid input .
* /
diff _match _patch . prototype . patch _fromText = function ( textline ) {
var patches = [ ] ;
if ( ! textline ) {
return patches ;
}
var text = textline . split ( '\n' ) ;
var textPointer = 0 ;
var patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/ ;
while ( textPointer < text . length ) {
var m = text [ textPointer ] . match ( patchHeader ) ;
if ( ! m ) {
throw new Error ( 'Invalid patch string: ' + text [ textPointer ] ) ;
}
var patch = new diff _match _patch . patch _obj ( ) ;
patches . push ( patch ) ;
patch . start1 = parseInt ( m [ 1 ] , 10 ) ;
if ( m [ 2 ] === '' ) {
patch . start1 -- ;
patch . length1 = 1 ;
} else if ( m [ 2 ] == '0' ) {
patch . length1 = 0 ;
} else {
patch . start1 -- ;
patch . length1 = parseInt ( m [ 2 ] , 10 ) ;
}
patch . start2 = parseInt ( m [ 3 ] , 10 ) ;
if ( m [ 4 ] === '' ) {
patch . start2 -- ;
patch . length2 = 1 ;
} else if ( m [ 4 ] == '0' ) {
patch . length2 = 0 ;
} else {
patch . start2 -- ;
patch . length2 = parseInt ( m [ 4 ] , 10 ) ;
}
textPointer ++ ;
while ( textPointer < text . length ) {
var sign = text [ textPointer ] . charAt ( 0 ) ;
try {
var line = decodeURI ( text [ textPointer ] . substring ( 1 ) ) ;
} catch ( ex ) {
// Malformed URI sequence.
throw new Error ( 'Illegal escape in patch_fromText: ' + line ) ;
}
if ( sign == '-' ) {
// Deletion.
patch . diffs . push ( new diff _match _patch . Diff ( DIFF _DELETE , line ) ) ;
} else if ( sign == '+' ) {
// Insertion.
patch . diffs . push ( new diff _match _patch . Diff ( DIFF _INSERT , line ) ) ;
} else if ( sign == ' ' ) {
// Minor equality.
patch . diffs . push ( new diff _match _patch . Diff ( DIFF _EQUAL , line ) ) ;
} else if ( sign == '@' ) {
// Start of next patch.
break ;
} else if ( sign === '' ) ; else {
// WTF?
throw new Error ( 'Invalid patch mode "' + sign + '" in: ' + line ) ;
}
textPointer ++ ;
}
}
return patches ;
} ;
/ * *
* Class representing one patch operation .
* @ constructor
* /
diff _match _patch . patch _obj = function ( ) {
/** @type {!Array.<!diff_match_patch.Diff>} */
this . diffs = [ ] ;
/** @type {?number} */
this . start1 = null ;
/** @type {?number} */
this . start2 = null ;
/** @type {number} */
this . length1 = 0 ;
/** @type {number} */
this . length2 = 0 ;
} ;
/ * *
* Emulate GNU diff ' s format .
* Header : @ @ - 382 , 8 + 481 , 9 @ @
* Indices are printed as 1 - based , not 0 - based .
* @ return { string } The GNU diff string .
* /
diff _match _patch . patch _obj . prototype . toString = function ( ) {
var coords1 , coords2 ;
if ( this . length1 === 0 ) {
coords1 = this . start1 + ',0' ;
} else if ( this . length1 == 1 ) {
coords1 = this . start1 + 1 ;
} else {
coords1 = ( this . start1 + 1 ) + ',' + this . length1 ;
}
if ( this . length2 === 0 ) {
coords2 = this . start2 + ',0' ;
} else if ( this . length2 == 1 ) {
coords2 = this . start2 + 1 ;
} else {
coords2 = ( this . start2 + 1 ) + ',' + this . length2 ;
}
var text = [ '@@ -' + coords1 + ' +' + coords2 + ' @@\n' ] ;
var op ;
// Escape the body of the patch with %xx notation.
for ( var x = 0 ; x < this . diffs . length ; x ++ ) {
switch ( this . diffs [ x ] [ 0 ] ) {
case DIFF _INSERT :
op = '+' ;
break ;
case DIFF _DELETE :
op = '-' ;
break ;
case DIFF _EQUAL :
op = ' ' ;
break ;
}
text [ x + 1 ] = op + encodeURI ( this . diffs [ x ] [ 1 ] ) + '\n' ;
}
return text . join ( '' ) . replace ( /%20/g , ' ' ) ;
} ;
// The following export code was added by @ForbesLindesay
module . exports = diff _match _patch ;
module . exports [ 'diff_match_patch' ] = diff _match _patch ;
module . exports [ 'DIFF_DELETE' ] = DIFF _DELETE ;
module . exports [ 'DIFF_INSERT' ] = DIFF _INSERT ;
module . exports [ 'DIFF_EQUAL' ] = DIFF _EQUAL ;
} ) ;
2021-10-14 12:27:08 +02:00
// docs should be encoded as base64, so 1 char -> 1 bytes
// and cloudant limitation is 1MB , we use 900kb;
// const MAX_DOC_SIZE = 921600;
const MAX _DOC _SIZE = 921600 ;
2021-10-12 16:50:13 +02:00
const DEFAULT _SETTINGS = {
couchDB _URI : "" ,
couchDB _USER : "" ,
couchDB _PASSWORD : "" ,
2021-10-14 12:27:08 +02:00
liveSync : false ,
2021-10-12 16:50:13 +02:00
syncOnSave : false ,
syncOnStart : false ,
} ;
//-->Functions.
function arrayBufferToBase64 ( buffer ) {
var binary = "" ;
var bytes = new Uint8Array ( buffer ) ;
var len = bytes . byteLength ;
for ( var i = 0 ; i < len ; i ++ ) {
binary += String . fromCharCode ( bytes [ i ] ) ;
}
return window . btoa ( binary ) ;
}
function base64ToArrayBuffer ( base64 ) {
try {
var binary _string = window . atob ( base64 ) ;
var len = binary _string . length ;
var bytes = new Uint8Array ( len ) ;
for ( var i = 0 ; i < len ; i ++ ) {
bytes [ i ] = binary _string . charCodeAt ( i ) ;
}
return bytes . buffer ;
}
catch ( ex ) {
return null ;
}
}
function base64ToString ( base64 ) {
try {
var binary _string = window . atob ( base64 ) ;
var len = binary _string . length ;
var bytes = new Uint8Array ( len ) ;
for ( var i = 0 ; i < len ; i ++ ) {
bytes [ i ] = binary _string . charCodeAt ( i ) ;
}
return new TextDecoder ( ) . decode ( bytes ) ;
}
catch ( ex ) {
return null ;
}
}
const escapeStringToHTML = ( str ) => {
if ( ! str )
return ;
return str . replace ( /[<>&"'`]/g , ( match ) => {
const escape = {
"<" : "<" ,
">" : ">" ,
"&" : "&" ,
'"' : """ ,
"'" : "'" ,
"`" : "`" ,
} ;
return escape [ match ] ;
} ) ;
} ;
2021-10-13 14:38:44 +02:00
const isValidRemoteCouchDBURI = ( uri ) => {
if ( uri . startsWith ( "https://" ) )
return true ;
if ( uri . startsWith ( "http://" ) )
return true ;
return false ;
} ;
const connectRemoteCouchDB = ( uri , auth ) => _ _awaiter ( void 0 , void 0 , void 0 , function * ( ) {
if ( ! isValidRemoteCouchDBURI ( uri ) )
;
let db = new o ( uri , {
auth ,
} ) ;
try {
let info = yield db . info ( ) ;
return { db : db , info : info } ;
}
catch ( ex ) {
return ;
}
} ) ;
2021-10-12 16:50:13 +02:00
//<--Functions
2021-10-14 12:27:08 +02:00
class LocalPouchDB {
constructor ( app , plugin , dbname ) {
this . syncHandler = null ;
this . plugin = plugin ;
this . app = app ;
this . auth = {
username : "" ,
password : "" ,
} ;
this . dbname = dbname ;
this . addLog = this . plugin . addLog ;
this . initializeDatabase ( ) ;
}
close ( ) {
this . localDatabase . close ( ) ;
}
status ( ) {
if ( this . syncHandler == null ) {
return "connected" ;
}
return "disabled" ;
}
initializeDatabase ( ) {
if ( this . localDatabase != null )
this . localDatabase . close ( ) ;
this . localDatabase = null ;
this . localDatabase = new o ( this . dbname + "-livesync" , {
auto _compaction : true ,
revs _limit : 100 ,
deterministic _revs : true ,
} ) ;
}
getDatabaseDoc ( id , opt ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
try {
let obj = null ;
if ( opt ) {
obj = yield this . localDatabase . get ( id , opt ) ;
}
else {
obj = yield this . localDatabase . get ( id ) ;
}
if ( obj . type && obj . type == "leaf" ) {
//do nothing for leaf;
return false ;
}
//Check it out and fix docs to regular case
if ( ! obj . type || ( obj . type && obj . type == "notes" ) ) {
let note = obj ;
let doc = {
data : note . data ,
_id : note . _id ,
ctime : note . ctime ,
mtime : note . mtime ,
size : note . size ,
_deleted : obj . _deleted ,
_rev : obj . _rev ,
children : [ ] ,
} ;
return doc ;
// simple note
}
if ( obj . type == "newnote" ) {
// search childrens
try {
let childrens = [ ] ;
// let childPromise = [];
for ( var v of obj . children ) {
// childPromise.push(this.localDatabase.get(v));
let elem = yield this . localDatabase . get ( v ) ;
if ( elem . type && elem . type == "leaf" ) {
childrens . push ( elem ) ;
}
else {
throw new Error ( "linked document is not leaf" ) ;
}
}
// let childrens = await Promise.all(childPromise);
let data = childrens
// .filter((e) => e.type == "leaf")
// .map((e) => e as NoteLeaf)
. sort ( ( e ) => e . seq )
. map ( ( e ) => e . data )
. join ( "" ) ;
let doc = {
data : data ,
_id : obj . _id ,
ctime : obj . ctime ,
mtime : obj . mtime ,
size : obj . size ,
_deleted : obj . _deleted ,
_rev : obj . _rev ,
children : obj . children ,
} ;
return doc ;
}
catch ( ex ) {
if ( ex . status && ex . status == 404 ) {
this . addLog ( ` Missing document content!, could not read ${ obj . _id } from database. ` , true ) ;
// this.addLog(ex);
}
this . addLog ( ` Something went wrong on reading ${ obj . _id } from database. ` , true ) ;
this . addLog ( ex ) ;
}
}
}
catch ( ex ) {
if ( ex . status && ex . status == 404 ) {
return false ;
}
throw ex ;
}
return false ;
} ) ;
}
deleteDBEntry ( id , opt ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
try {
let obj = null ;
if ( opt ) {
obj = yield this . localDatabase . get ( id , opt ) ;
}
else {
obj = yield this . localDatabase . get ( id ) ;
}
if ( obj . type && obj . type == "leaf" ) {
//do nothing for leaf;
return false ;
}
//Check it out and fix docs to regular case
if ( ! obj . type || ( obj . type && obj . type == "notes" ) ) {
// let note = obj as Notes;
// note._deleted=true;
2021-10-14 18:26:15 +02:00
obj . _deleted = true ;
let r = yield this . localDatabase . put ( obj ) ;
2021-10-14 12:27:08 +02:00
return true ;
// simple note
}
if ( obj . type == "newnote" ) {
// search childrens
for ( var v of obj . children ) {
let d = yield this . localDatabase . get ( v ) ;
if ( d . type != "leaf" ) {
this . addLog ( ` structure went wrong: ${ id } - ${ v } ` ) ;
}
d . _deleted = true ;
yield this . localDatabase . put ( d ) ;
this . addLog ( ` content removed: ${ d . seq } ` ) ;
}
obj . _deleted = true ;
yield this . localDatabase . put ( obj ) ;
this . addLog ( ` entry removed: ${ obj . _id } ` ) ;
return true ;
}
}
catch ( ex ) {
if ( ex . status && ex . status == 404 ) {
return false ;
}
throw ex ;
}
} ) ;
}
putDBEntry ( note ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let leftData = note . data ;
let savenNotes = [ ] ; // something occured, kill this .
let seq = 0 ;
let now = Date . now ( ) ;
do {
let piece = leftData . substring ( 0 , MAX _DOC _SIZE ) ;
leftData = leftData . substring ( MAX _DOC _SIZE ) ;
seq ++ ;
let leafid = note . _id + "-" + now + "-" + seq ;
let d = {
_id : leafid ,
parent : note . _id ,
data : piece ,
seq : seq ,
type : "leaf" ,
} ;
yield this . localDatabase . put ( d ) ;
savenNotes . push ( leafid ) ;
} while ( leftData != "" ) ;
this . addLog ( ` note content saven, pieces: ${ seq } ` ) ;
let newDoc = {
NewNote : true ,
children : savenNotes ,
_id : note . _id ,
ctime : note . ctime ,
mtime : note . mtime ,
size : note . size ,
type : "newnote" ,
} ;
let deldocs = [ ] ;
// Here for upsert logic,
try {
let old = yield this . localDatabase . get ( newDoc . _id ) ;
if ( ! old . type || old . type == "notes" ) {
// simple use rev for new doc
newDoc . _rev = old . _rev ;
}
if ( old . type == "newnote" ) {
//when save finished, we have to garbage collect.
deldocs = old . children ;
newDoc . _rev = old . _rev ;
}
}
catch ( ex ) {
if ( ex . status && ex . status == 404 ) ;
else {
throw ex ;
}
}
yield this . localDatabase . put ( newDoc ) ;
this . addLog ( ` note saven: ${ newDoc . _id } ` ) ;
let items = 0 ;
for ( var v of deldocs ) {
items ++ ;
//TODO: Check for missing link
let d = yield this . localDatabase . get ( v ) ;
d . _deleted = true ;
yield this . localDatabase . put ( d ) ;
}
this . addLog ( ` old content deleted, pieces: ${ items } ` ) ;
} ) ;
}
openReplication ( setting , keepAlive , callback ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let uri = setting . couchDB _URI ;
let auth = {
username : setting . couchDB _USER ,
password : setting . couchDB _PASSWORD ,
} ;
if ( this . syncHandler != null ) {
this . addLog ( "Another replication running." , true ) ;
return false ;
}
let dbret = yield connectRemoteCouchDB ( uri , auth ) ;
if ( dbret === false ) {
this . addLog ( ` could not connect to ${ uri } ` , true ) ;
return ;
}
let syncOption = keepAlive ? { live : true , retry : true } : { } ;
let db = dbret . db ;
//replicate once
this . localDatabase . replicate
. from ( db )
. on ( "complete" , ( info ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . syncHandler = this . localDatabase . sync ( db , syncOption ) ;
this . syncHandler
. on ( "change" , ( e ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
try {
callback ( e ) ;
this . addLog ( ` replicated ${ e . change . docs . length } doc(s) ` ) ;
}
catch ( ex ) {
this . addLog ( "Replication callback error" ) ;
this . addLog ( ex ) ;
}
} ) )
. on ( "active" , ( ) => {
this . addLog ( "Replication activated" ) ;
} )
. on ( "complete" , ( e ) => {
this . addLog ( "Replication completed" , true ) ;
// this.addLog(e);
this . syncHandler = null ;
} )
. on ( "denied" , ( e ) => {
this . addLog ( "Replication denied" , true ) ;
// this.addLog(e);
} )
. on ( "error" , ( e ) => {
this . addLog ( "Replication error" , true ) ;
// this.addLog(e);
} )
. on ( "paused" , ( e ) => {
this . addLog ( "replication paused" ) ;
} ) ;
} ) )
. on ( "error" , ( ) => {
this . addLog ( "Pulling Replication error" , true ) ;
} ) ;
} ) ;
}
closeReplication ( ) {
if ( this . syncHandler == null ) {
return ;
}
this . syncHandler . cancel ( ) ;
this . syncHandler . removeAllListeners ( ) ;
this . syncHandler = null ;
2021-10-14 18:26:15 +02:00
this . addLog ( "Replication closed" ) ;
2021-10-14 12:27:08 +02:00
}
resetDatabase ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . closeReplication ( ) ;
yield this . localDatabase . destroy ( ) ;
yield this . initializeDatabase ( ) ;
this . addLog ( "Local Database Reset" , true ) ;
} ) ;
}
tryResetRemoteDatabase ( setting ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . closeReplication ( ) ;
yield this . closeReplication ( ) ;
let uri = setting . couchDB _URI ;
let auth = {
username : setting . couchDB _USER ,
password : setting . couchDB _PASSWORD ,
} ;
let con = yield connectRemoteCouchDB ( uri , auth ) ;
if ( con === false )
return ;
try {
yield con . db . destroy ( ) ;
this . addLog ( "Remote Database Destroyed" , true ) ;
yield this . tryCreateRemoteDatabase ( setting ) ;
}
catch ( ex ) {
this . addLog ( "something happend on Remote Database Destory" , true ) ;
}
} ) ;
}
tryCreateRemoteDatabase ( setting ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . closeReplication ( ) ;
let uri = setting . couchDB _URI ;
let auth = {
username : setting . couchDB _USER ,
password : setting . couchDB _PASSWORD ,
} ;
let con2 = yield connectRemoteCouchDB ( uri , auth ) ;
if ( con2 === false )
return ;
this . addLog ( "Remote Database Created or Connected" , true ) ;
} ) ;
}
}
2021-10-12 16:50:13 +02:00
class ObsidianLiveSyncPlugin extends obsidian . Plugin {
constructor ( ) {
super ( ... arguments ) ;
this . logMessage = [ ] ;
}
2021-10-14 12:27:08 +02:00
onload ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . addLog ( "loading plugin" ) ;
yield this . openDatabase ( ) ;
yield this . loadSettings ( ) ;
obsidian . addIcon ( "replicate" , ` <g transform="matrix(1.15 0 0 1.15 -8.31 -9.52)" fill="currentColor" fill-rule="evenodd">
< path d = "m85 22.2c-0.799-4.74-4.99-8.37-9.88-8.37-0.499 0-1.1 0.101-1.6 0.101-2.4-3.03-6.09-4.94-10.3-4.94-6.09 0-11.2 4.14-12.8 9.79-5.59 1.11-9.78 6.05-9.78 12 0 6.76 5.39 12.2 12 12.2h29.9c5.79 0 10.1-4.74 10.1-10.6 0-4.84-3.29-8.88-7.68-10.2zm-2.99 14.7h-29.5c-2.3-0.202-4.29-1.51-5.29-3.53-0.899-2.12-0.699-4.54 0.698-6.46 1.2-1.61 2.99-2.52 4.89-2.52 0.299 0 0.698 0 0.998 0.101l1.8 0.303v-2.02c0-3.63 2.4-6.76 5.89-7.57 0.599-0.101 1.2-0.202 1.8-0.202 2.89 0 5.49 1.62 6.79 4.24l0.598 1.21 1.3-0.504c0.599-0.202 1.3-0.303 2-0.303 1.3 0 2.5 0.404 3.59 1.11 1.6 1.21 2.6 3.13 2.6 5.15v1.61h2c2.6 0 4.69 2.12 4.69 4.74-0.099 2.52-2.2 4.64-4.79 4.64z" / >
< path d = "m53.2 49.2h-41.6c-1.8 0-3.2 1.4-3.2 3.2v28.6c0 1.8 1.4 3.2 3.2 3.2h15.8v4h-7v6h24v-6h-7v-4h15.8c1.8 0 3.2-1.4 3.2-3.2v-28.6c0-1.8-1.4-3.2-3.2-3.2zm-2.8 29h-36v-23h36z" / >
< path d = "m73 49.2c1.02 1.29 1.53 2.97 1.53 4.56 0 2.97-1.74 5.65-4.39 7.04v-4.06l-7.46 7.33 7.46 7.14v-4.06c7.66-1.98 12.2-9.61 10-17-0.102-0.297-0.205-0.595-0.307-0.892z" / >
< path d = "m24.1 43c-0.817-0.991-1.53-2.97-1.53-4.56 0-2.97 1.74-5.65 4.39-7.04v4.06l7.46-7.33-7.46-7.14v4.06c-7.66 1.98-12.2 9.61-10 17 0.102 0.297 0.205 0.595 0.307 0.892z" / >
< / g > ` ) ;
obsidian . addIcon ( "view-log" , ` <g transform="matrix(1.28 0 0 1.28 -131 -411)" fill="currentColor" fill-rule="evenodd">
< path d = "m103 330h76v12h-76z" / >
< path d = "m106 346v44h70v-44zm45 16h-20v-8h20z" / >
< / g > ` ) ;
this . addRibbonIcon ( "replicate" , "Replicate" , ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . replicate ( ) ;
} ) ) ;
this . addRibbonIcon ( "view-log" , "Show log" , ( ) => {
new LogDisplayModal ( this . app , this ) . open ( ) ;
} ) ;
this . statusBar = this . addStatusBarItem ( ) ;
this . statusBar2 = this . addStatusBarItem ( ) ;
this . watchVaultChange = obsidian . debounce ( this . watchVaultChange . bind ( this ) , 200 , false ) ;
this . watchVaultDelete = obsidian . debounce ( this . watchVaultDelete . bind ( this ) , 200 , false ) ;
this . watchVaultRename = obsidian . debounce ( this . watchVaultRename . bind ( this ) , 200 , false ) ;
this . watchWorkspaceOpen = obsidian . debounce ( this . watchWorkspaceOpen . bind ( this ) , 200 , false ) ;
this . registerWatchEvents ( ) ;
this . parseReplicationResult = this . parseReplicationResult . bind ( this ) ;
this . addSettingTab ( new ObsidianLiveSyncSettingTab ( this . app , this ) ) ;
setTimeout ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . initializeDatabase ( ) ;
this . realizeSettingSyncMode ( ) ;
if ( this . settings . syncOnStart ) {
yield this . replicate ( ) ;
}
} ) , 100 ) ;
// when in mobile, too long suspended , connection won't back if setting retry:true
this . registerInterval ( window . setInterval ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( this . settings . liveSync ) {
yield this . localDatabase . closeReplication ( ) ;
if ( this . settings . liveSync ) {
this . localDatabase . openReplication ( this . settings , true , this . parseReplicationResult ) ;
}
}
} ) , 60 * 1000 ) ) ;
} ) ;
}
onunload ( ) {
this . localDatabase . closeReplication ( ) ;
this . localDatabase . close ( ) ;
this . addLog ( "unloading plugin" ) ;
}
openDatabase ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( this . localDatabase != null ) {
this . localDatabase . close ( ) ;
}
let vaultName = this . app . vault . getName ( ) ;
this . localDatabase = new LocalPouchDB ( this . app , this , vaultName ) ;
this . localDatabase . initializeDatabase ( ) ;
} ) ;
}
loadSettings ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . settings = Object . assign ( { } , DEFAULT _SETTINGS , yield this . loadData ( ) ) ;
} ) ;
}
saveSettings ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . saveData ( this . settings ) ;
} ) ;
}
registerWatchEvents ( ) {
this . registerEvent ( this . app . vault . on ( "modify" , this . watchVaultChange ) ) ;
this . registerEvent ( this . app . vault . on ( "delete" , this . watchVaultDelete ) ) ;
this . registerEvent ( this . app . vault . on ( "rename" , this . watchVaultRename ) ) ;
this . registerEvent ( this . app . vault . on ( "create" , this . watchVaultChange ) ) ;
this . registerEvent ( this . app . workspace . on ( "file-open" , this . watchWorkspaceOpen ) ) ;
}
watchWorkspaceOpen ( file ) {
if ( file == null )
return ;
this . showIfConflicted ( file ) ;
}
watchVaultChange ( file , ... args ) {
this . updateIntoDB ( file ) ;
}
watchVaultDelete ( file ) {
if ( file . children ) {
//folder
this . deleteFolderOnDB ( file ) ;
// this.app.vault.delete(file);
}
else {
this . deleteFromDB ( file ) ;
}
}
watchVaultRename ( file , oldFile ) {
if ( file . children ) {
// this.renameFolder(file,oldFile);
this . addLog ( ` folder name changed:(this operation is not supported) ${ file . path } ` ) ;
}
else {
this . updateIntoDB ( file ) ;
this . deleteFromDBbyPath ( oldFile ) ;
}
}
//--> Basic document Functions
2021-10-13 14:38:44 +02:00
addLog ( message , isNotify ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let timestamp = new Date ( ) . toLocaleString ( ) ;
2021-10-13 14:38:44 +02:00
let messagecontent = typeof message == "string" ? message : JSON . stringify ( message , null , 2 ) ;
let newmessage = timestamp + "->" + messagecontent ;
2021-10-12 16:50:13 +02:00
this . logMessage = [ ] . concat ( this . logMessage ) . concat ( [ newmessage ] ) . slice ( - 100 ) ;
// this.logMessage = [...this.logMessage, timestamp + ":" + newmessage].slice(-100);
console . log ( newmessage ) ;
if ( this . statusBar2 != null ) {
2021-10-13 14:38:44 +02:00
this . statusBar2 . setText ( newmessage . substring ( 0 , 60 ) ) ;
}
if ( this . onLogChanged != null ) {
this . onLogChanged ( ) ;
}
if ( isNotify ) {
new obsidian . Notice ( messagecontent ) ;
2021-10-12 16:50:13 +02:00
}
} ) ;
}
ensureDirectory ( fullpath ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let pathElements = fullpath . split ( "/" ) ;
pathElements . pop ( ) ;
let c = "" ;
for ( var v of pathElements ) {
c += v ;
try {
yield this . app . vault . createFolder ( c ) ;
}
catch ( ex ) {
2021-10-13 14:38:44 +02:00
// basically skip exceptions.
if ( ex . message && ex . message == "Folder already exists." ) ;
else {
this . addLog ( "Folder Create Error" ) ;
this . addLog ( ex ) ;
}
2021-10-12 16:50:13 +02:00
}
c += "/" ;
}
} ) ;
}
2021-10-14 12:27:08 +02:00
doc2storage _create ( docEntry , force ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
let doc = yield this . localDatabase . getDatabaseDoc ( docEntry . _id , { _rev : docEntry . _rev } ) ;
if ( doc === false )
return ;
2021-10-12 16:50:13 +02:00
let bin = base64ToArrayBuffer ( doc . data ) ;
if ( bin != null ) {
yield this . ensureDirectory ( doc . _id ) ;
let newfile = yield this . app . vault . createBinary ( doc . _id , bin , { ctime : doc . ctime , mtime : doc . mtime } ) ;
this . addLog ( "live : write to local (newfile) " + doc . _id ) ;
yield this . app . vault . trigger ( "create" , newfile ) ;
}
} ) ;
}
2021-10-13 14:38:44 +02:00
deleteVaultItem ( file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let dir = file . parent ;
yield this . app . vault . delete ( file ) ;
this . addLog ( ` deleted: ${ file . path } ` ) ;
this . addLog ( ` other items: ${ dir . children . length } ` ) ;
if ( dir . children . length == 0 ) {
this . addLog ( ` all files deleted by replication, so delete dir ` ) ;
yield this . deleteVaultItem ( dir ) ;
}
} ) ;
}
2021-10-14 12:27:08 +02:00
doc2storate _modify ( docEntry , file , force ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
if ( docEntry . _deleted ) {
2021-10-12 16:50:13 +02:00
//basically pass.
//but if there're no docs left, delete file.
2021-10-14 12:27:08 +02:00
let lastDocs = yield this . localDatabase . getDatabaseDoc ( docEntry . _id ) ;
if ( lastDocs === false ) {
yield this . deleteVaultItem ( file ) ;
2021-10-12 16:50:13 +02:00
}
2021-10-14 12:27:08 +02:00
else {
this . addLog ( ` delete skipped: ${ lastDocs . _id } ` ) ;
2021-10-12 16:50:13 +02:00
}
return ;
}
2021-10-14 12:27:08 +02:00
if ( file . stat . mtime < docEntry . mtime || force ) {
let doc = yield this . localDatabase . getDatabaseDoc ( docEntry . _id ) ;
if ( doc === false )
return ;
2021-10-12 16:50:13 +02:00
let bin = base64ToArrayBuffer ( doc . data ) ;
if ( bin != null ) {
yield this . app . vault . modifyBinary ( file , bin , { ctime : doc . ctime , mtime : doc . mtime } ) ;
this . addLog ( "livesync : newer local files so write to local:" + file . path ) ;
yield this . app . vault . trigger ( "modify" , file ) ;
}
}
2021-10-14 12:27:08 +02:00
else if ( file . stat . mtime > docEntry . mtime ) ;
2021-10-12 16:50:13 +02:00
else ;
} ) ;
}
pouchdbChanged ( change ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let allfiles = this . app . vault . getFiles ( ) ;
let targetFiles = allfiles . filter ( ( e ) => e . path == change . _id ) ;
if ( targetFiles . length == 0 ) {
if ( change . _deleted ) {
return ;
}
let doc = change ;
yield this . doc2storage _create ( doc ) ;
}
if ( targetFiles . length == 1 ) {
let doc = change ;
let file = targetFiles [ 0 ] ;
yield this . doc2storate _modify ( doc , file ) ;
yield this . showIfConflicted ( file ) ;
}
} ) ;
}
2021-10-14 12:27:08 +02:00
//---> Sync
parseReplicationResult ( e ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
let docs = e . change . docs ;
for ( var change of docs ) {
this . addLog ( "replication change arrived" ) ;
// this.addLog(change);
yield this . pouchdbChanged ( change ) ;
2021-10-12 16:50:13 +02:00
}
} ) ;
}
realizeSettingSyncMode ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . localDatabase . closeReplication ( ) ;
if ( this . settings . liveSync ) {
this . localDatabase . openReplication ( this . settings , true , this . parseReplicationResult ) ;
this . refreshStatusText ( ) ;
2021-10-12 16:50:13 +02:00
}
} ) ;
}
refreshStatusText ( ) {
2021-10-14 12:27:08 +02:00
let statusStr = this . localDatabase . status ( ) ;
2021-10-12 16:50:13 +02:00
this . statusBar . setText ( "Sync:" + statusStr ) ;
}
replicate ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
this . localDatabase . openReplication ( this . settings , false , this . parseReplicationResult ) ;
2021-10-12 16:50:13 +02:00
} ) ;
}
2021-10-14 12:27:08 +02:00
//<-- Sync
initializeDatabase ( ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . openDatabase ( ) ;
yield this . syncAllFiles ( ) ;
2021-10-12 16:50:13 +02:00
} ) ;
}
syncAllFiles ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// synchronize all files between database and storage.
const filesStorage = this . app . vault . getFiles ( ) ;
const filesStorageName = filesStorage . map ( ( e ) => e . path ) ;
2021-10-14 12:27:08 +02:00
const wf = yield this . localDatabase . localDatabase . allDocs ( ) ;
2021-10-12 16:50:13 +02:00
const filesDatabase = wf . rows . map ( ( e ) => e . id ) ;
const onlyInStorage = filesStorage . filter ( ( e ) => filesDatabase . indexOf ( e . path ) == - 1 ) ;
const onlyInDatabase = filesDatabase . filter ( ( e ) => filesStorageName . indexOf ( e ) == - 1 ) ;
//simply realize it
const onlyInStorageNames = onlyInStorage . map ( ( e ) => e . path ) ;
//have to sync below..
const syncFiles = filesStorage . filter ( ( e ) => onlyInStorageNames . indexOf ( e . path ) == - 1 ) ;
for ( let v of onlyInStorage ) {
2021-10-14 12:27:08 +02:00
yield this . updateIntoDB ( v ) ;
2021-10-12 16:50:13 +02:00
}
for ( let v of onlyInDatabase ) {
yield this . pullFile ( v , filesStorage ) ;
}
for ( let v of syncFiles ) {
yield this . syncFileBetweenDBandStorage ( v , filesStorage ) ;
}
} ) ;
}
2021-10-13 14:38:44 +02:00
deleteFolderOnDB ( folder ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . addLog ( ` delete folder: ${ folder . path } ` ) ;
for ( var v of folder . children ) {
let entry = v ;
this . addLog ( ` ->entry: ${ entry . path } ` ) ;
if ( entry . children ) {
this . addLog ( ` ->is dir ` ) ;
yield this . deleteFolderOnDB ( entry ) ;
yield this . app . vault . delete ( entry ) ;
}
else {
this . addLog ( ` ->is file ` ) ;
2021-10-14 12:27:08 +02:00
yield this . deleteFromDB ( entry ) ;
2021-10-13 14:38:44 +02:00
}
}
yield this . app . vault . delete ( folder ) ;
} ) ;
2021-10-12 16:50:13 +02:00
}
2021-10-13 14:38:44 +02:00
renameFolder ( folder , oldFile ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
for ( var v of folder . children ) {
let entry = v ;
if ( entry . children ) {
yield this . deleteFolderOnDB ( entry ) ;
this . app . vault . delete ( entry ) ;
}
else {
2021-10-14 12:27:08 +02:00
yield this . deleteFromDB ( entry ) ;
2021-10-13 14:38:44 +02:00
}
}
} ) ;
2021-10-12 16:50:13 +02:00
}
// --> conflict resolving
getConflictedDoc ( path , rev ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
try {
2021-10-14 12:27:08 +02:00
let doc = yield this . localDatabase . getDatabaseDoc ( path , { rev : rev } ) ;
if ( doc === false )
return false ;
2021-10-12 16:50:13 +02:00
return {
ctime : doc . ctime ,
mtime : doc . mtime ,
rev : rev ,
data : base64ToString ( doc . data ) ,
} ;
}
catch ( ex ) {
if ( ex . status && ex . status == 404 ) {
return false ;
}
}
return false ;
} ) ;
}
getConflictedStatus ( path ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-13 14:38:44 +02:00
let test = null ;
try {
2021-10-14 12:27:08 +02:00
let testDoc = yield this . localDatabase . getDatabaseDoc ( path , { conflicts : true } ) ;
if ( testDoc === false )
return false ;
if ( "_rev" in testDoc ) {
test = testDoc ;
}
2021-10-13 14:38:44 +02:00
}
catch ( ex ) {
if ( ex . status && ex . status == 404 ) {
this . addLog ( ` Getting conflicted status, but there was not ${ path } ` ) ;
// NO OP.
}
else {
throw ex ;
}
}
if ( test == null )
return false ;
2021-10-12 16:50:13 +02:00
if ( ! test . _conflicts )
return false ;
if ( test . _conflicts . length == 0 )
return false ;
// should be two or more conflicts;
let leftLeaf = yield this . getConflictedDoc ( path , test . _rev ) ;
let rightLeaf = yield this . getConflictedDoc ( path , test . _conflicts [ 0 ] ) ;
if ( leftLeaf === false )
return false ;
if ( rightLeaf === false )
return false ;
// first,check for same contents
if ( leftLeaf . data == rightLeaf . data ) {
let leaf = leftLeaf ;
if ( leftLeaf . mtime > rightLeaf . mtime ) {
leaf = rightLeaf ;
}
2021-10-14 12:27:08 +02:00
yield this . localDatabase . deleteDBEntry ( path , leaf . rev ) ;
2021-10-12 16:50:13 +02:00
yield this . pullFile ( path , null , true ) ;
this . addLog ( ` automaticaly merged: ${ path } ` ) ;
return true ;
// }
}
let dmp = new diffMatchPatch . diff _match _patch ( ) ;
var diff = dmp . diff _main ( leftLeaf . data , rightLeaf . data ) ;
dmp . diff _cleanupSemantic ( diff ) ;
this . addLog ( ` conflict(s) found: ${ path } ` ) ;
return {
left : leftLeaf ,
right : rightLeaf ,
diff : diff ,
} ;
} ) ;
}
showIfConflicted ( file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let conflictCheckResult = yield this . getConflictedStatus ( file . path ) ;
if ( conflictCheckResult === false )
return ; //nothign to do.
if ( conflictCheckResult === true ) {
//auto resolved, but need check again;
setTimeout ( ( ) => {
this . showIfConflicted ( file ) ;
} , 50 ) ;
return ;
}
//there conflicts, and have to resolve ;
let leaf = this . app . workspace . activeLeaf ;
if ( leaf ) {
new ConflictResolveModal ( this . app , conflictCheckResult , ( selected ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let toDelete = selected ;
if ( toDelete == "" ) {
return ;
}
this . addLog ( ` resolved conflict: ${ file . path } ` ) ;
2021-10-14 12:27:08 +02:00
yield this . localDatabase . deleteDBEntry ( file . path , toDelete ) ;
2021-10-12 16:50:13 +02:00
yield this . pullFile ( file . path , null , true ) ;
setTimeout ( ( ) => {
//resolved, check again.
this . showIfConflicted ( file ) ;
} , 50 ) ;
} ) ) . open ( ) ;
}
} ) ;
}
pullFile ( filename , fileList , force ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( ! fileList ) {
fileList = this . app . vault . getFiles ( ) ;
}
let targetFiles = fileList . filter ( ( e ) => e . path == filename ) ;
if ( targetFiles . length == 0 ) {
//have to create;
2021-10-14 12:27:08 +02:00
let doc = yield this . localDatabase . getDatabaseDoc ( filename ) ;
if ( doc === false )
return ;
2021-10-12 16:50:13 +02:00
yield this . doc2storage _create ( doc , force ) ;
}
else if ( targetFiles . length == 1 ) {
//normal case
let file = targetFiles [ 0 ] ;
2021-10-14 12:27:08 +02:00
let doc = yield this . localDatabase . getDatabaseDoc ( filename ) ;
if ( doc === false )
return ;
2021-10-12 16:50:13 +02:00
yield this . doc2storate _modify ( doc , file , force ) ;
}
2021-10-13 14:38:44 +02:00
else {
this . addLog ( ` target files: ${ filename } is two or more files in your vault ` ) ;
//something went wrong..
}
2021-10-12 16:50:13 +02:00
//when to opened file;
} ) ;
}
syncFileBetweenDBandStorage ( file , fileList ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
let doc = yield this . localDatabase . getDatabaseDoc ( file . path ) ;
if ( doc === false )
return ;
2021-10-12 16:50:13 +02:00
if ( file . stat . mtime > doc . mtime ) {
//newer local file.
2021-10-14 12:27:08 +02:00
yield this . updateIntoDB ( file ) ;
2021-10-12 16:50:13 +02:00
this . addLog ( "sync : older databse files so write to database:" + file . path ) ;
}
else if ( file . stat . mtime < doc . mtime ) {
//newer database file.
this . addLog ( "sync : older storage files so write from database:" + file . path ) ;
yield this . doc2storate _modify ( doc , file ) ;
}
else ;
} ) ;
}
2021-10-14 12:27:08 +02:00
updateIntoDB ( file ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let contentBin = yield this . app . vault . readBinary ( file ) ;
let content = arrayBufferToBase64 ( contentBin ) ;
let fullpath = file . path ;
let d = {
_id : fullpath ,
data : content ,
ctime : file . stat . ctime ,
mtime : file . stat . mtime ,
size : file . stat . size ,
2021-10-14 12:27:08 +02:00
children : [ ] ,
2021-10-12 16:50:13 +02:00
} ;
2021-10-14 12:27:08 +02:00
//From here
let old = yield this . localDatabase . getDatabaseDoc ( fullpath ) ;
if ( old !== false ) {
2021-10-13 14:38:44 +02:00
let oldData = { data : old . data , deleted : old . _deleted } ;
let newData = { data : d . data , deleted : d . _deleted } ;
2021-10-12 16:50:13 +02:00
if ( JSON . stringify ( oldData ) == JSON . stringify ( newData ) ) {
2021-10-14 18:26:15 +02:00
this . addLog ( "no changed" + fullpath + d . _deleted ) ;
2021-10-12 16:50:13 +02:00
return ;
}
2021-10-14 12:27:08 +02:00
// d._rev = old._rev;
2021-10-12 16:50:13 +02:00
}
2021-10-14 12:27:08 +02:00
yield this . localDatabase . putDBEntry ( d ) ;
2021-10-13 14:38:44 +02:00
this . addLog ( "put database:" + fullpath ) ;
2021-10-12 16:50:13 +02:00
if ( this . settings . syncOnSave ) {
yield this . replicate ( ) ;
}
} ) ;
}
2021-10-14 12:27:08 +02:00
deleteFromDB ( file ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let fullpath = file . path ;
this . addLog ( ` deleteDB By path: ${ fullpath } ` ) ;
2021-10-14 12:27:08 +02:00
yield this . deleteFromDBbyPath ( fullpath ) ;
2021-10-12 16:50:13 +02:00
if ( this . settings . syncOnSave ) {
yield this . replicate ( ) ;
}
} ) ;
}
2021-10-14 12:27:08 +02:00
deleteFromDBbyPath ( fullpath ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . localDatabase . deleteDBEntry ( fullpath ) ;
2021-10-12 16:50:13 +02:00
if ( this . settings . syncOnSave ) {
yield this . replicate ( ) ;
}
} ) ;
}
resetLocalDatabase ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . localDatabase . resetDatabase ( ) ;
2021-10-12 16:50:13 +02:00
} ) ;
}
2021-10-14 12:27:08 +02:00
tryResetRemoteDatabase ( ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . localDatabase . tryResetRemoteDatabase ( this . settings ) ;
2021-10-12 16:50:13 +02:00
} ) ;
}
2021-10-14 12:27:08 +02:00
tryCreateRemoteDatabase ( ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . localDatabase . tryCreateRemoteDatabase ( this . settings ) ;
2021-10-12 16:50:13 +02:00
} ) ;
}
}
2021-10-13 14:38:44 +02:00
class LogDisplayModal extends obsidian . Modal {
constructor ( app , plugin ) {
super ( app ) ;
this . plugin = plugin ;
}
updateLog ( ) {
let logs = [ ... this . plugin . logMessage ] ;
let msg = "" ;
for ( var v of logs ) {
msg += escapeStringToHTML ( v ) + "<br>" ;
}
this . logEl . innerHTML = msg ;
}
onOpen ( ) {
let { contentEl } = this ;
contentEl . empty ( ) ;
contentEl . createEl ( "h2" , { text : "Sync Status" } ) ;
let div = contentEl . createDiv ( "" ) ;
div . addClass ( "op-scrollable" ) ;
div . addClass ( "op-pre" ) ;
this . logEl = div ;
this . plugin . onLogChanged = this . updateLog ;
this . updateLog ( ) ;
}
onClose ( ) {
let { contentEl } = this ;
contentEl . empty ( ) ;
this . plugin . onLogChanged = null ;
}
}
2021-10-12 16:50:13 +02:00
class ConflictResolveModal extends obsidian . Modal {
constructor ( app , diff , callback ) {
super ( app ) ;
this . result = diff ;
this . callback = callback ;
}
onOpen ( ) {
let { contentEl } = this ;
contentEl . empty ( ) ;
contentEl . createEl ( "h2" , { text : "This document has conflicted changes." } ) ;
let div = contentEl . createDiv ( "" ) ;
div . addClass ( "op-scrollable" ) ;
let diff = "" ;
// const showContents = this.result.map((e) => (e[0] == 1 ? "<span class='added'>" + htmlEscape(e[1]) + "</span>" : e[0] == -1 ? "<span class='deleted'>" + htmlEscape(e[1]) + "</span>" : "<span class='normal'>" + htmlEscape(e[1]) + "</span>"));
for ( let v of this . result . diff ) {
let x1 = v [ 0 ] ;
let x2 = v [ 1 ] ;
if ( x1 == diffMatchPatch . DIFF _DELETE ) {
diff += "<span class='deleted'>" + escapeStringToHTML ( x2 ) + "</span>" ;
}
else if ( x1 == diffMatchPatch . DIFF _EQUAL ) {
diff += "<span class='normal'>" + escapeStringToHTML ( x2 ) + "</span>" ;
}
else if ( x1 == diffMatchPatch . DIFF _INSERT ) {
diff += "<span class='added'>" + escapeStringToHTML ( x2 ) + "</span>" ;
}
}
diff = diff . replace ( /\n/g , "<br>" ) ;
div . innerHTML = diff ;
let div2 = contentEl . createDiv ( "" ) ;
let date1 = new Date ( this . result . left . mtime ) . toLocaleString ( ) ;
let date2 = new Date ( this . result . right . mtime ) . toLocaleString ( ) ;
div2 . innerHTML = `
< span class = 'deleted' > A : $ { date1 } < /span><br / > < span class = 'added' > B : $ { date2 } < / s p a n > < b r >
` ;
contentEl . createEl ( "button" , { text : "Keep A" } , ( e ) => {
e . addEventListener ( "click" , ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . callback ( this . result . right . rev ) ;
this . close ( ) ;
} ) ) ;
} ) ;
contentEl . createEl ( "button" , { text : "Keep B" } , ( e ) => {
e . addEventListener ( "click" , ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . callback ( this . result . left . rev ) ;
this . close ( ) ;
} ) ) ;
} ) ;
contentEl . createEl ( "button" , { text : "Not now" } , ( e ) => {
e . addEventListener ( "click" , ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . close ( ) ;
} ) ) ;
} ) ;
}
onClose ( ) {
let { contentEl } = this ;
contentEl . empty ( ) ;
}
}
class ObsidianLiveSyncSettingTab extends obsidian . PluginSettingTab {
constructor ( app , plugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
}
2021-10-14 12:27:08 +02:00
testConnection ( ) {
2021-10-12 16:50:13 +02:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-13 14:38:44 +02:00
let db = yield connectRemoteCouchDB ( this . plugin . settings . couchDB _URI , {
username : this . plugin . settings . couchDB _USER ,
password : this . plugin . settings . couchDB _PASSWORD ,
2021-10-12 16:50:13 +02:00
} ) ;
2021-10-13 14:38:44 +02:00
if ( db === false ) {
2021-10-14 12:27:08 +02:00
this . plugin . addLog ( ` could not connect to ${ this . plugin . settings . couchDB _URI } ` , true ) ;
2021-10-13 14:38:44 +02:00
return ;
2021-10-12 16:50:13 +02:00
}
2021-10-14 12:27:08 +02:00
this . plugin . addLog ( ` Connected to ${ db . info . db _name } ` , true ) ;
2021-10-12 16:50:13 +02:00
} ) ;
}
display ( ) {
let { containerEl } = this ;
containerEl . empty ( ) ;
containerEl . createEl ( "h2" , { text : "Settings for obsidian-livesync." } ) ;
2021-10-13 14:38:44 +02:00
new obsidian . Setting ( containerEl ) . setName ( "CouchDB Remote URI" ) . addText ( ( text ) => text
2021-10-12 16:50:13 +02:00
. setPlaceholder ( "https://........" )
. setValue ( this . plugin . settings . couchDB _URI )
. onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . couchDB _URI = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
new obsidian . Setting ( containerEl )
. setName ( "CouchDB Username" )
. setDesc ( "username" )
. addText ( ( text ) => text
. setPlaceholder ( "" )
. setValue ( this . plugin . settings . couchDB _USER )
. onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . couchDB _USER = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
new obsidian . Setting ( containerEl )
. setName ( "CouchDB Password" )
. setDesc ( "password" )
. addText ( ( text ) => {
text . setPlaceholder ( "" )
. setValue ( this . plugin . settings . couchDB _PASSWORD )
. onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . couchDB _PASSWORD = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
text . inputEl . setAttribute ( "type" , "password" ) ;
} ) ;
new obsidian . Setting ( containerEl ) . setName ( "Test DB" ) . addButton ( ( button ) => button
. setButtonText ( "Test Database Connection" )
. setDisabled ( false )
. onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2021-10-14 12:27:08 +02:00
yield this . testConnection ( ) ;
2021-10-12 16:50:13 +02:00
} ) ) ) ;
new obsidian . Setting ( containerEl )
. setName ( "LiveSync" )
. setDesc ( "Sync realtime" )
2021-10-14 12:27:08 +02:00
. addToggle ( ( toggle ) => toggle . setValue ( this . plugin . settings . liveSync ) . onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . liveSync = value ;
2021-10-12 16:50:13 +02:00
yield this . plugin . saveSettings ( ) ;
this . plugin . realizeSettingSyncMode ( ) ;
} ) ) ) ;
new obsidian . Setting ( containerEl )
. setName ( "Sync on Save" )
. setDesc ( "Sync on Save" )
. addToggle ( ( toggle ) => toggle . setValue ( this . plugin . settings . syncOnSave ) . onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . syncOnSave = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
new obsidian . Setting ( containerEl )
. setName ( "Sync on Start" )
. setDesc ( "Sync on Start" )
. addToggle ( ( toggle ) => toggle . setValue ( this . plugin . settings . syncOnStart ) . onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . syncOnStart = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
new obsidian . Setting ( containerEl )
2021-10-13 14:38:44 +02:00
. setName ( "Local Database Operations" )
2021-10-12 16:50:13 +02:00
. addButton ( ( button ) => button
. setButtonText ( "Reset local database" )
. setDisabled ( false )
. onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . plugin . resetLocalDatabase ( ) ;
//await this.test();
} ) ) )
. addButton ( ( button ) => button
. setButtonText ( "Reset local files" )
. setDisabled ( false )
. onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
//await this.test();
} ) ) ) ;
2021-10-14 12:27:08 +02:00
new obsidian . Setting ( containerEl ) . setName ( "Remote Database Operations" ) . addButton ( ( button ) => button
. setButtonText ( "Reset remote database" )
. setDisabled ( false )
. onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . plugin . tryResetRemoteDatabase ( ) ;
//await this.test();
} ) ) ) ;
new obsidian . Setting ( containerEl ) . setName ( "Remote Database Operations" ) . addButton ( ( button ) => button
. setButtonText ( "Create remote database" )
. setDisabled ( false )
. onClick ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . plugin . tryResetRemoteDatabase ( ) ;
//await this.test();
} ) ) ) ;
2021-10-12 16:50:13 +02:00
}
}
module . exports = ObsidianLiveSyncPlugin ;
2021-10-14 18:26:15 +02:00
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInBvdWNoZGItYnJvd3Nlci13ZWJwYWNrL2Rpc3QvcG91Y2hkYi1icm93c2VyLmpzIiwibm9kZV9tb2R1bGVzL2RpZmYtbWF0Y2gtcGF0Y2gvaW5kZXguanMiLCJtYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpudWxsLCJuYW1lcyI6WyJQb3VjaERCIiwiUGx1Z2luIiwiYWRkSWNvbiIsImRlYm91bmNlIiwiTm90aWNlIiwiZGlmZl9tYXRjaF9wYXRjaCIsIk1vZGFsIiwiRElGRl9ERUxFVEUiLCJESUZGX0VRVUFMIiwiRElGRl9JTlNFUlQiLCJQbHVnaW5TZXR0aW5nVGFiIiwiU2V0dGluZyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQXVEQTtBQUNPLFNBQVMsU0FBUyxDQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRTtBQUM3RCxJQUFJLFNBQVMsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLE9BQU8sS0FBSyxZQUFZLENBQUMsR0FBRyxLQUFLLEdBQUcsSUFBSSxDQUFDLENBQUMsVUFBVSxPQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRTtBQUNoSCxJQUFJLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQyxFQUFFLFVBQVUsT0FBTyxFQUFFLE1BQU0sRUFBRTtBQUMvRCxRQUFRLFNBQVMsU0FBUyxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7QUFDbkcsUUFBUSxTQUFTLFFBQVEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7QUFDdEcsUUFBUSxTQUFTLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxNQUFNLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDLEVBQUU7QUFDdEgsUUFBUSxJQUFJLENBQUMsQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsVUFBVSxJQUFJLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFDOUUsS0FBSyxDQUFDLENBQUM7QUFDUDs7QUM3RUEsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sUUFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sTUFBTSxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxJQUFJLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsY0FBYyxFQUFFLENBQUMsQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxHQUFHLENBQUM