1- /*! diff-merge v1 .0.1 | nighca([email protected] ) | Apache License(2.0) */ 1+ /*! universal-diff v2 .0.0 | nighca([email protected] ) | Apache License(2.0) */ 22
33( function ( global , undefined ) {
44
55
6- var compare = function ( cnt1 , cnt2 , splitter ) {
7- var SPLITTER = typeof splitter === 'string' ? splitter : '' ,
6+ // steps
87
9- MARK_EMPTY = - 1 ,
10- MARK_SAME = 0 ,
8+ var STEP_NOCHANGE = 0 ,
9+ STEP_REPLACE = 1 ,
10+ STEP_REMOVE = 2 ,
11+ STEP_INSERT = 3 ;
1112
12- STEP_NOCHANGE = 0 ,
13- STEP_REPLACE = 1 ,
14- STEP_REMOVE = 2 ,
15- STEP_INSERT = 3 ;
1613
17- // result object
18- var diff = [ ] ,
19- result = {
20- splitter : SPLITTER ,
21- diff : diff
22- } ;
14+ // script marks
2315
24- // if string equal
25- if ( cnt1 === cnt2 ) {
26- return result ;
27- }
16+ var MARK_EMPTY = - 1 ,
17+ MARK_SAME = 0 ;
18+
19+
20+ var defaultEqual = function ( a , b ) {
21+ return a === b ;
22+ } ;
23+
24+ // caculate min-edit-script (naive)
25+ var naiveCompare = function ( seq1 , seq2 , eq ) {
2826
29- // convert string to array
30- var arr1 , arr2 ;
31- if ( typeof splitter === 'function' ) {
32- arr1 = splitter ( cnt1 ) ;
33- arr2 = splitter ( cnt2 ) ;
34- } else {
35- arr1 = cnt1 . split ( SPLITTER ) ;
36- arr2 = cnt2 . split ( SPLITTER ) ;
27+ var l1 = seq1 . length ,
28+ l2 = seq2 . length ,
29+ distMap = Array . apply ( null , { length : l1 + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
30+ stepMap = Array . apply ( null , { length : l1 + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
31+ i , j ;
32+
33+ eq = eq || defaultEqual ;
34+
35+ for ( i = 0 ; i <= l1 ; i ++ ) {
36+ for ( j = 0 ; j <= l2 ; j ++ ) {
37+
38+ if ( i === 0 || j === 0 ) {
39+ distMap [ i ] [ j ] = i || j ;
40+ stepMap [ i ] [ j ] = i > 0 ? STEP_REMOVE : STEP_INSERT ;
41+
42+ } else {
43+ var equal = eq ( seq1 [ i - 1 ] , seq2 [ j - 1 ] ) ,
44+
45+ removeDist = distMap [ i - 1 ] [ j ] + 1 ,
46+ insertDist = distMap [ i ] [ j - 1 ] + 1 ,
47+ replaceDist = distMap [ i - 1 ] [ j - 1 ] + ( equal ? 0 : 2 ) ,
48+ dist = Math . min ( replaceDist , removeDist , insertDist ) ;
49+
50+ distMap [ i ] [ j ] = dist ;
51+
52+ switch ( dist ) {
53+
54+ case replaceDist :
55+ stepMap [ i ] [ j ] = equal ? STEP_NOCHANGE : STEP_REPLACE ;
56+ break ;
57+
58+ case removeDist :
59+ stepMap [ i ] [ j ] = STEP_REMOVE ;
60+ break ;
61+
62+ case insertDist :
63+ stepMap [ i ] [ j ] = STEP_INSERT ;
64+
65+ }
66+ }
67+ }
3768 }
3869
39- var N = arr1 . length ,
40- M = arr2 . length ,
70+ return stepMap ;
71+ } ;
72+
73+ // caculate min-edit-script (myers)
74+ var myersCompare = function ( seq1 , seq2 , eq ) {
75+
76+ var N = seq1 . length ,
77+ M = seq2 . length ,
4178 MAX = N + M ,
42- steps = Array . apply ( null , { length : M + N + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
79+ stepMap = Array . apply ( null , { length : M + N + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
4380 furthestReaching = [ ] ,
4481 dist = - 1 ;
4582
83+ eq = eq || defaultEqual ;
84+
4685 furthestReaching [ MAX + 1 ] = 0 ;
4786
4887 // caculate min distance & log each step
@@ -57,12 +96,12 @@ var compare = function(cnt1, cnt2, splitter){
5796 }
5897
5998 y = x - k ;
60- steps [ x ] [ y ] = step ;
99+ stepMap [ x ] [ y ] = step ;
61100
62- while ( x < N && y < M && arr1 [ x ] === arr2 [ y ] ) {
101+ while ( x < N && y < M && eq ( seq1 [ x ] , seq2 [ y ] ) ) {
63102 x ++ ;
64103 y ++ ;
65- steps [ x ] [ y ] = STEP_NOCHANGE ;
104+ stepMap [ x ] [ y ] = STEP_NOCHANGE ;
66105 }
67106
68107 furthestReaching [ k + MAX ] = x ;
@@ -73,47 +112,75 @@ var compare = function(cnt1, cnt2, splitter){
73112 }
74113 }
75114
115+ return stepMap ;
116+ } ;
117+
118+ // use myers as default
119+ var coreCompare = myersCompare ;
120+
121+ // stepMap to contrast array
122+ var transformStepMap = function ( seq1 , seq2 , stepMap ) {
76123 // get contrast arrays (src & target) by analyze step by step
77- var src = [ ] , target = [ ] ;
124+ var l1 = seq1 . length ,
125+ l2 = seq2 . length ,
126+ src = [ ] , target = [ ] ;
78127
79- for ( var i = N , j = M ; i > 0 || j > 0 ; ) {
80- switch ( steps [ i ] [ j ] ) {
128+ for ( var i = l1 , j = l2 ; i > 0 || j > 0 ; ) {
129+ switch ( stepMap [ i ] [ j ] ) {
81130
82131 case STEP_NOCHANGE :
83- src . unshift ( arr1 [ i - 1 ] ) ;
132+ src . unshift ( seq1 [ i - 1 ] ) ;
84133 target . unshift ( MARK_SAME ) ;
85134 i -= 1 ;
86135 j -= 1 ;
87136 break ;
88137
89138 case STEP_REPLACE :
90- src . unshift ( arr1 [ i - 1 ] ) ;
91- target . unshift ( arr2 [ j - 1 ] ) ;
139+ src . unshift ( seq1 [ i - 1 ] ) ;
140+ target . unshift ( seq2 [ j - 1 ] ) ;
92141 i -= 1 ;
93142 j -= 1 ;
94143 break ;
95144
96145 case STEP_REMOVE :
97- src . unshift ( arr1 [ i - 1 ] ) ;
146+ src . unshift ( seq1 [ i - 1 ] ) ;
98147 target . unshift ( MARK_EMPTY ) ;
99148 i -= 1 ;
100149 j -= 0 ;
101150 break ;
102151
103152 case STEP_INSERT :
104153 src . unshift ( MARK_EMPTY ) ;
105- target . unshift ( arr2 [ j - 1 ] ) ;
154+ target . unshift ( seq2 [ j - 1 ] ) ;
106155 i -= 0 ;
107156 j -= 1 ;
108157 break ;
109158
110159 }
111160 }
112161
113- // convert contrast arrays to diff array
162+ return {
163+ src : src ,
164+ target : target
165+ } ;
166+ } ;
167+
168+ // get edit script
169+ var compare = function ( seq1 , seq2 , eq ) {
170+
171+ // do compare
172+ var stepMap = coreCompare ( seq1 , seq2 , eq ) ;
173+
174+ // transform stepMap
175+ var contrast = transformStepMap ( seq1 , seq2 , stepMap ) ,
176+ src = contrast . src ,
177+ target = contrast . target ;
178+
179+ // convert contrast arrays to edit script
114180 var l = target . length ,
115181 start , len , to ,
116- notEmpty = function ( s ) { return s !== MARK_EMPTY ; } ;
182+ notEmpty = function ( s ) { return s !== MARK_EMPTY ; } ,
183+ script = [ ] ;
117184
118185 for ( i = l - 1 ; i >= 0 ; ) {
119186 // join continuous diffs
@@ -124,24 +191,57 @@ var compare = function(cnt1, cnt2, splitter){
124191 len = src . slice ( j + 1 , i + 1 ) . filter ( notEmpty ) . length ; // length should be replaced (on src)
125192 to = target . slice ( j + 1 , i + 1 ) . filter ( notEmpty ) ; // new content
126193
127- diff . unshift (
194+ script . unshift (
128195 to . length ?
129- [ start , len , to . join ( SPLITTER ) ] : // replace
196+ [ start , len , to ] : // replace
130197 [ start , len ] // remove
131198 ) ;
132199 }
133200
134201 i = j - 1 ;
135202 }
136203
204+ return script ;
205+ } ;
206+
207+ // merge
208+ var merge = function ( seq , script ) {
209+ var result = seq . slice ( ) ;
210+
211+ for ( var i = script . length - 1 , modify ; i >= 0 ; i -- ) {
212+ modify = script [ i ] ;
213+ var to = modify [ 2 ] ;
214+ if ( to ) {
215+ modify = modify . slice ( 0 , 2 ) . concat ( to ) ;
216+ }
217+ result . splice . apply ( result , modify ) ;
218+ }
219+
137220 return result ;
138221} ;
139222
140- if ( typeof module === "object" && typeof module . exports === "object" ) {
141- module . exports = compare ;
142- }
223+ // compare string (use splitter)
224+ var compareStr = function ( str1 , str2 , splitter ) {
225+ splitter = typeof splitter === 'string' ? splitter : '' ;
226+
227+ var seq1 = str1 . split ( splitter ) ,
228+ seq2 = str2 . split ( splitter ) ,
229+ script = compare ( seq1 , seq2 ) ;
230+
231+ script . forEach ( function ( change ) {
232+ if ( change [ 2 ] ) {
233+ change [ 2 ] = change [ 2 ] . join ( splitter ) ;
234+ }
235+ } ) ;
236+
237+ return {
238+ splitter : splitter ,
239+ diff : script
240+ } ;
241+ } ;
143242
144- var merge = function ( cnt , compareResult ) {
243+ // merge string (add spliter back)
244+ var mergeStr = function ( cnt , compareResult ) {
145245 var splitter = compareResult . splitter ,
146246 diff = compareResult . diff ,
147247 result = cnt . split ( splitter ) ;
@@ -154,13 +254,25 @@ var merge = function(cnt, compareResult){
154254 return result . join ( splitter ) ;
155255} ;
156256
157- if ( typeof module === "object" && typeof module . exports === "object" ) {
158- module . exports = merge ;
159- }
160-
161- global . diff = {
257+ var diff = {
258+ coreCompare : coreCompare ,
162259 compare : compare ,
163- merge : merge
260+ merge : merge ,
261+ compareStr : compareStr ,
262+ mergeStr : mergeStr
164263} ;
165264
265+ // RequireJS && SeaJS
266+ if ( typeof define === 'function' ) {
267+ define ( function ( ) {
268+ return diff ;
269+ } ) ;
270+
271+ // NodeJS
272+ } else if ( typeof exports !== 'undefined' ) {
273+ module . exports = diff ;
274+ } else {
275+ global . diff = diff ;
276+ }
277+
166278} ) ( this ) ;
0 commit comments