<?php
/**
* @package CSSTree
*/
/**
* PHP4 CSSTree class
* with this class Cascading Style sheets may be generated using a tree
* the nodes store the attributes and values
*
*<code>
* // examples follow
*$ddata=array("border"=>"1px solid black");
*$ddata2=array("border"=>"2px solid silver");
*$null=null;
*
*$tree = new CSSTree($null,"body",$ddata,'class');
*$tmp=&$tree->addChild('h1',$ddata2,'pseudo');
*$tmp=&$tree->addChild('h2',$ddata,'pseudo');
*</code>
*
* TODO: comments in the css are not handled, but they may work
*/
// include the Tree class
require_once 'Tree.inc.php';
class CSSTree extends Tree{
/**
* Variable declaration
*/
/**
* properties of this Node
* @access private
* @var array
*/
var $_cssproperties;
/**
* name of this Node (class=|id=|...)
* @access public
* @var string
*/
var $tagname;
/**
* type of CSS ('id'|'class'|'pseudo') not implemented: useless
* @access private
* @var string
*/
//var $_type;
/**
* default constructor
*
* creates a CSS node of type $type with $name, set properties
* @access public
*
*/
function CSSTree(&$_parent,$tagname,$properties=null) {
// call the constructor of the parent class
parent::Tree($_parent);
/* we don't need this stuff
switch ($type){
case 'class': $this->_type='class';break;
case 'id': $this->_type='id';break;
case 'pseudo': $this->_type='pseudo';break;
default: //raise an error, TODO: raise a real error :-)
echo '!!! no valid type for CSS element type, use either of id|class|pseudo !!!';
$this->_type='class';
break;
}
*/
// set properties, if given
if ((!$properties==null) & (is_array($properties))){
$this->_cssproperties=$properties;
}
else{
$this->_cssproperties=array();
}
$this->tagname=$tagname;
}
/**
* add a subclass
* @return Tree reference to the child
*/
function &addChild($tagname,$properties=null) {
$testifalreadyexists=$this->getChildByName($tagname);
if ($testifalreadyexists!==false){return $testifalreadyexists;}
$this->_children[]=&new CSSTree(&$this,$tagname,$properties);
//get the automatically set id (=array key) - it is the last element now
end($this->_children);
$key=key($this->_children);
$this->_children[$key]->_setId($key);
$this->_children[$key]->level = $this->_level + 1;
return $this->_children[$key];
}
/**
* output CSS properties for this node, for use in style=""
* @return string
*/
function renderCSSProperties(){
$css='';
foreach ($this->_cssproperties as $prop=>$value) {
$css.=$prop.': '.$value.';';
}
return $css;
}
/**
* output CSS for this node
* @return string
*/
function renderCSS(){
$css=' ';
$prop=$this->renderCSSProperties();
//skip if there are no properties
if ($prop!=''){
$css.=$this->tagname.'{';
$css.=$prop;
$css.="}\n";
}
return $css;
}
/**
* get a reference child by its name, otherwise false
* @return CSSTree reference to the child node or false
*
*/
function &getChildByName($name){
$ret=false;
//echo 'selecting child with name '.$name;
foreach ($this->_children as $child){
if (!strcasecmp($name,$child->tagname)){
$ret=&$this->_children[$child->getId()];
return $ret;
}
}
return $ret;
}
/**
* Add a property to the properties
*
*/
function setProperty($property, $value){
$this->_cssproperties[$property]=$value;
}
/**
* output CSS for the complete tree, for use in header (<style type="text/css">) or file
* @return string
*/
function renderCSSAll(){
$root=&$this->getRoot();
return $root->_renderCSSAll('');
}
/**
* internal function returning string for one node using the (cascaded) prefix
* @var $prefix the accumulated cascaded prefix (class/id/tag name)
* @return string
* @access private
*/
function _renderCSSAll($prefix){
$css='';
// check if this is a 'virtual node' without properties,
// then we dont need to output something
if (count($this->_cssproperties) != 0){
$css.=$prefix.$this->renderCSS();
}
//set prefix for the children
$prefix.=' '.$this->tagname;
foreach ($this->_children as $child) {
$css.=$child->_renderCSSAll($prefix);
}
return $css;
}
/**
* CSS file parser, returns the css properties as (new) tree
* @return CSSTree
* @param string filename
* @access public
*/
function parseFile($filename) {
/* Open File and Parse it */
$fp=fopen($filename,"r") or die("Error opening file $filename");
$css_str = fread($fp, filesize ($filename));
fclose($fp);
//echo "read: ".$css_str;
return($this->parse($css_str));
}
/**
* CSS string parser, returns the css properties as (new) tree
* @return CSSTree
* @param string cssstring
* @access public
*/
function &parse($cssstring){
$null=null;
// new CSSTree with empty tagname
$tree=&new CSSTree($null,'');
// replace unnecessary white spaces with one
$css_str=preg_replace("/[\s]+/",' ',$cssstring);
// divide into classes
$css_class = explode("}", $css_str);
//iterate through classes
while (list($key,$val) = each ($css_class)){
// divide into classes and their properties
$aCSSObj=explode("{",$val);
// find multiple definitions and create a node for any
$classes=explode(",",$aCSSObj[0]);
foreach ($classes as $class){
$nesting=explode(" ",trim($class));
$propval=explode(";",$aCSSObj[1]);
//print_r($properties);
// begin at root node and set nested properties
$tmp=&$tree;
foreach ($nesting as $newlevel){
$tmp=&$tmp->addChild($newlevel);
}
//populate properties
foreach ($propval as $prop){
$prop=explode(":",$prop);
$propname=trim($prop[0]);
// do only for non-empty ones (last may be empty because of trailing ';')
if ($propname!=''){
$tmp->setProperty($propname,$prop[1]);
}
}
}
}
return $tree;
}
/**
* echo the structure of the subnodes of our tree/node
* and tell us addtitionally the name
* @param string prefix (used for the recursive output)
* @return void
*/
function echoStructure($pre='') {
for ($i=0;$i<$this->getLevel();$i++){
$pre.='|';//$this->getLevel();
}
echo $pre."+[".$this->_id.' '.$this->tagname.']';
if (is_array($this->data)){
foreach ($this->data as $key=>$value) {
echo '('.$key.'|'.$value.')';
}
}
echo "\n";
if ($this->numChildren()>0){
foreach ($this->_children as $child) {
$child->echoStructure();
}
}
}
} // end class
?>