Commits

Anonymous committed e77a7d7

added missing stuff

Comments (0)

Files changed (19)

components/agent.php

-<?php
-/**
- * Helper for detecting user agents.
- *
- * @version 		1.0.1
- * @author 			Kiripolszky Károly <karcsi@ekezet.com>
- * @package 		page
- * @subpackage 	components
- */
-
-/**
- * User agent detection class.
- *
- * @package 		page
- * @subpackage 	components
- */
-class AgentComponent extends Loadable
-{
-	/**
-	 * The inspected user agent string.
-	 *
-	 * @var string
-	 */
-	private $userAgent = "";
-	function getUserAgent() { return $this->userAgent; }
-
-	/**
-	 * True if client is a mobile browser.
-	 *
-	 * @var boolean
-	 */
-	private $isMobile = false;
-	/**
-	 * @return boolean
-	 */
-	function isMobile() { return $this->isMobile; }
-
-	/**
-	 * Contains information about the browser.
-	 *
-	 * @var array
-	 * @see get_browser()
-	 */
-	private $browser = array();
-	/**
-	 * @return array
-	 */
-	function getBrowser() { return $this->browser; }
-
-	/**
-	 * @see Loadable::initialize()
-	 */
-	function initialize()
-	{
-		$this->update($_SERVER["HTTP_USER_AGENT"]);
-	}
-
-	/**
-	 * Updates client information.
-	 *
-	 * @param string $userAgent Optional user agent string.
-	 */
-	function update($userAgent=null)
-	{
-		$userAgent = empty($userAgent) ? $this->userAgent : $userAgent;
-		$this->userAgent = $userAgent;
-		$this->browser = @get_browser($userAgent, true);
-		$this->isMobile = stripos($userAgent, "Android") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "iPhone") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "Palm") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "Symbian") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "Mobile") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "MIDP") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "CLDC") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "Kindle") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "Windows CE") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "PPC") !== false;
-		$this->isMobile = $this->isMobile || stripos($userAgent, "Windows Phone") !== false;
-	}
-
-	/**
-	 * Magic properties for browser name testing.
-	 *
-	 * examples: $isIE, $isChrome, $isFirefox, etc.
-	 *
-	 * @param string $name
-	 * @return int
-	 * @see http://uk3.php.net/manual/en/misc.configuration.php#ini.browscap
-	 */
-	function __get($name)
-	{
-		if (strlen($name) > 2 && substr($name, 0, 2) === "is")
-		{
-			$what = substr($name, 2);
-			return intval($this->browser['browser'] === $what);
-		}
-		return 0;
-	}
-}
-?>
+<?php
+/**
+ * Helper for detecting user agents.
+ *
+ * @version 		1.0.1
+ * @author 			Kiripolszky Károly <karcsi@ekezet.com>
+ * @package 		page
+ * @subpackage 	components
+ */
+
+/**
+ * User agent detection class.
+ *
+ * @package 		page
+ * @subpackage 	components
+ */
+class AgentComponent extends Loadable
+{
+	/**
+	 * The inspected user agent string.
+	 *
+	 * @var string
+	 */
+	private $userAgent = "";
+	function getUserAgent() { return $this->userAgent; }
+
+	/**
+	 * True if client is a mobile browser.
+	 *
+	 * @var boolean
+	 */
+	private $isMobile = false;
+	/**
+	 * @return boolean
+	 */
+	function isMobile() { return $this->isMobile; }
+
+	/**
+	 * Contains information about the browser.
+	 *
+	 * @var array
+	 * @see get_browser()
+	 */
+	private $browser = array();
+	/**
+	 * @return array
+	 */
+	function getBrowser() { return $this->browser; }
+
+	/**
+	 * @see Loadable::initialize()
+	 */
+	function initialize()
+	{
+		$this->update($_SERVER["HTTP_USER_AGENT"]);
+	}
+
+	/**
+	 * Updates client information.
+	 *
+	 * @param string $userAgent Optional user agent string.
+	 */
+	function update($userAgent=null)
+	{
+		$userAgent = empty($userAgent) ? $this->userAgent : $userAgent;
+		$this->userAgent = $userAgent;
+		$this->browser = @get_browser($userAgent, true);
+		$this->isMobile = stripos($userAgent, "Android") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "iPhone") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "Palm") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "Symbian") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "Mobile") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "MIDP") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "CLDC") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "Kindle") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "Windows CE") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "PPC") !== false;
+		$this->isMobile = $this->isMobile || stripos($userAgent, "Windows Phone") !== false;
+	}
+
+	/**
+	 * Magic properties for browser name testing.
+	 *
+	 * examples: $isIE, $isChrome, $isFirefox, etc.
+	 *
+	 * @param string $name
+	 * @return int
+	 * @see http://uk3.php.net/manual/en/misc.configuration.php#ini.browscap
+	 */
+	function __get($name)
+	{
+		if (strlen($name) > 2 && substr($name, 0, 2) === "is")
+		{
+			$what = substr($name, 2);
+			return intval($this->browser['browser'] === $what);
+		}
+		return 0;
+	}
+}
+?>

components/html.php

-<?php
-/**
- * Standard helper for creating HTML.
- *
- * @author 			Kiripolszky Károly <karcsi@ekezet.com>
- * @package 		page
- * @subpackage 	components
- */
-
-/**
- * Helper class for generating HTML quickly.
- *
- * @package 		page
- * @subpackage 	components
- */
-class HtmlComponent extends Loadable
-{
-	protected $scripts = array();
-	protected $stylesheets = array();
-
-	/**
-	 * Returns a string of the proper tag closing characters.
-	 *
-	 * @return string
-	 * @uses page::contentType()
-	 */
-	private function endTag()
-	{
-		$isXHTML = true;
-		if (is_a($this->parent, "PageController"))
-		{
-			$isXHTML = in_array(page::contentType(), array("xhtml", "html5"));
-		}
-		return $isXHTML ? " />" : ">";
-	}
-
-	/**
-	 * Creates proper XML attribute list from an associative array having its
-	 * attribute names and their values.
-	 *
-	 * @param array $a
-	 * @return string
-	 */
-	public function toXmlAttrs($attrs)
-	{
-		if (empty($attrs) || !is_array($attrs))
-			return "";
-		$tmp = array();
-		foreach ($attrs as $k => $v)
-		{
-			$tmp[] = "$k=\"$v\"";
-		}
-		return " " . implode(" ", $tmp);
-	}
-
-	/**
-	 * Creates a proper content-type metatag.
-	 *
-	 * @param boolean $return
-	 * @return mixed Depends on return.
-	 * @uses page::charset()
-	 * @uses page::mimeType()
-	 * @uses e()
-	 */
-	function contentType($return = false)
-	{
-		if (page::contentType() == "html5")
-			$s = sprintf('<meta charset="%s" />', page::charset());
-		else
-			$s = sprintf('<meta http-equiv="Content-Type" content="%s; charset=%s"' . $this->endTag(), page::mimeType(), page::charset());
-		return e("$s\n", $return);
-	}
-
-	/**
-	 * Creates link tags for including the styleheets.
-	 *
-	 * @param boolean $return
-	 * @return mixed Depends on return.
-	 * @uses $stylesheets
-	 * @uses e()
-	 */
-	function stylesheets($return = false)
-	{
-		if (empty($this->stylesheets))
-			return false;
-		$s = "";
-		foreach ($this->stylesheets as $id => $stylesheet)
-		{
-			$attrs = $stylesheet['attrs'];
-			if (!isset($attrs['rel']))
-				$attrs['rel'] = "stylesheet";
-			if (!isset($attrs['type']))
-				$attrs['type'] = "text/css";
-			$s .= sprintf('<link%s href="%s"' . $this->endTag(), $this->toXmlAttrs($attrs), uri($stylesheet['uri']));
-			$s .= "\n";
-		}
-		return e($s, $return);
-	}
-
-	/**
-	 * Add a stylesheet to the page.
-	 *
-	 * @param string $uri The Unique Resource Identifier.
-	 * @param array $attrs Additional attributes.
-	 * @uses $stylesheets
-	 */
-	function addStylesheet($uri, $attrs = array())
-	{
-		$this->stylesheets[md5($uri)] = array('uri' => $uri, 'attrs' => $attrs);
-	}
-
-	/**
-	 * @return array
-	 * @uses $stylesheets
-	 */
-	function getStylesheets()
-	{
-		return $this->stylesheets;
-	}
-
-	/**
-	 * @param array $stylesheets
-	 * @uses $stylesheets
-	 */
-	function setStylesheets($stylesheets)
-	{
-		$this->stylesheets = $stylesheets;
-	}
-
-	/**
-	 * Creates link tags for including the scripts.
-	 *
-	 * @param boolean $return
-	 * @return mixed Depends on return.
-	 * @uses $scripts
-	 * @uses e()
-	 */
-	function scripts($return = false)
-	{
-		if (empty($this->scripts))
-			return false;
-		$s = "";
-		foreach ($this->scripts as $id => $script)
-		{
-			$attrs = $script['attrs'];
-			if ($this->contentType === "html5" && isset($attrs['type']))
-				unset($attrs['type']);
-			$s .= sprintf("<script%s></script>", $this->toXmlAttrs($attrs));
-			$s .= "\n";
-		}
-		return e($s, $return);
-	}
-
-	/**
-	 * Add a script to the page.
-	 *
-	 * @param string $uri The Unique Resource Identifier.
-	 * @param array $attrs Additional attributes.
-	 * @uses $scripts
-	 */
-	function addScript($uri, $attrs = array())
-	{
-		$this->scripts[md5($uri)] = array('uri' => $uri, 'attrs' => $attrs);
-	}
-
-	/**
-	 * @return array
-	 * @uses $scripts
-	 */
-	function getScripts()
-	{
-		return $this->scripts;
-	}
-
-	/**
-	 * @param array $scripts
-	 * @uses $cripts
-	 */
-	function setScripts($scripts)
-	{
-		$this->scripts = $scripts;
-	}
-
-	/**
-	 * Renders a reusable element (a piece of template).
-	 *
-	 * @param string $name Element name.
-	 * @param array $vars Template variables.
-	 * @param boolean $return
-	 * @return mixed False on error.
-	 * @uses $parent
-	 * @uses Controller::getHelpers()
-	 * @uses Geg::render()
-	 */
-	function element($name, $vars = array(), $return = false)
-	{
-		if (!is_a($this->parent, 'Controller'))
-		{
-			return e("<!-- HtmlComponent.element() FAIL [$name] -->", $return);
-		}
-		$vars["me"] = $this->parent;
-		$ecs = $this->parent->getHelpers();
-		if (!empty($ecs))
-			foreach ($ecs as $ec_name => $ec_obj)
-				$vars[$ec_name] = $ec_obj;
-		$doc = Geg::render('elements' . DS . $name, $vars, $return);
-		if (empty($doc))
-			return e("<!-- HtmlComponent.element() RENDER ERROR [$name] -->", $return);
-		return $doc;
-	}
-
-	/**
-	 * Creates an anchor tag.
-	 *
-	 * @param string $uri A relative or absolute URI.
-	 * @param string $name Becomes the URI when omitted.
-	 * @param array $attrs Additional attributes.
-	 * @param bool $return
-	 * @return mixed Depends on return.
-	 * @uses e()
-	 */
-	function link($uri = 'javascript:;', $name = false, $attrs = array(), $return = false)
-	{
-		$name = empty($name) ? $uri : $name;
-		if ($name == $uri && strlen($uri) >= 60)
-			$name = substr($name, 0, 59) . "&#x2026;";
-		$s = sprintf('<a href="%s"%s>%s</a>', uri($uri), $this->toXmlAttrs($attrs), $name);
-		return e($s, $return);
-	}
-
-	/**
-	 * Creates and anchor tag with a static URL.
-	 *
-	 * @param string $uri A relative or absolute URI.
-	 * @param string $name Becomes the URI when omitted.
-	 * @param array $attrs Additional attributes.
-	 * @param bool $return
-	 * @return mixed Depends on return.
-	 * @uses e()
-	 */
-	function links($uri, $name = false, $attrs = array(), $return = false)
-	{
-		$prefix = Config::get('app_url');
-		if (empty($prefix))
-			return $this->link($uri, $name, $attrs, $return);
-		$name = empty($name) ? $uri : $name;
-		if ($name == $uri && strlen($uri) >= 60)
-			$name = substr($name, 0, 59) . "&#x2026;";
-		$s = sprintf('<a href="%s"%s>%s</a>', $prefix . $uri, $this->toXmlAttrs($attrs), $name);
-		return e($s, $return);
-	}
-
-	/**
-	 * Creates a markup tag.
-	 *
-	 * @param string $name Name of tag.
-	 * @param array $attrs Array of attributes.
-	 * @param string $inner_html Set to an empty value for standalone tags like br
-	 * and hr.
-	 * @return string
-	 * @uses e()
-	 */
-	function tag($name, $inner_html = "", $attrs = array(), $return = true)
-	{
-		$s = "";
-		if ($inner_html === "")
-			$s = sprintf("<%s%s%s", $name, $this->toXmlAttrs($attrs), $this->endTag());
-		else
-			$s = sprintf("<%s%s>%s</%s>", $name, $this->toXmlAttrs($attrs), $inner_html, $name);
-		return e($s, $return);
-	}
-
-	/**
-	 * Creates an image tag.
-	 *
-	 * @param string $src URI of the image.
-	 * @param array $attrs Additional attributes.
-	 * @param bool $return
-	 * @return mixed Depends on return.
-	 * @uses e()
-	 */
-	function img($src, $attrs = array(), $return = false)
-	{
-		if (!isset($attrs['alt']))
-			$attrs['alt'] = basename($src);
-		$uri = uri($src);
-		if (function_exists("getimagesize") && strpos($src, "://") === false)
-		{
-			$imginfo = getimagesize(getcwd().$uri);
-			if ($imginfo !== false)
-			{
-				list($width, $height, $type, $attr) = $imginfo;
-				$attrs['width'] = $width;
-				$attrs['height'] = $height;
-			}
-		}
-		$s = sprintf('<img src="%s"%s' . $this->endTag(), $uri, $this->toXmlAttrs($attrs));
-		return e($s, $return);
-	}
-
-	/**
-	 * Creates a link tag to a favicon.
-	 *
-	 * @param string $src URI of the icon.
-	 * @param array $attrs Additional attributes.
-	 * @param bool $return
-	 * @return mixed Depends on return.
-	 * @uses e()
-	 */
-	function shortcutIcon($src, $type, $return = false)
-	{
-		$s = sprintf('<link rel="shortcut icon" type="%s" href="%s"' . $this->endTag(), $type, uri($src));
-		return e($s, $return);
-	}
-
-}
+<?php
+/**
+ * Standard helper for creating HTML.
+ *
+ * @author 			Kiripolszky Károly <karcsi@ekezet.com>
+ * @package 		page
+ * @subpackage 	components
+ */
+
+/**
+ * Helper class for generating HTML quickly.
+ *
+ * @package 		page
+ * @subpackage 	components
+ */
+class HtmlComponent extends Loadable
+{
+	protected $scripts = array();
+	protected $stylesheets = array();
+
+	/**
+	 * Returns a string of the proper tag closing characters.
+	 *
+	 * @return string
+	 * @uses page::contentType()
+	 */
+	private function endTag()
+	{
+		$isXHTML = true;
+		if (is_a($this->parent, "PageController"))
+		{
+			$isXHTML = in_array(page::contentType(), array("xhtml", "html5"));
+		}
+		return $isXHTML ? " />" : ">";
+	}
+
+	/**
+	 * Creates proper XML attribute list from an associative array having its
+	 * attribute names and their values.
+	 *
+	 * @param array $a
+	 * @return string
+	 */
+	public function toXmlAttrs($attrs)
+	{
+		if (empty($attrs) || !is_array($attrs))
+			return "";
+		$tmp = array();
+		foreach ($attrs as $k => $v)
+		{
+			$tmp[] = "$k=\"$v\"";
+		}
+		return " " . implode(" ", $tmp);
+	}
+
+	/**
+	 * Creates a proper content-type metatag.
+	 *
+	 * @param boolean $return
+	 * @return mixed Depends on return.
+	 * @uses page::charset()
+	 * @uses page::mimeType()
+	 * @uses e()
+	 */
+	function contentType($return = false)
+	{
+		if (page::contentType() == "html5")
+			$s = sprintf('<meta charset="%s" />', page::charset());
+		else
+			$s = sprintf('<meta http-equiv="Content-Type" content="%s; charset=%s"' . $this->endTag(), page::mimeType(), page::charset());
+		return e("$s\n", $return);
+	}
+
+	/**
+	 * Creates link tags for including the styleheets.
+	 *
+	 * @param boolean $return
+	 * @return mixed Depends on return.
+	 * @uses $stylesheets
+	 * @uses e()
+	 */
+	function stylesheets($return = false)
+	{
+		if (empty($this->stylesheets))
+			return false;
+		$s = "";
+		foreach ($this->stylesheets as $id => $stylesheet)
+		{
+			$attrs = $stylesheet['attrs'];
+			if (!isset($attrs['rel']))
+				$attrs['rel'] = "stylesheet";
+			if (!isset($attrs['type']))
+				$attrs['type'] = "text/css";
+			$s .= sprintf('<link%s href="%s"' . $this->endTag(), $this->toXmlAttrs($attrs), uri($stylesheet['uri']));
+			$s .= "\n";
+		}
+		return e($s, $return);
+	}
+
+	/**
+	 * Add a stylesheet to the page.
+	 *
+	 * @param string $uri The Unique Resource Identifier.
+	 * @param array $attrs Additional attributes.
+	 * @uses $stylesheets
+	 */
+	function addStylesheet($uri, $attrs = array())
+	{
+		$this->stylesheets[md5($uri)] = array('uri' => $uri, 'attrs' => $attrs);
+	}
+
+	/**
+	 * @return array
+	 * @uses $stylesheets
+	 */
+	function getStylesheets()
+	{
+		return $this->stylesheets;
+	}
+
+	/**
+	 * @param array $stylesheets
+	 * @uses $stylesheets
+	 */
+	function setStylesheets($stylesheets)
+	{
+		$this->stylesheets = $stylesheets;
+	}
+
+	/**
+	 * Creates link tags for including the scripts.
+	 *
+	 * @param boolean $return
+	 * @return mixed Depends on return.
+	 * @uses $scripts
+	 * @uses e()
+	 */
+	function scripts($return = false)
+	{
+		if (empty($this->scripts))
+			return false;
+		$s = "";
+		foreach ($this->scripts as $id => $script)
+		{
+			$attrs = $script['attrs'];
+			if ($this->contentType === "html5" && isset($attrs['type']))
+				unset($attrs['type']);
+			$s .= sprintf("<script%s></script>", $this->toXmlAttrs($attrs));
+			$s .= "\n";
+		}
+		return e($s, $return);
+	}
+
+	/**
+	 * Add a script to the page.
+	 *
+	 * @param string $uri The Unique Resource Identifier.
+	 * @param array $attrs Additional attributes.
+	 * @uses $scripts
+	 */
+	function addScript($uri, $attrs = array())
+	{
+		$this->scripts[md5($uri)] = array('uri' => $uri, 'attrs' => $attrs);
+	}
+
+	/**
+	 * @return array
+	 * @uses $scripts
+	 */
+	function getScripts()
+	{
+		return $this->scripts;
+	}
+
+	/**
+	 * @param array $scripts
+	 * @uses $cripts
+	 */
+	function setScripts($scripts)
+	{
+		$this->scripts = $scripts;
+	}
+
+	/**
+	 * Renders a reusable element (a piece of template).
+	 *
+	 * @param string $name Element name.
+	 * @param array $vars Template variables.
+	 * @param boolean $return
+	 * @return mixed False on error.
+	 * @uses $parent
+	 * @uses Controller::getHelpers()
+	 * @uses Geg::render()
+	 */
+	function element($name, $vars = array(), $return = false)
+	{
+		if (!is_a($this->parent, 'Controller'))
+		{
+			return e("<!-- HtmlComponent.element() FAIL [$name] -->", $return);
+		}
+		$vars["me"] = $this->parent;
+		$ecs = $this->parent->getHelpers();
+		if (!empty($ecs))
+			foreach ($ecs as $ec_name => $ec_obj)
+				$vars[$ec_name] = $ec_obj;
+		$doc = Geg::render('elements' . DS . $name, $vars, $return);
+		if (empty($doc))
+			return e("<!-- HtmlComponent.element() RENDER ERROR [$name] -->", $return);
+		return $doc;
+	}
+
+	/**
+	 * Creates an anchor tag.
+	 *
+	 * @param string $uri A relative or absolute URI.
+	 * @param string $name Becomes the URI when omitted.
+	 * @param array $attrs Additional attributes.
+	 * @param bool $return
+	 * @return mixed Depends on return.
+	 * @uses e()
+	 */
+	function link($uri = 'javascript:;', $name = false, $attrs = array(), $return = false)
+	{
+		$name = empty($name) ? $uri : $name;
+		if ($name == $uri && strlen($uri) >= 60)
+			$name = substr($name, 0, 59) . "&#x2026;";
+		$s = sprintf('<a href="%s"%s>%s</a>', uri($uri), $this->toXmlAttrs($attrs), $name);
+		return e($s, $return);
+	}
+
+	/**
+	 * Creates and anchor tag with a static URL.
+	 *
+	 * @param string $uri A relative or absolute URI.
+	 * @param string $name Becomes the URI when omitted.
+	 * @param array $attrs Additional attributes.
+	 * @param bool $return
+	 * @return mixed Depends on return.
+	 * @uses e()
+	 */
+	function links($uri, $name = false, $attrs = array(), $return = false)
+	{
+		$prefix = Config::get('app_url');
+		if (empty($prefix))
+			return $this->link($uri, $name, $attrs, $return);
+		$name = empty($name) ? $uri : $name;
+		if ($name == $uri && strlen($uri) >= 60)
+			$name = substr($name, 0, 59) . "&#x2026;";
+		$s = sprintf('<a href="%s"%s>%s</a>', $prefix . $uri, $this->toXmlAttrs($attrs), $name);
+		return e($s, $return);
+	}
+
+	/**
+	 * Creates a markup tag.
+	 *
+	 * @param string $name Name of tag.
+	 * @param array $attrs Array of attributes.
+	 * @param string $inner_html Set to an empty value for standalone tags like br
+	 * and hr.
+	 * @return string
+	 * @uses e()
+	 */
+	function tag($name, $inner_html = "", $attrs = array(), $return = true)
+	{
+		$s = "";
+		if ($inner_html === "")
+			$s = sprintf("<%s%s%s", $name, $this->toXmlAttrs($attrs), $this->endTag());
+		else
+			$s = sprintf("<%s%s>%s</%s>", $name, $this->toXmlAttrs($attrs), $inner_html, $name);
+		return e($s, $return);
+	}
+
+	/**
+	 * Creates an image tag.
+	 *
+	 * @param string $src URI of the image.
+	 * @param array $attrs Additional attributes.
+	 * @param bool $return
+	 * @return mixed Depends on return.
+	 * @uses e()
+	 */
+	function img($src, $attrs = array(), $return = false)
+	{
+		if (!isset($attrs['alt']))
+			$attrs['alt'] = basename($src);
+		$uri = uri($src);
+		if (function_exists("getimagesize") && strpos($src, "://") === false)
+		{
+			$imginfo = getimagesize(getcwd().$uri);
+			if ($imginfo !== false)
+			{
+				list($width, $height, $type, $attr) = $imginfo;
+				$attrs['width'] = $width;
+				$attrs['height'] = $height;
+			}
+		}
+		$s = sprintf('<img src="%s"%s' . $this->endTag(), $uri, $this->toXmlAttrs($attrs));
+		return e($s, $return);
+	}
+
+	/**
+	 * Creates a link tag to a favicon.
+	 *
+	 * @param string $src URI of the icon.
+	 * @param array $attrs Additional attributes.
+	 * @param bool $return
+	 * @return mixed Depends on return.
+	 * @uses e()
+	 */
+	function shortcutIcon($src, $type, $return = false)
+	{
+		$s = sprintf('<link rel="shortcut icon" type="%s" href="%s"' . $this->endTag(), $type, uri($src));
+		return e($s, $return);
+	}
+
+}
 ?>

components/session.php

-<?php
-/**
- * Session data helper.
- *
- * @author 			Kiripolszky Károly <karcsi@ekezet.com>
- * @package 		page
- * @subpackage 	components
- */
-
-/**
- * Session helper class.
- *
- * @package 		page
- * @subpackage 	components
- */
-class SessionComponent extends Loadable
-{
-	private $data = null;
-
-	/**
-	 * @see Loadable::initialize()
-	 */
-	function initialize()
-	{
-		session_name(Config::get("session_cookie"));
-		session_start();
-		$this->data = $_SESSION;
-	}
-
-	function reset()
-	{
-		$tmp = $this->data;
-		$this->destroy();
-		$this->initialize();
-		$this->data = $tmp;
-		$_SESSION = $tmp;
-	}
-
-	function regenerate()
-	{
-		$this->data = $_SESSION;
-		session_regenerate_id(true);
-		session_id(sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].microtime()));
-		$_SESSION = $this->data;
-	}
-
-	function finalize()
-	{
-		session_write_close();
-	}
-
-	function destroy()
-	{
-		$_SESSION = null;
-		$this->data = null;
-		session_destroy();
-	}
-
-	function write($key, $value)
-	{
-		$ret = mawrite($key, $value, $this->data);
-		$_SESSION = $this->data;
-		return $ret;
-	}
-
-	function read($key, $default=false)
-	{
-		return maread($key, $this->data, $default);
-	}
-
-	function del($key)
-	{
-		$ret = madel($key, $this->data);
-		$_SESSION = $this->data;
-		return $ret;
-	}
-}
+<?php
+/**
+ * Session data helper.
+ *
+ * @author 			Kiripolszky Károly <karcsi@ekezet.com>
+ * @package 		page
+ * @subpackage 	components
+ */
+
+/**
+ * Session helper class.
+ *
+ * @package 		page
+ * @subpackage 	components
+ */
+class SessionComponent extends Loadable
+{
+	private $data = null;
+
+	/**
+	 * @see Loadable::initialize()
+	 */
+	function initialize()
+	{
+		session_name(Config::get("session_cookie"));
+		session_start();
+		$this->data = $_SESSION;
+	}
+
+	function reset()
+	{
+		$tmp = $this->data;
+		$this->destroy();
+		$this->initialize();
+		$this->data = $tmp;
+		$_SESSION = $tmp;
+	}
+
+	function regenerate()
+	{
+		$this->data = $_SESSION;
+		session_regenerate_id(true);
+		session_id(sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].microtime()));
+		$_SESSION = $this->data;
+	}
+
+	function finalize()
+	{
+		session_write_close();
+	}
+
+	function destroy()
+	{
+		$_SESSION = null;
+		$this->data = null;
+		session_destroy();
+	}
+
+	function write($key, $value)
+	{
+		$ret = mawrite($key, $value, $this->data);
+		$_SESSION = $this->data;
+		return $ret;
+	}
+
+	function read($key, $default=false)
+	{
+		return maread($key, $this->data, $default);
+	}
+
+	function del($key)
+	{
+		$ret = madel($key, $this->data);
+		$_SESSION = $this->data;
+		return $ret;
+	}
+}
 ?>

components/widgets.php

-<?php
-/**
- * Widget support for page.
- *
- * @author Károly Kiripolszky <karcsi@ekezet.com>
- * @package page
- * @subpackage components
- */
-
-/**
- * Widget helper component.
- *
- * @package page
- * @subpackage components
- */
-class WidgetsComponent extends Loadable
-{
-	function render($name, $options=array(), $return=false)
-	{
-		$options['action'] = maread("action", $options, "index");
-		$options['path'] = maread("path", $options, APP_ROOT."widgets".DS.$name.DS.$name.".php");
-		$options['controller'] = maread("controller", $options, $name);
-		$options['className'] = maread("className", $options, cc($name)."Controller");
-
-		/**
-		 * @var PageController
-		 */
-		$ctrl = Lib::load("widgets", $name, $options);
-
-		$ctrl->viewPath = APP_ROOT."widgets".DS.$name.DS."views".DS;
-		$ctrl->doAction();
-		$s = $ctrl->render();
-		return e($s, $return);
-	}
-}
+<?php
+/**
+ * Widget support for page.
+ *
+ * @author Károly Kiripolszky <karcsi@ekezet.com>
+ * @package page
+ * @subpackage components
+ */
+
+/**
+ * Widget helper component.
+ *
+ * @package page
+ * @subpackage components
+ */
+class WidgetsComponent extends Loadable
+{
+	function render($name, $options=array(), $return=false)
+	{
+		$options['action'] = maread("action", $options, "index");
+		$options['path'] = maread("path", $options, APP_ROOT."widgets".DS.$name.DS.$name.".php");
+		$options['controller'] = maread("controller", $options, $name);
+		$options['className'] = maread("className", $options, cc($name)."Controller");
+
+		/**
+		 * @var PageController
+		 */
+		$ctrl = Lib::load("widgets", $name, $options);
+
+		$ctrl->viewPath = APP_ROOT."widgets".DS.$name.DS."views".DS;
+		$ctrl->doAction();
+		$s = $ctrl->render();
+		return e($s, $return);
+	}
+}
 ?>
-<?php
-/**
- * A static class to hold configuration data.
- *
- * @author Kiripolszky Károly <karcsi@ekezet.com>
- * @package page
- * @subpackage core
- */
-
-/**
- * Configuration class.
- *
- * @package page
- */
-final class Config
-{
-	static private $values = array();
-	static private $loaded = false;
-
-	/**
-	 * Reset all to factory defaults.
-	 *
-	 * {@source 3 26}
-	 */
-	final static public function loadDefaults()
-	{
-		self::$values = array(
-			'debug'   => 0,
-			'charset' => "utf-8",
-			'content_types' => array(
-				"html"   => "text/html",
-				"html5"   => "text/html",
-				"xhtml"  => "application/xhtml+xml",
-				"xml"    => "text/xml",
-				"json"   => "application/json",
-				"css"    => "text/css",
-				"js"     => "text/javascript",
-				"txt"    => "text/plain",
-				"stream" => "application/octet-stream"
-			),
-			'default_title'					=> 'Page',
-			'default_content_type'	=> 'html5',
-			'default_layout'				=> 'default',
-			'database_connection'		=> 'default'
-		);
-	}
-
-	/**
-	 * Includes the files from the config folder.
-	 *
-	 * @param boolean $force Force reloading.
-	 * @return boolean Returns true if config files are found and loaded.
-	 * @uses CONFIG_DIR
-	 */
-	final static public function loadFromDisk($force=false)
-	{
-		if (self::$loaded && !$force) return;
-		self::loadDefaults();
-		$cfg_files = array(
-			CONFIG_DIR."includes.php",
-			CONFIG_DIR."options.php",
-			CONFIG_DIR."urls.php"
-		);
-		foreach ($cfg_files as $cfg)
-		{
-			if (!file_exists($cfg))
-				return false;
-			else
-			 include_once($cfg);
-		}
-		self::$loaded = true;
-		return true;
-	}
-
-	/**
-	 * Returns true if config was loaded from disk.
-	 *
-	 * @return boolean
-	 */
-	final static public function isLoaded()
-	{
-		return self::$loaded;
-	}
-
-	/**
-	 * Gets the value of a configuration option.
-	 *
-	 * @param string $key
-	 * @param mixed $default
-	 * @return mixed
-	 * @see maread()
-	 */
-	final static public function get($key, $default=false)
-	{
-		return maread($key, self::$values, $default);
-	}
-
-	/**
-	 * Sets the value of a configuration option.
-	 *
-	 * @param string $key
-	 * @param mixed $value
-	 * @return boolean
-	 * @see mawrite()
-	 */
-	final static public function set($key, $value)
-	{
-		return mawrite($key, $value, self::$values);
-	}
-
-	/**
-	 * Shortcut for getting debug settings.
-	 *
-	 * @return integer
-	 */
-	final static public function debug()
-	{
-		return (int) self::$values['debug'];
-	}
-
-	/**
-	 * Adds a new content type to be recognized by the system.
-	 *
-	 * @final
-	 * @static
-	 * @param string $alias Short name for type, like "mpg".
-	 * @param string $mime_type Mime type for content, like "video/mpeg".
-	 */
-	final static public function addContentType($alias, $mime_type)
-	{
-		$types = self::get('content_types');
-		$types[$alias] = $mime_type;
-		self::set("content_types", $types);
-	}
-
-	/**
-	 * Tries to guess the mime type of a file.
-	 *
-	 * @param string $path Path to a file.
-	 */
-	final static public function guessMimeType($path)
-	{
-		$info = pathinfo($path, PATHINFO_EXTENSION);
-		return maread($info, self::$values["content_types"], self::$values["stream"]);
-	}
-
-	final private function __construct() { }
-
-	final private function __clone() { }
-}
+<?php
+/**
+ * A static class to hold configuration data.
+ *
+ * @author Kiripolszky Károly <karcsi@ekezet.com>
+ * @package page
+ * @subpackage core
+ */
+
+/**
+ * Configuration class.
+ *
+ * @package page
+ */
+final class Config
+{
+	static private $values = array();
+	static private $loaded = false;
+
+	/**
+	 * Reset all to factory defaults.
+	 *
+	 * {@source 3 26}
+	 */
+	final static public function loadDefaults()
+	{
+		self::$values = array(
+			'debug'   => 0,
+			'charset' => "utf-8",
+			'content_types' => array(
+				"html"   => "text/html",
+				"html5"   => "text/html",
+				"xhtml"  => "application/xhtml+xml",
+				"xml"    => "text/xml",
+				"json"   => "application/json",
+				"css"    => "text/css",
+				"js"     => "text/javascript",
+				"txt"    => "text/plain",
+				"stream" => "application/octet-stream"
+			),
+			'default_title'					=> 'Page',
+			'default_content_type'	=> 'html5',
+			'default_layout'				=> 'default',
+			'database_connection'		=> 'default'
+		);
+	}
+
+	/**
+	 * Includes the files from the config folder.
+	 *
+	 * @param boolean $force Force reloading.
+	 * @return boolean Returns true if config files are found and loaded.
+	 * @uses CONFIG_DIR
+	 */
+	final static public function loadFromDisk($force=false)
+	{
+		if (self::$loaded && !$force) return;
+		self::loadDefaults();
+		$cfg_files = array(
+			CONFIG_DIR."includes.php",
+			CONFIG_DIR."options.php",
+			CONFIG_DIR."urls.php"
+		);
+		foreach ($cfg_files as $cfg)
+		{
+			if (!file_exists($cfg))
+				return false;
+			else
+			 include_once($cfg);
+		}
+		self::$loaded = true;
+		return true;
+	}
+
+	/**
+	 * Returns true if config was loaded from disk.
+	 *
+	 * @return boolean
+	 */
+	final static public function isLoaded()
+	{
+		return self::$loaded;
+	}
+
+	/**
+	 * Gets the value of a configuration option.
+	 *
+	 * @param string $key
+	 * @param mixed $default
+	 * @return mixed
+	 * @see maread()
+	 */
+	final static public function get($key, $default=false)
+	{
+		return maread($key, self::$values, $default);
+	}
+
+	/**
+	 * Sets the value of a configuration option.
+	 *
+	 * @param string $key
+	 * @param mixed $value
+	 * @return boolean
+	 * @see mawrite()
+	 */
+	final static public function set($key, $value)
+	{
+		return mawrite($key, $value, self::$values);
+	}
+
+	/**
+	 * Shortcut for getting debug settings.
+	 *
+	 * @return integer
+	 */
+	final static public function debug()
+	{
+		return (int) self::$values['debug'];
+	}
+
+	/**
+	 * Adds a new content type to be recognized by the system.
+	 *
+	 * @final
+	 * @static
+	 * @param string $alias Short name for type, like "mpg".
+	 * @param string $mime_type Mime type for content, like "video/mpeg".
+	 */
+	final static public function addContentType($alias, $mime_type)
+	{
+		$types = self::get('content_types');
+		$types[$alias] = $mime_type;
+		self::set("content_types", $types);
+	}
+
+	/**
+	 * Tries to guess the mime type of a file.
+	 *
+	 * @param string $path Path to a file.
+	 */
+	final static public function guessMimeType($path)
+	{
+		$info = pathinfo($path, PATHINFO_EXTENSION);
+		return maread($info, self::$values["content_types"], self::$values["stream"]);
+	}
+
+	final private function __construct() { }
+
+	final private function __clone() { }
+}
 ?>
-<?php
-/**
- * The Controller class responds to requests.
- *
- * Event callbacks:
- *
- *   beforeAction()
- *   afterAction()
- *   beforeRender()
- *   afterRender()
- *
- * @author Kiripolszky Károly <karcsi@ekezet.com>
- * @package page
- * @subpackage core
- * @uses Geg
- * @uses Lib
- * @uses Config
- */
-
-/**
- * Base Controller class.
- *
- * @package page
- * @subpackage core
- */
-class PageController extends Component
-{
-	/**
-	 * Name of the layout template (without extension).
-	 *
-	 * @var string
-	 */
-	var $layout = "";
-
-	/**
-	 * Name of the controller source (w/o ".php").
-	 *
-	 * @see Controller
-	 * @var string
-	 */
-	var $controller = "";
-
-	/**
-	 * Name of the current action.
-	 *
-	 * @var string
-	 */
-	var $action = "";
-
-	/**
-	 * The view (template) to render. It's {@link $action} by default.
-	 *
-	 * You can set this to render a view other than the action name.
-	 *
-	 * @var string
-	 */
-	var $view = "";
-	/**
-	 * Returns the name of the current view.
-	 *
-	 * @return string
-	 */
-	final public function getView() { return empty($this->view) ? $this->action : $this->view; }
-
-	/**
-	 * Overwrite the template lookup path for this controller.
-	 *
-	 * @var string
-	 */
-	var $viewPath = "";
-
-	/**
-	 * Rendered content.
-	 *
-	 * @var string
-	 */
-	var $content = "";
-
-	/**
-	 * Arguments derived from the request URI.
-	 *
-	 * @see Router
-	 * @var array
-	 */
-	var $args = array();
-
-	/**
-	 * @var string Name of the layout template (without extension).
-	 */
-	var $pageTitle = "";
-
-	/**
-	 * Response charset, eg. "utf-8", etc.
-	 *
-	 * @var string
-	 */
-	var $charset = "";
-	/**
-	 * Returns current response charset.
-	 *
-	 * @return string
-	 */
-	final public function charset() { return $this->charset; }
-
-	/**
-	 * Components exposed to templates.
-	 *
-	 * @var array
-	 */
-	var $helpers = array();
-
-	/**
-	 * Models used by this controller.
-	 *
-	 * @var array
-	 */
-	var $models = array();
-
-	/**
-	 * Template variables.
-	 *
-	 * @var array
-	 */
-	protected $vars = array();
-	/**
-	 * Returns the template variables.
-	 *
-	 * @return array
-	 */
-	final public function getVars() { return $this->vars; }
-	/**
-	 * Sets one or more template variable.
-	 *
-	 * @param string|array $key Variable name or array of variables.
-	 * @param mixed $value Value for the variable.
-	 * @return bool
-	 * @see mawrite()
-	 */
-	final public function set($key, $value=true)
-	{
-		if (is_array($key))
-		{
-			foreach($key as $k => $v)
-				mawrite($k, $v, $this->vars);
-			return true;
-		}
-		return mawrite($key, $value, $this->vars);
-	}
-	/**
-	 * Gets the value of a template variable.
-	 *
-	 * @param string $key
-	 * @return mixed
-	 * @see maread()
-	 */
-	final public function get($key) { return maread($key, $this->vars); }
-
-	/**
-	 * @var array Page HTTP headers.
-	 * @since 0.2
-	 */
-	protected $headers = array();
-
-	/**
-	 * Response content type, like "html", "json", etc.
-	 *
-	 * @var string
-	 * @see Config::loadDefaults()
-	 */
-	protected $contentType = "";
-	/**
-	 * Sets the response content type for the current request.
-	 *
-	 * The content type must already be added to the configuration!
-	 *
-	 * @param string $type Internal content type alias, like "html", "json", etc.
-	 * @param string $charset Set the character set as well. Can be false to omit.
-	 * @return bool True if content type is successfully set.
-	 * @see Config::addContentType()
-	 */
-	final public function setContentType($type, $charset=null)
-	{
-		$types = array_keys(Config::get("content_types"));
-		if (!in_array($type, $types) || empty($type))
-			return false;
-		$this->contentType = $type;
-		$this->charset = empty($charset) ? $this->charset : $charset;
-		return true;
-	}
-	/**
-	 * Returns current response content type.
-	 *
-	 * @return string
-	 */
-	final public function contentType() { return $this->contentType; }
-	/**
-	 * Returns a MIME type for the current request, eg. "text/html".
-	 *
-	 * @return string
-	 * @uses $contentType
-	 */
-	final public function mimeType($type=null)
-	{
-		$types = Config::get("content_types");
-		$type = empty($type) ? $this->contentType : $type;
-		return $types[$type];
-	}
-
-	/**
-	 * Enable/disable HTTP caching for this controller.
-	 *
-	 * @var bool
-	 * @since 0.2
-	 */
-	protected $useCache = true;
-
-
-	/**
-	 * Perform a controller action.
-	 *
-	 * @param string $action Action name or null.
-	 * @uses beforeAction()
-	 * @uses afterAction()
-	 */
-	final public function doAction($action=null)
-	{
-		$action = empty($action) ? $this->action : $action;
-		$this->action = $action;
-		$this->beforeAction();
-		foreach ($this->helpers as $helper_id => $helper_opts)
-			if (is_numeric($helper_id))
-				$this->components []= $helper_opts;
-			else
-				$this->components[$helper_id] = $this->helpers[$helper_id];
-		$this->loadComponents();
-		$this->loadModels();
-		$code = sprintf('$this->%s(%s);', $action, implode(", ", quotes($this->args)));
-		eval($code);
-		$this->afterAction();
-	}
-
-	/**
-	 * Tells the app to setup headers and render the page into {@link $content}.
-	 *
-	 * @param string $view Overrides {@link $action} if not null.
-	 * @param string $layout Overrides {@link $layout} if not null.
-	 * @uses Config
-	 * @uses Geg::render()
-	 */
-	final public function render($view=null, $layout=null)
-	{
-		// setup variables
-		$start_time = microtime(true);
-		$this->beforeRender();
-		$view = empty($view) ? $this->getView() : $view;
-		$layout = empty($layout) ? (empty($this->layout) ? Config::get("default_layout") : $this->layout) : $layout;
-		$this->charset = empty($this->charset) ? Config::get("charset") : $this->charset;
-		$this->setContentType(empty($this->contentType) ? Config::get("default_content_type") : $this->contentType);
-
-		// check if client can accept the response content type
-		if ($this->accepts() == 0)
-		{
-			// look for an appropriate content type
-			$types = Config::get("content_types");
-			$fallback = false;
-
-			foreach ($types as $alias => $mime)
-			{
-				if ($this->accepts($alias))
-				{
-					$this->setContentType($alias);
-					$fallback = true;
-					break;
-				}
-			}
-
-			if (!$fallback)
-				htthrow(406);
-		}
-
-		// check for charset support
-		if ($this->acceptsCharset() == 0)
-			// charset not supported by client
-			htthrow(406);
-
-		$content_dir = $this->contentType == Config::get("default_content_type") ? DS : DS.$this->contentType.DS;
-		$this->pageTitle = empty($this->pageTitle) ? Config::get("default_title") : $this->pageTitle;
-		$this->set("me", $this);
-
-		// add helper components as template variables
-		$helpers = $this->getHelpers();
-		foreach ($helpers as $name => $obj)
-			$this->vars[$name] = $obj;
-
-		/*
-		 * Rendering below:
-		 */
-
-		$view_path = $this->viewPath.$this->controller.$content_dir.$view;
-		$content_for_layout = Geg::render($view_path, $this->vars, true);
-
-		// this might be the first failed attempt to render the layout content
-		if ($content_for_layout === false)
-		{
-			// ...on error look also in default view path
-			$view_path = $this->viewPath.$this->controller.DS.$view;
-			$content_for_layout = Geg::render($view_path, $this->vars, true);
-		}
-		// did the rendering fail again?
-		if ($content_for_layout === false && get_class($this) !== "ErrorController")
-		{
-			// if debug mode is on throw view_not_found error
-			if (Config::debug() > 0)
-				page::launchErrorController("view_not_found");
-			// ...otherwise it's the ultimate error
-			else
-				page::launchErrorController("not_found");
-		}
-
-		$this->set("content_for_layout", $content_for_layout);
-
-		// rendering layout...
-		$layout_path = $this->viewPath."layout".$content_dir.$layout;
-		$this->content = Geg::render($layout_path, $this->vars, true);
-		if ($this->content === false)
-		{
-			// ...on error look also in default layout path
-			$layout_path = $this->viewPath."layout".DS.$layout;
-			$this->content = Geg::render($layout_path, $this->vars, true);
-		}
-
-		// afterRender callback may modify the content
-		$this->afterRender();
-
-		// controller content self-check
-		if ($this->content === false)
-		{
-			// layout_not_found error
-			if (Config::debug() > 0)
-				page::launchErrorController("layout_not_found", array("layout"=>$layout));
-			// ultimate render error
-			else
-				page::launchErrorController("not_found");
-		}
-
-		// check cache and generate output
-		$this->checkCache(Geg::getPath($view_path), $this->content);
-		$this->addHeader("Content-Type: ".$this->mimeType()."; charset=".$this->charset);
-		if (Config::debug() == 0)
-			$this->addHeader("Content-Length: ".strlen($this->content), true, 200);
-		$time = microtime(true) - $start_time;
-		if (Config::debug() > 0 && in_array($this->contentType, array("html5", "html", "xhtml", "xml")))
-			$this->content .= "<!-- $time s -->";
-	}
-
-	/**
-	 * Returns true if HTTP_X_REQUESTED_WITH is set.
-	 *
-	 * @return bool
-	 */
-	final public function isAjax()
-	{
-		return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
-			(strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
-	}
-
-	/**
-	 * Returns a positive float larger than 0.0 if client accepts a certain content type.
-	 *
-	 * @param string $type Internal content type alias. Overrides {@link $contentType} if not null.
-	 * @return float Quality value (q parameter).
-	 * @see Config::addContentType()
-	 * @uses $contentType
-	 */
-	final public function accepts($type=null)
-	{
-		if (!isset($_SERVER['HTTP_ACCEPT']))
-			return 1.0;
-		$type = empty($type) ? $this->contentType : $type;
-		$types = explode(',', $_SERVER['HTTP_ACCEPT']);
-		foreach ($types as $mime)
-		{
-			$q = 1.0;
-			$duel = explode(";", $mime);
-			if (isset($duel[1]))
-				$q = floatval(substr(trim($duel[1]), 2));
-			if (fnmatch(trim($duel[0]), $this->mimeType($type)) && $q > 0)
-				return $q;
-		}
-		return 0.0;
-	}
-
-	/**
-	 * Checks if a charset is accepted by the client.
-	 *
-	 * @param string $charset Set to {@link $charset) if null.
-	 * @return float Quality value (q parameter).
-	 * @uses $charset
-	 */
-	final public function acceptsCharset($charset=null)
-	{
-		if (!isset($_SERVER['HTTP_ACCEPT_CHARSET']))
-			return true;
-		$charset = empty($charset) ? strtoupper($this->charset) : $charset;
-		$charsets = explode(',', $_SERVER['HTTP_ACCEPT_CHARSET']);
-		foreach ($charsets as $encoding)
-		{
-			$q = 1.0;
-			$duel = explode(";", $encoding);
-			if (isset($duel[1]))
-				$q = floatval(substr(trim($duel[1]), 2));
-			if (fnmatch(trim($duel[0]), $charset) && $q > 0)
-				return $q;
-		}
-		return 0.0;
-	}
-
-	/**
-	 * Returns an associative array of the components used as helpers.
-	 *
-	 * @return array Array of {@link Component} objects.
-	 * @uses $helpers
-	 * @uses cc()
-	 * @uses Lib::load()
-	 */
-	final public function getHelpers()
-	{
-		$ret = array();
-		if (!empty($this->helpers))
-			foreach ($this->helpers as $helper)
-			{
-				if (!is_string($helper))
-					continue;
-				if (isset($this->components[$helper])
-					&& is_array($this->components[$helper])
-					&& isset($this->components[$helper]['attachName']))
-					$member = $this->components[$helper]['attachName'];
-				elseif (is_string($helper) && in_array($helper, $this->components))
-					$member = cc($helper);
-				$ret[$helper] = $this->{$member};
-			}
-		return $ret;
-	}
-
-	/**
-	 * Loads the models for this controller.
-	 *
-	 * @since 0.3
-	 * @uses $models
-	 * @uses Component::setParent()
-	 * @uses cc()
-	 * @uses Lib::load()
-	 */
-	final public function loadModels()
-	{
-		if (!is_array($this->models) || empty($this->models))
-			return;
-		foreach ($this->models as $model_id => $model_opts)
-		{
-			$options = array();
-			$model_name = $model_id;
-			if (is_numeric($model_id))
-			{
-				$model_name = $model_opts;
-				$options['attachName'] = cc($model_opts);
-				$options['className'] = $options['attachName'] . 'Model';
-			} elseif (is_string($model_id))
-			{
-				$options = $model_opts;
-				$options['attachName'] = empty($options['attachName']) ? cc($model_id) : $options['attachName'];
-				$options['className'] = empty($options['className']) ? $options['attachName'] . 'Model' : $options['className'];
-			}
-			if (!property_exists($this, $options['attachName']))
-			{
-				$model = Lib::load("models", $model_name, $options);
-				$model->setParent($this);
-				$this->{$options['attachName']} = $model;
-			}
-		}
-	}
-
-	/**
-	 * Sends prepared HTTP headers to client.
-	 *
-	 * @since 0.2
-	 * @uses $headers
-	 */
-	final public function sendHeaders()
-	{
-		foreach ($this->headers as $header)
-			header($header['string'], $header['replace'], $header['code']);
-	}
-
-	/**
-	 * Adds a HTTP header to queue.
-	 *
-	 * @param string $string
-	 * @param bool $replace
-	 * @param int $code
-	 * @since 0.2
-	 * @uses $headers
-	 */
-	final public function addHeader($string, $replace=true, $code=null)
-	{
-		$this->headers []= array(
-			'string'   => $string,
-			'replace'	 => $replace,
-			'code'		 => $code
-		);
-	}
-
-	/**
-	 * Updates HTTP cache control headers.
-	 *
-	 * @param string $path Path to file.
-	 * @param string $content File contents.
-	 * @since 0.2
-	 * @uses addHeader()
-	 */
-	final protected function checkCache($path, $content)
-	{
-		if (Config::debug() == 0 && $this->useCache)
-		{
-			$view_cache_time = filemtime($path);
-			$http_view_cache_time = gmdate("D, d M Y H:i:s", $view_cache_time)." GMT";
-			$etag = md5($content);
-			if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $etag === $_SERVER['HTTP_IF_NONE_MATCH'])
-				htthrow(304);
-			if (isset($_SERVER['HTTP_IF_MATCH']) && $etag !== $_SERVER['HTTP_IF_MATCH'])
-				htthrow(304);
-			if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $http_view_cache_time == $_SERVER['HTTP_IF_MODIFIED_SINCE'])
-				htthrow(304);
-			$this->addHeader("Last-Modified: ".$http_view_cache_time);
-			$this->addHeader("Etag: ".$etag);
-		}
-	}
-
-	/**
-	 * Common presentation action has to be defined.
-	 */
-	public function index()
-	{
-		// override this
-	}
-
-	/**
-	 * This method is called before performing an action.
-	 */
-	public function beforeAction() { }
-
-	/**
-	 * This method is called after an action has been performed.
-	 */
-	public function afterAction() { }
-
-	/**
-	 * This method is called before rendering a page.
-	 */
-	public function beforeRender() { }
-
-	/**
-	 * This method is called after a page has been rendered.
-	 */
-	public function afterRender() { }
-}
+<?php
+/**
+ * The Controller class responds to requests.
+ *
+ * Event callbacks:
+ *
+ *   beforeAction()
+ *   afterAction()
+ *   beforeRender()
+ *   afterRender()
+ *
+ * @author Kiripolszky Károly <karcsi@ekezet.com>
+ * @package page
+ * @subpackage core
+ * @uses Geg
+ * @uses Lib
+ * @uses Config
+ */
+
+/**
+ * Base Controller class.
+ *
+ * @package page
+ * @subpackage core
+ */
+class PageController extends Component
+{
+	/**
+	 * Name of the layout template (without extension).
+	 *
+	 * @var string
+	 */
+	var $layout = "";
+
+	/**
+	 * Name of the controller source (w/o ".php").
+	 *
+	 * @see Controller
+	 * @var string
+	 */
+	var $controller = "";
+
+	/**
+	 * Name of the current action.
+	 *
+	 * @var string
+	 */
+	var $action = "";
+
+	/**
+	 * The view (template) to render. It's {@link $action} by default.
+	 *
+	 * You can set this to render a view other than the action name.
+	 *
+	 * @var string
+	 */
+	var $view = "";
+	/**
+	 * Returns the name of the current view.
+	 *
+	 * @return string
+	 */
+	final public function getView() { return empty($this->view) ? $this->action : $this->view; }
+
+	/**
+	 * Overwrite the template lookup path for this controller.
+	 *
+	 * @var string
+	 */
+	var $viewPath = "";
+
+	/**
+	 * Rendered content.
+	 *
+	 * @var string
+	 */
+	var $content = "";
+
+	/**
+	 * Arguments derived from the request URI.
+	 *
+	 * @see Router
+	 * @var array
+	 */
+	var $args = array();
+
+	/**
+	 * @var string Name of the layout template (without extension).
+	 */
+	var $pageTitle = "";
+
+	/**
+	 * Response charset, eg. "utf-8", etc.
+	 *
+	 * @var string
+	 */
+	var $charset = "";
+	/**
+	 * Returns current response charset.
+	 *
+	 * @return string
+	 */
+	final public function charset() { return $this->charset; }
+
+	/**
+	 * Components exposed to templates.
+	 *
+	 * @var array
+	 */
+	var $helpers = array();
+
+	/**
+	 * Models used by this controller.
+	 *
+	 * @var array
+	 */
+	var $models = array();
+
+	/**
+	 * Template variables.
+	 *
+	 * @var array
+	 */
+	protected $vars = array();
+	/**
+	 * Returns the template variables.
+	 *
+	 * @return array
+	 */
+	final public function getVars() { return $this->vars; }
+	/**
+	 * Sets one or more template variable.
+	 *
+	 * @param string|array $key Variable name or array of variables.
+	 * @param mixed $value Value for the variable.
+	 * @return bool
+	 * @see mawrite()
+	 */
+	final public function set($key, $value=true)
+	{
+		if (is_array($key))
+		{
+			foreach($key as $k => $v)
+				mawrite($k, $v, $this->vars);
+			return true;
+		}
+		return mawrite($key, $value, $this->vars);
+	}
+	/**
+	 * Gets the value of a template variable.
+	 *
+	 * @param string $key
+	 * @return mixed
+	 * @see maread()
+	 */
+	final public function get($key) { return maread($key, $this->vars); }
+
+	/**
+	 * @var array Page HTTP headers.
+	 * @since 0.2
+	 */
+	protected $headers = array();
+
+	/**
+	 * Response content type, like "html", "json", etc.
+	 *
+	 * @var string
+	 * @see Config::loadDefaults()
+	 */
+	protected $contentType = "";
+	/**
+	 * Sets the response content type for the current request.
+	 *
+	 * The content type must already be added to the configuration!
+	 *
+	 * @param string $type Internal content type alias, like "html", "json", etc.
+	 * @param string $charset Set the character set as well. Can be false to omit.
+	 * @return bool True if content type is successfully set.
+	 * @see Config::addContentType()
+	 */
+	final public function setContentType($type, $charset=null)
+	{
+		$types = array_keys(Config::get("content_types"));
+		if (!in_array($type, $types) || empty($type))
+			return false;
+		$this->contentType = $type;
+		$this->charset = empty($charset) ? $this->charset : $charset;
+		return true;
+	}
+	/**
+	 * Returns current response content type.
+	 *
+	 * @return string
+	 */
+	final public function contentType() { return $this->contentType; }
+	/**
+	 * Returns a MIME type for the current request, eg. "text/html".
+	 *
+	 * @return string
+	 * @uses $contentType
+	 */
+	final public function mimeType($type=null)
+	{
+		$types = Config::get("content_types");
+		$type = empty($type) ? $this->contentType : $type;
+		return $types[$type];
+	}
+
+	/**
+	 * Enable/disable HTTP caching for this controller.
+	 *
+	 * @var bool
+	 * @since 0.2
+	 */
+	protected $useCache = true;
+
+
+	/**
+	 * Perform a controller action.
+	 *
+	 * @param string $action Action name or null.
+	 * @uses beforeAction()
+	 * @uses afterAction()
+	 */
+	final public function doAction($action=null)
+	{
+		$action = empty($action) ? $this->action : $action;
+		$this->action = $action;
+		$this->beforeAction();
+		foreach ($this->helpers as $helper_id => $helper_opts)
+			if (is_numeric($helper_id))
+				$this->components []= $helper_opts;
+			else
+				$this->components[$helper_id] = $this->helpers[$helper_id];
+		$this->loadComponents();
+		$this->loadModels();
+		$code = sprintf('$this->%s(%s);', $action, implode(", ", quotes($this->args)));
+		eval($code);
+		$this->afterAction();
+	}
+
+	/**
+	 * Tells the app to setup headers and render the page into {@link $content}.
+	 *
+	 * @param string $view Overrides {@link $action} if not null.
+	 * @param string $layout Overrides {@link $layout} if not null.
+	 * @uses Config
+	 * @uses Geg::render()
+	 */
+	final public function render($view=null, $layout=null)
+	{
+		// setup variables
+		$start_time = microtime(true);
+		$this->beforeRender();
+		$view = empty($view) ? $this->getView() : $view;
+		$layout = empty($layout) ? (empty($this->layout) ? Config::get("default_layout") : $this->layout) : $layout;
+		$this->charset = empty($this->charset) ? Config::get("charset") : $this->charset;
+		$this->setContentType(empty($this->contentType) ? Config::get("default_content_type") : $this->contentType);
+
+		// check if client can accept the response content type
+		if ($this->accepts() == 0)
+		{
+			// look for an appropriate content type
+			$types = Config::get("content_types");
+			$fallback = false;
+
+			foreach ($types as $alias => $mime)
+			{
+				if ($this->accepts($alias))
+				{
+					$this->setContentType($alias);
+					$fallback = true;
+					break;
+				}
+			}
+
+			if (!$fallback)
+				htthrow(406);
+		}
+
+		// check for charset support
+		if ($this->acceptsCharset() == 0)
+			// charset not supported by client
+			htthrow(406);
+
+		$content_dir = $this->contentType == Config::get("default_content_type") ? DS : DS.$this->contentType.DS;
+		$this->pageTitle = empty($this->pageTitle) ? Config::get("default_title") : $this->pageTitle;
+		$this->set("me", $this);
+
+		// add helper components as template variables
+		$helpers = $this->getHelpers();
+		foreach ($helpers as $name => $obj)
+			$this->vars[$name] = $obj;
+
+		/*
+		 * Rendering below:
+		 */
+
+		$view_path = $this->viewPath.$this->controller.$content_dir.$view;
+		$content_for_layout = Geg::render($view_path, $this->vars, true);
+
+		// this might be the first failed attempt to render the layout content
+		if ($content_for_layout === false)
+		{
+			// ...on error look also in default view path
+			$view_path = $this->viewPath.$this->controller.DS.$view;
+			$content_for_layout = Geg::render($view_path, $this->vars, true);
+		}
+		// did the rendering fail again?
+		if ($content_for_layout === false && get_class($this) !== "ErrorController")
+		{
+			// if debug mode is on throw view_not_found error
+			if (Config::debug() > 0)
+				page::launchErrorController("view_not_found");
+			// ...otherwise it's the ultimate error
+			else
+				page::launchErrorController("not_found");
+		}
+
+		$this->set("content_for_layout", $content_for_layout);
+
+		// rendering layout...
+		$layout_path = $this->viewPath."layout".$content_dir.$layout;