Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • Arrays
  • Finder
  • Html
  • Json
  • LimitedScope
  • MimeTypeDetector
  • Neon
  • NeonEntity
  • Paginator
  • Strings
  • Tokenizer
  • Validators

Exceptions

  • AssertionException
  • JsonException
  • NeonException
  • RegexpException
  • TokenizerException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\Utils;
 13: 
 14: use Nette;
 15: 
 16: 
 17: /**
 18:  * HTML helper.
 19:  *
 20:  * <code>
 21:  * $el = Html::el('a')->href($link)->setText('Nette');
 22:  * $el->class = 'myclass';
 23:  * echo $el;
 24:  *
 25:  * echo $el->startTag(), $el->endTag();
 26:  * </code>
 27:  *
 28:  * @author     David Grudl
 29:  */
 30: class Html extends Nette\Object implements \ArrayAccess, \Countable, \IteratorAggregate
 31: {
 32:     /** @var string  element's name */
 33:     private $name;
 34: 
 35:     /** @var bool  is element empty? */
 36:     private $isEmpty;
 37: 
 38:     /** @var array  element's attributes */
 39:     public $attrs = array();
 40: 
 41:     /** @var array  of Html | string nodes */
 42:     protected $children = array();
 43: 
 44:     /** @var bool  use XHTML syntax? */
 45:     public static $xhtml = TRUE;
 46: 
 47:     /** @var array  empty (void) elements */
 48:     public static $emptyElements = array('img'=>1,'hr'=>1,'br'=>1,'input'=>1,'meta'=>1,'area'=>1,'embed'=>1,'keygen'=>1,
 49:         'source'=>1,'base'=>1,'col'=>1,'link'=>1,'param'=>1,'basefont'=>1,'frame'=>1,'isindex'=>1,'wbr'=>1,'command'=>1,'track'=>1);
 50: 
 51: 
 52:     /**
 53:      * Static factory.
 54:      * @param  string element name (or NULL)
 55:      * @param  array|string element's attributes (or textual content)
 56:      * @return Html
 57:      */
 58:     public static function el($name = NULL, $attrs = NULL)
 59:     {
 60:         $el = new static;
 61:         $parts = explode(' ', $name, 2);
 62:         $el->setName($parts[0]);
 63: 
 64:         if (is_array($attrs)) {
 65:             $el->attrs = $attrs;
 66: 
 67:         } elseif ($attrs !== NULL) {
 68:             $el->setText($attrs);
 69:         }
 70: 
 71:         if (isset($parts[1])) {
 72:             foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\\2|\s))?#i') as $m) {
 73:                 $el->attrs[$m[1]] = isset($m[3]) ? $m[3] : TRUE;
 74:             }
 75:         }
 76: 
 77:         return $el;
 78:     }
 79: 
 80: 
 81:     /**
 82:      * Changes element's name.
 83:      * @param  string
 84:      * @param  bool  Is element empty?
 85:      * @return self
 86:      * @throws Nette\InvalidArgumentException
 87:      */
 88:     final public function setName($name, $isEmpty = NULL)
 89:     {
 90:         if ($name !== NULL && !is_string($name)) {
 91:             throw new Nette\InvalidArgumentException("Name must be string or NULL, " . gettype($name) ." given.");
 92:         }
 93: 
 94:         $this->name = $name;
 95:         $this->isEmpty = $isEmpty === NULL ? isset(static::$emptyElements[$name]) : (bool) $isEmpty;
 96:         return $this;
 97:     }
 98: 
 99: 
100:     /**
101:      * Returns element's name.
102:      * @return string
103:      */
104:     final public function getName()
105:     {
106:         return $this->name;
107:     }
108: 
109: 
110:     /**
111:      * Is element empty?
112:      * @return bool
113:      */
114:     final public function isEmpty()
115:     {
116:         return $this->isEmpty;
117:     }
118: 
119: 
120:     /**
121:      * Sets multiple attributes.
122:      * @param  array
123:      * @return self
124:      */
125:     public function addAttributes(array $attrs)
126:     {
127:         $this->attrs = $attrs + $this->attrs;
128:         return $this;
129:     }
130: 
131: 
132:     /**
133:      * Overloaded setter for element's attribute.
134:      * @param  string    HTML attribute name
135:      * @param  mixed     HTML attribute value
136:      * @return void
137:      */
138:     final public function __set($name, $value)
139:     {
140:         $this->attrs[$name] = $value;
141:     }
142: 
143: 
144:     /**
145:      * Overloaded getter for element's attribute.
146:      * @param  string    HTML attribute name
147:      * @return mixed     HTML attribute value
148:      */
149:     final public function &__get($name)
150:     {
151:         return $this->attrs[$name];
152:     }
153: 
154: 
155:     /**
156:      * Overloaded tester for element's attribute.
157:      * @param  string    HTML attribute name
158:      * @return void
159:      */
160:     final public function __isset($name)
161:     {
162:         return isset($this->attrs[$name]);
163:     }
164: 
165: 
166:     /**
167:      * Overloaded unsetter for element's attribute.
168:      * @param  string    HTML attribute name
169:      * @return void
170:      */
171:     final public function __unset($name)
172:     {
173:         unset($this->attrs[$name]);
174:     }
175: 
176: 
177:     /**
178:      * Overloaded setter for element's attribute.
179:      * @param  string  HTML attribute name
180:      * @param  array   (string) HTML attribute value or pair?
181:      * @return self
182:      */
183:     final public function __call($m, $args)
184:     {
185:         $p = substr($m, 0, 3);
186:         if ($p === 'get' || $p === 'set' || $p === 'add') {
187:             $m = substr($m, 3);
188:             $m[0] = $m[0] | "\x20";
189:             if ($p === 'get') {
190:                 return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
191: 
192:             } elseif ($p === 'add') {
193:                 $args[] = TRUE;
194:             }
195:         }
196: 
197:         if (count($args) === 0) { // invalid
198: 
199:         } elseif (count($args) === 1) { // set
200:             $this->attrs[$m] = $args[0];
201: 
202:         } elseif ((string) $args[0] === '') {
203:             $tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
204: 
205:         } elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
206:             $this->attrs[$m][$args[0]] = $args[1];
207: 
208:         } else {
209:             $this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
210:         }
211: 
212:         return $this;
213:     }
214: 
215: 
216:     /**
217:      * Special setter for element's attribute.
218:      * @param  string path
219:      * @param  array query
220:      * @return self
221:      */
222:     final public function href($path, $query = NULL)
223:     {
224:         if ($query) {
225:             $query = http_build_query($query, NULL, '&');
226:             if ($query !== '') {
227:                 $path .= '?' . $query;
228:             }
229:         }
230:         $this->attrs['href'] = $path;
231:         return $this;
232:     }
233: 
234: 
235:     /**
236:      * Sets element's HTML content.
237:      * @param  string
238:      * @return self
239:      * @throws Nette\InvalidArgumentException
240:      */
241:     final public function setHtml($html)
242:     {
243:         if ($html === NULL) {
244:             $html = '';
245: 
246:         } elseif (is_array($html)) {
247:             throw new Nette\InvalidArgumentException("Textual content must be a scalar, " . gettype($html) ." given.");
248: 
249:         } else {
250:             $html = (string) $html;
251:         }
252: 
253:         $this->removeChildren();
254:         $this->children[] = $html;
255:         return $this;
256:     }
257: 
258: 
259:     /**
260:      * Returns element's HTML content.
261:      * @return string
262:      */
263:     final public function getHtml()
264:     {
265:         $s = '';
266:         foreach ($this->children as $child) {
267:             if (is_object($child)) {
268:                 $s .= $child->render();
269:             } else {
270:                 $s .= $child;
271:             }
272:         }
273:         return $s;
274:     }
275: 
276: 
277:     /**
278:      * Sets element's textual content.
279:      * @param  string
280:      * @return self
281:      * @throws Nette\InvalidArgumentException
282:      */
283:     final public function setText($text)
284:     {
285:         if (!is_array($text)) {
286:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES);
287:         }
288:         return $this->setHtml($text);
289:     }
290: 
291: 
292:     /**
293:      * Returns element's textual content.
294:      * @return string
295:      */
296:     final public function getText()
297:     {
298:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
299:     }
300: 
301: 
302:     /**
303:      * Adds new element's child.
304:      * @param  Html|string child node
305:      * @return self
306:      */
307:     final public function add($child)
308:     {
309:         return $this->insert(NULL, $child);
310:     }
311: 
312: 
313:     /**
314:      * Creates and adds a new Html child.
315:      * @param  string  elements's name
316:      * @param  array|string element's attributes (or textual content)
317:      * @return Html  created element
318:      */
319:     final public function create($name, $attrs = NULL)
320:     {
321:         $this->insert(NULL, $child = static::el($name, $attrs));
322:         return $child;
323:     }
324: 
325: 
326:     /**
327:      * Inserts child node.
328:      * @param  int
329:      * @param  Html node
330:      * @param  bool
331:      * @return self
332:      * @throws \Exception
333:      */
334:     public function insert($index, $child, $replace = FALSE)
335:     {
336:         if ($child instanceof Html || is_scalar($child)) {
337:             if ($index === NULL) { // append
338:                 $this->children[] = $child;
339: 
340:             } else { // insert or replace
341:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
342:             }
343: 
344:         } else {
345:             throw new Nette\InvalidArgumentException("Child node must be scalar or Html object, " . (is_object($child) ? get_class($child) : gettype($child)) ." given.");
346:         }
347: 
348:         return $this;
349:     }
350: 
351: 
352:     /**
353:      * Inserts (replaces) child node (\ArrayAccess implementation).
354:      * @param  int
355:      * @param  Html node
356:      * @return void
357:      */
358:     final public function offsetSet($index, $child)
359:     {
360:         $this->insert($index, $child, TRUE);
361:     }
362: 
363: 
364:     /**
365:      * Returns child node (\ArrayAccess implementation).
366:      * @param  int index
367:      * @return mixed
368:      */
369:     final public function offsetGet($index)
370:     {
371:         return $this->children[$index];
372:     }
373: 
374: 
375:     /**
376:      * Exists child node? (\ArrayAccess implementation).
377:      * @param  int index
378:      * @return bool
379:      */
380:     final public function offsetExists($index)
381:     {
382:         return isset($this->children[$index]);
383:     }
384: 
385: 
386:     /**
387:      * Removes child node (\ArrayAccess implementation).
388:      * @param  int index
389:      * @return void
390:      */
391:     public function offsetUnset($index)
392:     {
393:         if (isset($this->children[$index])) {
394:             array_splice($this->children, (int) $index, 1);
395:         }
396:     }
397: 
398: 
399:     /**
400:      * Required by the \Countable interface.
401:      * @return int
402:      */
403:     final public function count()
404:     {
405:         return count($this->children);
406:     }
407: 
408: 
409:     /**
410:      * Removed all children.
411:      * @return void
412:      */
413:     public function removeChildren()
414:     {
415:         $this->children = array();
416:     }
417: 
418: 
419:     /**
420:      * Iterates over a elements.
421:      * @param  bool    recursive?
422:      * @param  string  class types filter
423:      * @return \RecursiveIterator
424:      */
425:     final public function getIterator($deep = FALSE)
426:     {
427:         if ($deep) {
428:             $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
429:             return new \RecursiveIteratorIterator(new Nette\Iterators\Recursor(new \ArrayIterator($this->children)), $deep);
430: 
431:         } else {
432:             return new Nette\Iterators\Recursor(new \ArrayIterator($this->children));
433:         }
434:     }
435: 
436: 
437:     /**
438:      * Returns all of children.
439:      * @return array
440:      */
441:     final public function getChildren()
442:     {
443:         return $this->children;
444:     }
445: 
446: 
447:     /**
448:      * Renders element's start tag, content and end tag.
449:      * @param  int indent
450:      * @return string
451:      */
452:     final public function render($indent = NULL)
453:     {
454:         $s = $this->startTag();
455: 
456:         if (!$this->isEmpty) {
457:             // add content
458:             if ($indent !== NULL) {
459:                 $indent++;
460:             }
461:             foreach ($this->children as $child) {
462:                 if (is_object($child)) {
463:                     $s .= $child->render($indent);
464:                 } else {
465:                     $s .= $child;
466:                 }
467:             }
468: 
469:             // add end tag
470:             $s .= $this->endTag();
471:         }
472: 
473:         if ($indent !== NULL) {
474:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
475:         }
476:         return $s;
477:     }
478: 
479: 
480:     final public function __toString()
481:     {
482:         return $this->render();
483:     }
484: 
485: 
486:     /**
487:      * Returns element's start tag.
488:      * @return string
489:      */
490:     final public function startTag()
491:     {
492:         if ($this->name) {
493:             return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
494: 
495:         } else {
496:             return '';
497:         }
498:     }
499: 
500: 
501:     /**
502:      * Returns element's end tag.
503:      * @return string
504:      */
505:     final public function endTag()
506:     {
507:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
508:     }
509: 
510: 
511:     /**
512:      * Returns element's attributes.
513:      * @return string
514:      */
515:     final public function attributes()
516:     {
517:         if (!is_array($this->attrs)) {
518:             return '';
519:         }
520: 
521:         $s = '';
522:         foreach ($this->attrs as $key => $value) {
523:             if ($value === NULL || $value === FALSE) {
524:                 continue;
525: 
526:             } elseif ($value === TRUE) {
527:                 if (static::$xhtml) {
528:                     $s .= ' ' . $key . '="' . $key . '"';
529:                 } else {
530:                     $s .= ' ' . $key;
531:                 }
532:                 continue;
533: 
534:             } elseif (is_array($value)) {
535:                 if ($key === 'data') {
536:                     foreach ($value as $k => $v) {
537:                         if ($v !== NULL && $v !== FALSE) {
538:                             $q = strpos($v, '"') === FALSE ? '"' : "'";
539:                             $s .= ' data-' . $k . '=' . $q . str_replace(array('&', $q), array('&amp;', $q === '"' ? '&quot;' : '&#39;'), $v) . $q;
540:                         }
541:                     }
542:                     continue;
543:                 }
544: 
545:                 $tmp = NULL;
546:                 foreach ($value as $k => $v) {
547:                     if ($v != NULL) { // intentionally ==, skip NULLs & empty string
548:                         //  composite 'style' vs. 'others'
549:                         $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
550:                     }
551:                 }
552:                 if ($tmp === NULL) {
553:                     continue;
554:                 }
555: 
556:                 $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
557: 
558:             } else {
559:                 $value = (string) $value;
560:             }
561: 
562:             $q = strpos($value, '"') === FALSE ? '"' : "'";
563:             $s .= ' ' . $key . '=' . $q . str_replace(array('&', $q), array('&amp;', $q === '"' ? '&quot;' : '&#39;'), $value) . $q;
564:         }
565: 
566:         $s = str_replace('@', '&#64;', $s);
567:         return $s;
568:     }
569: 
570: 
571:     /**
572:      * Clones all children too.
573:      */
574:     public function __clone()
575:     {
576:         foreach ($this->children as $key => $value) {
577:             if (is_object($value)) {
578:                 $this->children[$key] = clone $value;
579:             }
580:         }
581:     }
582: 
583: }
584: 
Nette Framework 2.0.11 API API documentation generated by ApiGen 2.8.0