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) */
2
2
3
3
( function ( global , undefined ) {
4
4
5
5
6
- var compare = function ( cnt1 , cnt2 , splitter ) {
7
- var SPLITTER = typeof splitter === 'string' ? splitter : '' ,
6
+ // steps
8
7
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 ;
11
12
12
- STEP_NOCHANGE = 0 ,
13
- STEP_REPLACE = 1 ,
14
- STEP_REMOVE = 2 ,
15
- STEP_INSERT = 3 ;
16
13
17
- // result object
18
- var diff = [ ] ,
19
- result = {
20
- splitter : SPLITTER ,
21
- diff : diff
22
- } ;
14
+ // script marks
23
15
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 ) {
28
26
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
+ }
37
68
}
38
69
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 ,
41
78
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 [ ] ; } ) ,
43
80
furthestReaching = [ ] ,
44
81
dist = - 1 ;
45
82
83
+ eq = eq || defaultEqual ;
84
+
46
85
furthestReaching [ MAX + 1 ] = 0 ;
47
86
48
87
// caculate min distance & log each step
@@ -57,12 +96,12 @@ var compare = function(cnt1, cnt2, splitter){
57
96
}
58
97
59
98
y = x - k ;
60
- steps [ x ] [ y ] = step ;
99
+ stepMap [ x ] [ y ] = step ;
61
100
62
- while ( x < N && y < M && arr1 [ x ] === arr2 [ y ] ) {
101
+ while ( x < N && y < M && eq ( seq1 [ x ] , seq2 [ y ] ) ) {
63
102
x ++ ;
64
103
y ++ ;
65
- steps [ x ] [ y ] = STEP_NOCHANGE ;
104
+ stepMap [ x ] [ y ] = STEP_NOCHANGE ;
66
105
}
67
106
68
107
furthestReaching [ k + MAX ] = x ;
@@ -73,47 +112,75 @@ var compare = function(cnt1, cnt2, splitter){
73
112
}
74
113
}
75
114
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 ) {
76
123
// 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 = [ ] ;
78
127
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 ] ) {
81
130
82
131
case STEP_NOCHANGE :
83
- src . unshift ( arr1 [ i - 1 ] ) ;
132
+ src . unshift ( seq1 [ i - 1 ] ) ;
84
133
target . unshift ( MARK_SAME ) ;
85
134
i -= 1 ;
86
135
j -= 1 ;
87
136
break ;
88
137
89
138
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 ] ) ;
92
141
i -= 1 ;
93
142
j -= 1 ;
94
143
break ;
95
144
96
145
case STEP_REMOVE :
97
- src . unshift ( arr1 [ i - 1 ] ) ;
146
+ src . unshift ( seq1 [ i - 1 ] ) ;
98
147
target . unshift ( MARK_EMPTY ) ;
99
148
i -= 1 ;
100
149
j -= 0 ;
101
150
break ;
102
151
103
152
case STEP_INSERT :
104
153
src . unshift ( MARK_EMPTY ) ;
105
- target . unshift ( arr2 [ j - 1 ] ) ;
154
+ target . unshift ( seq2 [ j - 1 ] ) ;
106
155
i -= 0 ;
107
156
j -= 1 ;
108
157
break ;
109
158
110
159
}
111
160
}
112
161
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
114
180
var l = target . length ,
115
181
start , len , to ,
116
- notEmpty = function ( s ) { return s !== MARK_EMPTY ; } ;
182
+ notEmpty = function ( s ) { return s !== MARK_EMPTY ; } ,
183
+ script = [ ] ;
117
184
118
185
for ( i = l - 1 ; i >= 0 ; ) {
119
186
// join continuous diffs
@@ -124,24 +191,57 @@ var compare = function(cnt1, cnt2, splitter){
124
191
len = src . slice ( j + 1 , i + 1 ) . filter ( notEmpty ) . length ; // length should be replaced (on src)
125
192
to = target . slice ( j + 1 , i + 1 ) . filter ( notEmpty ) ; // new content
126
193
127
- diff . unshift (
194
+ script . unshift (
128
195
to . length ?
129
- [ start , len , to . join ( SPLITTER ) ] : // replace
196
+ [ start , len , to ] : // replace
130
197
[ start , len ] // remove
131
198
) ;
132
199
}
133
200
134
201
i = j - 1 ;
135
202
}
136
203
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
+
137
220
return result ;
138
221
} ;
139
222
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
+ } ;
143
242
144
- var merge = function ( cnt , compareResult ) {
243
+ // merge string (add spliter back)
244
+ var mergeStr = function ( cnt , compareResult ) {
145
245
var splitter = compareResult . splitter ,
146
246
diff = compareResult . diff ,
147
247
result = cnt . split ( splitter ) ;
@@ -154,13 +254,25 @@ var merge = function(cnt, compareResult){
154
254
return result . join ( splitter ) ;
155
255
} ;
156
256
157
- if ( typeof module === "object" && typeof module . exports === "object" ) {
158
- module . exports = merge ;
159
- }
160
-
161
- global . diff = {
257
+ var diff = {
258
+ coreCompare : coreCompare ,
162
259
compare : compare ,
163
- merge : merge
260
+ merge : merge ,
261
+ compareStr : compareStr ,
262
+ mergeStr : mergeStr
164
263
} ;
165
264
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
+
166
278
} ) ( this ) ;
0 commit comments