<?php
/*
«Copyright 2007 Max Barel hide@address.com»
Release : 0.4 ($Rev: 47 $)
$Date: 2007-09-09 15:43:54 +0200 (Dim, 09 sep 2007) $
This file is part of HoP.
HoP is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
HoP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HoP; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//XHTML element.
class Hop_element extends Hop_node {
const INDENT = "\t"; //define the indentation string
//singleton
protected static $n_el = 0; //element count in the pool added for time optimization
public static $pool; //a "flat" container object for all Hop_elements.
//Use it to direct access to an object : Hop_element::$pool->object_id
//See also the commodity function in Hop class
//mandatory properties values
public $type; //string, element type eg. 'div','p'
public $id; //id of this object, automatic generation if not given
//optional properties
public $attributes = null; //stdclass object: html attributes container
public $auto_id = false; //should 'id' html attribute be reflecting the object id
public $auto_name = false; //should 'name' html attribute be reflecting the object id (eg. for input element)
public $empty_el = false; //is this element type empty (otional empty should be set to false)
public $nl = 1; //newlines when rendering: 0 for inline (e.g. <span>),
// 1 for newline before the opening tag (one line elements: <p> or <legend>),
// 2 for newline before the closing tag (container elements : <body>, <div>)
public static $comment_tag = true; //optional comment at closing tag for readability. Global to the whole rendering
//constructor
function __construct($type, $options = null) {
$this->type = $type; // set the element type
//process options
if (is_string($options)) $options = array('id' => $options); // a string option default to the object id
elseif ($options and !is_object($options)) $options = (object) $options; //cast to object for uniform processing
if (isset($options->content) ) {
//content should be handled by setter for correct parent linking
$this->set_content($options->content);
unset ($options->content);
}
if ($options) foreach ($options as $k => $v) $this->$k = $v; //make other options properies of the element
//if (isset($this->$k))
//cast attributes to object for easy access (might be array or simple string)
if ($this->attributes and !is_object($this->attributes)) $this->attributes = (object) $this->attributes;
// if ($this->attributes and !is_object($this->attributes)) settype($this->attributes, 'object');
//if no id (from options) generate one, for the safeness of the pool. The element will then be difficult to address.
if (!$this->id or !is_string($this->id)) $this->id = $type;
self::$n_el ++;
if (isset(self::$pool->{$this->id})) $this->id = "{$type}_" . self::$n_el;
//add self to the pool
self::$pool->{$this->id} = $this;
}
function __clone() {
parent::__clone(); //called before renaming for easier access by name : $parent->child
self::$n_el ++;
$this->id = "{$this->id}_clone_" . self::$n_el;
self::$pool->{$this->id} = $this;
if ($this->attributes) $this->attributes = clone $this->attributes;
}
protected function set_container($node) {
//redefined to add a reverse link from parent to child through an id named property
parent::set_container($node); //=> $this->container = $node;
// if (is_a($node, __CLASS__))
$node->{$this->id} = $this; //cannot be done at node level becaus nodes have no id
}
function cut() {
unset($this->container->{$this->id});
return parent::cut();
}
//rendering function.
function render($indent = '') {
$html_string = '';
if ($this->auto_id and empty($this->attributes->id) ) $this->set_attr('id', $this->id);
if ($this->auto_name and empty($this->attributes->name) ) $this->set_attr('name', $this->id);
if ($this->nl) $html_string .= "\n$indent";
$html_string .= "<$this->type";
if ($this->attributes) {
//cast in case it has been set to a string
if (!is_object($this->attributes)) $this->attributes = (object) $this->attributes;
foreach ($this->attributes as $k => $value) {
if ($k == 'raw' or $k == 'scalar') $html_string .= " $value"; //for raw formatted attributes
else $html_string .= " $k=\"$value\"";
}
}
if ($this->empty_el) {
$html_string .= ' />';
} else {
$html_string .= '>';
$html_string .= parent::render(self::INDENT . $indent);
if ($this->nl > 1) $html_string .= "\n$indent";
$html_string .= "</$this->type>";
//optional, for code documentation
if ($this->nl > 1 and self::$comment_tag and isset($this->attributes->id)) $html_string .= "<!-- {$this->attributes->id} -->";
}
return $html_string;
}
//properties setter
function auto_id($val=true) { $this->auto_id = $val; return $this; }
function auto_name($val=true) { $this->auto_name = $val; return $this; }
function empty_el($val=true) { $this->empty_el = $val; return $this; }
function nl($val=1) { $this->nl = $val; return $this; }
//commodity function to set an attribute
function set_attr($attr, $value=null) {
$this->attributes->$attr = htmlspecialchars($value); //htmlspecialchars htmlentities
return $this;
}
//commodity function to get an attribute
function get_attr($attr) {
return @$this->attributes->$attr;
}
//remove an attribute
function remove_attr($attr) {
unset ($this->attributes->$attr);
return $this;
}
//generic attibute setter
private function __call($setter, $params) {
if (ereg('(set|get|remove)_(.*)', $setter, $e_res)) {
switch ($e_res[1]) {
case 'set': return $this->set_attr($e_res[2], $params[0]);
case 'get': return $this->get_attr($e_res[2]);
case 'remove': return $this->remove_attr($e_res[2]);
}
} else throw new Exception("invalide function call : $setter");
}
//specific class methods
public function add_class($class = null) {
if ($class) $this->set_attr('class', $this->get_attr('class') . " $class");
return $this;
}
public function remove_class($class = null) {
if ($class) $this->set_attr('class', trim(ereg_replace(" *$class", '', $this->get_attr('class'))));
return $this;
}
}
?>