Packages

  • 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

  • NArrays
  • NFinder
  • NHtml
  • NJson
  • NLimitedScope
  • NMimeTypeDetector
  • NNeon
  • NNeonEntity
  • NPaginator
  • NStrings
  • NTokenizer
  • NValidators

Exceptions

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