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