Skip to content

Commit 606ad6a

Browse files
committed
Updated ListDiff to handle lists within lists.
1 parent d63c736 commit 606ad6a

File tree

2 files changed

+138
-39
lines changed

2 files changed

+138
-39
lines changed

demo/index.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@
2121

2222
if ($input) {
2323
$data = json_decode($input, true);
24-
$diff = new HtmlDiff($_POST['oldText'], $_POST['newText'], 'UTF-8', array());
25-
try{
24+
$diff = new HtmlDiff($data['oldText'], $data['newText'], 'UTF-8', array());
25+
$diff->build();
26+
/*try{
2627
echo $diff->build();
2728
} catch (Exception $e) {
2829
echo $e->getMessage();
29-
}
30+
}*/
3031

31-
//header('Content-Type: application/json');
32-
//echo json_encode(array('diff' => $diff->getDifference()));
32+
header('Content-Type: application/json');
33+
echo json_encode(array('diff' => $diff->getDifference()));
3334
} else {
3435
header('Content-Type: text/html');
3536
echo file_get_contents('demo.html');

lib/Caxy/HtmlDiff/ListDiff.php

Lines changed: 132 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ protected function matchAndCompareLists()
9999
* Index the list, starting positions, so that we can refer back to it later.
100100
* This is used to see where one list node starts and another ends.
101101
*/
102-
//$this->indexLists();
102+
$this->indexLists();
103103

104104
/**
105105
* Compare the lists and build $textMatches array with the matches.
@@ -114,13 +114,13 @@ protected function compareChildLists()
114114
// Always compare the new against the old.
115115
// Compare each new string against each old string.
116116
$bestMatchPercentages = array();
117-
$this->dump($this->childLists['new'], '=========comparechildlists');
118-
$this->dump($this->childLists['old'], '=========comparechildlists');
117+
//$this->dump($this->childLists['new'], '=========NEW comparechildlists');
118+
//$this->dump($this->childLists['old'], '=========OLD comparechildlists');
119119
foreach ($this->childLists['new'] as $thisKey => $thisList) {
120120
$bestMatchPercentages[$thisKey] = array();
121121
foreach ($this->childLists['old'] as $thatKey => $thatList) {
122122
// Save the percent amount each new list content compares against the old list content.
123-
similar_text($thisList, $thatList, $percentage);
123+
similar_text($thisList['content'], $thatList['content'], $percentage);
124124
$bestMatchPercentages[$thisKey][] = $percentage;
125125
}
126126
}
@@ -203,8 +203,7 @@ function ($v) use ($percent) {
203203

204204
// Save the matches.
205205
$this->textMatches = $matches;
206-
$this->dump($this->textMatches);
207-
die;
206+
//$this->dump($this->textMatches);
208207
}
209208

210209
/**
@@ -214,7 +213,6 @@ protected function buildChildLists()
214213
{
215214
$this->childLists['old'] = $this->getListsContent($this->list['old']);
216215
$this->childLists['new'] = $this->getListsContent($this->list['new']);
217-
$this->dump($this->childLists);
218216
}
219217

220218
/**
@@ -225,29 +223,72 @@ protected function diff()
225223
{
226224
// Add the opening parent node from listType. So if ol, <ol>, etc.
227225
$this->content = $this->addListTypeWrapper();
228-
226+
//$this->dump($this->textMatches, "============ THE TEXT MATCHES");
227+
//$this->dump($this->childLists, " XXX CHILD LISTS XXX");
229228
foreach ($this->textMatches as $key => $matches) {
229+
//$this->dump($matches, " ////////// matches prep");
230230
$oldText = $matches['old'] !== null ? $this->childLists['old'][$matches['old']] : '';
231231
$newText = $matches['new'] !== null ? $this->childLists['new'][$matches['new']] : '';
232+
//$this->dump($oldText, " Old Text");
233+
//$this->dump($newText, " New Text");
234+
//$this->dump($this->convertListContentArrayToString($oldText), " +++++++++++ CONTENT TO STRING OLD");
235+
//$this->dump($this->convertListContentArrayToString($newText), " +++++++++++ CONTENT TO STRING NEW");
236+
/*$this->dump($this->diffElements(
237+
$this->convertListContentArrayToString($oldText),
238+
$this->convertListContentArrayToString($newText)
239+
), " ====== DIFF ELEMENTS");*/
232240

233241
// Add the opened and closed the list
234242
$this->content .= "<li>";
235243
// Process any placeholders, if they exist.
236244
// Placeholders would be nested lists (a nested ol, ul, dl for example).
237-
$this->content .= $this->processPlaceholders($this->diffElements($oldText, $newText), $matches);
245+
$this->content .= $this->processPlaceholders(
246+
$this->diffElements(
247+
$this->convertListContentArrayToString($oldText),
248+
$this->convertListContentArrayToString($newText)
249+
),
250+
$matches
251+
);
238252
$this->content .= "</li>";
239253
}
240254

241255
// Add the closing parent node from listType. So if ol, </ol>, etc.
242256
$this->content .= $this->addListTypeWrapper(false);
243257
}
244258

259+
protected function convertListContentArrayToString($listContentArray)
260+
{
261+
if (!is_array($listContentArray)) {
262+
return $listContentArray;
263+
}
264+
265+
$content = array();
266+
$listString = '[[REPLACE_LIST_ITEM]]';
267+
////$this->dump($listContentArray, "=================++++++WHAT IS IT");
268+
$words = explode(" ", $listContentArray['content']);
269+
$nestedListCount = 0;
270+
foreach ($words as $word) {
271+
$match = $word == $listString;
272+
273+
$content[] = $match
274+
? "<li>" . $this->convertListContentArrayToString($listContentArray['kids'][$nestedListCount]) . "</li>"
275+
: $word;
276+
277+
if ($match) {
278+
$nestedListCount++;
279+
}
280+
}
281+
282+
return implode(" ", $content);
283+
}
284+
245285
/**
246286
* Return the contents of each list node.
247287
* Process any placeholders for nested lists.
248288
*/
249289
protected function processPlaceholders($text, array $matches)
250290
{
291+
//$this->dump(func_get_args(), "================= processPlaceholders function");
251292
// Prepare return
252293
$returnText = array();
253294
// Save the contents of all list nodes, new and old.
@@ -256,8 +297,11 @@ protected function processPlaceholders($text, array $matches)
256297
'new' => $this->getListContent('new', $matches)
257298
);
258299

300+
//$this->dump($contentVault, "==============CONTENT VAULT"); //die;
301+
259302
$count = 0;
260303
// Loop through the text checking for placeholders. If a nested list is found, create a new ListDiff object for it.
304+
//$this->dump($text, " ----------- PRECONTENT PLACEHOLDER TEXT ");
261305
foreach (explode(' ', $text) as $word) {
262306
$preContent = $this->checkWordForDiffTag($this->stripNewLine($word));
263307

@@ -266,6 +310,8 @@ protected function processPlaceholders($text, array $matches)
266310
$this->isolatedDiffTags
267311
)
268312
) {
313+
//$this->dump($word, " ----------- WORD ");
314+
//$this->dump($preContent, " ----------- PRECONTENT PLACEHOLDER ");
269315
$oldText = implode('', $contentVault['old'][$count]);
270316
$newText = implode('', $contentVault['new'][$count]);
271317
$content = $this->diffList($oldText, $newText);
@@ -312,17 +358,59 @@ protected function stripNewLine($text)
312358
*/
313359
protected function getListContent($indexKey = 'new', array $matches)
314360
{
361+
//$this->dump(func_get_args(), " ------------ GET LIST CONTENT FUNCTION -------------- ");
362+
/*if ($matches == array('new' => 3, 'old' => 3)) {
363+
//$this->dump($this->listsIndex, '---------- FULL LIST INDEX --------');
364+
//$this->dump($this->list, "---------------------- FULL LIST");
365+
//$this->dump($this->listsIndex[$indexKey][$matches[$indexKey]], '---------- START --------');
366+
//$this->dump(array_key_exists(($matches[$indexKey] + 1), $this->listsIndex[$indexKey])
367+
? $this->listsIndex[$indexKey][$matches[$indexKey] + 1]
368+
: $this->findEndForIndex($this->list[$indexKey], $this->listsIndex[$indexKey][$matches[$indexKey]], true), '---------- STOP --------');
369+
//$this->dump($this->isolatedDiffTags, "==== ISOLATED DIFF TAGS ==== ");
370+
}*/
315371
$bucket = array();
316372
$start = $this->listsIndex[$indexKey][$matches[$indexKey]];
317-
$stop = $this->listsIndex[$indexKey][$matches[$indexKey] + 1];
373+
$stop = array_key_exists(($matches[$indexKey] + 1), $this->listsIndex[$indexKey])
374+
? $this->listsIndex[$indexKey][$matches[$indexKey] + 1]
375+
: $this->findEndForIndex($this->list[$indexKey], $start);
376+
377+
//$this->dump(array('type' => $indexKey, 'start' => $start, 'stop' => $stop), " AHHHHHHHHHHHHHHHHHHHHHHHHH");
378+
//$this->dump($this->list[$indexKey]);
318379
for ($x = $start; $x < $stop; $x++) {
319380
if (in_array($this->list[$indexKey][$x], $this->isolatedDiffTags)) {
320381
$bucket[] = $this->listIsolatedDiffTags[$indexKey][$x];
321382
}
322383
}
384+
385+
//$this->dump($bucket, " ------------ GET LIST BUCKET -------------- ");
323386
return $bucket;
324387
}
325388

389+
protected function findEndForIndex($index, $start, $debug = false)
390+
{
391+
$array = array_splice($index, $start);
392+
//if ($debug) {//$this->dump($array, "SPLICE ------------");}
393+
$count = 0;
394+
foreach ($array as $key => $item) {
395+
if ($this->isOpeningListTag($item)) {
396+
$count++;
397+
}
398+
399+
if ($this->isClosingListTag($item)) {
400+
$count--;
401+
if ($debug) {
402+
//$this->dump($start ." + ". $key, "FOUND FOR END OF INDEX");
403+
//$this->dump($item, "ITEM");
404+
}
405+
if ($count === 0) {
406+
return $start + $key;
407+
}
408+
}
409+
}
410+
411+
return $start + count($array);
412+
}
413+
326414
/**
327415
* indexLists
328416
*
@@ -331,18 +419,29 @@ protected function getListContent($indexKey = 'new', array $matches)
331419
*/
332420
protected function indexLists()
333421
{
422+
//$this->dump($this->list, " INDEX LISTS ACTUAL LIST HELPER");
334423
$this->listsIndex = array();
335-
$lookingFor = "<li>";
336-
424+
$count = 0;
337425
foreach ($this->list as $type => $list) {
338426
$this->listsIndex[$type] = array();
339427

340428
foreach ($list as $key => $listItem) {
341-
if ($listItem == $lookingFor) {
342-
$this->listsIndex[$type][] = $key;
429+
if ($this->isOpeningListTag($listItem)) {
430+
$count++;
431+
if ($count === 1) {
432+
$this->listsIndex[$type][] = $key;
433+
}
343434
}
435+
436+
if ($this->isClosingListTag($listItem)) {
437+
$count--;
438+
}
439+
440+
////$this->dump(array('c' => $count, 'i' => $listItem), "RESULT");
344441
}
345442
}
443+
444+
////$this->dump($this->listsIndex, " = ================ END OF INDEX LISTS FUNCTION"); die;
346445
}
347446

348447
/**
@@ -358,7 +457,6 @@ protected function addListTypeWrapper($opening = true)
358457
*/
359458
public function replaceListIsolatedDiffTags()
360459
{
361-
//var_dump($this->list);die;
362460
$this->listIsolatedDiffTags['old'] = $this->createIsolatedDiffTagPlaceholders($this->list['old']);
363461
$this->listIsolatedDiffTags['new'] = $this->createIsolatedDiffTagPlaceholders($this->list['new']);
364462
}
@@ -381,36 +479,38 @@ protected function getListsContent(array $contentArray, $stripTags = true)
381479
} else {
382480
$nestedCount[$arrayDepth]++;
383481
}
384-
//$this->dump(array('arrayDepth' => $arrayDepth, 'prev' => $previousDepth, 'action' => '++', 'word' => $word, 'changed' => $changed), $status);
482+
////$this->dump(array('arrayDepth' => $arrayDepth, 'prev' => $previousDepth, 'action' => '++', 'word' => $word, 'changed' => $changed), $status);
385483
continue;
386484
}
387485

388486
if ($this->isClosingListTag($word)) {
389487
$arrayDepth--;
390-
//$this->dump(array('arrayDepth' => $arrayDepth, 'prev' => $previousDepth, 'action' => '--', 'word' => $word, 'changed' => $changed), $status);
488+
////$this->dump(array('arrayDepth' => $arrayDepth, 'prev' => $previousDepth, 'action' => '--', 'word' => $word, 'changed' => $changed), $status);
391489
continue;
392490
}
393491

394492
if ($arrayDepth > 0) {
395-
//$this->dump(array('arrayDepth' => $arrayDepth, 'prev' => $previousDepth, 'action' => '==', 'word' => $word, 'changed' => $changed), $status);
493+
////$this->dump(array('arrayDepth' => $arrayDepth, 'prev' => $previousDepth, 'action' => '==', 'word' => $word, 'changed' => $changed), $status);
396494
$this->addStringToArrayByDepth($word, $lematches, $arrayDepth, 1, $nestedCount);
397-
$this->dump($lematches, '---------- total array at end of this loop ---------');
495+
////$this->dump($lematches, '---------- total array at end of this loop ---------');
398496
}
399497
}
400498

401-
$this->dump($lematches);
402-
die;
499+
////$this->dump($lematches);
500+
//die;
403501
//var_dump($contentArray);
404502
//var_dump(implode('', $contentArray));
405503
preg_match_all('/<li>(.*?)<\/li>/s', implode('', $contentArray), $matches);
406-
$this->dump($matches[intval($stripTags)], 'XXXXXXXXXXXXXXXXx - matches dump');
504+
//$this->dump($matches, "================== OLD LIST CONTENT");
505+
//$this->dump($lematches, "================== NEW LIST CONTENT");
506+
////$this->dump($matches[intval($stripTags)], 'XXXXXXXXXXXXXXXXx - matches dump');
407507
return $lematches;
408508
}
409509

410510
protected function addStringToArrayByDepth($word, &$array, $targetDepth, $thisDepth, $nestedCount)
411511
{
412-
$this->dump(func_get_args(), '============ addstringfunction vars');
413-
//$this->dump($array);
512+
////$this->dump(func_get_args(), '============ addstringfunction vars');
513+
////$this->dump($array);
414514

415515
// determine what depth we're at
416516
if ($targetDepth == $thisDepth) {
@@ -427,15 +527,10 @@ protected function addStringToArrayByDepth($word, &$array, $targetDepth, $thisDe
427527
$array[count($array) - 1]['content'] .= $word;
428528
}
429529

430-
//$this->dump($array, '========= ADDED CONTENT TO THIS ARRAY ==========');
530+
////$this->dump($array, '========= ADDED CONTENT TO THIS ARRAY ==========');
431531
} else {
432532

433533
// create first kid if not exist
434-
/*if (count($array['kids']) < 1) {
435-
$this->dump('', "!!! count of kid less than one");
436-
$newArray = array('content' => '', 'kids' => array());
437-
$array['kids'][] = $newArray;
438-
}*/
439534
$newArray = array('content' => '', 'kids' => array());
440535

441536
if (array_key_exists('kids', $array)) {
@@ -458,12 +553,12 @@ protected function addStringToArrayByDepth($word, &$array, $targetDepth, $thisDe
458553
);
459554

460555
} else {
461-
$this->dump($array, "================PREP");
556+
////$this->dump($array, "================PREP");
462557
if ($nestedCount[$targetDepth] > count($array[count($array) - 1]['kids'])) {
463558
$array[count($array) - 1]['kids'][] = $newArray;
464559
$array[count($array) - 1]['content'] .= "[[REPLACE_LIST_ITEM]]";
465560
}
466-
$this->dump($array, "================POSTPREP");
561+
////$this->dump($array, "================POSTPREP");
467562
// continue to the next depth
468563
$thisDepth++;
469564

@@ -490,7 +585,7 @@ protected function addStringToArrayByDepth($word, &$array, $targetDepth, $thisDe
490585
* )
491586
*
492587
493-
//$this->dump(func_get_args(), "======func args========");
588+
////$this->dump(func_get_args(), "======func args========");
494589
495590
if ($depth === 1) {
496591
if ($changed && $previousDepth > $depth) {
@@ -502,14 +597,17 @@ protected function addStringToArrayByDepth($word, &$array, $targetDepth, $thisDe
502597
503598
} else {
504599
$depth--;
505-
$this->dump($array, "---------------DOWN");
600+
//$this->dump($array, "---------------DOWN");
506601
$this->addStringToArrayByDepth($word, $array, $depth, $changed, true);
507602
}*/
508603
}
509604

510605
protected function dump($content, $text = null)
511606
{
512607
ini_set('xdebug.var_display_max_depth', '10');
608+
ini_set('xdebug.var_display_max_data', '4096');
609+
ini_set('xdebug.max_nesting_level', '200');
610+
ini_set('xdebug.var_display_max_children', 256);
513611
if ($text) {
514612
var_dump($text);
515613
}

0 commit comments

Comments
 (0)