|
695 | 695 | }, |
696 | 696 | findInnerDiff: function(t1, t2, route) { |
697 | 697 |
|
698 | | - var subtrees = markSubTrees(t1, t2), |
699 | | - mappings = subtrees.length, |
| 698 | + var subtrees = (t1.childNodes && t2.childNodes) ? markSubTrees(t1, t2) : [], |
700 | 699 | t1_child_nodes = t1.childNodes ? t1.childNodes : [], |
701 | 700 | t2_child_nodes = t2.childNodes ? t2.childNodes : [], |
702 | | - diffs, i, last, e1, e2; |
| 701 | + childNodesLengthDifference, diffs, i, last, e1, e2; |
703 | 702 |
|
704 | | - // no correspondence whatsoever |
705 | | - if (mappings === 0) { |
706 | | - // two text nodes with differences |
707 | | - if (t1.nodeName === '#text' && t2.nodeName === '#text' && t1.data !== t2.data) { |
708 | | - return [new Diff({ |
709 | | - action: 'modiFyTextElement', |
710 | | - oldValue: t1.data, |
711 | | - newValue: t2.data, |
712 | | - route: route |
713 | | - })]; |
714 | | - } |
| 703 | + if (subtrees.length > 1) { |
| 704 | + /* Two or more groups have been identified among the childnodes of t1 |
| 705 | + * and t2. |
| 706 | + */ |
| 707 | + return this.attemptGroupRelocation(t1, t2, subtrees, route); |
715 | 708 | } |
716 | | - // possibly identical content: verify |
717 | | - if (mappings < 2) { |
| 709 | + |
| 710 | + /* 0 or 1 groups of similar child nodes have been found |
| 711 | + * for t1 and t2. 1 If there is 1, it could be a sign that the |
| 712 | + * contents are the same. When the number of groups is below 2, |
| 713 | + * t1 and t2 are made to have the same length and each of the |
| 714 | + * pairs of child nodes are diffed. |
| 715 | + */ |
| 716 | + |
| 717 | + /* if (subtrees.length === 0) { |
| 718 | + // No subtrees - could text nodes as they won't have any childNodes. |
| 719 | + // |
| 720 | + if (t1.nodeName === '#text' && t2.nodeName === '#text' && t1.data !== t2.data) { |
| 721 | + return [new Diff({ |
| 722 | + action: 'modiFyTextElement', |
| 723 | + oldValue: t1.data, |
| 724 | + newValue: t2.data, |
| 725 | + route: route |
| 726 | + })]; |
| 727 | + } |
| 728 | + } */ |
| 729 | + |
| 730 | + |
718 | 731 | last = Math.max(t1_child_nodes.length, t2_child_nodes.length); |
| 732 | + if (t1_child_nodes.length !== t2_child_nodes.length) { |
| 733 | + childNodesLengthDifference = true; |
| 734 | + } |
| 735 | + |
719 | 736 | for (i = 0; i < last; i += 1) { |
720 | 737 | e1 = t1_child_nodes[i]; |
721 | 738 | e2 = t2_child_nodes[i]; |
722 | 739 |
|
723 | | - if (e1 && !e2) { |
724 | | - if (e1.nodeName === '#text') { |
| 740 | + if (childNodesLengthDifference) { |
| 741 | + /* t1 and t2 have different amounts of childNodes. Add |
| 742 | + * and remove as necessary to obtain the same length */ |
| 743 | + if (e1 && !e2) { |
| 744 | + if (e1.nodeName === '#text') { |
| 745 | + return [new Diff({ |
| 746 | + action: 'removeTextElement', |
| 747 | + route: route.concat(i), |
| 748 | + value: e1.data |
| 749 | + })]; |
| 750 | + } |
725 | 751 | return [new Diff({ |
726 | | - action: 'removeTextElement', |
| 752 | + action: 'removeElement', |
727 | 753 | route: route.concat(i), |
728 | | - value: e1.data |
| 754 | + element: cloneObj(e1) |
729 | 755 | })]; |
730 | 756 | } |
731 | | - return [new Diff({ |
732 | | - action: 'removeElement', |
733 | | - route: route.concat(i), |
734 | | - element: cloneObj(e1) |
735 | | - })]; |
736 | | - } |
737 | | - if (e2 && !e1) { |
738 | | - if (e2.nodeName === '#text') { |
| 757 | + if (e2 && !e1) { |
| 758 | + if (e2.nodeName === '#text') { |
| 759 | + return [new Diff({ |
| 760 | + action: 'addTextElement', |
| 761 | + route: route.concat(i), |
| 762 | + value: e2.data |
| 763 | + })]; |
| 764 | + } |
739 | 765 | return [new Diff({ |
740 | | - action: 'addTextElement', |
| 766 | + action: 'addElement', |
741 | 767 | route: route.concat(i), |
742 | | - value: e2.data |
| 768 | + element: cloneObj(e2) |
743 | 769 | })]; |
744 | 770 | } |
745 | | - return [new Diff({ |
746 | | - action: 'addElement', |
747 | | - route: route.concat(i), |
748 | | - element: cloneObj(e2) |
749 | | - })]; |
750 | 771 | } |
| 772 | + /* We are now guaranteed that childNodes e1 and e2 exist, |
| 773 | + * and that they can be diffed. |
| 774 | + */ |
751 | 775 |
|
752 | 776 | diffs = this.findNextDiff(e1, e2, route.concat(i)); |
753 | 777 | if (diffs && diffs.length > 0) { |
754 | 778 | return diffs; |
755 | 779 | } |
756 | 780 |
|
757 | 781 | } |
758 | | - } |
759 | 782 |
|
760 | | - // one or more differences: find first diff |
761 | | - return this.attemptGroupRelocation(t1, t2, subtrees, route); |
| 783 | + return []; |
| 784 | + |
762 | 785 | }, |
763 | 786 |
|
764 | 787 | attemptGroupRelocation: function(t1, t2, subtrees, route) { |
765 | | - /* Once t1.childNodes and t2.childNodes have the same length, |
766 | | - * attempts are made at equalizing the two. First all initial elements |
767 | | - * with no group affiliation (gaps=true) are removed (if in t1) or |
768 | | - * added (if in t2). Then the creation of a group relocation diff is |
769 | | - * attempted. |
| 788 | + /* Either t1.childNodes and t2.childNodes have the same length, or |
| 789 | + * there are at least two groups of similar elements can be found. |
| 790 | + * attempts are made at equalizing t1 with t2. First all initial |
| 791 | + * elements with no group affiliation (gaps=true) are removed (if |
| 792 | + * only in t1) or added (if only in t2). Then the creation of a group |
| 793 | + * relocation diff is attempted. |
770 | 794 | */ |
771 | 795 |
|
772 | 796 | var gapInformation = getGapInformation(t1, t2, subtrees), |
773 | 797 | gaps1 = gapInformation.gaps1, |
774 | 798 | gaps2 = gapInformation.gaps2, |
| 799 | + shortest = Math.min(gaps1.length, gaps2.length), |
775 | 800 | destinationDifferent, toGroup, |
776 | 801 | group, node, similarNode, testI, |
777 | 802 | i, j; |
778 | 803 |
|
779 | 804 | // group relocation |
780 | | - for (i = 0; i < gaps1.length; i += 1) { |
| 805 | + for (i = 0; i < shortest; i += 1) { |
781 | 806 | if (gaps1[i] === true) { |
782 | 807 | node = t1.childNodes[i]; |
783 | 808 | if (node.nodeName === '#text') { |
|
0 commit comments