3
3
( function ( global , undefined ) {
4
4
5
5
6
- var compare = function ( cnt1 , cnt2 , splitter ) {
7
- var SPLITTER = typeof splitter === 'string' ? splitter : '' ,
8
-
9
- MARK_EMPTY = - 1 ,
10
- MARK_SAME = 0 ,
11
-
12
- STEP_NOCHANGE = 0 ,
13
- STEP_REPLACE = 1 ,
14
- STEP_REMOVE = 2 ,
15
- STEP_INSERT = 3 ;
16
-
17
- // result object
18
- var diff = [ ] ,
19
- result = {
20
- splitter : SPLITTER ,
21
- diff : diff
22
- } ;
23
-
24
- // if string equal
25
- if ( cnt1 === cnt2 ) {
26
- return result ;
27
- }
6
+ // steps
7
+
8
+ var STEP_NOCHANGE = 0 ,
9
+ STEP_REPLACE = 1 ,
10
+ STEP_REMOVE = 2 ,
11
+ STEP_INSERT = 3 ;
12
+
13
+
14
+ // script marks
15
+
16
+ var MARK_EMPTY = - 1 ,
17
+ MARK_SAME = 0 ;
18
+
19
+
20
+ // caculate min-edit-script (naive)
21
+ var naiveCompare = function ( seq1 , seq2 ) {
22
+
23
+ var l1 = seq1 . length ,
24
+ l2 = seq2 . length ,
25
+ distMap = Array . apply ( null , { length : l1 + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
26
+ stepMap = Array . apply ( null , { length : l1 + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
27
+ i , j ;
28
28
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 ) ;
29
+ for ( i = 0 ; i <= l1 ; i ++ ) {
30
+ for ( j = 0 ; j <= l2 ; j ++ ) {
31
+
32
+ if ( i === 0 || j === 0 ) {
33
+ distMap [ i ] [ j ] = i || j ;
34
+ stepMap [ i ] [ j ] = i > 0 ? STEP_REMOVE : STEP_INSERT ;
35
+
36
+ } else {
37
+ var equal = seq1 [ i - 1 ] === seq2 [ j - 1 ] ,
38
+
39
+ removeDist = distMap [ i - 1 ] [ j ] + 1 ,
40
+ insertDist = distMap [ i ] [ j - 1 ] + 1 ,
41
+ replaceDist = distMap [ i - 1 ] [ j - 1 ] + ( equal ? 0 : 2 ) ,
42
+ dist = Math . min ( replaceDist , removeDist , insertDist ) ;
43
+
44
+ distMap [ i ] [ j ] = dist ;
45
+
46
+ switch ( dist ) {
47
+
48
+ case replaceDist :
49
+ stepMap [ i ] [ j ] = equal ? STEP_NOCHANGE : STEP_REPLACE ;
50
+ break ;
51
+
52
+ case removeDist :
53
+ stepMap [ i ] [ j ] = STEP_REMOVE ;
54
+ break ;
55
+
56
+ case insertDist :
57
+ stepMap [ i ] [ j ] = STEP_INSERT ;
58
+
59
+ }
60
+ }
61
+ }
37
62
}
38
63
39
- var N = arr1 . length ,
40
- M = arr2 . length ,
64
+ return stepMap ;
65
+ } ;
66
+
67
+ // caculate min-edit-script (myers)
68
+ var myersCompare = function ( seq1 , seq2 ) {
69
+
70
+ var N = seq1 . length ,
71
+ M = seq2 . length ,
41
72
MAX = N + M ,
42
- steps = Array . apply ( null , { length : M + N + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
73
+ stepMap = Array . apply ( null , { length : M + N + 1 } ) . map ( function ( ) { return [ ] ; } ) ,
43
74
furthestReaching = [ ] ,
44
75
dist = - 1 ;
45
76
@@ -57,12 +88,12 @@ var compare = function(cnt1, cnt2, splitter){
57
88
}
58
89
59
90
y = x - k ;
60
- steps [ x ] [ y ] = step ;
91
+ stepMap [ x ] [ y ] = step ;
61
92
62
- while ( x < N && y < M && arr1 [ x ] === arr2 [ y ] ) {
93
+ while ( x < N && y < M && seq1 [ x ] === seq2 [ y ] ) {
63
94
x ++ ;
64
95
y ++ ;
65
- steps [ x ] [ y ] = STEP_NOCHANGE ;
96
+ stepMap [ x ] [ y ] = STEP_NOCHANGE ;
66
97
}
67
98
68
99
furthestReaching [ k + MAX ] = x ;
@@ -73,47 +104,75 @@ var compare = function(cnt1, cnt2, splitter){
73
104
}
74
105
}
75
106
107
+ return stepMap ;
108
+ } ;
109
+
110
+ // use myers as default
111
+ var coreCompare = myersCompare ;
112
+
113
+ // stepMap to contrast array
114
+ var transformStepMap = function ( seq1 , seq2 , stepMap ) {
76
115
// get contrast arrays (src & target) by analyze step by step
77
- var src = [ ] , target = [ ] ;
116
+ var l1 = seq1 . length ,
117
+ l2 = seq2 . length ,
118
+ src = [ ] , target = [ ] ;
78
119
79
- for ( var i = N , j = M ; i > 0 || j > 0 ; ) {
80
- switch ( steps [ i ] [ j ] ) {
120
+ for ( var i = l1 , j = l2 ; i > 0 || j > 0 ; ) {
121
+ switch ( stepMap [ i ] [ j ] ) {
81
122
82
123
case STEP_NOCHANGE :
83
- src . unshift ( arr1 [ i - 1 ] ) ;
124
+ src . unshift ( seq1 [ i - 1 ] ) ;
84
125
target . unshift ( MARK_SAME ) ;
85
126
i -= 1 ;
86
127
j -= 1 ;
87
128
break ;
88
129
89
130
case STEP_REPLACE :
90
- src . unshift ( arr1 [ i - 1 ] ) ;
91
- target . unshift ( arr2 [ j - 1 ] ) ;
131
+ src . unshift ( seq1 [ i - 1 ] ) ;
132
+ target . unshift ( seq2 [ j - 1 ] ) ;
92
133
i -= 1 ;
93
134
j -= 1 ;
94
135
break ;
95
136
96
137
case STEP_REMOVE :
97
- src . unshift ( arr1 [ i - 1 ] ) ;
138
+ src . unshift ( seq1 [ i - 1 ] ) ;
98
139
target . unshift ( MARK_EMPTY ) ;
99
140
i -= 1 ;
100
141
j -= 0 ;
101
142
break ;
102
143
103
144
case STEP_INSERT :
104
145
src . unshift ( MARK_EMPTY ) ;
105
- target . unshift ( arr2 [ j - 1 ] ) ;
146
+ target . unshift ( seq2 [ j - 1 ] ) ;
106
147
i -= 0 ;
107
148
j -= 1 ;
108
149
break ;
109
150
110
151
}
111
152
}
112
153
113
- // convert contrast arrays to diff array
154
+ return {
155
+ src : src ,
156
+ target : target
157
+ } ;
158
+ } ;
159
+
160
+ // get edit script
161
+ var compare = function ( seq1 , seq2 ) {
162
+
163
+ // do compare
164
+ var stepMap = coreCompare ( seq1 , seq2 ) ;
165
+
166
+ // transform stepMap
167
+ var contrast = transformStepMap ( seq1 , seq2 , stepMap ) ,
168
+ src = contrast . src ,
169
+ target = contrast . target ;
170
+
171
+ // convert contrast arrays to edit script
114
172
var l = target . length ,
115
173
start , len , to ,
116
- notEmpty = function ( s ) { return s !== MARK_EMPTY ; } ;
174
+ notEmpty = function ( s ) { return s !== MARK_EMPTY ; } ,
175
+ script = [ ] ;
117
176
118
177
for ( i = l - 1 ; i >= 0 ; ) {
119
178
// join continuous diffs
@@ -124,24 +183,41 @@ var compare = function(cnt1, cnt2, splitter){
124
183
len = src . slice ( j + 1 , i + 1 ) . filter ( notEmpty ) . length ; // length should be replaced (on src)
125
184
to = target . slice ( j + 1 , i + 1 ) . filter ( notEmpty ) ; // new content
126
185
127
- diff . unshift (
186
+ script . unshift (
128
187
to . length ?
129
- [ start , len , to . join ( SPLITTER ) ] : // replace
188
+ [ start , len , to ] : // replace
130
189
[ start , len ] // remove
131
190
) ;
132
191
}
133
192
134
193
i = j - 1 ;
135
194
}
136
195
137
- return result ;
196
+ return script ;
138
197
} ;
139
198
140
- if ( typeof module === "object" && typeof module . exports === "object" ) {
141
- module . exports = compare ;
142
- }
199
+ // compare string (use splitter)
200
+ var compareStr = function ( str1 , str2 , splitter ) {
201
+ splitter = typeof splitter === 'string' ? splitter : '' ;
202
+
203
+ var seq1 = str1 . split ( splitter ) ,
204
+ seq2 = str2 . split ( splitter ) ,
205
+ script = compare ( seq1 , seq2 ) ;
206
+
207
+ script . forEach ( function ( change ) {
208
+ if ( change [ 2 ] ) {
209
+ change [ 2 ] = change [ 2 ] . join ( splitter ) ;
210
+ }
211
+ } ) ;
143
212
144
- var merge = function ( cnt , compareResult ) {
213
+ return {
214
+ splitter : splitter ,
215
+ diff : script
216
+ } ;
217
+ } ;
218
+
219
+ // merge string (add spliter bacj=k)
220
+ var mergeStr = function ( cnt , compareResult ) {
145
221
var splitter = compareResult . splitter ,
146
222
diff = compareResult . diff ,
147
223
result = cnt . split ( splitter ) ;
@@ -154,13 +230,11 @@ var merge = function(cnt, compareResult){
154
230
return result . join ( splitter ) ;
155
231
} ;
156
232
157
- if ( typeof module === "object" && typeof module . exports === "object" ) {
158
- module . exports = merge ;
159
- }
160
-
161
233
global . diff = {
234
+ coreCompare : coreCompare ,
162
235
compare : compare ,
163
- merge : merge
236
+ compareStr : compareStr ,
237
+ mergeStr : mergeStr
164
238
} ;
165
239
166
240
} ) ( this ) ;
0 commit comments