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
  • 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 unsetter for element's attribute.
164:      * @param  string    HTML attribute name
165:      * @return void
166:      */
167:     final public function __unset($name)
168:     {
169:         unset($this->attrs[$name]);
170:     }
171: 
172: 
173: 
174:     /**
175:      * Overloaded setter for element's attribute.
176:      * @param  string  HTML attribute name
177:      * @param  array   (string) HTML attribute value or pair?
178:      * @return NHtml  provides a fluent interface
179:      */
180:     final public function __call($m, $args)
181:     {
182:         $p = substr($m, 0, 3);
183:         if ($p === 'get' || $p === 'set' || $p === 'add') {
184:             $m = substr($m, 3);
185:             $m[0] = $m[0] | "\x20";
186:             if ($p === 'get') {
187:                 return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
188: 
189:             } elseif ($p === 'add') {
190:                 $args[] = TRUE;
191:             }
192:         }
193: 
194:         if (count($args) === 0) { // invalid
195: 
196:         } elseif (count($args) === 1) { // set
197:             $this->attrs[$m] = $args[0];
198: 
199:         } elseif ((string) $args[0] === '') {
200:             $tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
201: 
202:         } elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
203:             $this->attrs[$m][$args[0]] = $args[1];
204: 
205:         } else {
206:             $this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
207:         }
208: 
209:         return $this;
210:     }
211: 
212: 
213: 
214:     /**
215:      * Special setter for element's attribute.
216:      * @param  string path
217:      * @param  array query
218:      * @return NHtml  provides a fluent interface
219:      */
220:     final public function href($path, $query = NULL)
221:     {
222:         if ($query) {
223:             $query = http_build_query($query, NULL, '&');
224:             if ($query !== '') {
225:                 $path .= '?' . $query;
226:             }
227:         }
228:         $this->attrs['href'] = $path;
229:         return $this;
230:     }
231: 
232: 
233: 
234:     /**
235:      * Sets element's HTML content.
236:      * @param  string
237:      * @return NHtml  provides a fluent interface
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:     /**
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:     /**
279:      * Sets element's textual content.
280:      * @param  string
281:      * @return NHtml  provides a fluent interface
282:      * @throws InvalidArgumentException
283:      */
284:     final public function setText($text)
285:     {
286:         if (!is_array($text)) {
287:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES);
288:         }
289:         return $this->setHtml($text);
290:     }
291: 
292: 
293: 
294:     /**
295:      * Returns element's textual content.
296:      * @return string
297:      */
298:     final public function getText()
299:     {
300:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
301:     }
302: 
303: 
304: 
305:     /**
306:      * Adds new element's child.
307:      * @param  NHtml|string child node
308:      * @return NHtml  provides a fluent interface
309:      */
310:     final public function add($child)
311:     {
312:         return $this->insert(NULL, $child);
313:     }
314: 
315: 
316: 
317:     /**
318:      * Creates and adds a new Html child.
319:      * @param  string  elements's name
320:      * @param  array|string element's attributes (or textual content)
321:      * @return NHtml  created element
322:      */
323:     final public function create($name, $attrs = NULL)
324:     {
325:         $this->insert(NULL, $child = self::el($name, $attrs));
326:         return $child;
327:     }
328: 
329: 
330: 
331:     /**
332:      * Inserts child node.
333:      * @param  int
334:      * @param  NHtml node
335:      * @param  bool
336:      * @return NHtml  provides a fluent interface
337:      * @throws Exception
338:      */
339:     public function insert($index, $child, $replace = FALSE)
340:     {
341:         if ($child instanceof NHtml || is_scalar($child)) {
342:             if ($index === NULL) { // append
343:                 $this->children[] = $child;
344: 
345:             } else { // insert or replace
346:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
347:             }
348: 
349:         } else {
350:             throw new InvalidArgumentException("Child node must be scalar or Html object, " . (is_object($child) ? get_class($child) : gettype($child)) ." given.");
351:         }
352: 
353:         return $this;
354:     }
355: 
356: 
357: 
358:     /**
359:      * Inserts (replaces) child node (ArrayAccess implementation).
360:      * @param  int
361:      * @param  NHtml node
362:      * @return void
363:      */
364:     final public function offsetSet($index, $child)
365:     {
366:         $this->insert($index, $child, TRUE);
367:     }
368: 
369: 
370: 
371:     /**
372:      * Returns child node (ArrayAccess implementation).
373:      * @param  int index
374:      * @return mixed
375:      */
376:     final public function offsetGet($index)
377:     {
378:         return $this->children[$index];
379:     }
380: 
381: 
382: 
383:     /**
384:      * Exists child node? (ArrayAccess implementation).
385:      * @param  int index
386:      * @return bool
387:      */
388:     final public function offsetExists($index)
389:     {
390:         return isset($this->children[$index]);
391:     }
392: 
393: 
394: 
395:     /**
396:      * Removes child node (ArrayAccess implementation).
397:      * @param  int index
398:      * @return void
399:      */
400:     public function offsetUnset($index)
401:     {
402:         if (isset($this->children[$index])) {
403:             array_splice($this->children, (int) $index, 1);
404:         }
405:     }
406: 
407: 
408: 
409:     /**
410:      * Required by the Countable interface.
411:      * @return int
412:      */
413:     final public function count()
414:     {
415:         return count($this->children);
416:     }
417: 
418: 
419: 
420:     /**
421:      * Removed all children.
422:      * @return void
423:      */
424:     public function removeChildren()
425:     {
426:         $this->children = array();
427:     }
428: 
429: 
430: 
431:     /**
432:      * Iterates over a elements.
433:      * @param  bool    recursive?
434:      * @param  string  class types filter
435:      * @return RecursiveIterator
436:      */
437:     final public function getIterator($deep = FALSE)
438:     {
439:         if ($deep) {
440:             $deep = $deep > 0 ? RecursiveIteratorIterator::SELF_FIRST : RecursiveIteratorIterator::CHILD_FIRST;
441:             return new RecursiveIteratorIterator(new NGenericRecursiveIterator(new ArrayIterator($this->children)), $deep);
442: 
443:         } else {
444:             return new NGenericRecursiveIterator(new ArrayIterator($this->children));
445:         }
446:     }
447: 
448: 
449: 
450:     /**
451:      * Returns all of children.
452:      * return array
453:      */
454:     final public function getChildren()
455:     {
456:         return $this->children;
457:     }
458: 
459: 
460: 
461:     /**
462:      * Renders element's start tag, content and end tag.
463:      * @param  int indent
464:      * @return string
465:      */
466:     final public function render($indent = NULL)
467:     {
468:         $s = $this->startTag();
469: 
470:         if (!$this->isEmpty) {
471:             // add content
472:             if ($indent !== NULL) {
473:                 $indent++;
474:             }
475:             foreach ($this->children as $child) {
476:                 if (is_object($child)) {
477:                     $s .= $child->render($indent);
478:                 } else {
479:                     $s .= $child;
480:                 }
481:             }
482: 
483:             // add end tag
484:             $s .= $this->endTag();
485:         }
486: 
487:         if ($indent !== NULL) {
488:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
489:         }
490:         return $s;
491:     }
492: 
493: 
494: 
495:     final public function __toString()
496:     {
497:         return $this->render();
498:     }
499: 
500: 
501: 
502:     /**
503:      * Returns element's start tag.
504:      * @return string
505:      */
506:     final public function startTag()
507:     {
508:         if ($this->name) {
509:             return '<' . $this->name . $this->attributes() . (self::$xhtml && $this->isEmpty ? ' />' : '>');
510: 
511:         } else {
512:             return '';
513:         }
514:     }
515: 
516: 
517: 
518:     /**
519:      * Returns element's end tag.
520:      * @return string
521:      */
522:     final public function endTag()
523:     {
524:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
525:     }
526: 
527: 
528: 
529:     /**
530:      * Returns element's attributes.
531:      * @return string
532:      */
533:     final public function attributes()
534:     {
535:         if (!is_array($this->attrs)) {
536:             return '';
537:         }
538: 
539:         $s = '';
540:         foreach ($this->attrs as $key => $value) {
541:             if ($value === NULL || $value === FALSE) {
542:                 continue;
543: 
544:             } elseif ($value === TRUE) {
545:                 if (self::$xhtml) {
546:                     $s .= ' ' . $key . '="' . $key . '"';
547:                 } else {
548:                     $s .= ' ' . $key;
549:                 }
550:                 continue;
551: 
552:             } elseif (is_array($value)) {
553:                 if ($key === 'data') {
554:                     foreach ($value as $k => $v) {
555:                         if ($v !== NULL && $v !== FALSE) {
556:                             $s .= ' data-' . $k . '="' . htmlspecialchars((string) $v) . '"';
557:                         }
558:                     }
559:                     continue;
560:                 }
561: 
562:                 $tmp = NULL;
563:                 foreach ($value as $k => $v) {
564:                     if ($v != NULL) { // intentionally ==, skip NULLs & empty string
565:                         //  composite 'style' vs. 'others'
566:                         $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
567:                     }
568:                 }
569:                 if ($tmp === NULL) {
570:                     continue;
571:                 }
572: 
573:                 $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
574: 
575:             } else {
576:                 $value = (string) $value;
577:             }
578: 
579:             $s .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
580:         }
581: 
582:         $s = str_replace('@', '&#64;', $s);
583:         return $s;
584:     }
585: 
586: 
587: 
588:     /**
589:      * Clones all children too.
590:      */
591:     public function __clone()
592:     {
593:         foreach ($this->children as $key => $value) {
594:             if (is_object($value)) {
595:                 $this->children[$key] = clone $value;
596:             }
597:         }
598:     }
599: 
600: }
601: 
Nette Framework 2.0.1 (for PHP 5.2, prefixed) API API documentation generated by ApiGen 2.7.0