From 946984b6e4fd237bfdbd9da4474dc92c43fd5144 Mon Sep 17 00:00:00 2001 From: Matt Karl Date: Fri, 31 Oct 2014 12:34:20 -0400 Subject: [PATCH] Fixed example, optional global function, added Specification --- composer.json | 7 +- examples/document.php | 6 +- src/Document.php | 7 +- src/HTML5.php | 165 +++++++++++++++++ src/Node.php | 28 ++- src/NodeContainer.php | 5 +- src/SimpleList.php | 3 +- src/Specification.php | 406 ++++++++++++++++++++++++++++++++++++++++++ src/Table.php | 2 +- src/html.php | 232 ++---------------------- 10 files changed, 610 insertions(+), 251 deletions(-) create mode 100644 src/HTML5.php create mode 100644 src/Specification.php diff --git a/composer.json b/composer.json index e0a6815..e38e1c3 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,12 @@ { "name": "canteen/html5", "description" : "Create dynamic, valid HTML5 markup with a simple an intuitive PHP API", - "version" : "1.0.1", + "version" : "1.1.0", "type": "library", "keywords": ["html5", "markup", "document", "html", "tags"], "license": "MIT", "homepage" : "http://github.com/Canteen/CanteenHTML5", - "time": "2013-10-12", + "time": "2014-10-31", "authors": [ { "name": "Matt Karl", @@ -16,8 +16,7 @@ } ], "autoload": { - "psr-4": {"Canteen\\HTML5\\": "src/"}, - "files": ["src/html.php"] + "psr-4": {"Canteen\\HTML5\\": "src/"} }, "require": { "php": ">=5.3.0" diff --git a/examples/document.php b/examples/document.php index 2a6f229..5275e40 100644 --- a/examples/document.php +++ b/examples/document.php @@ -1,6 +1,10 @@ docType = html('doctype'); $this->head = html('head'); diff --git a/src/HTML5.php b/src/HTML5.php new file mode 100644 index 0000000..04b0128 --- /dev/null +++ b/src/HTML5.php @@ -0,0 +1,165 @@ +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); + } + // 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 diff --git a/src/Node.php b/src/Node.php index 3b3a719..ce94b26 100644 --- a/src/Node.php +++ b/src/Node.php @@ -15,7 +15,6 @@ namespace Canteen\HTML5 * @constructor * @param {String} [tag=null] The name of the tag * @param {Array|String} [attributes=null] The collection of tag attributes - * @param {String} [validAttrs=null] The list of non-global valid attributes for the tag, comma separated */ class Node { @@ -47,15 +46,7 @@ namespace Canteen\HTML5 */ protected $_validAttrs; - /** - * The default valid attributes - * @property {String} GLOBAL_ATTRS - * @final - * @static - */ - const GLOBAL_ATTRS = 'accesskey,class,contenteditable,contextmenu,dir,draggable,dropzone,hidden,id,lang,spellcheck,style,tabindex,title,translate'; - - public function __construct($tag = null, $attributes = null, $validAttrs = null) + public function __construct($tag = null, $attributes = null) { if ($this->isEmpty($tag)) { @@ -64,13 +55,17 @@ namespace Canteen\HTML5 $this->_parent = null; $this->_tag = $tag; $this->_attributes = array(); - - $validAttrs = is_string($validAttrs) ? explode(',', $validAttrs) : $validAttrs; - $this->_validAttrs = explode(',', self::GLOBAL_ATTRS); - - if ($validAttrs !== null) + + if (isset(Specification::$TAGS[$tag])) { - $this->_validAttrs = array_merge($this->_validAttrs, $validAttrs); + $this->_validAttrs = array_merge( + Specification::$TAGS[$tag], + Specification::$ATTRIBUTES + ); + } + else + { + $this->_validAttrs = array(); } if ($attributes !== null) @@ -299,7 +294,6 @@ namespace Canteen\HTML5 { return $this->setAttribute($name, $value); } - return parent::__set($name); } /** diff --git a/src/NodeContainer.php b/src/NodeContainer.php index f56c08e..8e48c77 100644 --- a/src/NodeContainer.php +++ b/src/NodeContainer.php @@ -17,7 +17,6 @@ namespace Canteen\HTML5 * @param {String} [tag=null] The name of the tag element * @param {Node|Array} [children=null] The collection of children or single child * @param {String|Dictionary} [attributes=null] The tag attributes - * @param {String} [validAttrs=null] Valid attributes specific to the HTML5 element, comma separated */ class NodeContainer extends Node { @@ -28,13 +27,13 @@ namespace Canteen\HTML5 */ private $_children; - public function __construct($tag = null, $children = null, $attributes = null, $validAttrs=null) + public function __construct($tag = null, $children = null, $attributes = null) { if ($this->isEmpty($tag)) { throw new HTML5Error(HTML5Error::EMPTY_NODE_TAG); } - parent::__construct($tag, $attributes, $validAttrs); + parent::__construct($tag, $attributes); $this->_children = array(); diff --git a/src/SimpleList.php b/src/SimpleList.php index 49d491b..6e81b8e 100644 --- a/src/SimpleList.php +++ b/src/SimpleList.php @@ -31,8 +31,7 @@ namespace Canteen\HTML5 { public function __construct($elements=null, $attributes=null, $type='ul') { - parent::__construct($type, null, $attributes, - $type == 'ol' ? 'reversed,start,type' : null); + parent::__construct($type, null, $attributes); if ($elements != null) { diff --git a/src/Specification.php b/src/Specification.php new file mode 100644 index 0000000..a745612 --- /dev/null +++ b/src/Specification.php @@ -0,0 +1,406 @@ + 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(), + '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' + ); + + /** + * 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', + + // 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 diff --git a/src/Table.php b/src/Table.php index 5166d42..abccebc 100644 --- a/src/Table.php +++ b/src/Table.php @@ -32,7 +32,7 @@ namespace Canteen\HTML5 { public function __construct($data, $headers=null, $checkbox=null) { - parent::__construct('table', null, null, 'border'); + parent::__construct('table', null, null); if ($headers != null && is_array($headers)) { diff --git a/src/html.php b/src/html.php index 7776c7e..f92b002 100644 --- a/src/html.php +++ b/src/html.php @@ -1,48 +1,13 @@ -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')); - * - * - * + * The global method which is an alias for Canteen\HTML5\html() + * to use this method globally call Canteen\HTML5\HTML5::useGlobal() * @class html * @constructor * @param {String} tag The name of the tag as a string for example 'tr', 'table', can be followed @@ -55,183 +20,6 @@ */ 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); - - $s = false; // if the html is a single tag like
- $tag = strtolower($tagParts[1]); // the name of the tag - $a = ''; // Valid extra attributes for tags - switch($tag) - { - case 'a': $a = 'href,hreflang,media,rel,target,type'; break; - case 'abbr': break; - case 'address': break; - case 'area': $s = true; $a = 'alt,coords,href,hreflang,media,rel,shape,target,type'; break; - case 'article': break; - case 'aside': break; - case 'audio': $a = 'autoplay,controls,loop,muted,preload,src'; break; - case 'b': break; - case 'base': $s = true; $a = 'href,target'; break; - case 'bdo': break; - case 'blockquote': $a = 'cite'; break; - case 'body': break; - case 'br': $s = true; break; - case 'button': $a = 'autofocus,disabled,form,formaction,formenctype,formmethod,formnovalidate,formtarget,name,type,value'; break; - case 'canvas': $a = 'height,width'; break; - case 'caption': break; - case 'cite': break; - case 'code': break; - case 'col': $s = true; break; - case 'colgroup': $a = 'span'; break; - case 'command': $s = true; $a = 'checked,disabled,icon,label,radiogroup,type'; break; - case 'comment': return new Comment($childrenOrAttributes); - case 'doctype': return ''; - case 'datalist': break; - case 'dd': break; - case 'del': $a = 'cite,datetime'; break; - case 'dfn': break; - case 'div': break; - case 'dl': break; - case 'dt': break; - case 'em': break; - case 'embed': $s = true; $a = 'height,src,type,width'; break; - case 'fieldset': $a = 'disabled,form_id,text'; break; - case 'figcaption': break; - case 'figure': break; - case 'footer': break; - case 'form': $a = 'accept,accept-charset,action,autocomplete,enctype,method,name,novalidate,target'; break; - case 'h1': break; - case 'h2': break; - case 'h3': break; - case 'h4': break; - case 'h5': break; - case 'h6': break; - case 'head': break; - case 'header': break; - case 'hgroup': break; - case 'hr': $s = true; break; - case 'html': $a = 'manifest'; break; - case 'img': $s = true; $a = 'alt,crossorigin,height,src,usemap,width'; break; - case 'i': break; - case 'input': $s = true; $a = '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'; break; - case 'keygen': $s = true; $a = 'autofocus,challenge,disabled,form,keytype,name'; break; - case 'label': $a = 'for,form'; break; - case 'legend': break; - case 'li': break; - case 'link': $s = true; $a = 'href,hreflang,media,rel,sizes,type'; break; - case 'map': $a = 'name'; break; - case 'mark': break; - case 'menu': break; - case 'meta': $s = true; $a = 'charset,content,http-equiv,name'; break; - case 'meter': $a = 'form,heigh,low,max,min,optimum,value'; break; - case 'nav': break; - case 'noscript': break; - case 'object': $a = 'data,form,height,name,type,usemap,width'; break; - case 'ol': $a = 'reversed,start,type'; break; - case 'optgroup': $a = 'disabled,label'; break; - case 'option': $a = 'disabled,label,selected,value'; break; - case 'output': $a = 'for,form,name'; break; - case 'p': break; - case 'param': $s = true; $a = 'name,value'; break; - case 'pre': break; - case 'progress': $a = 'max,value'; break; - case 'q': $a = 'cite'; break; - case 'rp': break; - case 'rt': break; - case 'ruby': break; - case 's': break; - case 'sample': break; - case 'script': $a = 'async,charset,defer,src,type'; break; - case 'section': break; - case 'select': $a = 'autofocus,disabled,form,multiple,name,required,size'; break; - case 'small': break; - case 'source': $s = true; $a = 'media,src,type'; break; - case 'span': break; - case 'strong': break; - case 'style': $a = 'media,scoped,type'; break; - case 'sub': break; - case 'table': $a = 'border'; break; - case 'tbody': break; - case 'td': $a = 'colspan,headers,scope'; break; - case 'text': return new Text($childrenOrAttributes); - case 'textarea': $a = 'autofocus,cols,disabled,form,maxlength,name,placeholder,readonly,required,row,wrap'; break; - case 'tfoot': break; - case 'th': $a = 'colspan,headers,rowspan,scope'; break; - case 'thead': break; - case 'time': $a = 'datetime'; break; - case 'title': break; - case 'tr': break; - case 'track': $s = true; $a = 'default,kind,label,src,srclang'; break; - case 'u': break; - case 'ul': break; - case 'var': break; - case 'video': $a = 'autoplay,controls,height,loop,muted,poster,preload,src,width'; break; - case 'wbr': $s = true; break; - default: - throw new HTML5Error(HTML5Error::INVALID_TAG, $tag); - break; - } - - // Create the attributes collection, either string or array - $attributes = $s ? $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 = ($s) ? - new Node($tag, $attributes, $a) : - new NodeContainer($tag, $childrenOrAttributes, $attributes, $a); - - // 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; + return Canteen\HTML5\html($tag, $childrenOrAttributes, $attributes); } - -?> \ No newline at end of file +} \ No newline at end of file