PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/HTML/Common2.php

https://github.com/quarkness/piwik
PHP | 488 lines | 378 code | 8 blank | 102 comment | 8 complexity | a9c9b708cd3f2691192171bb78177db3 MD5 | raw file
  1. <?php
  2. /**
  3. * HTML_Common2: port of HTML_Common package to PHP5
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE:
  8. *
  9. * Copyright (c) 2004-2009, Alexey Borzov <avb@php.net>
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * * Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * * The names of the authors may not be used to endorse or promote products
  23. * derived from this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  26. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  27. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  28. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  29. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  30. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  31. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  33. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category HTML
  38. * @package HTML_Common2
  39. * @author Alexey Borzov <avb@php.net>
  40. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  41. * @version CVS: $Id: Common2.php 295050 2010-02-14 05:01:19Z clockwerx $
  42. * @link http://pear.php.net/package/HTML_Common2
  43. */
  44. /**
  45. * Base class for HTML classes
  46. *
  47. * Implements methods for working with HTML attributes, parsing and generating
  48. * attribute strings. Port of HTML_Common class for PHP4 originally written by
  49. * Adam Daniel with contributions from numerous other developers.
  50. *
  51. * @category HTML
  52. * @package HTML_Common2
  53. * @author Alexey Borzov <avb@php.net>
  54. * @version Release: @package_version@
  55. */
  56. abstract class HTML_Common2
  57. {
  58. /**
  59. * Associative array of attributes
  60. * @var array
  61. */
  62. protected $attributes = array();
  63. /**
  64. * List of attribites changes to which will be announced via onAttributeChange()
  65. * method rather than performed by HTML_Common2 class itself
  66. * @var array
  67. * @see onAttributeChange()
  68. */
  69. protected $watchedAttributes = array();
  70. /**
  71. * Indentation level of the element
  72. * @var int
  73. */
  74. private $_indentLevel = 0;
  75. /**
  76. * Comment associated with the element
  77. * @var string
  78. */
  79. private $_comment = null;
  80. /**
  81. * Global options for all elements generated by subclasses of HTML_Common2
  82. *
  83. * Preset options are
  84. * - 'charset': charset parameter used in htmlspecialchars() calls,
  85. * defaults to 'ISO-8859-1'
  86. * - 'indent': string used to indent HTML elements, defaults to "\11"
  87. * - 'linebreak': string used to indicate linebreak, defaults to "\12"
  88. *
  89. * @var array
  90. */
  91. private static $_options = array(
  92. 'charset' => 'ISO-8859-1',
  93. 'indent' => "\11",
  94. 'linebreak' => "\12"
  95. );
  96. /**
  97. * Sets global option(s)
  98. *
  99. * @param string|array Option name or array ('option name' => 'option value')
  100. * @param mixed Option value, if first argument is not an array
  101. */
  102. public static function setOption($nameOrOptions, $value = null)
  103. {
  104. if (is_array($nameOrOptions)) {
  105. foreach ($nameOrOptions as $k => $v) {
  106. self::setOption($k, $v);
  107. }
  108. } else {
  109. $linebreaks = array('win' => "\15\12", 'unix' => "\12", 'mac' => "\15");
  110. if ('linebreak' == $nameOrOptions && isset($linebreaks[$value])) {
  111. $value = $linebreaks[$value];
  112. }
  113. self::$_options[$nameOrOptions] = $value;
  114. }
  115. }
  116. /**
  117. * Returns global option(s)
  118. *
  119. * @param string Option name
  120. * @return mixed Option value, null if option does not exist,
  121. * array of all options if $name is not given
  122. */
  123. public static function getOption($name = null)
  124. {
  125. if (null === $name) {
  126. return self::$_options;
  127. } else {
  128. return isset(self::$_options[$name])? self::$_options[$name]: null;
  129. }
  130. }
  131. /**
  132. * Parses the HTML attributes given as string
  133. *
  134. * @param string HTML attribute string
  135. * @return array An associative aray of attributes
  136. */
  137. protected static function parseAttributes($attrString)
  138. {
  139. $attributes = array();
  140. if (preg_match_all(
  141. "/(([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*)" .
  142. "([ \\n\\t\\r]+)?(=([ \\n\\t\\r]+)?(\"[^\"]*\"|'[^']*'|[^ \\n\\t\\r]*))?/",
  143. $attrString,
  144. $regs
  145. )) {
  146. for ($i = 0; $i < count($regs[1]); $i++) {
  147. $name = trim($regs[1][$i]);
  148. $check = trim($regs[0][$i]);
  149. $value = trim($regs[7][$i]);
  150. if ($name == $check) {
  151. $attributes[strtolower($name)] = strtolower($name);
  152. } else {
  153. if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) {
  154. $value = substr($value, 1, -1);
  155. }
  156. $attributes[strtolower($name)] = $value;
  157. }
  158. }
  159. }
  160. return $attributes;
  161. }
  162. /**
  163. * Creates a valid attribute array from either a string or an array
  164. *
  165. * @param mixed Array of attributes or HTML attribute string
  166. * @return array An associative aray of attributes
  167. */
  168. protected static function prepareAttributes($attributes)
  169. {
  170. $prepared = array();
  171. if (is_string($attributes)) {
  172. return self::parseAttributes($attributes);
  173. } elseif (is_array($attributes)) {
  174. foreach ($attributes as $key => $value) {
  175. if (is_int($key)) {
  176. $key = strtolower($value);
  177. $prepared[$key] = $key;
  178. } else {
  179. $prepared[strtolower($key)] = (string)$value;
  180. }
  181. }
  182. }
  183. return $prepared;
  184. }
  185. /**
  186. * Removes an attribute from an attribute array
  187. *
  188. * @param array Attribute array
  189. * @param string Name of attribute to remove
  190. */
  191. protected static function removeAttributeArray(&$attributes, $name)
  192. {
  193. unset($attributes[strtolower($name)]);
  194. }
  195. /**
  196. * Creates HTML attribute string from array
  197. *
  198. * @param array Attribute array
  199. * @return string Attribute string
  200. */
  201. protected static function getAttributesString($attributes)
  202. {
  203. $str = '';
  204. if (is_array($attributes)) {
  205. $charset = self::getOption('charset');
  206. foreach ($attributes as $key => $value) {
  207. $str .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES, $charset) . '"';
  208. }
  209. }
  210. return $str;
  211. }
  212. /**
  213. * Class constructor, sets default attributes
  214. *
  215. * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string
  216. */
  217. public function __construct($attributes = null)
  218. {
  219. $this->mergeAttributes($attributes);
  220. }
  221. /**
  222. * Sets the value of the attribute
  223. *
  224. * @param string Attribute name
  225. * @param string Attribute value (will be set to $name if omitted)
  226. * @return HTML_Common2
  227. */
  228. public function setAttribute($name, $value = null)
  229. {
  230. $name = strtolower($name);
  231. if (is_null($value)) {
  232. $value = $name;
  233. }
  234. if (in_array($name, $this->watchedAttributes)) {
  235. $this->onAttributeChange($name, $value);
  236. } else {
  237. $this->attributes[$name] = (string)$value;
  238. }
  239. return $this;
  240. }
  241. /**
  242. * Returns the value of an attribute
  243. *
  244. * @param string Attribute name
  245. * @return string Attribute value, null if attribute does not exist
  246. */
  247. public function getAttribute($name)
  248. {
  249. $name = strtolower($name);
  250. return isset($this->attributes[$name])? $this->attributes[$name]: null;
  251. }
  252. /**
  253. * Sets the attributes
  254. *
  255. * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string
  256. * @return HTML_Common2
  257. */
  258. public function setAttributes($attributes)
  259. {
  260. $attributes = self::prepareAttributes($attributes);
  261. $watched = array();
  262. foreach ($this->watchedAttributes as $watchedKey) {
  263. if (isset($attributes[$watchedKey])) {
  264. $this->setAttribute($watchedKey, $attributes[$watchedKey]);
  265. unset($attributes[$watchedKey]);
  266. } else {
  267. $this->removeAttribute($watchedKey);
  268. }
  269. if (isset($this->attributes[$watchedKey])) {
  270. $watched[$watchedKey] = $this->attributes[$watchedKey];
  271. }
  272. }
  273. $this->attributes = array_merge($watched, $attributes);
  274. return $this;
  275. }
  276. /**
  277. * Returns the attribute array or string
  278. *
  279. * @param bool Whether to return attributes as string
  280. * @return mixed Either an array or string of attributes
  281. */
  282. public function getAttributes($asString = false)
  283. {
  284. if ($asString) {
  285. return self::getAttributesString($this->attributes);
  286. } else {
  287. return $this->attributes;
  288. }
  289. }
  290. /**
  291. * Merges the existing attributes with the new ones
  292. *
  293. * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string
  294. * @return HTML_Common2
  295. */
  296. public function mergeAttributes($attributes)
  297. {
  298. $attributes = self::prepareAttributes($attributes);
  299. foreach ($this->watchedAttributes as $watchedKey) {
  300. if (isset($attributes[$watchedKey])) {
  301. $this->onAttributeChange($watchedKey, $attributes[$watchedKey]);
  302. unset($attributes[$watchedKey]);
  303. }
  304. }
  305. $this->attributes = array_merge($this->attributes, $attributes);
  306. return $this;
  307. }
  308. /**
  309. * Removes an attribute
  310. *
  311. * @param string Name of attribute to remove
  312. * @return HTML_Common2
  313. */
  314. public function removeAttribute($attribute)
  315. {
  316. if (in_array(strtolower($attribute), $this->watchedAttributes)) {
  317. $this->onAttributeChange(strtolower($attribute), null);
  318. } else {
  319. self::removeAttributeArray($this->attributes, $attribute);
  320. }
  321. return $this;
  322. }
  323. /**
  324. * Sets the indentation level
  325. *
  326. * @param int
  327. * @return HTML_Common2
  328. */
  329. public function setIndentLevel($level)
  330. {
  331. $level = intval($level);
  332. if (0 <= $level) {
  333. $this->_indentLevel = $level;
  334. }
  335. return $this;
  336. }
  337. /**
  338. * Gets the indentation level
  339. *
  340. * @return int
  341. */
  342. public function getIndentLevel()
  343. {
  344. return $this->_indentLevel;
  345. }
  346. /**
  347. * Returns the string to indent the element
  348. *
  349. * @return string
  350. */
  351. protected function getIndent()
  352. {
  353. return str_repeat(self::getOption('indent'), $this->getIndentLevel());
  354. }
  355. /**
  356. * Sets the comment for the element
  357. *
  358. * @param string
  359. * @return HTML_Common2
  360. */
  361. public function setComment($comment)
  362. {
  363. $this->_comment = $comment;
  364. return $this;
  365. }
  366. /**
  367. * Returns the comment associated with the element
  368. *
  369. * @return string
  370. */
  371. public function getComment()
  372. {
  373. return $this->_comment;
  374. }
  375. /**
  376. * Checks whether the element has given CSS class
  377. *
  378. * @param string Class name
  379. * @return bool
  380. */
  381. public function hasClass($class)
  382. {
  383. $regex = '/(^|\s)' . preg_quote($class, '/') . '(\s|$)/';
  384. return (bool)preg_match($regex, $this->getAttribute('class'));
  385. }
  386. /**
  387. * Adds the given CSS class(es) to the element
  388. *
  389. * @param string|array Class name, multiple class names separated by
  390. * whitespace, array of class names
  391. * @return HTML_Common2
  392. */
  393. public function addClass($class)
  394. {
  395. if (!is_array($class)) {
  396. $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY);
  397. }
  398. $curClass = preg_split('/\s+/', $this->getAttribute('class'),
  399. null, PREG_SPLIT_NO_EMPTY);
  400. foreach ($class as $c) {
  401. if (!in_array($c, $curClass)) {
  402. $curClass[] = $c;
  403. }
  404. }
  405. $this->setAttribute('class', implode(' ', $curClass));
  406. return $this;
  407. }
  408. /**
  409. * Removes the given CSS class(es) from the element
  410. *
  411. * @param string|array Class name, multiple class names separated by
  412. * whitespace, array of class names
  413. * @return HTML_Common2
  414. */
  415. public function removeClass($class)
  416. {
  417. if (!is_array($class)) {
  418. $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY);
  419. }
  420. $curClass = array_diff(
  421. preg_split('/\s+/', $this->getAttribute('class'),
  422. null, PREG_SPLIT_NO_EMPTY),
  423. $class
  424. );
  425. if (0 == count($curClass)) {
  426. $this->removeAttribute('class');
  427. } else {
  428. $this->setAttribute('class', implode(' ', $curClass));
  429. }
  430. return $this;
  431. }
  432. /**
  433. * Returns the HTML representation of the element
  434. *
  435. * This magic method allows using the instances of HTML_Common2 in string
  436. * contexts
  437. *
  438. * @return string
  439. */
  440. abstract public function __toString();
  441. /**
  442. * Called if trying to change an attribute with name in $watchedAttributes
  443. *
  444. * This method is called for each attribute whose name is in the
  445. * $watchedAttributes array and which is being changed by setAttribute(),
  446. * setAttributes() or mergeAttributes() or removed via removeAttribute().
  447. * Note that the operation for the attribute is not carried on after calling
  448. * this method, it is the responsibility of this method to change or remove
  449. * (or not) the attribute.
  450. *
  451. * @param string Attribute name
  452. * @param string Attribute value, null if attribute is being removed
  453. */
  454. protected function onAttributeChange($name, $value = null)
  455. {
  456. }
  457. }
  458. ?>