<?php
// $Id: /cvsroot/tikiwiki/tiki/lib/diff/renderer_htmldiff.php,v 1.1.2.1 2008-03-04 15:48:27 sept_7 Exp $

/**
 * HTML diff renderer.
 *
 * This class renders the diff of an HTML page with best effort.
 *
 * (c) 2007, Stéphane Casset <sept@logidee.com> and Coucil of Europe
 * Licence : LGPL v2.1
 */

include_once("Renderer.php");

class Text_Diff_Renderer_htmldiff extends Tiki_Text_Diff_Renderer {

    function Text_Diff_Renderer_htmldiff($context_lines = 0, $words = 0)
    {
        $this->_leading_context_lines = $context_lines;
        $this->_trailing_context_lines = $context_lines;
	$this->_words = $words;
    }
    
    function _startDiff()
    {
        ob_start();
	$this->original = "";
	$this->final = "";
	$this->n = 0;
	$this->rspan = false;
	$this->lspan = false;
	//$this->tracked_tags = array ("table","ul","div");
	$this->tracked_tags = array ("table","ul");
    }

    function _endDiff()
    {
	for ($i=0; $i <= $this->n; $i++) {
		if ($this->original[$i] != "" and $this->final[$i] != "") {
			echo "<tr><td width='50%' colspan='2' style='vertical-align:top'>".$this->original[$i]."</td><td width='50%' colspan='2' style='vertical-align:top'>".$this->final[$i]."</td></tr>\n";
		}
	}
        //echo '</table>';
        $val = ob_get_contents();
        ob_end_clean();
        return $val;
    }

    function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
    {
        return "$xbeg,$xlen,$ybeg,$ylen";
    }

    function _startBlock($header)
    {
    }

    function _endBlock()
    {
    }

    function _insert_tag($line,$tag,&$span) {
      $string = "";
	if ($line != '') {
          if (strstr($line,"<") === FALSE) {
       	    if ($span === false) {
  	      $string .= "<span class='$tag'>";
  	      $span = true;
  	    }
  	    $string .= $line;
  	  } else {
  	    if ($span === true) {
  	      $string .= "</span class='fin'>";
  	      $span = false;
  	    }
	    if (strstr($line,"class=")  === FALSE) {
               $string .= preg_replace("#<([^/> ]+)(.*[^/]?)?>#","<$1 class='$tag' $2>",$line);
	       $string = preg_replace("#<br class='(.*)'\s*/>#","<span class='$1'>&crarr;</span><br class='$1' />",$string);
	    } else {
               $string .= preg_replace("#<([^/> ]+)(.*)class=[\"']?([^\"']+)[\"']?(.*[^/]?)?>#","<$1$2 class='$3 $tag' $4>",$line);
	    }
          } 
	}
	return $string;
    }

    function _count_tags($line, $version) {
	preg_match("#<(/?)([^ >]+)#",$line,$out);
	if (in_array($out[2],$this->tracked_tags)) {
          if ($out[1] == '/') {
	    $this->tags[$version][$out[2]]--;
	  } else {
	    $this->tags[$version][$out[2]]++;
	  }
	}
    }

    function _can_break($line) {

      if (preg_match("#<(p|h\d|br)#",$line) == 0) {
        return false;
      }

      foreach($this->tags as $v) {
        foreach($v as $tag) {
          if ($tag != 0) {
	    return false;
	  }
	}
      }
      return true;
    }

    function _lines($type, $lines, $prefix = '')
    {
	static $context = 0;

	switch($type) {
		case 'context':
			foreach($lines as $line) {
				if ($context == 0 and $this->_can_break($line)) {
		  			$context = 1;
					$this->n++;
				}

				$this->_count_tags($line,'original');
				$this->_count_tags($line,'final');
				if ($this->lspan === true) {
					$this->original[$this->n] .= "</span>";
					$this->lspan = false;
				}
				if ($this->rspan === true) {
					$this->final[$this->n] .= "</span>";
					$this->rspan = false;
				}
				$this->original[$this->n] .= "$line";
				$this->final[$this->n] .= "$line";
			}
			break;
		case 'change-added':
		case 'added':
			foreach($lines as $line) {
				if ($line != '') {
				  $this->_count_tags($line,'final');
				  $this->final[$this->n] .= $this->_insert_tag($line,'diffadded',$this->rspan);
				  $context = 0;
				}
			}
			break;
		case 'deleted':
		case 'change-deleted':
			foreach($lines as $line) {
				if ($line != '') {
				  $this->_count_tags($line,'original');
				  $this->original[$this->n] .= $this->_insert_tag($line,'diffdeleted',$this->lspan);
				  $context = 0;
				}
			}
			break;
		}
    }

    function _context($lines)
    {
        $this->_lines('context', $lines);
    }

    function _added($lines, $changemode = FALSE)
    {
        if ($changemode) {
        	$this->_lines('change-added', $lines, '+');
        } else {
        	$this->_lines('added', $lines, '+');
        }
    }

    function _deleted($lines, $changemode = FALSE)
    {
        if ($changemode) {
        	$this->_lines('change-deleted', $lines, '-');
        } else {
	        $this->_lines('deleted', $lines, '-');
        }
    }

    function _changed($orig, $final)
    {
        $this->_deleted($orig, TRUE);
        $this->_added($final, TRUE);
    }

}
