Code: Select all
<?php
/**
* Hierarchical Tree (in) Massive List
* http://www.recursive.dk
**/
class HTMList {
// ~ FUNCTION LIST ~
// __construct
// set
// get
// sub
// remove
// rename
// add_before
// add_after
// add_around
// indent
// outdent
// add_script_before
// add_script_after
// javascript_array
// add_css_after
// add_css_before
// css_array
// toROWS
// fromROW
// isSingleROW
// insertValue
// getInsertSafeInt
// fixIntegerKeys
// debug
// translate
// str
private $debugmode, $debuginfo;
private $rows;
/**
* Called when creating your root object, and each time an indent is used
* Only accepts arrays, and has the ability to enable debug info
**/
function __construct($a=array(), $setdebug=false) {
//Show error messages?
$this -> debugmode = $setdebug;
$this -> debuginfo = array();
$this -> toROWS($a);
$this -> rows = $a; //MAIN VAR
}
/**
* Change an existing row, or the whole tree on blank argument
**/
public function set($a, $n="") {
$this -> toROWS($a);
if(trim($n)=="") {
if($this -> isSingleROW($a)) $a = array($a);
$this -> rows = $a;
} else {
// Constrain withing string if it's a single item array
$this -> isSingleROW($a);
if(is_array($a)) {
$this -> rows[$n] = new HTMList($a);
} else {
$this -> rows[$n] = $a;
}
}
}
/**
* Get an existing row, or the whole tree on blank argument
**/
public function get($n="") {
if(trim($n)=="") return $this -> rows;
else return $this -> rows[$n];
}
/**
* Traverse the htm tree
**/
public function sub($n) {
return (is_object($this -> rows[$n])) ? $this -> rows[$n] : null ;
}
/**
* Delete row
**/
public function remove($n) {
if(!isset($this -> rows[$n])) $this -> debug("Trying to remove a row that doesn't exists.", "warning");
unset($this -> rows[$n]);
}
/**
* Give sub another name
**/
public function rename($n, $m) {
if(isset($this -> rows[$m])) $this -> debug("Renaming row to one that already exists.", "warning");
$b = array();
foreach($this -> rows as $key => $val) {
if($key==$n) $b[$m] = $val;
else $b[$key] = $val;
}
$this -> set($b);
}
/**
* Inject row(s) before another row, possibly overwriting itself if there's a naming conflict
**/
public function add_before($a, $oldN="", $newN="") {
$this -> toROWS($a);
$this -> isSingleROW($a);
//Determine position to add before
if(trim($oldN)!="") {
//Iterate through all rows
$b = array();
foreach($this -> rows as $label => $htm) {
if(strcmp($label, $oldN)==0) {
//Add new rows before choosen row
if(!is_array($a)) {
$this -> insertValue($b, $a, $newN);
} else {
foreach($a as $label2 => $htm2) {
$this -> insertValue($b, $htm2, $label2);
}
}
}
$b[$label] = $htm;
}
$this -> rows = $b;
} else {
//Add at beginning
$b = array();
if(!is_array($a)) {
$this -> insertValue($b, $a, $newN);
} else {
foreach($a as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
}
//Remaining rows come afterwards
foreach($this -> rows as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
$this -> rows = $b;
}
//Maintain integer indexes
$this -> fixIntegerKeys();
}
/**
* Append row(s) to another row, possibly overwriting itself if there's a naming conflict
**/
public function add_after($a, $oldN="", $newN="") {
$this -> toROWS($a);
$this -> isSingleROW($a);
//Determine position to add after
if(trim($oldN)!="") {
//Iterate through all rows
$b = array();
foreach($this -> rows as $label => $htm) {
$this -> insertValue($b, $htm, $label);
if(strcmp($label, $oldN)==0) {
//Add new rows after choosen row
if(!is_array($a)) {
$this -> insertValue($b, $a, $newN);
} else {
foreach($a as $label2 => $htm2) {
$this -> insertValue($b, $htm2, $label2);
}
}
}
}
$this -> rows = $b;
} else {
//Add at the end
$b = array();
//Original rows come first
foreach($this -> rows as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
//Followed by new rows
if(!is_array($a)) {
$this -> insertValue($b, $a, $newN);
} else {
foreach($a as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
}
$this -> rows = $b;
}
//Maintain integer indexes
$this -> fixIntegerKeys();
}
/**
* Put row(s) around block of rows, possibly overwriting itself if there's a naming conflict
**/
public function add_around($a1, $a2, $oldN="", $newN1="", $newN2="") {
$this -> toROWS($a1);
$this -> isSingleROW($a1);
$this -> toROWS($a2);
$this -> isSingleROW($a2);
//Determine position to add around
if(trim($oldN)!="") {
//Iterate through all rows
$b = array();
foreach($this -> rows as $label => $htm) {
if(strcmp($label, $oldN)==0) {
//Add new rows before choosen row
if(!is_array($a1)) {
$this -> insertValue($b, $a1, $newN1);
} else {
foreach($a1 as $label2 => $htm2) {
$this -> insertValue($b, $htm2, $label2);
}
}
}
$this -> insertValue($b, $htm, $label);
if(strcmp($label, $oldN)==0) {
//Add new rows after choosen row
if(!is_array($a2)) {
$this -> insertValue($b, $a2, $newN2);
} else {
foreach($a2 as $label2 => $htm2) {
$this -> insertValue($b, $htm2, $label2);
}
}
}
}
$this -> rows = $b;
} else {
//Add around the whole block
$b = array();
if(!is_array($a1)) {
$this -> insertValue($b, $a1, $newN1);
} else {
foreach($a1 as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
}
//Original rows in the middle
foreach($this -> rows as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
//Followed by last rows
if(!is_array($a2)) {
$this -> insertValue($b, $a2, $newN2);
} else {
foreach($a2 as $label => $htm) {
$this -> insertValue($b, $htm, $label);
}
}
$this -> rows = $b;
}
//Maintain integer indexes
$this -> fixIntegerKeys();
}
/**
* Makes white space on the output by making inN an object
**/
public function indent($inN, $newN) {
$htm = $this -> get($inN);
$temp = new HTMList(array($newN => $htm));
$this -> set($temp, $inN);
}
/**
* Clears white space from the output
**/
public function outdent($inN) {
$htm = $this -> get($inN);
if(is_object($htm)) {
$htm = $htm -> get();
if(is_array($htm)) {
$this -> add_after($htm, $inN);
$this -> remove($inN);
} else $this -> set($htm, $inN);
}
}
/**
* Adds javascript before a html chunk
**/
public function add_script_before($a, $oldN="", $newN="", $v="") {
$scr = $this -> javascript_array($a, $v);
$this -> add_before($scr[0], $oldN);
$this -> add_before($scr[1], $oldN);
$this -> add_before($scr[2], $oldN);
}
/**
* Adds javascript after a html chunk
**/
public function add_script_after($a, $oldN="", $newN="", $v="") {
$scr = $this -> javascript_array($a, $v);
$this -> add_after($scr[2], $oldN);
$this -> add_after($scr[1], $oldN);
$this -> add_after($scr[0], $oldN);
}
/**
* Wraps javascript in an array
**/
private function javascript_array($script, $v) {
if(!is_array($script)) $script = array($script);
$version = "";
if(trim($v)!="") $version = " language=\"javascript".$v."\"";
$starttag = array(
"<script type=\"text/javascript\"".$version.">",
"<!--"
);
$code = array(
"",
$script,
""
);
$endtag = array(
"// -->",
"</script>"
);
return array($starttag, $code, $endtag);
}
/**
* Adds css before a html chunk
**/
public function add_css_before($a, $oldN="", $newN="") {
$style = $this -> css_array($a);
$this -> add_before($style[2], $oldN);
$this -> add_before($style[1], $oldN);
$this -> add_before($style[0], $oldN);
}
/**
* Adds css after a html chunk
**/
public function add_css_after($a, $oldN="", $newN="") {
$style = $this -> css_array($a);
$this -> add_after($style[0], $oldN);
$this -> add_after($style[1], $oldN);
$this -> add_after($style[2], $oldN);
}
/**
* Wraps css in an array
**/
private function css_array($style) {
if(!is_array($style)) $style = array($style);
$starttag = array(
"<style>"
);
$sheet = array(
"",
$style,
""
);
$endtag = array(
"</style>"
);
return array($starttag, $sheet, $endtag);
}
/**
* Prepares input for use inside the class, by turning it into an array containing only labeled strings and labeled objects with arrays in them
* The array is allowed to contain one or more HTMLists
**/
private function toROWS(&$x) {
if(is_object($x)) $x = $x -> get();
if(!is_array($x)) $x = array("".$x);
else {
//Turn vals into string
foreach($x as $label => $htm) {
if(!is_object($htm) && !is_array($htm)) $x[$label] = "".$htm;
}
}
//Check that everything has a text label, -otherwise make a digit one
//-By making a clone of the array and then digit-labeling that
$y = array();
$n = 0;
$madeN = false;
foreach($x as $label => $htm) {
if(!is_string($label)) {
$y[$n] = $htm;
$madeN = true;
} else {
$y[$label] = $htm;
}
$n++;
}
if($madeN) {
$x = $y;
$this -> debug("Unlabeled rows found.", "notice");
}
//Convert all arrays and objects into objects
foreach($x as $label => $htm) {
if(is_array($htm)) {
$x[$label] = new HTMList($htm);
}
}
return true;
}
/**
* Fetch a row either as string or as object's row array
**/
private function fromROW($n="") {
if(trim($n)=="") return $this -> rows;
if(is_object($this -> rows[$n])) return $this -> rows[$n] -> get();
else return $this -> rows[$n];
}
/**
* Prepares an array for use inside the class
* The array is allowed to contain one or more HTMLists
**/
private function isSingleROW(&$x) {
if(is_array($x)) {
if(count($x)<=1) {
$k = array_keys($x);
foreach($k as $v) {
if(preg_match("/\d+/", $v)) {
$x = $x[$v]; // >:D
return true;
} else {
return false;
}
}
}
}
return false;
}
/**
* Returns an integer for use in array label that doesn't exist already
**/
private function insertValue(&$a, $val, $label) {
if(!is_string($label) || trim($label)=="") $a[$this -> getInsertSafeInt($a)] = $val;
else {
if(isset($a[$label])) $this -> debug("Overwriting existing keys.", "warning");
$a[$label] = $val;
}
}
/**
* Returns an integer for use in array label that doesn't exist already
**/
private function getInsertSafeInt($arr) {
$maxI = 0;
foreach($arr as $int => $v) {
if(!is_string($int)) {
if($int>$maxI) $maxI=$int;
}
}
$maxI++;
return $maxI;
}
/**
* Anonymous rows have a digit label as array key
* This function makes sure they match their positions
**/
private function fixIntegerKeys() {
$a = $this -> rows;
//Check if things are already as they should be
$ok = true;
$n=0;
foreach($a as $label => $htm) {
if(!is_string($label)) {
if($label!=$n) $ok=false;
}
$n++;
}
if($ok) return false;
$this -> debug("Fixing integer keys.", "notice");
$b = array();
$n = 0;
//Fix integer keys
foreach($a as $label => $htm) {
if(!is_string($label)) {
$b[$n] = $htm;
} else {
$b[$label] = $htm;
}
$n++;
}
$this -> rows = $b;
return true;
}
/**
* Helps you use this class by writing to a string each time something unintended happens, if enabled.
* Feel free to change debug method :)
**/
private function debug($mes, $kind="notice") {
//echo "<!-- NOTICE: ".$mes." --><br>\n";
if($this -> debugmode) {
$this -> debuginfo[] = "<!-- NOTICE: ".$mes." -->\n";
}
}
/**
* Gives each row a line break, indent etc.
**/
private function translate($a, $t, $first) {
$r = "";
foreach($a as $row) {
if(is_object($row)) {
$r .= $this -> translate($row -> get(), $t."\t", false);
} else {
$r .= $t.$row."\n";
}
}
return $r;
}
/**
* Output the final code
**/
public function str($output=false) {
$ret = "".$this -> translate($this -> rows, "", false);
if($output) echo $ret;
return $ret;
}
}
?>Thank you