diff --git a/examples/document.php b/examples/document.php index 0fd3834..6e24b35 100644 --- a/examples/document.php +++ b/examples/document.php @@ -1,57 +1,55 @@ beautify = true; - - // Add a link to the page - $link = html('a#google.external rel=external', 'Link', 'class="test something" target=blank rel=test'); - $link->href = 'http://google.com'; - $link->onclick = "return confirm('Navigate away?');"; - $link->appendTo($doc->body); - - // Create an unordered list for an array of items - // the array can be other html elements or text - $list = new SimpleList( - array( - html('b', 'first'), - 'second', - 'third', - array( - 'sub-third', - 'sub-forth' - ) - ) - ); - $list->appendTo($doc->body); - - // Create a sample table with some rows of dummy data - $table = new Table( - array( - array('id'=>1, 'first'=>'James', 'last'=>'Smith'), - array('id'=>2, 'first'=>'Mary', 'last'=>'Denver'), - array('id'=>3, 'first'=>'Charlie', 'last'=>'Rose') - ), - array('ID', 'First Name', 'Last Name') - ); - - // We'll set some of the table properties - $table->style = 'border:1px solid black'; - $table->border = 0; - $table->id = 'people'; - $table->appendTo($doc->body); - - // Output the result formatted nice with indents - echo $doc; + HTML5::autoload(); // Autoload the classes, helpful if using outside composer + HTML5::useGlobal(); // if you want to use the global html() method instead of namespaced one -?> \ No newline at end of file + use Canteen\HTML5\Document; + use Canteen\HTML5\SimpleList; + use Canteen\HTML5\Table; + + // Create a new document + $doc = new Document('Test Document'); + $doc->beautify = true; + + // Add a link to the page + $link = html('a#google.external rel=external', 'Link', 'class="test something" target=blank rel=test'); + $link->href = 'http://google.com'; + $link->onclick = "return confirm('Navigate away?');"; + $link->appendTo($doc->body); + + // Create an unordered list for an array of items + // the array can be other html elements or text + $list = new SimpleList( + array( + html('b', 'first'), + 'second', + 'third', + array( + 'sub-third', + 'sub-forth' + ) + ) + ); + $list->appendTo($doc->body); + + // Create a sample table with some rows of dummy data + $table = new Table( + array( + array('id'=>1, 'first'=>'James', 'last'=>'Smith'), + array('id'=>2, 'first'=>'Mary', 'last'=>'Denver'), + array('id'=>3, 'first'=>'Charlie', 'last'=>'Rose') + ), + array('ID', 'First Name', 'Last Name') + ); + + // We'll set some of the table properties + $table->style = 'border:1px solid black'; + $table->border = 0; + $table->id = 'people'; + $table->appendTo($doc->body); + + // Output the result formatted nice with indents + echo $doc; diff --git a/src/Attribute.php b/src/Attribute.php index b25c29f..585df8c 100644 --- a/src/Attribute.php +++ b/src/Attribute.php @@ -1,170 +1,176 @@ setAttribute('class', 'button') - * ->setAttribute('href', 'about.html'); - * - * @class Attribute - * @constructor - * @param {String} [name=null] The name of the attribute - * @param {String} [value=null] The value of the attribute - */ - class Attribute - { - /** - * The name of the attribute - * @property {String} _name - * @private - */ - private $_name; - - /** - * The value of the attribute - * @property {String} _value - * @private - */ - private $_value; + /** + * An HTML attribute used on the Node, this is used internally. + * Do not initiate this class directly, use the `html()` function + * to create attributes on elements. + * + * echo html('a', 'Link', 'class=button href="about.html"'); + * + * echo html('a', 'Link') + * ->setAttribute('class', 'button') + * ->setAttribute('href', 'about.html'); + * + * @class Attribute + * @constructor + * @param {String} [name=null] The name of the attribute + * @param {String} [value=null] The value of the attribute + */ + class Attribute + { + /** + * The name of the attribute + * @property {String} _name + * @private + */ + private $_name; - public function __construct($name = null, $value = null) - { - $this->name = $name; - $this->value = $value; - } + /** + * The value of the attribute + * @property {String} _value + * @private + */ + private $_value; - /** - * Convert the attribute to an HTML tag attribute string - * @method __toString - * @return {String} String representation of attribute - */ - public function __toString() - { - return " " . $this->_name . "=\"" . $this->_value . "\""; - } + public function __construct($name = null, $value = null) + { + $this->name = $name; + $this->value = $value; + } - /** - * Get the name of this attribute - * @method getName - * @return {String} The attribute's name - */ - public function getName() - { - return $this->_name; - } + /** + * Convert the attribute to an HTML tag attribute string + * @method __toString + * @return {String} String representation of attribute + */ + public function __toString() + { + return " " . $this->_name . "=\"" . $this->_value . "\""; + } - /** - * Set the name of this attribute, cannot be empty - * @method setName - * @param {String} [name=null] The name of the attribute - */ - public function setName($name = null) - { - if (is_null($name) || empty($name)) - { - throw new HTML5Error(HTML5Error::EMPTY_ATTRIBUTE_NAME); - } - $this->_name = $name; - } - - /** - * Get the value of this attribute - * @method getValue - * @protected - * @return {String} The value of attribute - */ - protected function getValue() - { - return $this->_value; - } + /** + * Get the name of this attribute + * @method getName + * @return {String} The attribute's name + */ + public function getName() + { + return $this->_name; + } - /** - * Set the value of this attribute, this cannot be empty - * @method setValue - * @protected - * @param {String} value The value to set - */ - protected function setValue($value) - { - $this->_value = $value; - } - - /** - * Convert a string into an associative array - * @method shorthand - * @static - * @param {String} str The string, delineated by semicolons, and colons for attributes:values - * @return {Dictionary} The collection of attributes - */ - static public function shorthand($str) - { - $res = array(); - - // Match the name=value in the attributes string - preg_match_all('/([a-z]+[a-z\-]*)\=("[^\"]*"|\'[^\']*\'|[^\s\"\']*)/',$str, $arr); - - foreach($arr[1] as $i=>$name) - { - $value = $arr[2][$i]; - - // Remove containing quotes if present - if (preg_match('/^[\'\"][^\n]*[\'\"]$/', $value)) - { - $value = substr($value, 1, -1); - } - $res[$name] = $value; - } - return $res; - } - - /** - * General purpose getter for getting attribute->name and attribute->value - * @public __get - * @param {String} name The name of the property to get - */ - public function __get($name) - { - if (method_exists($this , $method =('get' . ucfirst($name)))) - return $this->$method(); - else - throw new HTML5Error(HTML5Error::INVALID_GETTER, $name); - } + /** + * Set the name of this attribute, cannot be empty + * @method setName + * @param {String} [name=null] The name of the attribute + */ + public function setName($name = null) + { + if (is_null($name) || empty($name)) + { + throw new HTML5Error(HTML5Error::EMPTY_ATTRIBUTE_NAME); + } + $this->_name = $name; + } - /** - * General purpose setter for setting attribute->name and attribute->value - * @public __set - * @param {String} name The name of the attribute - * @param {String} value The value of the attribute - */ - public function __set($name, $value) - { - if (method_exists($this , $method =('set' . ucfirst($name)))) - return $this->$method($value); - else - throw new HTML5Error(HTML5Error::INVALID_SETTER, $name); - } + /** + * Get the value of this attribute + * @method getValue + * @protected + * @return {String} The value of attribute + */ + protected function getValue() + { + return $this->_value; + } - /** - * See if a property exists - * @method __isset - * @param {String} name The name of the property - */ - public function __isset($name) - { - return method_exists($this , 'get' . ucfirst($name)) - || method_exists($this , 'set' . ucfirst($name)); - } - } + /** + * Set the value of this attribute, this cannot be empty + * @method setValue + * @protected + * @param {String} value The value to set + */ + protected function setValue($value) + { + $this->_value = $value; + } + + /** + * Convert a string into an associative array + * @method shorthand + * @static + * @param {String} str The string, delineated by semicolons, and colons for attributes:values + * @return {Dictionary} The collection of attributes + */ + static public function shorthand($str) + { + $res = array(); + + // Match the name=value in the attributes string + preg_match_all('/([a-z]+[a-z\-]*)\=("[^\"]*"|\'[^\']*\'|[^\s\"\']*)/',$str, $arr); + + foreach ($arr[1] as $i=>$name) + { + $value = $arr[2][$i]; + + // Remove containing quotes if present + if (preg_match('/^[\'\"][^\n]*[\'\"]$/', $value)) + { + $value = substr($value, 1, -1); + } + $res[$name] = $value; + } + return $res; + } + + /** + * General purpose getter for getting attribute->name and attribute->value + * @public __get + * @param {String} name The name of the property to get + */ + public function __get($name) + { + if (method_exists($this , $method =('get' . ucfirst($name)))) + { + return $this->$method(); + } + else + { + throw new HTML5Error(HTML5Error::INVALID_GETTER, $name); + } + } + + /** + * General purpose setter for setting attribute->name and attribute->value + * @public __set + * @param {String} name The name of the attribute + * @param {String} value The value of the attribute + */ + public function __set($name, $value) + { + if (method_exists($this , $method =('set' . ucfirst($name)))) + { + return $this->$method($value); + } + else + { + throw new HTML5Error(HTML5Error::INVALID_SETTER, $name); + } + } + + /** + * See if a property exists + * @method __isset + * @param {String} name The name of the property + */ + public function __isset($name) + { + return method_exists($this , 'get' . ucfirst($name)) + || method_exists($this , 'set' . ucfirst($name)); + } + } } - -?> \ No newline at end of file diff --git a/src/Comment.php b/src/Comment.php index 44ffe22..f50beae 100644 --- a/src/Comment.php +++ b/src/Comment.php @@ -1,38 +1,36 @@ _tag.' -->'; - } - } -} +{ + /** + * Special node type representing an HTML5 inline comment. + * Do not initiate this class directly, use the `html('comment')` function: + * + * echo html('comment', 'Hidden HTML comment here'); + * + * @class Comment + * @extends NodeContainer + * @constructor + * @param {String} text the plain text string + */ + class Comment extends NodeContainer + { + public function __construct($text) + { + parent::__construct($text); + } -?> \ No newline at end of file + /** + * Write to HTML + * @method __toString + * @return {String} The string representation of this HTML node + */ + public function __toString() + { + return ''; + } + } +} diff --git a/src/Document.php b/src/Document.php index fe126c0..3138203 100644 --- a/src/Document.php +++ b/src/Document.php @@ -1,117 +1,117 @@ head->addChild(html('script src=main.js')); - * $doc->body->addChild(html('div#frame')); - * echo $doc; - * - * @class Document - * @extends NodeContainer - * @constructor - * @param {String} [title=''] The title of the document - * @param {String} [charset='utf-8'] The character encoding set of this HTML document - * @param {Boolean} [beautify=false] If we should add whitespace to the output to make it look nice markup. - */ - class Document extends NodeContainer - { - /** - * The document type - * @property {NodeContainer} docType - */ - private $docType; +{ + /** + * Create an HTML document. Basic barebones structure. + * Located in the namespace __Canteen\HTML5__. + * + * $doc = new HTML5\Document('Untitled'); + * $doc->head->addChild(html('script src=main.js')); + * $doc->body->addChild(html('div#frame')); + * echo $doc; + * + * @class Document + * @extends NodeContainer + * @constructor + * @param {String} [title=''] The title of the document + * @param {String} [charset='utf-8'] The character encoding set of this HTML document + * @param {Boolean} [beautify=false] If we should add whitespace to the output to make it look nice markup. + */ + class Document extends NodeContainer + { + /** + * The document type + * @property {NodeContainer} docType + */ + private $docType; - /** - * The head node - * @property {NodeContainer} head - */ - public $head; - - /** - * The body node - * @property {NodeContainer} body - */ - public $body; - - /** - * The title node - * @property {NodeContainer} title - */ - public $title; - - /** - * Beautify the output - * @property {Boolean} beautify - */ - public $beautify = false; - - /** - * Constructor for Docs - */ - public function __construct($title='', $charset='utf-8', $beautify=false) - { - parent::__construct('html', null, null, - array_merge( - array('manifest'), - Specification::$ATTRIBUTES - ) - ); - - $this->docType = html('doctype'); - $this->head = html('head'); - $this->body = html('body'); - $this->title = html('title', $title); - - $this->head->addChild(html('meta', 'charset='.$charset)); - $this->head->addChild($this->title); - - $this->addChild($this->head); - $this->addChild($this->body); - } - - /** - * Write to HTML - * @method __toString - * @return {String} The string representation of this HTML node - */ - public function __toString() - { - $result = $this->docType . parent::__toString(); - if ($this->beautify) - $result = self::beautify($result); - return $result; - } - - /** - * Beautifies an HTML string into a human-readable and indented work of art. - * @method beautify - * @static - * @param {String} html The XML-compatible HTML as a string - * @return {String} The formatted string - */ - public static function beautify($html) - { - // Conver the HTML -> SimpleXML -> DOMDocument - $dom = dom_import_simplexml(new \SimpleXMLElement($html))->ownerDocument; - - // Format the DOMDocument - $dom->formatOutput = true; - - // Save the output as XML - $buffer = $dom->saveXML(); - - // Remove the first line which has the XML declaration - return substr($buffer, strpos($buffer, "\n")+1); - } - } + /** + * The head node + * @property {NodeContainer} head + */ + public $head; + + /** + * The body node + * @property {NodeContainer} body + */ + public $body; + + /** + * The title node + * @property {NodeContainer} title + */ + public $title; + + /** + * Beautify the output + * @property {Boolean} beautify + */ + public $beautify = false; + + /** + * Constructor for Docs + */ + public function __construct($title='', $charset='utf-8', $beautify=false) + { + parent::__construct('html', null, null, + array_merge( + array('manifest'), + Specification::$ATTRIBUTES + ) + ); + + $this->docType = html('doctype'); + $this->head = html('head'); + $this->body = html('body'); + $this->title = html('title', $title); + + $this->head->addChild(html('meta', 'charset=' . $charset)); + $this->head->addChild($this->title); + + $this->addChild($this->head); + $this->addChild($this->body); + } + + /** + * Write to HTML + * @method __toString + * @return {String} The string representation of this HTML node + */ + public function __toString() + { + $result = $this->docType . parent::__toString(); + if ($this->beautify) + { + $result = self::beautify($result); + } + return $result; + } + + /** + * Beautifies an HTML string into a human-readable and indented work of art. + * @method beautify + * @static + * @param {String} html The XML-compatible HTML as a string + * @return {String} The formatted string + */ + public static function beautify($html) + { + // Conver the HTML -> SimpleXML -> DOMDocument + $dom = dom_import_simplexml(new \SimpleXMLElement($html))->ownerDocument; + + // Format the DOMDocument + $dom->formatOutput = true; + + // Save the output as XML + $buffer = $dom->saveXML(); + + // Remove the first line which has the XML declaration + return substr($buffer, strpos($buffer, "\n")+1); + } + } } - -?> \ No newline at end of file diff --git a/src/Fragment.php b/src/Fragment.php index dd9f0a7..a702a5f 100644 --- a/src/Fragment.php +++ b/src/Fragment.php @@ -5,39 +5,37 @@ */ namespace Canteen\HTML5 { - /** - * Represents a set of HTML tags without a wrapper. - * Do not initiate this class directly, use the `html()` function: - * - * $div = html('fragment'); - * - * @class Fragment - * @extends NodeContainer - * @constructor - * @param {Node|Array} [children=null] The collection of children or single child - */ - class Fragment extends NodeContainer - { - public function __construct($children = null) - { - parent::__construct('fragment', $children, null); - } + /** + * Represents a set of HTML tags without a wrapper. + * Do not initiate this class directly, use the `html()` function: + * + * $div = html('fragment'); + * + * @class Fragment + * @extends NodeContainer + * @constructor + * @param {Node|Array} [children=null] The collection of children or single child + */ + class Fragment extends NodeContainer + { + public function __construct($children = null) + { + parent::__construct('fragment', $children, null); + } - /** - * Write to HTML - * @method __toString - * @return {String} The string representation of this HTML node - */ - public function __toString() - { - $buffer = ''; - foreach($this->getChildren() as $child) - { - $buffer .= $child->__toString(); - } - return $buffer; - } - } + /** + * Write to HTML + * @method __toString + * @return {String} The string representation of this HTML node + */ + public function __toString() + { + $buffer = ''; + foreach ($this->getChildren() as $child) + { + $buffer .= $child->__toString(); + } + return $buffer; + } + } } - -?> \ No newline at end of file diff --git a/src/HTML5.php b/src/HTML5.php index 090d10b..8fbe21c 100644 --- a/src/HTML5.php +++ b/src/HTML5.php @@ -1,170 +1,175 @@ -GitHub project. - * To install the library simply include `html.php`, this takes care of any autoloading that's needed - * for the rest of the library. - * - * echo html('img src=home.jpg'); - * echo html('img', 'src=home.jpg'); - * echo html('a', array('href'=>'about.html')); - * - * - * - * @method html - * @param {String} tag The name of the tag as a string for example 'tr', 'table', can be followed - * by CSS selector, e.g. 'a#backButton' or 'a.button' - * @param {Dictionary|Node|String|Array} [childrenOrAttributes=null] If the tag is a NodeContainer, this can be an array - * of attributes, another html node or some text. If the tag is a single node, this can - * be an array or chain of attributes - * @param {Dictionary|String} [attributes=null] The attributes list for container tags (e.g., 'class:selected') - * @return {Node} Return the html node - */ - function html($tag, $childrenOrAttributes=null, $attributes=null) - { - // Get the tag ID from the tag string - // for instance 'a.button rel=external', a.button is the tagId, the rest are the attributes - $endPos = strpos(trim($tag), ' '); - - // The tag attributes - $tagAttributes = array(); - - // If the tag also has some attributes - if ($endPos !== false) - { - $tagOriginal = $tag; - $tag = substr($tag, 0, $endPos); - $tagAttributes = Attribute::shorthand(substr($tagOriginal, $endPos + 1)); - } - - // Match the tag name without the CSS selectors - preg_match('/^([a-z1-6]{1,10})(.*)/', $tag, $tagParts); - - // Valid class ane id names must begin with a -, _, or a-z letter - preg_match_all('/(\.|\#)\-?[\_a-zA-Z]+[\_a-zA-Z0-9\-]*/', $tagParts[2], $selectors); - - $tag = strtolower($tagParts[1]); // the name of the tag - $selfClosing = false; + // Convert the rest to directories + $name = str_replace("\\", '/', $name); - // Comment tags are special - if ($tag == 'comment') - { - return new Comment($childrenOrAttributes); - } - // Document type declaration - else if ($tag == 'doctype') - { - return ''; - } - // Any normal text - else if ($tag == 'text') - { - return new Text($childrenOrAttributes); - } - // Untagged container - else if ($tag == 'fragment') - { - return new Fragment($childrenOrAttributes); - } - // Check for task specification - else if (isset(Specification::$TAGS[$tag])) - { - // Check to see if this is a self closing tag - $selfClosing = in_array($tag, Specification::$SELF_CLOSING); - } - else - { - throw new HTML5Error(HTML5Error::INVALID_TAG, $tag); - } - - // Create the attributes collection, either string or array - $attributes = $selfClosing ? $childrenOrAttributes : $attributes; - - // If there are attributes and they are in a string format - // convert to an attributes array - if ($attributes !== null && is_string($attributes)) - { - $attributes = Attribute::shorthand($attributes); - } - - // Combine the attributes and the tags - if (is_array($attributes)) - { - $attributes = array_merge($tagAttributes, $attributes); - } - // Or just add any tag attributes - else if (count($tagAttributes)) - { - $attributes = $tagAttributes; - } - - // Create the node or container - $node = $selfClosing ? - new Node($tag, $attributes) : - new NodeContainer($tag, $childrenOrAttributes, $attributes); - - // Take the selectors convert them into id or class - foreach($selectors[0] as $selector) - { - switch($selector[0]) - { - case '#' : - $node->id = substr($selector, 1); - break; - case '.' : - if ($node->class) $node->class .= ' '; - $node->class .= substr($selector, 1); - break; - } - } - return $node; - } -} \ No newline at end of file + // Include the class relative to here + include __DIR__ . '/' . $name . '.php'; + }); + } + + /** + * Use the global `html()` method + * @method {Boolean} useGlobal + */ + static public function useGlobal() + { + include __DIR__ . '/html.php'; + } + } + + /** + * This is the global function is the main entry for interacting with the HTML5 for PHP library. + * using `html()` global function you can create HTML5 quickly and easily. For more + * examples and instruction on how to use this library, please refer to the the + * GitHub project. + * To install the library simply include `html.php`, this takes care of any autoloading that's needed + * for the rest of the library. + * + * echo html('img src=home.jpg'); + * echo html('img', 'src=home.jpg'); + * echo html('a', array('href'=>'about.html')); + * + * + * + * @method html + * @param {String} tag The name of the tag as a string for example 'tr', 'table', can be followed + * by CSS selector, e.g. 'a#backButton' or 'a.button' + * @param {Dictionary|Node|String|Array} [childrenOrAttributes=null] If the tag is a NodeContainer, this can be an array + * of attributes, another html node or some text. If the tag is a single node, this can + * be an array or chain of attributes + * @param {Dictionary|String} [attributes=null] The attributes list for container tags (e.g., 'class:selected') + * @return {Node} Return the html node + */ + function html($tag, $childrenOrAttributes=null, $attributes=null) + { + // Get the tag ID from the tag string + // for instance 'a.button rel=external', a.button is the tagId, the rest are the attributes + $endPos = strpos(trim($tag), ' '); + + // The tag attributes + $tagAttributes = array(); + + // If the tag also has some attributes + if ($endPos !== false) + { + $tagOriginal = $tag; + $tag = substr($tag, 0, $endPos); + $tagAttributes = Attribute::shorthand(substr($tagOriginal, $endPos + 1)); + } + + // Match the tag name without the CSS selectors + preg_match('/^([a-z1-6]{1,10})(.*)/', $tag, $tagParts); + + // Valid class ane id names must begin with a -, _, or a-z letter + preg_match_all('/(\.|\#)\-?[\_a-zA-Z]+[\_a-zA-Z0-9\-]*/', $tagParts[2], $selectors); + + $tag = strtolower($tagParts[1]); // the name of the tag + $selfClosing = false; + + // Comment tags are special + if ($tag == 'comment') + { + return new Comment($childrenOrAttributes); + } + // Document type declaration + else if ($tag == 'doctype') + { + return ''; + } + // Any normal text + else if ($tag == 'text') + { + return new Text($childrenOrAttributes); + } + // Untagged container + else if ($tag == 'fragment') + { + return new Fragment($childrenOrAttributes); + } + // Check for task specification + else if (isset(Specification::$TAGS[$tag])) + { + // Check to see if this is a self closing tag + $selfClosing = in_array($tag, Specification::$SELF_CLOSING); + } + else + { + throw new HTML5Error(HTML5Error::INVALID_TAG, $tag); + } + + // Create the attributes collection, either string or array + $attributes = $selfClosing ? $childrenOrAttributes : $attributes; + + // If there are attributes and they are in a string format + // convert to an attributes array + if ($attributes !== null && is_string($attributes)) + { + $attributes = Attribute::shorthand($attributes); + } + + // Combine the attributes and the tags + if (is_array($attributes)) + { + $attributes = array_merge($tagAttributes, $attributes); + } + // Or just add any tag attributes + else if (count($tagAttributes)) + { + $attributes = $tagAttributes; + } + + // Create the node or container + $node = $selfClosing ? + new Node($tag, $attributes) : + new NodeContainer($tag, $childrenOrAttributes, $attributes); + + // Take the selectors convert them into id or class + foreach ($selectors[0] as $selector) + { + switch ($selector[0]) + { + case '#' : + $node->id = substr($selector, 1); + break; + case '.' : + if ($node->class) { + $node->class .= ' '; + } + $node->class .= substr($selector, 1); + break; + } + } + return $node; + } +} diff --git a/src/HTML5Error.php b/src/HTML5Error.php index a9a9cdd..adea14f 100644 --- a/src/HTML5Error.php +++ b/src/HTML5Error.php @@ -1,144 +1,142 @@ getMessage(); - * } - * - * @class HTML5Error - * @extends Exception - * @constructor - * @param {int} code The code of the error - * @param {String} [data=''] Additional data to associate with this error - */ - class HTML5Error extends \Exception - { - /** - * The database connection failed - * @property {int} EMPTY_ATTRIBUTE_NAME - * @static - * @final - */ - const EMPTY_ATTRIBUTE_NAME = 500; - - /** - * The alias for a database is invalid - * @property {int} EMPTY_ATTRIBUTE_VALUE - * @static - * @final - */ - const EMPTY_ATTRIBUTE_VALUE = 501; - - /** - * The database name we're trying to switch to is invalid - * @property {int} INVALID_SETTER - * @static - * @final - */ - const INVALID_SETTER = 502; - - /** - * The mysql where trying to execute was a problem - * @property {int} INVALID_GETTER - * @static - * @final - */ - const INVALID_GETTER = 503; - - /** - * The html tag name is invalid - * @property {int} INVALID_TAG - * @static - * @final - */ - const INVALID_TAG = 504; - - /** - * When trying to create a node, the name is empty - * @property {int} EMPTY_NODE_TAG - * @static - * @final - */ - const EMPTY_NODE_TAG = 505; - - /** - * The parent cannot be empty - * @property {int} EMPTY_PARENT - * @static - * @final - */ - const EMPTY_PARENT = 506; - - /** - * THe addChildAt is out of bounds - * @property {int} OUT_OF_BOUNDS - * @static - * @final - */ - const OUT_OF_BOUNDS = 507; - - /** - * The child node is empty - * @property {int} EMPTY_CHILD - * @static - * @final - */ - const EMPTY_CHILD = 508; - - /** - * The node is not of instance type Node - * @property {int} INVALID_NODE - * @static - * @final - */ - const INVALID_NODE = 509; - - /** - * Look-up for error messages - * @property {Dictionary} messages - * @private - * @static - */ - private static $messages = array( - self::EMPTY_ATTRIBUTE_NAME => 'Attribute names cannot be empty', - self::EMPTY_ATTRIBUTE_VALUE => 'Attribute values cannot be empty', - self::INVALID_SETTER => 'Cannot set the property because name is invalid', - self::INVALID_GETTER => 'Cannot get the property because name is invalid', - self::INVALID_TAG => 'Not a valid HTML5 tag name', - self::EMPTY_NODE_TAG => 'Node tag is empty', - self::EMPTY_PARENT => 'The parent cannot be empty', - self::OUT_OF_BOUNDS => 'The index is out of bounds', - self::EMPTY_CHILD => 'Cannot addChild an empty child node', - self::INVALID_NODE => 'Child node must be a valid tag' - ); - - /** - * The label for an error that is unknown or unfound in messages - * @property {String} UNKNOWN - * @static - * @final - */ - const UNKNOWN = 'Unknown error'; - - public function __construct($code, $data='') - { - $message = (isset(self::$messages[$code]) ? self::$messages[$code]: self::UNKNOWN) - . ($data ? ' : ' . $data : $data); - parent::__construct($message, $code); - } - } -} +{ + /** + * Exceptions with using the HTML5 API. + * + * try + * { + * html('invalid', 'something'); + * } + * catch(Canteen\HTML5\HTML5Error $e) + * { + * $e->getMessage(); + * } + * + * @class HTML5Error + * @extends Exception + * @constructor + * @param {int} code The code of the error + * @param {String} [data=''] Additional data to associate with this error + */ + class HTML5Error extends \Exception + { + /** + * The database connection failed + * @property {int} EMPTY_ATTRIBUTE_NAME + * @static + * @final + */ + const EMPTY_ATTRIBUTE_NAME = 500; -?> \ No newline at end of file + /** + * The alias for a database is invalid + * @property {int} EMPTY_ATTRIBUTE_VALUE + * @static + * @final + */ + const EMPTY_ATTRIBUTE_VALUE = 501; + + /** + * The database name we're trying to switch to is invalid + * @property {int} INVALID_SETTER + * @static + * @final + */ + const INVALID_SETTER = 502; + + /** + * The mysql where trying to execute was a problem + * @property {int} INVALID_GETTER + * @static + * @final + */ + const INVALID_GETTER = 503; + + /** + * The html tag name is invalid + * @property {int} INVALID_TAG + * @static + * @final + */ + const INVALID_TAG = 504; + + /** + * When trying to create a node, the name is empty + * @property {int} EMPTY_NODE_TAG + * @static + * @final + */ + const EMPTY_NODE_TAG = 505; + + /** + * The parent cannot be empty + * @property {int} EMPTY_PARENT + * @static + * @final + */ + const EMPTY_PARENT = 506; + + /** + * THe addChildAt is out of bounds + * @property {int} OUT_OF_BOUNDS + * @static + * @final + */ + const OUT_OF_BOUNDS = 507; + + /** + * The child node is empty + * @property {int} EMPTY_CHILD + * @static + * @final + */ + const EMPTY_CHILD = 508; + + /** + * The node is not of instance type Node + * @property {int} INVALID_NODE + * @static + * @final + */ + const INVALID_NODE = 509; + + /** + * Look-up for error messages + * @property {Dictionary} messages + * @private + * @static + */ + private static $messages = array( + self::EMPTY_ATTRIBUTE_NAME => 'Attribute names cannot be empty', + self::EMPTY_ATTRIBUTE_VALUE => 'Attribute values cannot be empty', + self::INVALID_SETTER => 'Cannot set the property because name is invalid', + self::INVALID_GETTER => 'Cannot get the property because name is invalid', + self::INVALID_TAG => 'Not a valid HTML5 tag name', + self::EMPTY_NODE_TAG => 'Node tag is empty', + self::EMPTY_PARENT => 'The parent cannot be empty', + self::OUT_OF_BOUNDS => 'The index is out of bounds', + self::EMPTY_CHILD => 'Cannot addChild an empty child node', + self::INVALID_NODE => 'Child node must be a valid tag' + ); + + /** + * The label for an error that is unknown or unfound in messages + * @property {String} UNKNOWN + * @static + * @final + */ + const UNKNOWN = 'Unknown error'; + + public function __construct($code, $data='') + { + $message = (isset(self::$messages[$code]) ? self::$messages[$code]: self::UNKNOWN) + . ($data ? ' : ' . $data : $data); + parent::__construct($message, $code); + } + } +} diff --git a/src/Node.php b/src/Node.php index 02d8cdf..129bafa 100644 --- a/src/Node.php +++ b/src/Node.php @@ -1,341 +1,343 @@ isEmpty($tag)) - { - throw new HTML5Error(HTML5Error::EMPTY_NODE_TAG); - } - $this->_parent = null; - $this->_tag = $tag; - $this->_attributes = array(); + /** + * A generic html tag with any children or closing tag. (e.g., img, br, hr). + * Do not initiate this class directly, use the `html()` function: + * + * echo html('br'); + * + * @class Node + * @extends Proto + * @constructor + * @param {String} [tag=null] The name of the tag + * @param {Array|String} [attributes=null] The collection of tag attributes + */ + class Node extends Proto + { + /** + * The string name of the tag + * @property {String} _tag + * @protected + */ + protected $_tag; - if (isset(Specification::$TAGS[$tag])) - { - $this->_validAttrs = array_merge( - Specification::$TAGS[$tag], - Specification::$ATTRIBUTES - ); - } - else - { - $this->_validAttrs = array(); - } - - if ($attributes !== null) - { - if (is_string($attributes)) - { - $attributes = Attribute::shorthand($attributes); - } - - if (is_array($attributes)) - { - $this->setAttributes($attributes); - } - } - } - - /** - * Returns the parent node of this node, if - * a parent exists. If no parent exists, - * this function returns null. - * @method getParent - * @private - * @return {NodeContainer} The parent node object - */ - private function getParent() - { - return $this->_parent; - } - - /** - * Sets the parent of this Node. Note that this - * function is protected and can only be called by - * classes that extend Node. The parent cannot - * be null; this function will throw an Exception - * if the parent node is empty. - * @method setParent - * @protected - * @param {NodeContainer} [parent=null] The parent container node - */ - protected function setParent(NodeContainer $parent = null) - { - if ($this->isEmpty($parent)) - { - throw new HTML5Error(HTML5Error::EMPTY_PARENT); - } - $this->_parent = $parent; - } - - /** - * Given a name and value pair, sets an attribute on this Node. - * The name and value cannot be empty; if so, this function - * will throw an Exception. Note if the attribute already exists - * and the caller wants to set an attribute of the same name, - * this function will not create a new Attribute, but rather - * update the value of the existing named attribute. - * - * @method setAttribute - * @param {String} [name=null] The name of the attribute to add - * @param {String} [value=null] The value of the attribute - * @param {Node} The instance of this node - */ - public function setAttribute($name = null, $value = null) - { - if ($this->isEmpty($name)) - { - throw new HTML5Error(HTML5Error::EMPTY_ATTRIBUTE_NAME); - } - foreach($this->_attributes as $i=>$attribute) - { - if ($attribute->name === $name) - { - if (!$this->isEmpty($value)) - $attribute->value = $value; - else - unset($this->_attributes[$i]); - return $this; - } - } - $this->_attributes[] = new Attribute($name, $value); - return $this; - } - - /** - * Fetch and attribute by name from this Node. The attribute - * name cannot be null; if so, this function will throw an - * Exception. - * @method getAttribute - * @param {String} [name=null] The name of the attribute to fetch - * @return {String} The attribute's value, if any or null - */ - protected function getAttribute($name = null) - { - $returnAttr = null; + /** + * The collection of Attributes objects + * @property {Array} _attributes + * @protected + */ + protected $_attributes; - if ($this->isEmpty($name)) - { - throw new HTML5Error(HTML5Error::EMPTY_ATTRIBUTE_NAME); - } - foreach($this->_attributes as $attribute) - { - if ($attribute->name === $name) - { - $returnAttr = $attribute->value; - break; - } - } - return $returnAttr; - } - - /** - * Set the list of all attributes. - * @method setAttributes - * @param {Dictionary} values An attributes array(name=>value, name=>value) - * @return {Node} The instance of this Node - */ - public function setAttributes($values) - { - if (is_array($values)) - { - foreach($values as $name=>$value) - { - $this->setAttribute($name, $value); - } - return $this; - } - } - - /** - * Set the a data-* HTML5 Attribute - * @param {String} name The name of the data, for instance "id" is an attribute "data-id" - * @param {String} value The value of the attribute - * @return {Node} The instance of this Node - */ - public function setData($name, $value) - { - return $this->setAttribute('data-'.$name, $value); - } - - /** - * Add this child to a node container at the end - * @method appendTo - * @param {NodeContainer} container The node container to add to - * @return {Node} The instance of this Node - */ - public function appendTo(NodeContainer $container) - { - $container->addChild($this); - return $this; - } - - /** - * Add this child to the beginning of a node container - * @method prependTo - * @param {NodeContainer} container The node container to prepend to to - * @return {Node} The instance of this Node - */ - public function prependTo(NodeContainer $container) - { - $container->addChildAt($this, 0); - return $this; - } - - /** - * Get the data-* HTML5 attribute value, if set - * @method getData - * @param {String} name The name of the data attribute - * @return {String} The value of the data - */ - public function getData($name) - { - return $this->getAttribute('data-'.$name); - } - - /** - * Write to HTML - * @method __toString - * @return {String} The string representation of this HTML node - */ - public function __toString() - { - return $this->writeOpen(); - } - - /** - * Start the writing the tag - * @method writeOpen - * @protected - * @param {Boolean} [selfClose=true] If the tag is a self closing tag (e.g., br, img, hr) - * @return {String} The buffer of HTML - */ - protected function writeOpen($selfClose=true) - { - $buffer = '<'; - $buffer .= $this->_tag; - foreach($this->_attributes as $attribute) - { - $buffer .= (string)$attribute; - } - $buffer .= ($selfClose ? ' />' : '>'); - return $buffer; - } - - /** - * General purpose getter to get attribute values - * @method __get - * @param {String} name The name of the property to set - */ - public function __get($name) - { - if (in_array($name, $this->_validAttrs) || strpos($name, 'data-') === 0) - { - return $this->getAttribute($name); - } - return parent::__get($name); - } + /** + * The parent node, if any + * @property {NodeContainer} _parent + * @protected + */ + protected $_parent; - /** - * General purpose setter to set attribute values - * @method __set - * @param {String} name The name of the attribute - * @param {String} value The value of the attribute - */ - public function __set($name, $value) - { - if (in_array($name, $this->_validAttrs) || strpos($name, 'data-') === 0) - { - return $this->setAttribute($name, $value); - } - } + /** + * The collection of valid attributes names for given tag + * @property {Array} _validAttrs + * @protected + */ + protected $_validAttrs; - /** - * See if a property exists - * @method __isset - * @param {String} name The name of the attribute - */ - public function __isset($name) - { - return in_array($name, $this->_validAttrs) || parent::__isset($name); - } - - /** - * Checks if a variable is really "empty". Code borrowed from PHP.net at - * http://us3.php.net/manual/en/function.empty.php#90767 because we were - * previously using empty() to see if a variable is empty or not. But - * empty() dosen't work for attributes that have a value of "0", so we need - * something more robust here. - * - * @method isEmpty - * @protected - * @param {mixed} var The variable to check for empty on - */ - protected function isEmpty($var) - { - return (!isset($var) || is_null($var) || - (!is_object($var) && is_string($var) && trim($var) == '' && !is_bool($var)) || - (is_bool($var) && $var === false) || - (is_array($var) && empty($var))); - } - } + public function __construct($tag = null, $attributes = null) + { + if ($this->isEmpty($tag)) + { + throw new HTML5Error(HTML5Error::EMPTY_NODE_TAG); + } + $this->_parent = null; + $this->_tag = $tag; + $this->_attributes = array(); + + if (isset(Specification::$TAGS[$tag])) + { + $this->_validAttrs = array_merge( + Specification::$TAGS[$tag], + Specification::$ATTRIBUTES + ); + } + else + { + $this->_validAttrs = array(); + } + + if ($attributes !== null) + { + if (is_string($attributes)) + { + $attributes = Attribute::shorthand($attributes); + } + + if (is_array($attributes)) + { + $this->setAttributes($attributes); + } + } + } + + /** + * Returns the parent node of this node, if + * a parent exists. If no parent exists, + * this function returns null. + * @method getParent + * @private + * @return {NodeContainer} The parent node object + */ + private function getParent() + { + return $this->_parent; + } + + /** + * Sets the parent of this Node. Note that this + * function is protected and can only be called by + * classes that extend Node. The parent cannot + * be null; this function will throw an Exception + * if the parent node is empty. + * @method setParent + * @protected + * @param {NodeContainer} [parent=null] The parent container node + */ + protected function setParent(NodeContainer $parent = null) + { + if ($this->isEmpty($parent)) + { + throw new HTML5Error(HTML5Error::EMPTY_PARENT); + } + $this->_parent = $parent; + } + + /** + * Given a name and value pair, sets an attribute on this Node. + * The name and value cannot be empty; if so, this function + * will throw an Exception. Note if the attribute already exists + * and the caller wants to set an attribute of the same name, + * this function will not create a new Attribute, but rather + * update the value of the existing named attribute. + * + * @method setAttribute + * @param {String} [name=null] The name of the attribute to add + * @param {String} [value=null] The value of the attribute + * @param {Node} The instance of this node + */ + public function setAttribute($name = null, $value = null) + { + if ($this->isEmpty($name)) + { + throw new HTML5Error(HTML5Error::EMPTY_ATTRIBUTE_NAME); + } + foreach ($this->_attributes as $i=>$attribute) + { + if ($attribute->name === $name) + { + if (!$this->isEmpty($value)) + { + $attribute->value = $value; + } + else + { + unset($this->_attributes[$i]); + } + return $this; + } + } + $this->_attributes[] = new Attribute($name, $value); + return $this; + } + + /** + * Fetch and attribute by name from this Node. The attribute + * name cannot be null; if so, this function will throw an + * Exception. + * @method getAttribute + * @param {String} [name=null] The name of the attribute to fetch + * @return {String} The attribute's value, if any or null + */ + protected function getAttribute($name = null) + { + $returnAttr = null; + + if ($this->isEmpty($name)) + { + throw new HTML5Error(HTML5Error::EMPTY_ATTRIBUTE_NAME); + } + foreach ($this->_attributes as $attribute) + { + if ($attribute->name === $name) + { + $returnAttr = $attribute->value; + break; + } + } + return $returnAttr; + } + + /** + * Set the list of all attributes. + * @method setAttributes + * @param {Dictionary} values An attributes array(name=>value, name=>value) + * @return {Node} The instance of this Node + */ + public function setAttributes($values) + { + if (is_array($values)) + { + foreach ($values as $name=>$value) + { + $this->setAttribute($name, $value); + } + return $this; + } + } + + /** + * Set the a data-* HTML5 Attribute + * @param {String} name The name of the data, for instance "id" is an attribute "data-id" + * @param {String} value The value of the attribute + * @return {Node} The instance of this Node + */ + public function setData($name, $value) + { + return $this->setAttribute('data-'.$name, $value); + } + + /** + * Add this child to a node container at the end + * @method appendTo + * @param {NodeContainer} container The node container to add to + * @return {Node} The instance of this Node + */ + public function appendTo(NodeContainer $container) + { + $container->addChild($this); + return $this; + } + + /** + * Add this child to the beginning of a node container + * @method prependTo + * @param {NodeContainer} container The node container to prepend to to + * @return {Node} The instance of this Node + */ + public function prependTo(NodeContainer $container) + { + $container->addChildAt($this, 0); + return $this; + } + + /** + * Get the data-* HTML5 attribute value, if set + * @method getData + * @param {String} name The name of the data attribute + * @return {String} The value of the data + */ + public function getData($name) + { + return $this->getAttribute('data-' . $name); + } + + /** + * Write to HTML + * @method __toString + * @return {String} The string representation of this HTML node + */ + public function __toString() + { + return $this->writeOpen(); + } + + /** + * Start the writing the tag + * @method writeOpen + * @protected + * @param {Boolean} [selfClose=true] If the tag is a self closing tag (e.g., br, img, hr) + * @return {String} The buffer of HTML + */ + protected function writeOpen($selfClose=true) + { + $buffer = '<'; + $buffer .= $this->_tag; + foreach ($this->_attributes as $attribute) + { + $buffer .= (string)$attribute; + } + $buffer .= ($selfClose ? ' />' : '>'); + return $buffer; + } + + /** + * General purpose getter to get attribute values + * @method __get + * @param {String} name The name of the property to set + */ + public function __get($name) + { + if (in_array($name, $this->_validAttrs) || strpos($name, 'data-') === 0) + { + return $this->getAttribute($name); + } + return parent::__get($name); + } + + /** + * General purpose setter to set attribute values + * @method __set + * @param {String} name The name of the attribute + * @param {String} value The value of the attribute + */ + public function __set($name, $value) + { + if (in_array($name, $this->_validAttrs) || strpos($name, 'data-') === 0) + { + return $this->setAttribute($name, $value); + } + } + + /** + * See if a property exists + * @method __isset + * @param {String} name The name of the attribute + */ + public function __isset($name) + { + return in_array($name, $this->_validAttrs) || parent::__isset($name); + } + + /** + * Checks if a variable is really "empty". Code borrowed from PHP.net at + * http://us3.php.net/manual/en/function.empty.php#90767 because we were + * previously using empty() to see if a variable is empty or not. But + * empty() dosen't work for attributes that have a value of "0", so we need + * something more robust here. + * + * @method isEmpty + * @protected + * @param {mixed} var The variable to check for empty on + */ + protected function isEmpty($var) + { + return (!isset($var) || is_null($var) || + (!is_object($var) && is_string($var) && trim($var) == '' && !is_bool($var)) || + (is_bool($var) && $var === false) || + (is_array($var) && empty($var))); + } + } } - -?> \ No newline at end of file diff --git a/src/NodeContainer.php b/src/NodeContainer.php index fbbd8e4..24b5494 100644 --- a/src/NodeContainer.php +++ b/src/NodeContainer.php @@ -1,237 +1,235 @@ isEmpty($tag)) - { - throw new HTML5Error(HTML5Error::EMPTY_NODE_TAG); - } - parent::__construct($tag, $attributes); - - $this->_children = array(); - - if (!$this->isEmpty($children)) - { - if (!is_array($children)) - { - $children = array($children); - } - if (is_array($children)) - { - foreach($children as $child) - { - $this->addChild($child); - } - } - } - } + public function __construct($tag = null, $children = null, $attributes = null) + { + if ($this->isEmpty($tag)) + { + throw new HTML5Error(HTML5Error::EMPTY_NODE_TAG); + } + parent::__construct($tag, $attributes); - /** - * Add's a child to this NodeContainer. The child to add cannot be null. - * @method addChild - * @param {Node|String|Number|Boolean} childNode The child Node to add - * @return {NodeContainer} The instance of this container - */ - public function addChild($childNode) - { - array_push($this->_children, $this->prepareChild($childNode)); - return $this; - } - - /** - * Add a child at a specific index - * @method addChildAt - * @param {Node|String|Number|Boolean} childNode The child Node to add - * @param {int} index The index to add child at, 0 is top - * @return {NodeContainer} The instance of this container - */ - public function addChildAt($childNode, $index) - { - if ($index < 0) - { - throw new HTML5Error(HTML5Error::OUT_OF_BOUNDS, $index); - } - $childNode = $this->prepareChild($childNode); - if ($index == 0) - { - array_unshift($this->_children, $childNode); - } - else if ($index > (count($this->_children) - 1)) - { - $this->addChild($childNode); - } - else - { - array_splice($this->_children, $index , 0, array($childNode)); - } - return $this; - } - - /** - * Before adding a child, we should do some checking for basic types - * and convert it into a more useable Node object. - * @method prepareChild - * @protected - * @param {Node|String|Number|Boolean} childNode The child node to add - * @return {Node} The child node - */ - protected function prepareChild($childNode) - { - if ($this->isEmpty($childNode)) - { - throw new HTML5Error(HTML5Error::EMPTY_CHILD); - } - if (is_bool($childNode)) - { - $childNode = new Text($childNode ? 'true' : 'false'); - } - else if (is_string($childNode) || is_numeric($childNode)) - { - $childNode = new Text($childNode); - } - if (!($childNode instanceof Node)) - { - throw new HTML5Error(HTML5Error::INVALID_NODE); - } - $childNode->setParent($this); - return $childNode; - } + $this->_children = array(); - /** - * Removes the first instance of child from this. - * Once the first instance of the child - * is removed, this function will return. It returns - * true if a child was removed and false if no child - * was removed. - * @method removeChild - * @param {Node} [childNode=null] The node to remove - * @return {Boolean} If successfully removed - */ - public function removeChild(Node $childNode = null) - { - if ($this->isEmpty($childNode)) - { - throw new HTML5Error(HTML5Error::EMPTY_CHILD); - } + if (!$this->isEmpty($children)) + { + if (!is_array($children)) + { + $children = array($children); + } + if (is_array($children)) + { + foreach ($children as $child) + { + $this->addChild($child); + } + } + } + } - for($i = 0; $i < count($this->_children); $i++) - { - $child = $this->_children[$i]; - if ($child === $childNode) - { - unset($this->_children[$i]); - return true; - } - } - return false; - } - - /** - * Remove a child as a specific index - * @method removeChildAt - * @param {int} index The index to remove child at - * @return {NodeContainer} The instance of the node container - */ - public function removeChildAt($index) - { - if ($index >= $this->_children || $index < 0) - { - throw new HTML5Error(HTML5Error::OUT_OF_BOUNDS, $index); - } - array_splice($this->_children, $index, 1); - return $this; - } + /** + * Add's a child to this NodeContainer. The child to add cannot be null. + * @method addChild + * @param {Node|String|Number|Boolean} childNode The child Node to add + * @return {NodeContainer} The instance of this container + */ + public function addChild($childNode) + { + array_push($this->_children, $this->prepareChild($childNode)); + return $this; + } - /** - * Removes all children attached to this Node container - * @method removeChildren - * @return {NodeContainer} The instance of the node container - */ - public function removeChildren() - { - unset($this->_children); - $this->_children = array(); - return $this; - } + /** + * Add a child at a specific index + * @method addChildAt + * @param {Node|String|Number|Boolean} childNode The child Node to add + * @param {int} index The index to add child at, 0 is top + * @return {NodeContainer} The instance of this container + */ + public function addChildAt($childNode, $index) + { + if ($index < 0) + { + throw new HTML5Error(HTML5Error::OUT_OF_BOUNDS, $index); + } + $childNode = $this->prepareChild($childNode); + if ($index == 0) + { + array_unshift($this->_children, $childNode); + } + else if ($index > (count($this->_children) - 1)) + { + $this->addChild($childNode); + } + else + { + array_splice($this->_children, $index , 0, array($childNode)); + } + return $this; + } - /** - * Returns an array of all children attached to this Node container. - * @method getChildren - * @return {Array} The collection of Node objects - */ - public function getChildren() - { - return $this->_children; - } + /** + * Before adding a child, we should do some checking for basic types + * and convert it into a more useable Node object. + * @method prepareChild + * @protected + * @param {Node|String|Number|Boolean} childNode The child node to add + * @return {Node} The child node + */ + protected function prepareChild($childNode) + { + if ($this->isEmpty($childNode)) + { + throw new HTML5Error(HTML5Error::EMPTY_CHILD); + } + if (is_bool($childNode)) + { + $childNode = new Text($childNode ? 'true' : 'false'); + } + else if (is_string($childNode) || is_numeric($childNode)) + { + $childNode = new Text($childNode); + } + if (!($childNode instanceof Node)) + { + throw new HTML5Error(HTML5Error::INVALID_NODE); + } + $childNode->setParent($this); + return $childNode; + } - /** - * Gets a child of this Node container at given - * index. If no index is passed in, getChild() - * will return the child at index zero (0). - * @method getChildAt - * @param {int} [index=0] The index to fetch child Node at - * @return {Node} The child Node - */ - public function getChildAt($index = 0) - { - return $this->_children[$index]; - } - - /** - * Close the writing of this container as HTML - * @method writeClose - * @protected - * @return {String} The closing HTML tag element - */ - protected function writeClose() - { - return "_tag . ">"; - } + /** + * Removes the first instance of child from this. + * Once the first instance of the child + * is removed, this function will return. It returns + * true if a child was removed and false if no child + * was removed. + * @method removeChild + * @param {Node} [childNode=null] The node to remove + * @return {Boolean} If successfully removed + */ + public function removeChild(Node $childNode = null) + { + if ($this->isEmpty($childNode)) + { + throw new HTML5Error(HTML5Error::EMPTY_CHILD); + } - /** - * Write to HTML - * @method __toString - * @return {String} The string representation of this HTML node - */ - public function __toString() - { - $buffer = $this->writeOpen(false); - foreach($this->_children as $child) - { - $buffer .= $child->__toString(); - } - $buffer .= $this->writeClose(); + for ($i = 0; $i < count($this->_children); $i++) + { + $child = $this->_children[$i]; + if ($child === $childNode) + { + unset($this->_children[$i]); + return true; + } + } + return false; + } - return $buffer; - } - } + /** + * Remove a child as a specific index + * @method removeChildAt + * @param {int} index The index to remove child at + * @return {NodeContainer} The instance of the node container + */ + public function removeChildAt($index) + { + if ($index >= $this->_children || $index < 0) + { + throw new HTML5Error(HTML5Error::OUT_OF_BOUNDS, $index); + } + array_splice($this->_children, $index, 1); + return $this; + } + + /** + * Removes all children attached to this Node container + * @method removeChildren + * @return {NodeContainer} The instance of the node container + */ + public function removeChildren() + { + unset($this->_children); + $this->_children = array(); + return $this; + } + + /** + * Returns an array of all children attached to this Node container. + * @method getChildren + * @return {Array} The collection of Node objects + */ + public function getChildren() + { + return $this->_children; + } + + /** + * Gets a child of this Node container at given + * index. If no index is passed in, getChild() + * will return the child at index zero (0). + * @method getChildAt + * @param {int} [index=0] The index to fetch child Node at + * @return {Node} The child Node + */ + public function getChildAt($index = 0) + { + return $this->_children[$index]; + } + + /** + * Close the writing of this container as HTML + * @method writeClose + * @protected + * @return {String} The closing HTML tag element + */ + protected function writeClose() + { + return "_tag . ">"; + } + + /** + * Write to HTML + * @method __toString + * @return {String} The string representation of this HTML node + */ + public function __toString() + { + $buffer = $this->writeOpen(false); + foreach ($this->_children as $child) + { + $buffer .= $child->__toString(); + } + $buffer .= $this->writeClose(); + + return $buffer; + } + } } - -?> \ No newline at end of file diff --git a/src/SimpleList.php b/src/SimpleList.php index 6e81b8e..ddae704 100644 --- a/src/SimpleList.php +++ b/src/SimpleList.php @@ -1,72 +1,70 @@ addChild($child); - } - } - } - - /** - * Override for the prepareChild method on NodeContainer which - * wraps each elements in a list item - * @method prepareChild - * @protected - * @param {Node|String|Number|Boolean|Array} childNode The child node to add, an array will get converted into another list elements. - * @return {Node} The child node - */ - protected function prepareChild($childNode) - { - // Recursively create new lists for each array - if (is_array($childNode)) - { - $list = new SimpleList($childNode, null, $this->_tag); - return $this->prepareChild($list); - } - else - { - $childNode = parent::prepareChild($childNode); - return html('li', $childNode); - } - } - } + if ($elements != null) + { + assert(is_array($elements)); + + foreach ($elements as $child) + { + $this->addChild($child); + } + } + } + + /** + * Override for the prepareChild method on NodeContainer which + * wraps each elements in a list item + * @method prepareChild + * @protected + * @param {Node|String|Number|Boolean|Array} childNode The child node to add, an array will get converted into another list elements. + * @return {Node} The child node + */ + protected function prepareChild($childNode) + { + // Recursively create new lists for each array + if (is_array($childNode)) + { + $list = new SimpleList($childNode, null, $this->_tag); + return $this->prepareChild($list); + } + else + { + $childNode = parent::prepareChild($childNode); + return html('li', $childNode); + } + } + } } - -?> \ No newline at end of file diff --git a/src/Specification.php b/src/Specification.php index 95cdc71..09577aa 100644 --- a/src/Specification.php +++ b/src/Specification.php @@ -1,413 +1,429 @@ array( - 'href', - 'hreflang', - 'media', - 'rel', - 'target', - 'type' - ), - 'abbr' => array(), - 'address' => array(), - 'area' => array( - 'alt', - 'coords', - 'href', - 'hreflang', - 'media', - 'rel', - 'shape', - 'target', - 'type' - ), - 'article' => array(), - 'aside' => array(), - 'audio' => array( - 'autoplay', - 'controls', - 'loop', - 'muted', - 'preload', - 'src' - ), - 'b' => array(), - 'base' => array( - 'href', - 'target' - ), - 'bdo' => array(), - 'blockquote' => array('cite'), - 'body' => array(), - 'br' => array(), - 'button' => array( - 'autofocus', - 'disabled', - 'form', - 'formaction', - 'formenctype', - 'formmethod', - 'formnovalidate', - 'formtarget', - 'name', - 'type', - 'value' - ), - 'canvas' => array( - 'height', - 'width' - ), - 'caption' => array(), - 'cite' => array(), - 'code' => array(), - 'col' => null, - 'colgroup' => array('span'), - 'command' => array( - 'checked', - 'disabled', - 'icon', - 'label', - 'radiogroup', - 'type' - ), - 'datalist' => array(), - 'dd' => array(), - 'del' => array( - 'cite', - 'datetime' - ), - 'dfn' => array(), - 'div' => array(), - 'dl' => array(), - 'dt' => array(), - 'em' => array(), - 'embed' => array( - 'height', - 'src', - 'type', - 'width' - ), - 'fieldset' => array( - 'disabled', - 'form_id', - 'text' - ), - 'figcaption' => array(), - 'figure' => array(), - 'footer' => array(), - 'form' => array( - 'accept', - 'accept-charset', - 'action', - 'autocomplete', - 'enctype', - 'method', - 'name', - 'novalidate', - 'target' - ), - 'h1' => array(), - 'h2' => array(), - 'h3' => array(), - 'h4' => array(), - 'h5' => array(), - 'h6' => array(), - 'head' => array(), - 'header' => array(), - 'hgroup' => array(), - 'hr' => array(), - 'html' => array('manifest'), - 'img' => array( - 'alt', - 'crossorigin', - 'height', - 'src', - 'usemap', - 'width' - ), - 'i' => array(), - 'iframe' => array( - 'src', - 'srcdoc', - 'name', - 'width', - 'height' - ), - 'input' => array( - 'accept', - 'alt', - 'autocomplete', - 'autofocus', - 'checked', - 'disabled', - 'form', - 'formaction', - 'formenctype', - 'formmethod', - 'formnovalidate', - 'formtarget', - 'height', - 'list', - 'max', - 'maxlength', - 'min', - 'multiple', - 'name', - 'pattern', - 'placeholder', - 'readonly', - 'required', - 'size', - 'src', - 'step', - 'type', - 'value', - 'width' - ), - 'keygen' => array( - 'autofocus', - 'challenge', - 'disabled', - 'form', - 'keytype', - 'name' - ), - 'label' => array( - 'for', - 'form' - ), - 'legend' => array(), - 'li' => array(), - 'link' => array( - 'href', - 'hreflang', - 'media', - 'rel', - 'sizes', - 'type' - ), - 'map' => array('name'), - 'mark' => array(), - 'menu' => array(), - 'meta' => array( - 'charset', - 'content', - 'http-equiv', - 'name' - ), - 'meter' => array( - 'form', - 'heigh', - 'low', - 'max', - 'min', - 'optimum', - 'value' - ), - 'nav' => array(), - 'noscript' => array(), - 'object' => array( - 'data', - 'form', - 'height', - 'name', - 'type', - 'usemap', - 'width' - ), - 'ol' => array( - 'reversed', - 'start', - 'type' - ), - 'optgroup' => array( - 'disabled', - 'label' - ), - 'option' => array( - 'disabled', - 'label', - 'selected', - 'value' - ), - 'output' => array( - 'for', - 'form', - 'name' - ), - 'p' => array(), - 'param' => array( - 'name', - 'value' - ), - 'pre' => array(), - 'progress' => array( - 'max', - 'value' - ), - 'q' => array('cite'), - 'rp' => array(), - 'rt' => array(), - 'ruby' => array(), - 's' => array(), - 'sample' => array(), - 'script' => array( - 'async', - 'charset', - 'defer', - 'src', - 'type' - ), - 'section' => array(), - 'select' => array( - 'autofocus', - 'disabled', - 'form', - 'multiple', - 'name', - 'required', - 'size' - ), - 'small' => array(), - 'source' => array('media', - 'src', - 'type' - ), - 'span' => array(), - 'strong' => array(), - 'style' => array('media', - 'scoped', - 'type' - ), - 'sub' => array(), - 'table' => array('border'), - 'tbody' => array(), - 'td' => array( - 'colspan', - 'headers', - 'scope' - ), - 'textarea' => array( - 'autofocus', - 'cols', - 'disabled', - 'form', - 'maxlength', - 'name', - 'placeholder', - 'readonly', - 'required', - 'row', - 'wrap' - ), - 'tfoot' => array(), - 'th' => array( - 'colspan', - 'headers', - 'rowspan', - 'scope' - ), - 'thead' => array(), - 'time' => array('datetime'), - 'title' => array(), - 'tr' => array(), - 'track' => array( - 'default', - 'kind', - 'label', - 'src', - 'srclang' - ), - 'u' => array(), - 'ul' => array(), - 'var' => array(), - 'video' => array( - 'autoplay', - 'controls', - 'height', - 'loop', - 'muted', - 'poster', - 'preload', - 'src', - 'width' - ), - 'wbr' => null - ); +{ + /** + * The HTML5 Specification + * + * @class Specification + * @constructor + */ + class Specification + { + /** + * The list of all tags and their specific attributes + * @property {array} TAGS + * @final + * @readOnly + * @static + */ + public static $TAGS = array( + 'a' => array( + 'href', + 'hreflang', + 'media', + 'rel', + 'target', + 'type' + ), + 'abbr' => array(), + 'address' => array(), + 'area' => array( + 'alt', + 'coords', + 'href', + 'hreflang', + 'media', + 'rel', + 'shape', + 'target', + 'type' + ), + 'article' => array(), + 'aside' => array(), + 'audio' => array( + 'autoplay', + 'controls', + 'loop', + 'muted', + 'preload', + 'src' + ), + 'b' => array(), + 'base' => array( + 'href', + 'target' + ), + 'bdo' => array(), + 'blockquote' => array( + 'cite' + ), + 'body' => array(), + 'br' => array(), + 'button' => array( + 'autofocus', + 'disabled', + 'form', + 'formaction', + 'formenctype', + 'formmethod', + 'formnovalidate', + 'formtarget', + 'name', + 'type', + 'value' + ), + 'canvas' => array( + 'height', + 'width' + ), + 'caption' => array(), + 'cite' => array(), + 'code' => array(), + 'col' => null, + 'colgroup' => array( + 'span' + ), + 'command' => array( + 'checked', + 'disabled', + 'icon', + 'label', + 'radiogroup', + 'type' + ), + 'datalist' => array(), + 'dd' => array(), + 'del' => array( + 'cite', + 'datetime' + ), + 'dfn' => array(), + 'div' => array(), + 'dl' => array(), + 'dt' => array(), + 'em' => array(), + 'embed' => array( + 'height', + 'src', + 'type', + 'width' + ), + 'fieldset' => array( + 'disabled', + 'form_id', + 'text' + ), + 'figcaption' => array(), + 'figure' => array(), + 'footer' => array(), + 'form' => array( + 'accept', + 'accept-charset', + 'action', + 'autocomplete', + 'enctype', + 'method', + 'name', + 'novalidate', + 'target' + ), + 'h1' => array(), + 'h2' => array(), + 'h3' => array(), + 'h4' => array(), + 'h5' => array(), + 'h6' => array(), + 'head' => array(), + 'header' => array(), + 'hgroup' => array(), + 'hr' => array(), + 'html' => array( + 'manifest' + ), + 'img' => array( + 'alt', + 'crossorigin', + 'height', + 'src', + 'usemap', + 'width' + ), + 'i' => array(), + 'iframe' => array( + 'src', + 'srcdoc', + 'name', + 'width', + 'height' + ), + 'input' => array( + 'accept', + 'alt', + 'autocomplete', + 'autofocus', + 'checked', + 'disabled', + 'form', + 'formaction', + 'formenctype', + 'formmethod', + 'formnovalidate', + 'formtarget', + 'height', + 'list', + 'max', + 'maxlength', + 'min', + 'multiple', + 'name', + 'pattern', + 'placeholder', + 'readonly', + 'required', + 'size', + 'src', + 'step', + 'type', + 'value', + 'width' + ), + 'keygen' => array( + 'autofocus', + 'challenge', + 'disabled', + 'form', + 'keytype', + 'name' + ), + 'label' => array( + 'for', + 'form' + ), + 'legend' => array(), + 'li' => array(), + 'link' => array( + 'href', + 'hreflang', + 'media', + 'rel', + 'sizes', + 'type' + ), + 'map' => array( + 'name' + ), + 'mark' => array(), + 'menu' => array(), + 'meta' => array( + 'charset', + 'content', + 'http-equiv', + 'name' + ), + 'meter' => array( + 'form', + 'heigh', + 'low', + 'max', + 'min', + 'optimum', + 'value' + ), + 'nav' => array(), + 'noscript' => array(), + 'object' => array( + 'data', + 'form', + 'height', + 'name', + 'type', + 'usemap', + 'width' + ), + 'ol' => array( + 'reversed', + 'start', + 'type' + ), + 'optgroup' => array( + 'disabled', + 'label' + ), + 'option' => array( + 'disabled', + 'label', + 'selected', + 'value' + ), + 'output' => array( + 'for', + 'form', + 'name' + ), + 'p' => array(), + 'param' => array( + 'name', + 'value' + ), + 'pre' => array(), + 'progress' => array( + 'max', + 'value' + ), + 'q' => array( + 'cite' + ), + 'rp' => array(), + 'rt' => array(), + 'ruby' => array(), + 's' => array(), + 'sample' => array(), + 'script' => array( + 'async', + 'charset', + 'defer', + 'src', + 'type' + ), + 'section' => array(), + 'select' => array( + 'autofocus', + 'disabled', + 'form', + 'multiple', + 'name', + 'required', + 'size' + ), + 'small' => array(), + 'source' => array( + 'media', + 'src', + 'type' + ), + 'span' => array(), + 'strong' => array(), + 'style' => array( + 'media', + 'scoped', + 'type' + ), + 'sub' => array(), + 'table' => array( + 'border' + ), + 'tbody' => array(), + 'td' => array( + 'colspan', + 'headers', + 'scope' + ), + 'textarea' => array( + 'autofocus', + 'cols', + 'disabled', + 'form', + 'maxlength', + 'name', + 'placeholder', + 'readonly', + 'required', + 'row', + 'wrap' + ), + 'tfoot' => array(), + 'th' => array( + 'colspan', + 'headers', + 'rowspan', + 'scope' + ), + 'thead' => array(), + 'time' => array( + 'datetime' + ), + 'title' => array(), + 'tr' => array(), + 'track' => array( + 'default', + 'kind', + 'label', + 'src', + 'srclang' + ), + 'u' => array(), + 'ul' => array(), + 'var' => array(), + 'video' => array( + 'autoplay', + 'controls', + 'height', + 'loop', + 'muted', + 'poster', + 'preload', + 'src', + 'width' + ), + 'wbr' => null + ); - /** - * The list of self-closing tags - * @property {array} SELF_CLOSING - * @final - * @readOnly - * @static - */ - public static $SELF_CLOSING = array( - 'area', - 'base', - 'br', - 'col', - 'command', - 'embed', - 'hr', - 'img', - 'input', - 'keygen', - 'link', - 'meta', - 'param', - 'source', - 'track', - 'wbr' - ); + /** + * The list of self-closing tags + * @property {array} SELF_CLOSING + * @final + * @readOnly + * @static + */ + public static $SELF_CLOSING = array( + 'area', + 'base', + 'br', + 'col', + 'command', + 'embed', + 'hr', + 'img', + 'input', + 'keygen', + 'link', + 'meta', + 'param', + 'source', + 'track', + 'wbr' + ); - /** - * Global valid attributes for all HTML5 tags - * See: http://www.w3.org/TR/html5/dom.html#global-attributes - * @property {Array} ATTRIBUTES - * @final - * @static - * @readOnly - */ - public static $ATTRIBUTES = array( + /** + * Global valid attributes for all HTML5 tags + * See: http://www.w3.org/TR/html5/dom.html#global-attributes + * @property {Array} ATTRIBUTES + * @final + * @static + * @readOnly + */ + public static $ATTRIBUTES = array( - // Event handler context attributes - 'onabort', 'onblur', 'oncancel', 'oncanplay', 'oncanplaythrough', - 'onchange', 'onclick', 'oncuechange', 'ondblclick', 'ondurationchange', - 'onemptied', 'onended', 'onerror', 'onfocus', 'oninput', 'oninvalid', - 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onloadeddata', - 'onloadedmetadata', 'onloadstart', 'onmousedown', 'onmouseenter', - 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', - 'onmousewheel', 'onpause', 'onplay', 'onplaying', 'onprogress', - 'onratechange', 'onreset', 'onresize', 'onscroll', 'onseeked', - 'onseeking', 'onselect', 'onshow', 'onstalled', 'onsubmit', 'onsuspend', - 'ontimeupdate', 'ontoggle', 'onvolumechange', 'onwaiting', + // Event handler context attributes + 'onabort', 'onblur', 'oncancel', 'oncanplay', 'oncanplaythrough', + 'onchange', 'onclick', 'oncuechange', 'ondblclick', 'ondurationchange', + 'onemptied', 'onended', 'onerror', 'onfocus', 'oninput', 'oninvalid', + 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onloadeddata', + 'onloadedmetadata', 'onloadstart', 'onmousedown', 'onmouseenter', + 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', + 'onmousewheel', 'onpause', 'onplay', 'onplaying', 'onprogress', + 'onratechange', 'onreset', 'onresize', 'onscroll', 'onseeked', + 'onseeking', 'onselect', 'onshow', 'onstalled', 'onsubmit', 'onsuspend', + 'ontimeupdate', 'ontoggle', 'onvolumechange', 'onwaiting', - // Allow-able on all tags - 'accesskey', 'class', 'contenteditable', 'contextmenu', 'dir', 'draggable', - 'dropzone', 'hidden', 'id', 'lang', 'spellcheck', 'style', 'tabindex', - 'title', 'translate' - ); - } -} \ No newline at end of file + // Allow-able on all tags + 'accesskey', 'class', 'contenteditable', 'contextmenu', 'dir', 'draggable', + 'dropzone', 'hidden', 'id', 'lang', 'spellcheck', 'style', 'tabindex', + 'title', 'translate' + ); + } +} diff --git a/src/Table.php b/src/Table.php index abccebc..c75abeb 100644 --- a/src/Table.php +++ b/src/Table.php @@ -1,86 +1,87 @@ 1, 'first'=>'James', 'last'=>'Smith'), - * array('id'=>2, 'first'=>'Mary', 'last'=>'Denver'), - * array('id'=>3, 'first'=>'Charlie', 'last'=>'Rose') - * ), - * array('ID', 'First Name', 'Last Name') - * ); - * - * @class Table - * @extends NodeContainer - * @constructor - * @param {Array} data The collection of Dictionary objects - * @param {Array} [headers=null] An optional collection of header labels for each value - * @param {String} [checkbox=null] If we should add a checkbox to each row, this is the name - * of the attribute to use as a value. For instance `array('id'=>2)` is - * `` - */ - class Table extends NodeContainer - { - public function __construct($data, $headers=null, $checkbox=null) - { - parent::__construct('table', null, null); - - if ($headers != null && is_array($headers)) - { - $head = html('thead'); - $this->addChild($head); - - $row = html('tr'); - - if ($checkbox != null) - { - $row->addChild(html('th', html('span', $checkbox))); - } - - foreach($headers as $header) - { - $row->addChild(html('th', $header)); - } - $head->addChild($row); - } - - $body = html('tbody'); - - foreach($data as $d) - { - $row = html('tr'); - - if ($checkbox != null) - { - $td = html('td', - html( - 'input', - 'type=checkbox name='.$checkbox.'[] value='.$d[$checkbox] - ), - 'class='.$checkbox - ); - $row->addChild($td); - } - foreach($d as $name=>$value) - { - if ($name == $checkbox) continue; - $td = html('td', $value, 'class='.$name); - $row->addChild($td); - } - $body->addChild($row); - } - $this->addChild($body); - } - } -} +{ + /** + * Convenience class for building a Table. Useful for display + * rows of data from a database or another collection + * of associative arrays. + * + * $table = new Canteen\HTML5\Table( + * array( + * array('id'=>1, 'first'=>'James', 'last'=>'Smith'), + * array('id'=>2, 'first'=>'Mary', 'last'=>'Denver'), + * array('id'=>3, 'first'=>'Charlie', 'last'=>'Rose') + * ), + * array('ID', 'First Name', 'Last Name') + * ); + * + * @class Table + * @extends NodeContainer + * @constructor + * @param {Array} data The collection of Dictionary objects + * @param {Array} [headers=null] An optional collection of header labels for each value + * @param {String} [checkbox=null] If we should add a checkbox to each row, this is the name + * of the attribute to use as a value. For instance `array('id'=>2)` is + * `` + */ + class Table extends NodeContainer + { + public function __construct($data, $headers=null, $checkbox=null) + { + parent::__construct('table', null, null); -?> \ No newline at end of file + if ($headers != null && is_array($headers)) + { + $head = html('thead'); + $this->addChild($head); + + $row = html('tr'); + + if ($checkbox != null) + { + $row->addChild(html('th', html('span', $checkbox))); + } + + foreach ($headers as $header) + { + $row->addChild(html('th', $header)); + } + $head->addChild($row); + } + + $body = html('tbody'); + + foreach ($data as $d) + { + $row = html('tr'); + + if ($checkbox != null) + { + $td = html('td', + html( + 'input', + 'type=checkbox name=' . $checkbox . '[] value='.$d[$checkbox] + ), + 'class=' . $checkbox + ); + $row->addChild($td); + } + foreach ($d as $name=>$value) + { + if ($name == $checkbox) + { + continue; + } + $td = html('td', $value, 'class=' . $name); + $row->addChild($td); + } + $body->addChild($row); + } + $this->addChild($body); + } + } +} diff --git a/src/Text.php b/src/Text.php index a6b75cc..da39688 100644 --- a/src/Text.php +++ b/src/Text.php @@ -5,35 +5,33 @@ */ namespace Canteen\HTML5 { - /** - * Special Node representing plain text. Do not initiate this - * class directly, it is created whenever a text is passed into - * a container tag: - * - * echo html('p', 'Some Text Here'); - * - * @class Text - * @extends Node - * @constructor - * @param {String} text the plain text string - */ - class Text extends Node - { - public function __construct($text) - { - parent::__construct($text); - } - - /** - * Write to HTML - * @method __toString - * @return {String} The string representation of this HTML node - */ - public function __toString() - { - return $this->_tag; - } - } -} + /** + * Special Node representing plain text. Do not initiate this + * class directly, it is created whenever a text is passed into + * a container tag: + * + * echo html('p', 'Some Text Here'); + * + * @class Text + * @extends Node + * @constructor + * @param {String} text the plain text string + */ + class Text extends Node + { + public function __construct($text) + { + parent::__construct($text); + } -?> \ No newline at end of file + /** + * Write to HTML + * @method __toString + * @return {String} The string representation of this HTML node + */ + public function __toString() + { + return $this->_tag; + } + } +} diff --git a/src/html.php b/src/html.php index f92b002..7ac86f1 100644 --- a/src/html.php +++ b/src/html.php @@ -1,25 +1,25 @@ -