58 * @since 2.6.0 |
58 * @since 2.6.0 |
59 */ |
59 */ |
60 var $inline_diff_renderer = 'WP_Text_Diff_Renderer_inline'; |
60 var $inline_diff_renderer = 'WP_Text_Diff_Renderer_inline'; |
61 |
61 |
62 /** |
62 /** |
63 * PHP4 Constructor - Call parent constructor with params array. |
63 * Constructor - Call parent constructor with params array. |
64 * |
64 * |
65 * This will set class properties based on the key value pairs in the array. |
65 * This will set class properties based on the key value pairs in the array. |
66 * |
66 * |
67 * @since unknown |
67 * @since 2.6.0 |
68 * |
68 * |
69 * @param array $params |
69 * @param array $params |
70 */ |
70 */ |
71 function Text_Diff_Renderer_Table( $params = array() ) { |
71 function __construct( $params = array() ) { |
72 $parent = get_parent_class($this); |
72 parent::__construct( $params ); |
73 $this->$parent( $params ); |
|
74 } |
73 } |
75 |
74 |
76 /** |
75 /** |
77 * @ignore |
76 * @ignore |
78 * |
77 * |
201 */ |
200 */ |
202 function _changed( $orig, $final ) { |
201 function _changed( $orig, $final ) { |
203 $r = ''; |
202 $r = ''; |
204 |
203 |
205 // Does the aforementioned additional processing |
204 // Does the aforementioned additional processing |
206 // *_matches tell what rows are "the same" in orig and final. Those pairs will be diffed to get word changes |
205 // *_matches tell what rows are "the same" in orig and final. Those pairs will be diffed to get word changes |
207 // match is numeric: an index in other column |
206 // match is numeric: an index in other column |
208 // match is 'X': no match. It is a new row |
207 // match is 'X': no match. It is a new row |
209 // *_rows are column vectors for the orig column and the final column. |
208 // *_rows are column vectors for the orig column and the final column. |
210 // row >= 0: an indix of the $orig or $final array |
209 // row >= 0: an indix of the $orig or $final array |
211 // row < 0: a blank row for that column |
210 // row < 0: a blank row for that column |
212 list($orig_matches, $final_matches, $orig_rows, $final_rows) = $this->interleave_changed_lines( $orig, $final ); |
211 list($orig_matches, $final_matches, $orig_rows, $final_rows) = $this->interleave_changed_lines( $orig, $final ); |
213 |
|
214 |
212 |
215 // These will hold the word changes as determined by an inline diff |
213 // These will hold the word changes as determined by an inline diff |
216 $orig_diffs = array(); |
214 $orig_diffs = array(); |
217 $final_diffs = array(); |
215 $final_diffs = array(); |
218 |
216 |
230 // since we count lengith of text between <ins> or <del> (instead of picking just one), |
228 // since we count lengith of text between <ins> or <del> (instead of picking just one), |
231 // we double the length of chars not in those tags. |
229 // we double the length of chars not in those tags. |
232 $stripped_diff = strlen(strip_tags( $diff )) * 2 - $stripped_matches; |
230 $stripped_diff = strlen(strip_tags( $diff )) * 2 - $stripped_matches; |
233 $diff_ratio = $stripped_matches / $stripped_diff; |
231 $diff_ratio = $stripped_matches / $stripped_diff; |
234 if ( $diff_ratio > $this->_diff_threshold ) |
232 if ( $diff_ratio > $this->_diff_threshold ) |
235 continue; // Too different. Don't save diffs. |
233 continue; // Too different. Don't save diffs. |
236 } |
234 } |
237 |
235 |
238 // Un-inline the diffs by removing del or ins |
236 // Un-inline the diffs by removing del or ins |
239 $orig_diffs[$o] = preg_replace( '|<ins>.*?</ins>|', '', $diff ); |
237 $orig_diffs[$o] = preg_replace( '|<ins>.*?</ins>|', '', $diff ); |
240 $final_diffs[$f] = preg_replace( '|<del>.*?</del>|', '', $diff ); |
238 $final_diffs[$f] = preg_replace( '|<del>.*?</del>|', '', $diff ); |
241 } |
239 } |
242 } |
240 } |
243 |
241 |
244 foreach ( array_keys($orig_rows) as $row ) { |
242 foreach ( array_keys($orig_rows) as $row ) { |
245 // Both columns have blanks. Ignore them. |
243 // Both columns have blanks. Ignore them. |
246 if ( $orig_rows[$row] < 0 && $final_rows[$row] < 0 ) |
244 if ( $orig_rows[$row] < 0 && $final_rows[$row] < 0 ) |
247 continue; |
245 continue; |
248 |
246 |
249 // If we have a word based diff, use it. Otherwise, use the normal line. |
247 // If we have a word based diff, use it. Otherwise, use the normal line. |
250 $orig_line = isset($orig_diffs[$orig_rows[$row]]) |
248 if ( isset( $orig_diffs[$orig_rows[$row]] ) ) |
251 ? $orig_diffs[$orig_rows[$row]] |
249 $orig_line = $orig_diffs[$orig_rows[$row]]; |
252 : htmlspecialchars($orig[$orig_rows[$row]]); |
250 elseif ( isset( $orig[$orig_rows[$row]] ) ) |
253 $final_line = isset($final_diffs[$final_rows[$row]]) |
251 $orig_line = htmlspecialchars($orig[$orig_rows[$row]]); |
254 ? $final_diffs[$final_rows[$row]] |
252 else |
255 : htmlspecialchars($final[$final_rows[$row]]); |
253 $orig_line = ''; |
256 |
254 |
257 if ( $orig_rows[$row] < 0 ) { // Orig is blank. This is really an added row. |
255 if ( isset( $final_diffs[$final_rows[$row]] ) ) |
|
256 $final_line = $final_diffs[$final_rows[$row]]; |
|
257 elseif ( isset( $final[$final_rows[$row]] ) ) |
|
258 $final_line = htmlspecialchars($final[$final_rows[$row]]); |
|
259 else |
|
260 $final_line = ''; |
|
261 |
|
262 if ( $orig_rows[$row] < 0 ) { // Orig is blank. This is really an added row. |
258 $r .= $this->_added( array($final_line), false ); |
263 $r .= $this->_added( array($final_line), false ); |
259 } elseif ( $final_rows[$row] < 0 ) { // Final is blank. This is really a deleted row. |
264 } elseif ( $final_rows[$row] < 0 ) { // Final is blank. This is really a deleted row. |
260 $r .= $this->_deleted( array($orig_line), false ); |
265 $r .= $this->_deleted( array($orig_line), false ); |
261 } else { // A true changed row. |
266 } else { // A true changed row. |
262 $r .= '<tr>' . $this->deletedLine( $orig_line ) . $this->addedLine( $final_line ) . "</tr>\n"; |
267 $r .= '<tr>' . $this->deletedLine( $orig_line ) . $this->addedLine( $final_line ) . "</tr>\n"; |
263 } |
268 } |
264 } |
269 } |
280 * @param unknown_type $final |
285 * @param unknown_type $final |
281 * @return unknown |
286 * @return unknown |
282 */ |
287 */ |
283 function interleave_changed_lines( $orig, $final ) { |
288 function interleave_changed_lines( $orig, $final ) { |
284 |
289 |
285 // Contains all pairwise string comparisons. Keys are such that this need only be a one dimensional array. |
290 // Contains all pairwise string comparisons. Keys are such that this need only be a one dimensional array. |
286 $matches = array(); |
291 $matches = array(); |
287 foreach ( array_keys($orig) as $o ) { |
292 foreach ( array_keys($orig) as $o ) { |
288 foreach ( array_keys($final) as $f ) { |
293 foreach ( array_keys($final) as $f ) { |
289 $matches["$o,$f"] = $this->compute_string_distance( $orig[$o], $final[$f] ); |
294 $matches["$o,$f"] = $this->compute_string_distance( $orig[$o], $final[$f] ); |
290 } |
295 } |
321 |
326 |
322 // We read the text in this order |
327 // We read the text in this order |
323 ksort($orig_matches); |
328 ksort($orig_matches); |
324 ksort($final_matches); |
329 ksort($final_matches); |
325 |
330 |
326 |
|
327 // Stores rows and blanks for each column. |
331 // Stores rows and blanks for each column. |
328 $orig_rows = $orig_rows_copy = array_keys($orig_matches); |
332 $orig_rows = $orig_rows_copy = array_keys($orig_matches); |
329 $final_rows = array_keys($final_matches); |
333 $final_rows = array_keys($final_matches); |
330 |
334 |
331 // Interleaves rows with blanks to keep matches aligned. |
335 // Interleaves rows with blanks to keep matches aligned. |
334 $final_pos = array_search($orig_matches[$orig_row], $final_rows, true); |
338 $final_pos = array_search($orig_matches[$orig_row], $final_rows, true); |
335 $orig_pos = (int) array_search($orig_row, $orig_rows, true); |
339 $orig_pos = (int) array_search($orig_row, $orig_rows, true); |
336 |
340 |
337 if ( false === $final_pos ) { // This orig is paired with a blank final. |
341 if ( false === $final_pos ) { // This orig is paired with a blank final. |
338 array_splice( $final_rows, $orig_pos, 0, -1 ); |
342 array_splice( $final_rows, $orig_pos, 0, -1 ); |
339 } elseif ( $final_pos < $orig_pos ) { // This orig's match is up a ways. Pad final with blank rows. |
343 } elseif ( $final_pos < $orig_pos ) { // This orig's match is up a ways. Pad final with blank rows. |
340 $diff_pos = $final_pos - $orig_pos; |
344 $diff_pos = $final_pos - $orig_pos; |
341 while ( $diff_pos < 0 ) |
345 while ( $diff_pos < 0 ) |
342 array_splice( $final_rows, $orig_pos, 0, $diff_pos++ ); |
346 array_splice( $final_rows, $orig_pos, 0, $diff_pos++ ); |
343 } elseif ( $final_pos > $orig_pos ) { // This orig's match is down a ways. Pad orig with blank rows. |
347 } elseif ( $final_pos > $orig_pos ) { // This orig's match is down a ways. Pad orig with blank rows. |
344 $diff_pos = $orig_pos - $final_pos; |
348 $diff_pos = $orig_pos - $final_pos; |
345 while ( $diff_pos < 0 ) |
349 while ( $diff_pos < 0 ) |
346 array_splice( $orig_rows, $orig_pos, 0, $diff_pos++ ); |
350 array_splice( $orig_rows, $orig_pos, 0, $diff_pos++ ); |
347 } |
351 } |
348 } |
352 } |
349 |
|
350 |
353 |
351 // Pad the ends with blank rows if the columns aren't the same length |
354 // Pad the ends with blank rows if the columns aren't the same length |
352 $diff_count = count($orig_rows) - count($final_rows); |
355 $diff_count = count($orig_rows) - count($final_rows); |
353 if ( $diff_count < 0 ) { |
356 if ( $diff_count < 0 ) { |
354 while ( $diff_count < 0 ) |
357 while ( $diff_count < 0 ) |
419 $chars2 = count_chars($string2); |
422 $chars2 = count_chars($string2); |
420 |
423 |
421 // L1-norm of difference vector. |
424 // L1-norm of difference vector. |
422 $difference = array_sum( array_map( array(&$this, 'difference'), $chars1, $chars2 ) ); |
425 $difference = array_sum( array_map( array(&$this, 'difference'), $chars1, $chars2 ) ); |
423 |
426 |
424 // $string1 has zero length? Odd. Give huge penalty by not dividing. |
427 // $string1 has zero length? Odd. Give huge penalty by not dividing. |
425 if ( !$string1 ) |
428 if ( !$string1 ) |
426 return $difference; |
429 return $difference; |
427 |
430 |
428 // Return distance per charcter (of string1) |
431 // Return distance per charcter (of string1) |
429 return $difference / strlen($string1); |
432 return $difference / strlen($string1); |