Index: /trunk/.htaccess
===================================================================
--- /trunk/.htaccess	(revision 69)
+++ /trunk/.htaccess	(revision 69)
@@ -0,0 +1,12 @@
+<Files ReadMe.txt>
+  order allow,deny
+  deny from all
+</Files>
+
+RewriteEngine On
+RewriteCond  %{REQUEST_FILENAME}  !-f
+RewriteCond  %{REQUEST_FILENAME}  !-d
+RewriteRule   ^(.*)$ %{CONTEXT_PREFIX}/index.php?$1
+
+#ErrorDocument 403 "index.php?page=403"
+#ErrorDocument 404 "index.php?page=404" 
Index: unk/Application.php
===================================================================
--- /trunk/Application.php	(revision 68)
+++ 	(revision )
@@ -1,43 +1,0 @@
-<?php
-
-class Application
-{
-  var $Database;
-
-  function Start()
-  {
-    global $Config, $DatabaseRevision;
-
-    $FileName = dirname(__FILE__).'/config.php';
-    if(file_exists($FileName)) include_once($FileName);
-    else {
-      die('Configuration file "'.$FileName.'" not found.');
-    }
-
-    session_start();
-
-    $ErrorHandler = new ErrorHandler();
-    $ErrorHandler->ShowError = $Config['Web']['ShowError'];
-    $ErrorHandler->Start();
-
-    $this->Database = new Database();
-    $this->Database->Connect($Config['Database']['Host'], $Config['Database']['User'],
-      $Config['Database']['Password'], $Config['Database']['Database']);
-    $this->Database->Prefix = $Config['Database']['Prefix'];
-    $this->Database->charset($Config['Database']['Charset']);
-    $this->Database->ShowSQLError = $Config['Web']['ShowSQLError'];
-    $this->Database->ShowSQLQuery = $Config['Web']['ShowSQLQuery'];
-
-    // SQL injection hack protection
-    foreach($_POST as $Index => $Item) $_POST[$Index] = addslashes($Item);
-    foreach($_GET as $Index => $Item) $_GET[$Index] = addslashes($Item);
-
-    // Check database persistence structure
-    $UpdateManager = new UpdateManager();
-    $UpdateManager->Database = $this->Database;
-    $UpdateManager->Revision = $DatabaseRevision;
-    if(!$UpdateManager->IsInstalled()) die('Systém vyžaduje instalaci databáze.');
-    if(!$UpdateManager->IsUpToDate()) die('Systém vyžaduje aktualizaci databáze.');
-
-  }
-}
Index: /trunk/Application/Core.php
===================================================================
--- /trunk/Application/Core.php	(revision 69)
+++ /trunk/Application/Core.php	(revision 69)
@@ -0,0 +1,140 @@
+<?php
+
+$ConfigFileName = dirname(__FILE__).'/../Config/Config.php';
+if(file_exists($ConfigFileName)) include_once($ConfigFileName);
+
+include_once(dirname(__FILE__).'/../Global.php');
+include_once(dirname(__FILE__).'/Version.php');
+include_once(dirname(__FILE__).'/View.php');
+include_once(dirname(__FILE__).'/UpdateTrace.php');
+include_once(dirname(__FILE__).'/DefaultConfig.php');
+
+class Core extends Application
+{
+  var $Pages;
+  var $ShowPage;
+  var $BaseURL;
+
+  function __construct()
+  {
+    parent::__construct();
+    $this->Pages = array();
+    $this->ShowPage = true;
+    $this->BaseURL = $_SERVER['SCRIPT_NAME'];
+    if(substr($this->BaseURL, -10, 10) == '/index.php')
+      $this->BaseURL = substr($this->BaseURL, 0, -10);
+  }
+
+  function RunCommon()
+  {
+    global $Config, $DatabaseRevision;
+
+    session_start();
+
+    $ErrorHandler = new ErrorHandler();
+    $ErrorHandler->ShowError = $Config['Web']['ShowError'];
+    $ErrorHandler->Start();
+
+    $this->Database = new Database();
+    $this->Database->Connect($Config['Database']['Host'], $Config['Database']['User'],
+      $Config['Database']['Password'], $Config['Database']['Database']);
+    $this->Database->Prefix = $Config['Database']['Prefix'];
+    $this->Database->charset($Config['Database']['Charset']);
+    $this->Database->ShowSQLError = $Config['Web']['ShowSQLError'];
+    $this->Database->ShowSQLQuery = $Config['Web']['ShowSQLQuery'];
+
+    // SQL injection hack protection
+    foreach($_POST as $Index => $Item) $_POST[$Index] = addslashes($Item);
+    foreach($_GET as $Index => $Item) $_GET[$Index] = addslashes($Item);
+
+    $this->Config = &$Config;
+
+    // Register and start existing modules
+    $this->Setup = new Setup($this);
+    $this->Setup->Start();
+    if($this->Setup->CheckState())
+    {
+      $this->ModuleManager->Start();
+    }
+  }
+
+  function Run()
+  {
+    $this->RunCommon();
+    if($this->ShowPage)
+    {
+      $this->PathItems = ProcessURL();
+      $this->ShowPage();
+    }
+  }
+
+  function ShowPage()
+  {
+    $this->BaseView = new BaseView($this);
+
+    /* @var $Page Page */
+    $ClassName = $this->SearchPage($this->PathItems, $this->Pages);
+    if($ClassName != '')
+    {
+      $Page = new $ClassName($this);
+    } else {
+      $Page = new PageMissing($this);
+    }
+    echo($this->BaseView->GetOutput($Page));
+  }
+
+  function RegisterPage($Path, $Handler)
+  {
+    if(is_array($Path))
+    {
+      $Page = &$this->Pages;
+      $LastKey = array_pop($Path);
+      foreach($Path as $PathItem)
+      {
+        $Page = &$Page[$PathItem];
+      }
+      if(!is_array($Page)) $Page = array('' => $Page);
+      $Page[$LastKey] = $Handler;
+    } else $this->Pages[$Path] = $Handler;
+  }
+
+  function UnregisterPage($Path)
+  {
+    unset($this->Pages[$Path]);
+  }
+
+  function SearchPage($PathItems, $Pages)
+  {
+    if(count($PathItems) > 0) $PathItem = $PathItems[0];
+      else $PathItem = '';
+    if(array_key_exists($PathItem, $Pages))
+    {
+      if(is_array($Pages[$PathItem]))
+      {
+        array_shift($PathItems);
+        return($this->SearchPage($PathItems, $Pages[$PathItem]));
+      } else return($Pages[$PathItem]);
+    } else return('');
+  }
+
+  function Link($Target)
+  {
+    return($this->BaseURL.$Target);
+  }
+}
+
+class PageMissing extends Page
+{
+  function __construct($System)
+  {
+    parent::__construct($System);
+    $this->ParentClass = '';
+    $this->Title = T('Page not found');
+  }
+
+  function Show()
+  {
+    Header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
+    return('<h3 align="center">'.T('Required page not found'));
+  }
+}
Index: /trunk/Application/DefaultConfig.php
===================================================================
--- /trunk/Application/DefaultConfig.php	(revision 69)
+++ /trunk/Application/DefaultConfig.php	(revision 69)
@@ -0,0 +1,57 @@
+<?php
+
+class DefaultConfig
+{
+  function Get()
+  {
+    $IsDeveloper = in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1'));
+    return(array
+    (
+        array('Name' => 'SystemPassword', 'Type' => 'PasswordEncoded', 'Default' => '', 'Title' => 'Systémové heslo'),
+        array('Name' => 'Database/Host', 'Type' => 'String', 'Default' => 'localhost', 'Title' => 'Server'),
+        array('Name' => 'Database/User', 'Type' => 'String', 'Default' => 'root', 'Title' => 'Uživatel'),
+        array('Name' => 'Database/Password', 'Type' => 'Password', 'Default' => '', 'Title' => 'Heslo'),
+        array('Name' => 'Database/Database', 'Type' => 'String', 'Default' => 'centrala', 'Title' => 'Databáze'),
+        array('Name' => 'Database/Prefix', 'Type' => 'String', 'Default' => '', 'Title' => 'Prefix'),
+        array('Name' => 'Database/Charset', 'Type' => 'String', 'Default' => 'utf8', 'Title' => 'Znaková sada'),
+        array('Name' => 'Web/Style', 'Type' => 'String', 'Default' => 'new', 'Title' => 'Styl'),
+        array('Name' => 'Web/FormatHTML', 'Type' => 'String', 'Default' => '', 'Title' => 'Formátovat HTML'),
+        array('Name' => 'Web/Charset', 'Type' => 'String', 'Default' => 'utf-8', 'Title' => 'Znaková sada'),
+        array('Name' => 'Web/RootFolder', 'Type' => 'String', 'Default' => '', 'Title' => 'Kořenová složka'),
+        array('Name' => 'Web/Host', 'Type' => 'String', 'Default' => 'localhost', 'Title' => 'Doménové jméno'),
+        array('Name' => 'Web/Title', 'Type' => 'String', 'Default' => 'Síť', 'Title' => 'Titulek'),
+        array('Name' => 'Web/Description', 'Type' => 'String', 'Default' => 'Komunitní počítačová síť', 'Title' => 'Popis'),
+        array('Name' => 'Web/Admin', 'Type' => 'String', 'Default' => 'Admin', 'Title' => 'Jméno správce'),
+        array('Name' => 'Web/AdminEmail', 'Type' => 'String', 'Default' => 'admin@localhost', 'Title' => 'E-mail správce'),
+        array('Name' => 'Web/ErrorLogFile', 'Type' => 'String', 'Default' => 'php_script_error.log', 'Title' => 'Soubor záznamu chyb'),
+        array('Name' => 'Web/WebcamPassword', 'Type' => 'Password', 'Default' => '', 'Title' => 'Heslo web kamery'),
+        array('Name' => 'Web/WebcamRefresh', 'Type' => 'Integer', 'Default' => '5', 'Title' => 'Interval obnovení'),
+        array('Name' => 'Web/UploadFileFolder', 'Type' => 'String', 'Default' => 'files', 'Title' => 'Složka načtených souborů'),
+        array('Name' => 'Web/News/DaysAgo', 'Type' => 'Integer', 'Default' => '30', 'Title' => 'Stáří zobrazovaných aktualit'),
+        array('Name' => 'Web/News/Count', 'Type' => 'Integer', 'Default' => '5', 'Title' => 'Počet zobrazovaných aktualit na skupinu'),
+        array('Name' => 'Web/News/DirectoryId', 'Type' => 'Integer', 'Default' => '1', 'Title' => 'Id adresáře souborů příloh aktualit'),
+        array('Name' => 'Map/GoogleMapsApiKey', 'Type' => 'String', 'Default' => '', 'Title' => 'API google map'),
+        array('Name' => 'Map/DefaultLatitude', 'Type' => 'Float', 'Default' => '49.260422', 'Title' => 'Výchozí pozice na mapě'),
+        array('Name' => 'Map/DefaultLongitude', 'Type' => 'Float', 'Default' => '18.081179', 'Title' => 'Výchozí pozice na mapě'),
+        array('Name' => 'Map/DefaultZoom', 'Type' => 'Float', 'Default' => '15', 'Title' => 'Výchozí přihlížení mapy'),
+        array('Name' => 'Web/ItemsPerPage', 'Type' => 'Integer', 'Default' => '50', 'Title' => 'Položek na stránku'),
+        array('Name' => 'Web/VisiblePagingItems', 'Type' => 'Integer', 'Default' => '20', 'Title' => 'Položek stránkování'),
+        array('Name' => 'Web/FormatHTML', 'Type' => 'Boolean', 'Default' => '0', 'Title' => 'Formátovat výsledný kód'),
+        array('Name' => 'Web/ShowSQLError', 'Type' => 'Boolean', 'Default' => '0', 'Title' => 'Ukazovat SQL chyby'),
+        array('Name' => 'Web/ShowSQLQuery', 'Type' => 'Boolean', 'Default' => '0', 'Title' => 'Ukazovat SQL dotazy'),
+        array('Name' => 'Web/ShowPHPError', 'Type' => 'Boolean', 'Default' => '0', 'Title' => 'Ukazovat PHP chyby'),
+        array('Name' => 'Web/ShowRuntimeInfo', 'Type' => 'Boolean', 'Default' => '0', 'Title' => 'Ukazovat běhové informace'),
+        array('Name' => 'Finance/MainUserId', 'Type' => 'Integer', 'Default' => '1', 'Title' => 'Id hlavního uživatele'),
+        array('Name' => 'Finance/MainSubjectId', 'Type' => 'Integer', 'Default' => '1', 'Title' => 'Id hlavního subjektu'),
+        array('Name' => 'Finance/DirectoryId', 'Type' => 'Integer', 'Default' => '1', 'Title' => 'Id adresáře souborů dokladů'),
+        array('Name' => 'Finance/Rounding', 'Type' => 'Integer', 'Default' => '2', 'Title' => 'Počet desetinných míst zaokrouhlování'),
+        array('Name' => 'MainRouter/HostName', 'Type' => 'String', 'Default' => 'localhost', 'Title' => 'Hlavní brána'),
+        array('Name' => 'MainRouter/UserName', 'Type' => 'String', 'Default' => 'admin', 'Title' => 'Uživatelské jméno'),
+        array('Name' => 'MainRouter/Password', 'Type' => 'Password', 'Default' => '', 'Title' => 'Heslo'),
+        array('Name' => 'MainRouter/InetInterface', 'Type' => 'String', 'Default' => 'ether_wan', 'Title' => 'WAN rozhraní'),
+        array('Name' => 'MainRouter/LocalInterface', 'Type' => 'String', 'Default' => 'ether_lan', 'Title' => 'LAN rozhraní'),
+        array('Name' => 'MainRouter/ConnectTimeout', 'Type' => 'Integer', 'Default' => '5', 'Title' => 'Vypršení času'),
+        array('Name' => 'MainRouter/MangleRuleSubgroupMinPrefix', 'Type' => 'Integer', 'Default' => '28', 'Title' => 'Nejmenší prefix podsítě pro mangle pravidla'),
+    ));
+  }
+}
Index: /trunk/Application/UpdateTrace.php
===================================================================
--- /trunk/Application/UpdateTrace.php	(revision 69)
+++ /trunk/Application/UpdateTrace.php	(revision 69)
@@ -0,0 +1,68 @@
+<?php
+
+function FullInstall($Manager)
+{
+   $Manager->Execute('CREATE TABLE IF NOT EXISTS `SystemVersion` (
+  `Id` int(11) NOT NULL AUTO_INCREMENT,
+  `Revision` int(11) NOT NULL,
+  PRIMARY KEY (`Id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
+  $Manager->Execute('INSERT INTO `SystemVersion` (`Id`, `Revision`) VALUES (NULL, 65);');
+  $Manager->Execute('CREATE TABLE IF NOT EXISTS `measure` (
+  `Id` int(11) NOT NULL auto_increment,
+  `Name` varchar(255) collate utf8_general_ci NOT NULL,
+  `Description` varchar(255) collate utf8_general_ci NOT NULL,
+  `Divider` int(11) NOT NULL default 1,
+  `Unit` varchar(16) collate utf8_general_ci NOT NULL,
+  `Continuity` tinyint(1) NOT NULL default 0,
+  `Period` int(11) NOT NULL default 60,
+  `OldName` varchar(32) collate utf8_general_ci NOT NULL,
+  `PermissionView` varchar(255) collate utf8_general_ci NOT NULL default "all",
+  `PermissionAdd` varchar(255) collate utf8_general_ci NOT NULL default "localhost.localdomain",
+  `Info` varchar(255) collate utf8_general_ci NOT NULL,
+  `Enabled` int(11) NOT NULL default 1,
+  `Cumulative` int(11) NOT NULL default 0,
+  `DataTable` varchar(32) collate utf8_general_ci NOT NULL default "data",
+  `DataType` varchar(32) collate utf8_general_ci NOT NULL,
+  PRIMARY KEY  (`Id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8;');
+}
+
+function UpdateTo67($Manager)
+{
+  $Manager->Execute('RENAME TABLE `measure` TO `Measure`;');
+/*
+   $Manager->Execute('CREATE TABLE IF NOT EXISTS `Permission` (
+  `Id` int(11) NOT NULL AUTO_INCREMENT,
+  `Measure` int(11) NOT NULL,
+  `Address` varchar(255) NOT NULL,
+  `Operation` varchar(255) NOT NULL,
+  PRIMARY KEY (`Id`),
+  KEY `Measure` (`Measure`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
+  $Manager->Execute('ALTER TABLE `Permission` ADD FOREIGN KEY (`Measure`) '.
+  'REFERENCES `Measure`(`Id`) ON DELETE RESTRICT ON UPDATE RESTRICT;');
+  $DbResult = $Manager->Execute('SELECT `Id`, `PermissionAdd` FROM `Measure`');
+  while($DbRow = $DbResult->fetch_assoc())
+  {
+    $Manager->Execute('INSERT INTO `Permission` (`Measure`, `Operation`) VALUES ('.
+	    $DbRow['Id'].', "add");');
+	}
+  $DbResult = $Manager->Execute('SELECT `Id`, `PermissionView` FROM `Measure`');
+  while($DbRow = $DbResult->fetch_assoc())
+  {
+	  $Manager->Execute('INSERT INTO `Permission` (`Measure`, `Operation`) VALUES ('.
+	    $DbRow['Id'].', "view");');
+  }
+  */
+}
+
+class Updates
+{
+  function Get()
+  {
+    return(array(
+      65 => array('Revision' => 67, 'Function' => 'UpdateTo67'),
+    ));
+  }
+}
Index: /trunk/Application/Version.php
===================================================================
--- /trunk/Application/Version.php	(revision 69)
+++ /trunk/Application/Version.php	(revision 69)
@@ -0,0 +1,10 @@
+<?php
+
+// Subversion revision have to be updated always to next revision number
+// before commiting new changes.
+// Database revision have to be updated if database structure is changed
+// and system will need database update.
+
+$Revision = 67;
+$DatabaseRevision = 67;
+$ReleaseTime = strtotime('2016-02-28');
Index: /trunk/Application/View.php
===================================================================
--- /trunk/Application/View.php	(revision 69)
+++ /trunk/Application/View.php	(revision 69)
@@ -0,0 +1,33 @@
+<?php
+
+class BaseView extends View
+{
+  function ShowPage($Content)
+  {
+    global $ReleaseTime, $Revision;
+
+    $Output = '<?xml version="1.0" encoding="'.$this->System->Config['Web']['Charset'].'"?>'."\n".
+    '<!DOCTYPE html>'.
+    '<html>'.
+    '<head>'.
+    '<meta http-equiv="Content-Language" content="cs"/>'.
+    '<meta http-equiv="content-type" content="text/html; charset='.$this->System->Config['Web']['Charset'].'" />'.
+    '<meta name="robots" content="all" />'.
+    '<title>'.$this->System->Config['Web']['Title'].'</title>'.
+    '<link rel="StyleSheet" href="'.$this->System->Link('/style/style.css').'" type="text/css" media="all"/>'.
+    '</head><body>';
+    $Output .= $Content;
+    $Output .= '<br/><div style="text-align: center; font-size: small;">Verze: '.$Revision.' ('.HumanDate($ReleaseTime).')'.
+      ' &nbsp; <a href="http://svn.zdechov.net/trac/statistic/browser/trunk">Zdrojový kód</a> &nbsp; '.
+      '<a href="http://svn.zdechov.net/trac/statistic/log/trunk?verbose=on">Historie změn</a></div>';
+    $Output .= '</body></html>';
+    echo($Output);
+  }
+
+  function GetOutput($Page)
+  {
+    $Output = $this->ShowPage($Page->Show());
+    return($Output);
+  }
+}
+
Index: /trunk/Global.php
===================================================================
--- /trunk/Global.php	(revision 68)
+++ /trunk/Global.php	(revision 69)
@@ -1,11 +1,7 @@
 <?php
 
-include_once('Version.php');
 include_once('Types.php');
 include_once('Classes.php');
 include_once('Packages/Common/Common.php');
-include_once('Measure.php');
-include_once('Application.php');
-include_once('Update.php');
 
 function HumanDate($Time)
@@ -18,26 +14,4 @@
   list($Usec, $Sec) = explode(" ", microtime());
   return ((float)$Usec + (float)$Sec);
-}
-
-function ShowPage($Content)
-{
-  global $Config, $ReleaseTime, $Revision;
-
-  $Output = '<?xml version="1.0" encoding="'.$Config['Web']['Charset'].'"?>'."\n".
-  '<!DOCTYPE html>'.
-  '<html>'.
-  '<head>'.
-  '<meta http-equiv="Content-Language" content="cs"/>'.
-  '<meta http-equiv="content-type" content="text/html; charset='.$Config['Web']['Charset'].'" />'.
-  '<meta name="robots" content="all" />'.
-  '<title>'.$Config['Web']['Title'].'</title>'.
-  '<link rel="StyleSheet" href="style/style.css" type="text/css" media="all"/>'.
-  '</head><body>';
-  $Output .= $Content;
-  $Output .= '<br/><div style="text-align: center; font-size: small;">Verze: '.$Revision.' ('.HumanDate($ReleaseTime).')'.
-    ' &nbsp; <a href="http://svn.zdechov.net/trac/statistic/browser/trunk">Zdrojový kód</a> &nbsp; '.
-    '<a href="http://svn.zdechov.net/trac/statistic/log/trunk?verbose=on">Historie změn</a></div>';
-  $Output .= '</body></html>';
-  echo($Output);
 }
 
@@ -97,2 +71,42 @@
   return($Output);
 }
+
+function ProcessURL()
+{
+  if(array_key_exists('REDIRECT_QUERY_STRING', $_SERVER))
+    $PathString = $_SERVER['REDIRECT_QUERY_STRING'];
+    else $PathString = '';
+  if(substr($PathString, -1, 1) == '/') $PathString = substr($PathString, 0, -1);
+  $PathItems = explode('/', $PathString);
+  if(array_key_exists('REQUEST_URI', $_SERVER) and (strpos($_SERVER['REQUEST_URI'], '?') !== false))
+    $_SERVER['QUERY_STRING'] = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], '?') + 1);
+    else $_SERVER['QUERY_STRING'] = '';
+  parse_str($_SERVER['QUERY_STRING'], $_GET);
+  return($PathItems);
+}
+
+function GetQueryStringArray($QueryString)
+{
+  $Result = array();
+  $Parts = explode('&', $QueryString);
+  foreach($Parts as $Part)
+  {
+    if($Part != '')
+    {
+      if(!strpos($Part, '=')) $Part .= '=';
+      $Item = explode('=', $Part);
+      $Result[$Item[0]] = $Item[1];
+    }
+  }
+  return($Result);
+}
+
+function SetQueryStringArray($QueryStringArray)
+{
+  $Parts = array();
+  foreach($QueryStringArray as $Index => $Item)
+  {
+    $Parts[] = $Index.'='.$Item;
+  }
+  return(implode('&amp;', $Parts));
+}
Index: /trunk/Graph.php
===================================================================
--- /trunk/Graph.php	(revision 68)
+++ /trunk/Graph.php	(revision 69)
@@ -1,5 +1,5 @@
 <?php
 
-include('Global.php');
+include_once('Application/Core.php');
 
 class MeasureGraph
@@ -216,6 +216,7 @@
 }
 
-$Application = new Application();
-$Application->Start();
-$Graph = new MeasureGraph($this->Database);
+$Core = new Core();
+$Core->ShowPage = false;
+$Core->Run();
+$Graph = new MeasureGraph($Core->Database);
 $Graph->Render();
Index: unk/Measure.php
===================================================================
--- /trunk/Measure.php	(revision 68)
+++ 	(revision )
@@ -1,476 +1,0 @@
-<?php
-
-include_once('Point.php');
-
-class Measure
-{
-  var $Data;
-  var $ValueTypes;
-  var $LevelReducing;
-  var $Database;
-  var $MaxLevel;
-  var $ReferenceTime;
-  var $Differential;
-  var $DivisionCount;
-  var $PeriodTolerance;
-
-  function __construct(Database $Database)
-  {
-    $this->Id = 0;
-    $this->ValueTypes = array('min', 'avg', 'max');
-    $this->Database = &$Database;
-    $this->LevelReducing = 5;
-    $this->MaxLevel = 4;
-    $this->ReferenceTime = 0;
-    $this->Differential = 0;
-    $this->DivisionCount = 500;
-    $this->PeriodTolerance = 0.25; // 25%
-  }
-
-  function TimeSegment($Level)
-  {
-    return(pow($this->LevelReducing, $Level) * 60);
-  }
-
-  function StatTableName($Level)
-  {
-    if($Level == 0) return('data');
-      else return('data_cache');
-  }
-
-  function AlignTime($Time, $TimeSegment)
-  {
-    return(round(($Time - $this->ReferenceTime) / $TimeSegment) * $TimeSegment +
-      $this->ReferenceTime);
-  }
-
-  function Load($Id)
-  {
-    $Result = $this->Database->select('measure', '*', 'Id='.$Id);
-    if($Result->num_rows > 0)
-    {
-      $this->Data = $Result->fetch_array();
-      if($this->Data['Continuity'] == 0) $this->Data['ContinuityEnabled'] = 0;  // non continuous
-        else $this->Data['ContinuityEnabled'] = 2;    // continuous graph
-    } else die('Měřená veličina nenalezena');
-  }
-
-  function AddValue($Value)
-  {
-    $Time = time();
-
-    $Result = $this->Database->select($this->Data['DataTable'], '*', '(`measure`='.$this->Data['Id'].') AND '.
-      '(`level`=0) ORDER BY `time` DESC LIMIT 2');
-    if($Result->num_rows == 0) {
-      // No measure value found. Simply insert new first value.
-      $this->Database->insert($this->Data['DataTable'],
-      array('min' => $Value, 'avg' => $Value, 'max' => $Value, 'level' => 0,
-      'measure' => $this->Data['Id'], 'time' => TimeToMysqlDateTime($Time),
-      'continuity' => 0));
-    } else if($Result->num_rows == 1) {
-      // One value exists. Add second value.
-      $this->Database->insert($this->Data['DataTable'],
-      array('min' => $Value, 'avg' => $Value, 'max' => $Value, 'level' => 0,
-      'measure' => $this->Data['Id'], 'time' => TimeToMysqlDateTime($Time),
-      'continuity' => 1));
-    } else {
-      // Two values already exist in measure table
-      $LastValue = $Result->fetch_array();
-      $NextToLastValue = $Result->fetch_array();
-      if(($Time - MysqlDateTimeToTime($LastValue['time'])) < (1 - $this->PeriodTolerance) * $this->Data['Period'])
-      {
-        // New value added too quickly. Need to wait for next measure period.
-      }
-      else
-      {
-        // We are near defined period and can add new value.
-        if(($Time - MysqlDateTimeToTime($LastValue['time'])) < (1 + $this->PeriodTolerance) * $this->Data['Period']) {
-          // New value added near defined measure period inside tolerance. Keep continuity.
-          $Continuity = 1;
-        } else {
-          // New value added too late after defined measure period. Stop
-          // continuity and let gap to be added.
-          $Continuity = 0;
-        }
-        if(($LastValue['avg'] == $NextToLastValue['avg']) and ($LastValue['avg'] == $Value) and
-          ($LastValue['continuity'] == 1) and ($Continuity == 1))
-        {
-          // New value is same as last value and continuity mode is enabled and
-          // continuity flag is present. Just shift forward time for last value.
-          $this->Database->update($this->Data['DataTable'], '(`time`="'.$LastValue['time'].'") AND '.
-          '(`level`=0) AND (`measure`='.$this->Data['Id'].')', array('time' => TimeToMysqlDateTime($Time)));
-        } else
-        {
-          // Last value is different or not with continuity flag. Need to add new value.
-          $this->Database->insert($this->Data['DataTable'], array('min' => $Value,
-            'avg' => $Value, 'max' => $Value, 'level' => 0, 'measure' => $this->Data['Id'], 'time' => TimeToMysqlDateTime($Time),
-            'continuity' => $Continuity));
-        }
-      }
-    }
-
-    // Update higher levels
-    for($Level = 1; $Level <= $this->MaxLevel; $Level++)
-    {
-      $TimeSegment = $this->TimeSegment($Level);
-      $EndTime = $this->AlignTime($Time, $TimeSegment);
-      //if($EndTime < $Time) $EndTime = $EndTime + $TimeSegment;
-      $StartTime = $EndTime - $TimeSegment;
-
-      // Load values in time range
-      $Values = array();
-      $Result = $this->Database->select($this->Data['DataTable'], '*', '(`time` > "'.
-        TimeToMysqlDateTime($StartTime).'") AND '.
-        '(`time` < "'.TimeToMysqlDateTime($EndTime).'") AND '.
-        '(`measure`='.$this->Data['Id'].') AND (`level`='.($Level - 1).') ORDER BY `time`');
-      while($Row = $Result->fetch_array())
-      {
-        $Row['time'] = MysqlDateTimeToTime($Row['time']);
-        $Values[] = $Row;
-      }
-      //array_pop($Values);
-
-      // Load subsidary values
-      $Values = array_merge(
-        $this->LoadLeftSideValue($Level - 1, $StartTime),
-        $Values,
-        $this->LoadRightSideValue($Level - 1, $EndTime)
-      );
-
-      $Point = $this->ComputeOneValue($StartTime, $EndTime, $Values, $Level);
-
-      $this->Database->delete($this->Data['DataTable'], '(`time` > "'.TimeToMysqlDateTime($StartTime).'") AND
-        (`time` < "'.TimeToMysqlDateTime($EndTime).'") AND (`measure`='.$this->Data['Id'].') '.
-        'AND (`level`='.$Level.')');
-      $Continuity = $Values[1]['continuity'];
-      $this->Database->insert($this->Data['DataTable'], array('level' => $Level,
-        'measure' => $this->Data['Id'], 'min' => $Point['min'],
-        'avg' => $Point['avg'], 'max' => $Point['max'], 'continuity' => $Continuity,
-        'time' => TimeToMysqlDateTime($StartTime + ($EndTime - $StartTime) / 2)));
-    }
-  }
-
-  /* Compute one value for upper time level from multiple values */
-  function ComputeOneValue($LeftTime, $RightTime, $Values, $Level)
-  {
-    $NewValue = array('min' => +1000000000000000000, 'avg' => 0, 'max' => -1000000000000000000);
-
-    // Trim outside parts
-    foreach($this->ValueTypes as $ValueType)
-    {
-      $Values[0][$ValueType] = Interpolation(
-        NewPoint($Values[0]['time'], $Values[0][$ValueType]),
-        NewPoint($Values[1]['time'], $Values[1][$ValueType]), $LeftTime);
-    }
-    $Values[0]['time'] = $LeftTime;
-    foreach($this->ValueTypes as $ValueType)
-    {
-      $Values[count($Values) - 1][$ValueType] = Interpolation(
-        NewPoint($Values[count($Values) - 2]['time'], $Values[count($Values) - 2][$ValueType]),
-        NewPoint($Values[count($Values) - 1]['time'], $Values[count($Values) - 1][$ValueType]),
-        $RightTime);
-    }
-    $Values[count($Values) - 1]['time'] = $RightTime;
-
-    // Perform computation
-    foreach($this->ValueTypes as $ValueType)
-    {
-      // Compute new value
-      for($I = 0; $I < (count($Values) - 1); $I++)
-      {
-        if($ValueType == 'avg')
-        {
-          if($Values[$I + 1]['continuity'] == $this->Data['ContinuityEnabled']);
-            else if($this->Differential == 0)
-            {
-              $NewValue[$ValueType] = $NewValue[$ValueType] + ($Values[$I + 1]['time'] - $Values[$I]['time']) *
-                (($Values[$I + 1][$ValueType] - $Values[$I][$ValueType]) / 2 + $Values[$I][$ValueType]);
-            } else {
-              $NewValue[$ValueType] = $NewValue[$ValueType] + ($Values[$I + 1]['time'] - $Values[$I]['time']) *
-               (($Values[$I + 1][$ValueType] - $Values[$I][$ValueType]) / 2);
-            }
-        }
-        else if($ValueType == 'max')
-        {
-          if($Values[$I + 1]['continuity'] == $this->Data['ContinuityEnabled'])
-          {
-            if(0 > $NewValue[$ValueType]) $NewValue[$ValueType] = 0;
-          }
-          else
-          {
-            if($this->Differential == 0)
-            {
-              if($Values[$I + 1][$ValueType] > $NewValue[$ValueType]) $NewValue[$ValueType] = $Values[$I + 1][$ValueType];
-            } else {
-              $Difference = $Values[$I + 1][$ValueType] - $Values[$I][$ValueType];
-              if($Difference > $NewValue[$ValueType]) $NewValue[$ValueType] = $Difference;
-            }
-          }
-        }
-        else if($ValueType == 'min')
-        {
-          if($Values[$I + 1]['continuity'] == $this->Data['ContinuityEnabled'])
-          {
-            if(0 < $NewValue[$ValueType]) $NewValue[$ValueType] = 0;
-          } else {
-            if($this->Differential == 0)
-            {
-              if($Values[$I + 1][$ValueType] < $NewValue[$ValueType]) $NewValue[$ValueType] = $Values[$I + 1][$ValueType];
-            } else {
-              $Difference = $Values[$I + 1][$ValueType] - $Values[$I][$ValueType];
-              if($Difference < $NewValue[$ValueType]) $NewValue[$ValueType] = $Difference;
-            }
-          }
-        }
-      }
-      $NewValue[$ValueType] = $NewValue[$ValueType];
-    }
-    //if(($RightTime - $LeftTime) > 0)
-    if($this->Data['Cumulative'] == 0)
-    {
-      $NewValue['avg'] = $NewValue['avg'] / ($RightTime - $LeftTime);
-    }
-    return($NewValue);
-  }
-
-  function GetTimeRange($Level)
-  {
-    // Get first and last time
-    $Result = $this->Database->select($this->Data['DataTable'], '*', '(`measure`='.$this->Data['Id'].') AND '.
-      '(`level`='.$Level.') ORDER BY `time` LIMIT 1');
-    if($Result->num_rows > 0)
-    {
-      $Row = $Result->fetch_array();
-      $AbsoluteLeftTime = MysqlDateTimeToTime($Row['time']);
-    } else $AbsoluteLeftTime = 0;
-
-    $Result = $this->Database->select($this->Data['DataTable'], '*', '(`measure`='.$this->Data['Id'].') AND '.
-      '(`level`='.$Level.') ORDER BY `time` DESC LIMIT 1');
-    if($Result->num_rows > 0)
-    {
-      $Row = $Result->fetch_array();
-      $AbsoluteRightTime = MysqlDateTimeToTime($Row['time']);
-    } else $AbsoluteRightTime = 0;
-
-    return(array('left' => $AbsoluteLeftTime, 'right' => $AbsoluteRightTime));
-  }
-
-  // Load one nearest value newer then given time
-  function LoadRightSideValue($Level, $Time)
-  {
-    $Result = array();
-    $DbResult = $this->Database->select($this->Data['DataTable'], '*',
-      '(`time` > "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.
-      $this->Data['Id'].') AND (`level`='.$Level.') ORDER BY `time` ASC LIMIT 1');
-    if($DbResult->num_rows > 0)
-    {
-      // Found one value, simply return it
-      $Row = $DbResult->fetch_array();
-      $Row['time'] = MysqlDateTimeToTime($Row['time']);
-      return(array($Row));
-    } else
-    {
-      // Not found any value. Calculate it
-      //$Time = $Values[count($Values) - 1]['time'] + 60;
-      //array_push($Values, array('time' => $Time, 'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
-      $Result[] = array('time' => ($Time + $this->TimeSegment($Level)), 'min' => 0,
-        'avg' => 0, 'max' => 0, 'continuity' => 0);
-      $DbResult = $this->Database->select($this->Data['DataTable'], '*',
-        '(`time` < "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.$this->Data['Id'].') '.
-        'AND (`level`='.$Level.') ORDER BY `time` DESC LIMIT 1');
-      if($DbResult->num_rows > 0)
-      {
-        $Row = $DbResult->fetch_array();
-        array_unshift($Result, array('time' => (MysqlDateTimeToTime($Row['time']) + 10),
-          'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
-      }
-      return($Result);
-    }
-  }
-
-  // Load one nearest value older then given time
-  function LoadLeftSideValue($Level, $Time)
-  {
-    $Result = array();
-    $DbResult = $this->Database->select($this->Data['DataTable'], '*',
-      '(`time` < "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.$this->Data['Id'].') '.
-      'AND (`level`='.$Level.') ORDER BY `time` DESC LIMIT 1');
-    if($DbResult->num_rows > 0)
-    {
-      // Found one value, simply return it
-      $Row = $DbResult->fetch_array();
-      $Row['time'] = MysqlDateTimeToTime($Row['time']);
-      return(array($Row));
-    } else {
-      // Not found any value. Calculate it
-      //$Time = $Values[0]['time'] - 60;
-      //array_unshift($Values, array('time' => $Time, 'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
-      $Result[] = array('time' => ($Time - $this->TimeSegment($Level)), 'min' => 0,
-        'avg' => 0, 'max' => 0, 'continuity' => 0);
-
-      $DbResult = $this->Database->select($this->Data['DataTable'], '*',
-        '(`time` > "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.$this->Data['Id'].') '.
-        'AND (`level`='.$Level.') ORDER BY `time` ASC LIMIT 1');
-      if($DbResult->num_rows > 0)
-      {
-        $Row = $DbResult->fetch_array();
-        array_push($Result, array('time' => (MysqlDateTimeToTime($Row['time']) - 10),
-          'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
-      }
-      return($Result);
-    }
-  }
-
-  function GetValues($TimeFrom, $TimeTo, $Level)
-  {
-  //$AbsoluteTime = GetTimeRange($this->DataId);
-
-//  if(($TimeFrom > $AbsoluteLeftTime) and ($TimeStart < $AbsoluteRightTime) and
-//    ($TimeTo > $AbsoluteLeftTime) and ($TimeTo < $AbsoluteRightTime))
-//  {
-
-    // Load values in time range
-    $Result = $this->Database->select($this->Data['DataTable'], '`time`, `min`, '.
-      '`avg`, `max`, `continuity`',
-      '(`time` > "'.TimeToMysqlDateTime($TimeFrom).'") AND '.
-      '(`time` < "'.TimeToMysqlDateTime($TimeTo).'") AND '.
-      '(`measure`='.$this->Data['Id'].') AND (`level`='.$Level.') ORDER BY `time`');
-    $Values = array();
-    while($Row = $Result->fetch_array())
-    {
-      $Values[] = array('time' => MysqlDateTimeToTime($Row['time']),
-        'min' => $Row['min'], 'avg' => $Row['avg'], 'max' => $Row['max'],
-        'continuity' => $Row['continuity']);
-    }
-   // array_pop($Values);
-
-    $Points = array();
-    if(count($Values) > 0)
-    {
-      $Values = array_merge(
-        $this->LoadLeftSideValue($Level, $TimeFrom),
-        $Values,
-        $this->LoadRightSideValue($Level, $TimeTo));
-
-      $StartIndex = 0;
-      $Points = array();
-      for($I = 0; $I < $this->DivisionCount; $I++)
-      {
-        $TimeStart = $TimeFrom + (($TimeTo - $TimeFrom) / $this->DivisionCount) * $I;
-        $TimeEnd = $TimeFrom + (($TimeTo - $TimeFrom) / $this->DivisionCount) * ($I + 1);
-
-        $EndIndex = $StartIndex;
-        while($Values[$EndIndex]['time'] < $TimeEnd) $EndIndex = $EndIndex + 1;
-        $SubValues = array_slice($Values, $StartIndex, $EndIndex - $StartIndex + 1);
-        $Points[] = $this->ComputeOneValue($TimeStart, $TimeEnd, $SubValues, $Level);
-        $StartIndex = $EndIndex - 1;
-      }
-    } else $Points[] = array('min' => 0, 'avg' => 0, 'max' => 0);
-    return($Points);
-  }
-
-  function RebuildMeasureCache()
-  {
-    echo('Velicina '.$this->Data['Name']."<br>\n");
-    if($this->Data['Continuity'] == 0) $this->Data['ContinuityEnabled'] = 0;  // non continuous
-      else $this->Data['ContinuityEnabled'] = 2;    // continuous graph
-
-    // Clear previous items
-    $DbResult = $this->Database->select($this->Data['DataTable'], 'COUNT(*)', '(`level`>0) AND (`measure`='.$this->Data['Id'].')');
-    $Row = $DbResult->fetch_array();
-    echo("Mazu starou cache (".$Row[0]." polozek)...");
-    $this->Database->delete($this->Data['DataTable'], '(`level`>0) AND (`measure`='.$this->Data['Id'].')');
-    echo("<br>\n");
-
-    for($Level = 1; $Level <= $this->MaxLevel; $Level++)
-    {
-      echo('Uroven '.$Level."<br>\n");
-      $TimeRange = $this->GetTimeRange($Level - 1);
-      $TimeSegment = $this->TimeSegment($Level);
-      $StartTime = $this->AlignTime($TimeRange['left'], $TimeSegment) - $TimeSegment;
-      $EndTime = $this->AlignTime($TimeRange['right'], $TimeSegment);
-      $BurstCount = 500;
-      echo('For 0 to '.round(($EndTime - $StartTime) / $TimeSegment / $BurstCount)."<br>\n");
-      for($I = 0; $I <= round(($EndTime - $StartTime) / $TimeSegment / $BurstCount); $I++)
-      {
-        echo($I.' ');
-        $StartTime2 = $StartTime + $I * $BurstCount * $TimeSegment;
-        $EndTime2 = $StartTime + ($I + 1) * $BurstCount * $TimeSegment;
-        $Values = array();
-        $DbResult = $this->Database->select($this->Data['DataTable'], '*', '(`time` > "'.TimeToMysqlDateTime($StartTime2).'") AND (`time` < "'.
-          TimeToMysqlDateTime($EndTime2).'") AND (`measure`='.$this->Data['Id'].') AND (`level`='.($Level - 1).') ORDER BY `time`');
-        while($Row = $DbResult->fetch_array())
-        {
-          $Row['time'] = MysqlDateTimeToTime($Row['time']);
-          $Values[] = $Row;
-        }
-
-        if(count($Values) > 0)
-        {
-          $Values = array_merge(
-            $this->LoadLeftSideValue($Level - 1, $StartTime2),
-            $Values,
-            $this->LoadRightSideValue($Level - 1, $EndTime2));
-
-          $StartIndex = 0;
-          for($B = 0; $B < $BurstCount; $B++)
-          {
-            echo('.');
-            $StartTime3 = $StartTime2 + (($EndTime2 - $StartTime2) / $BurstCount) * $B;
-            $EndTime3 = $StartTime2 + (($EndTime2 - $StartTime2) / $BurstCount) * ($B + 1);
-
-            $EndIndex = $StartIndex;
-            while($Values[$EndIndex]['time'] < $EndTime3) $EndIndex = $EndIndex + 1;
-            $SubValues = array_slice($Values, $StartIndex, $EndIndex - $StartIndex + 1);
-            if(count($SubValues) > 2)
-            {
-              $Point = $this->ComputeOneValue($StartTime3, $EndTime3, $SubValues, $Level);
-              $Continuity = $SubValues[1]['continuity'];
-              $this->Database->insert($this->Data['DataTable'], array('level' => $Level, 'measure' => $this->Data['Id'],
-                'min' => $Point['min'], 'avg' => $Point['avg'], 'max' => $Point['max'],
-                'continuity' => $Continuity, 'time' => TimeToMysqlDateTime($StartTime3 + ($EndTime3 - $StartTime3) / 2)));
-            }
-            $StartIndex = $EndIndex - 1;
-          }
-        }
-        // Load values in time range
-        //array_pop($NextValues);
-      }
-      echo("Uroven dokoncena<br>\n");
-      $DbResult = $this->Database->select($this->Data['DataTable'], 'COUNT(*)',
-        '(`level`='.$Level.') AND (`measure`='.$this->Data['Id'].')');
-      $Row = $DbResult->fetch_array();
-      echo("Vlozeno ".$Row[0]." polozek.<br>\n");
-    }
-  }
-
-  function RebuildAllMeasuresCache()
-  {
-    $Result = $this->Database->select('measure', 'Id');
-    while($Row = $Result->fetch_array())
-    {
-      $Measure = new Measure();
-      $Measure->Load($Row['Id']);
-      $Measure->RebuildMeasureCache();
-      echo('Velicina id '.$Row['Id'].' dokoncena<br/>');
-    }
-  }
-
-  function InitMeasureDataTable()
-  {
-    $this->Database->query('CREATE TABLE `data_'.$this->Data['Name'].'` (
-      `Time` TIMESTAMP NOT NULL ,
-      `Avg` '.$this->Data['DataType'].' NOT NULL ,
-      `Continuity` BOOL NOT NULL
-      ) ENGINE = MYISAM ;');
-
-    $this->Database->query('CREATE TABLE `data_'.$this->Data['Name'].'_cache` (
-`Time` TIMESTAMP NOT NULL ,
-`Level` TINYINT NOT NULL ,
-`Min` '.$this->Data['DataType'].' NOT NULL ,
-`Avg` '.$this->Data['DataType'].' NOT NULL ,
-`Max` '.$this->Data['DataType'].' NOT NULL ,
-`Continuity` BOOL NOT NULL
-) ENGINE = MYISAM ;');
-  }
-}
Index: /trunk/Modules/Measure/Measure.php
===================================================================
--- /trunk/Modules/Measure/Measure.php	(revision 69)
+++ /trunk/Modules/Measure/Measure.php	(revision 69)
@@ -0,0 +1,497 @@
+<?php
+
+include_once(dirname(__FILE__).'/Page.php');
+
+class ModuleMeasure extends AppModule
+{
+  function __construct($System)
+  {
+    parent::__construct($System);
+    $this->Name = 'Measure';
+    $this->Version = '1.0';
+    $this->Creator = 'Chronos';
+    $this->License = 'GNU/GPL';
+    $this->Description = 'Measurement processing';
+    $this->Dependencies = array();
+  }
+
+  function DoStart()
+  {
+    $this->System->RegisterPage('', 'PageMain');
+  }
+}
+
+include_once('Point.php');
+
+class Measure
+{
+  var $Data;
+  var $ValueTypes;
+  var $LevelReducing;
+  var $Database;
+  var $MaxLevel;
+  var $ReferenceTime;
+  var $Differential;
+  var $DivisionCount;
+  var $PeriodTolerance;
+
+  function __construct(Database $Database)
+  {
+    $this->Id = 0;
+    $this->ValueTypes = array('min', 'avg', 'max');
+    $this->Database = &$Database;
+    $this->LevelReducing = 5;
+    $this->MaxLevel = 4;
+    $this->ReferenceTime = 0;
+    $this->Differential = 0;
+    $this->DivisionCount = 500;
+    $this->PeriodTolerance = 0.25; // 25%
+  }
+
+  function TimeSegment($Level)
+  {
+    return(pow($this->LevelReducing, $Level) * 60);
+  }
+
+  function StatTableName($Level)
+  {
+    if($Level == 0) return('data');
+      else return('data_cache');
+  }
+
+  function AlignTime($Time, $TimeSegment)
+  {
+    return(round(($Time - $this->ReferenceTime) / $TimeSegment) * $TimeSegment +
+      $this->ReferenceTime);
+  }
+
+  function Load($Id)
+  {
+    $Result = $this->Database->select('Measure', '*', '`Id`='.$Id);
+    if($Result->num_rows > 0)
+    {
+      $this->Data = $Result->fetch_array();
+      if($this->Data['Continuity'] == 0) $this->Data['ContinuityEnabled'] = 0;  // non continuous
+        else $this->Data['ContinuityEnabled'] = 2;    // continuous graph
+    } else die('Měřená veličina nenalezena');
+  }
+
+  function AddValue($Value)
+  {
+    $Time = time();
+
+    $Result = $this->Database->select($this->Data['DataTable'], '*', '(`Measure`='.$this->Data['Id'].') AND '.
+      '(`level`=0) ORDER BY `time` DESC LIMIT 2');
+    if($Result->num_rows == 0) {
+      // No measure value found. Simply insert new first value.
+      $this->Database->insert($this->Data['DataTable'],
+      array('min' => $Value, 'avg' => $Value, 'max' => $Value, 'level' => 0,
+      'measure' => $this->Data['Id'], 'time' => TimeToMysqlDateTime($Time),
+      'continuity' => 0));
+    } else if($Result->num_rows == 1) {
+      // One value exists. Add second value.
+      $this->Database->insert($this->Data['DataTable'],
+      array('min' => $Value, 'avg' => $Value, 'max' => $Value, 'level' => 0,
+      'measure' => $this->Data['Id'], 'time' => TimeToMysqlDateTime($Time),
+      'continuity' => 1));
+    } else {
+      // Two values already exist in measure table
+      $LastValue = $Result->fetch_array();
+      $NextToLastValue = $Result->fetch_array();
+      if(($Time - MysqlDateTimeToTime($LastValue['time'])) < (1 - $this->PeriodTolerance) * $this->Data['Period'])
+      {
+        // New value added too quickly. Need to wait for next measure period.
+      }
+      else
+      {
+        // We are near defined period and can add new value.
+        if(($Time - MysqlDateTimeToTime($LastValue['time'])) < (1 + $this->PeriodTolerance) * $this->Data['Period']) {
+          // New value added near defined measure period inside tolerance. Keep continuity.
+          $Continuity = 1;
+        } else {
+          // New value added too late after defined measure period. Stop
+          // continuity and let gap to be added.
+          $Continuity = 0;
+        }
+        if(($LastValue['avg'] == $NextToLastValue['avg']) and ($LastValue['avg'] == $Value) and
+          ($LastValue['continuity'] == 1) and ($Continuity == 1))
+        {
+          // New value is same as last value and continuity mode is enabled and
+          // continuity flag is present. Just shift forward time for last value.
+          $this->Database->update($this->Data['DataTable'], '(`time`="'.$LastValue['time'].'") AND '.
+          '(`level`=0) AND (`measure`='.$this->Data['Id'].')', array('time' => TimeToMysqlDateTime($Time)));
+        } else
+        {
+          // Last value is different or not with continuity flag. Need to add new value.
+          $this->Database->insert($this->Data['DataTable'], array('min' => $Value,
+            'avg' => $Value, 'max' => $Value, 'level' => 0, 'measure' => $this->Data['Id'], 'time' => TimeToMysqlDateTime($Time),
+            'continuity' => $Continuity));
+        }
+      }
+    }
+
+    // Update higher levels
+    for($Level = 1; $Level <= $this->MaxLevel; $Level++)
+    {
+      $TimeSegment = $this->TimeSegment($Level);
+      $EndTime = $this->AlignTime($Time, $TimeSegment);
+      //if($EndTime < $Time) $EndTime = $EndTime + $TimeSegment;
+      $StartTime = $EndTime - $TimeSegment;
+
+      // Load values in time range
+      $Values = array();
+      $Result = $this->Database->select($this->Data['DataTable'], '*', '(`time` > "'.
+        TimeToMysqlDateTime($StartTime).'") AND '.
+        '(`time` < "'.TimeToMysqlDateTime($EndTime).'") AND '.
+        '(`measure`='.$this->Data['Id'].') AND (`level`='.($Level - 1).') ORDER BY `time`');
+      while($Row = $Result->fetch_array())
+      {
+        $Row['time'] = MysqlDateTimeToTime($Row['time']);
+        $Values[] = $Row;
+      }
+      //array_pop($Values);
+
+      // Load subsidary values
+      $Values = array_merge(
+        $this->LoadLeftSideValue($Level - 1, $StartTime),
+        $Values,
+        $this->LoadRightSideValue($Level - 1, $EndTime)
+      );
+
+      $Point = $this->ComputeOneValue($StartTime, $EndTime, $Values, $Level);
+
+      $this->Database->delete($this->Data['DataTable'], '(`time` > "'.TimeToMysqlDateTime($StartTime).'") AND
+        (`time` < "'.TimeToMysqlDateTime($EndTime).'") AND (`measure`='.$this->Data['Id'].') '.
+        'AND (`level`='.$Level.')');
+      $Continuity = $Values[1]['continuity'];
+      $this->Database->insert($this->Data['DataTable'], array('level' => $Level,
+        'measure' => $this->Data['Id'], 'min' => $Point['min'],
+        'avg' => $Point['avg'], 'max' => $Point['max'], 'continuity' => $Continuity,
+        'time' => TimeToMysqlDateTime($StartTime + ($EndTime - $StartTime) / 2)));
+    }
+  }
+
+  /* Compute one value for upper time level from multiple values */
+  function ComputeOneValue($LeftTime, $RightTime, $Values, $Level)
+  {
+    $NewValue = array('min' => +1000000000000000000, 'avg' => 0, 'max' => -1000000000000000000);
+
+    // Trim outside parts
+    foreach($this->ValueTypes as $ValueType)
+    {
+      $Values[0][$ValueType] = Interpolation(
+        NewPoint($Values[0]['time'], $Values[0][$ValueType]),
+        NewPoint($Values[1]['time'], $Values[1][$ValueType]), $LeftTime);
+    }
+    $Values[0]['time'] = $LeftTime;
+    foreach($this->ValueTypes as $ValueType)
+    {
+      $Values[count($Values) - 1][$ValueType] = Interpolation(
+        NewPoint($Values[count($Values) - 2]['time'], $Values[count($Values) - 2][$ValueType]),
+        NewPoint($Values[count($Values) - 1]['time'], $Values[count($Values) - 1][$ValueType]),
+        $RightTime);
+    }
+    $Values[count($Values) - 1]['time'] = $RightTime;
+
+    // Perform computation
+    foreach($this->ValueTypes as $ValueType)
+    {
+      // Compute new value
+      for($I = 0; $I < (count($Values) - 1); $I++)
+      {
+        if($ValueType == 'avg')
+        {
+          if($Values[$I + 1]['continuity'] == $this->Data['ContinuityEnabled']);
+            else if($this->Differential == 0)
+            {
+              $NewValue[$ValueType] = $NewValue[$ValueType] + ($Values[$I + 1]['time'] - $Values[$I]['time']) *
+                (($Values[$I + 1][$ValueType] - $Values[$I][$ValueType]) / 2 + $Values[$I][$ValueType]);
+            } else {
+              $NewValue[$ValueType] = $NewValue[$ValueType] + ($Values[$I + 1]['time'] - $Values[$I]['time']) *
+               (($Values[$I + 1][$ValueType] - $Values[$I][$ValueType]) / 2);
+            }
+        }
+        else if($ValueType == 'max')
+        {
+          if($Values[$I + 1]['continuity'] == $this->Data['ContinuityEnabled'])
+          {
+            if(0 > $NewValue[$ValueType]) $NewValue[$ValueType] = 0;
+          }
+          else
+          {
+            if($this->Differential == 0)
+            {
+              if($Values[$I + 1][$ValueType] > $NewValue[$ValueType]) $NewValue[$ValueType] = $Values[$I + 1][$ValueType];
+            } else {
+              $Difference = $Values[$I + 1][$ValueType] - $Values[$I][$ValueType];
+              if($Difference > $NewValue[$ValueType]) $NewValue[$ValueType] = $Difference;
+            }
+          }
+        }
+        else if($ValueType == 'min')
+        {
+          if($Values[$I + 1]['continuity'] == $this->Data['ContinuityEnabled'])
+          {
+            if(0 < $NewValue[$ValueType]) $NewValue[$ValueType] = 0;
+          } else {
+            if($this->Differential == 0)
+            {
+              if($Values[$I + 1][$ValueType] < $NewValue[$ValueType]) $NewValue[$ValueType] = $Values[$I + 1][$ValueType];
+            } else {
+              $Difference = $Values[$I + 1][$ValueType] - $Values[$I][$ValueType];
+              if($Difference < $NewValue[$ValueType]) $NewValue[$ValueType] = $Difference;
+            }
+          }
+        }
+      }
+      $NewValue[$ValueType] = $NewValue[$ValueType];
+    }
+    //if(($RightTime - $LeftTime) > 0)
+    if($this->Data['Cumulative'] == 0)
+    {
+      $NewValue['avg'] = $NewValue['avg'] / ($RightTime - $LeftTime);
+    }
+    return($NewValue);
+  }
+
+  function GetTimeRange($Level)
+  {
+    // Get first and last time
+    $Result = $this->Database->select($this->Data['DataTable'], '*', '(`measure`='.$this->Data['Id'].') AND '.
+      '(`level`='.$Level.') ORDER BY `time` LIMIT 1');
+    if($Result->num_rows > 0)
+    {
+      $Row = $Result->fetch_array();
+      $AbsoluteLeftTime = MysqlDateTimeToTime($Row['time']);
+    } else $AbsoluteLeftTime = 0;
+
+    $Result = $this->Database->select($this->Data['DataTable'], '*', '(`measure`='.$this->Data['Id'].') AND '.
+      '(`level`='.$Level.') ORDER BY `time` DESC LIMIT 1');
+    if($Result->num_rows > 0)
+    {
+      $Row = $Result->fetch_array();
+      $AbsoluteRightTime = MysqlDateTimeToTime($Row['time']);
+    } else $AbsoluteRightTime = 0;
+
+    return(array('left' => $AbsoluteLeftTime, 'right' => $AbsoluteRightTime));
+  }
+
+  // Load one nearest value newer then given time
+  function LoadRightSideValue($Level, $Time)
+  {
+    $Result = array();
+    $DbResult = $this->Database->select($this->Data['DataTable'], '*',
+      '(`time` > "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.
+      $this->Data['Id'].') AND (`level`='.$Level.') ORDER BY `time` ASC LIMIT 1');
+    if($DbResult->num_rows > 0)
+    {
+      // Found one value, simply return it
+      $Row = $DbResult->fetch_array();
+      $Row['time'] = MysqlDateTimeToTime($Row['time']);
+      return(array($Row));
+    } else
+    {
+      // Not found any value. Calculate it
+      //$Time = $Values[count($Values) - 1]['time'] + 60;
+      //array_push($Values, array('time' => $Time, 'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
+      $Result[] = array('time' => ($Time + $this->TimeSegment($Level)), 'min' => 0,
+        'avg' => 0, 'max' => 0, 'continuity' => 0);
+      $DbResult = $this->Database->select($this->Data['DataTable'], '*',
+        '(`time` < "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.$this->Data['Id'].') '.
+        'AND (`level`='.$Level.') ORDER BY `time` DESC LIMIT 1');
+      if($DbResult->num_rows > 0)
+      {
+        $Row = $DbResult->fetch_array();
+        array_unshift($Result, array('time' => (MysqlDateTimeToTime($Row['time']) + 10),
+          'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
+      }
+      return($Result);
+    }
+  }
+
+  // Load one nearest value older then given time
+  function LoadLeftSideValue($Level, $Time)
+  {
+    $Result = array();
+    $DbResult = $this->Database->select($this->Data['DataTable'], '*',
+      '(`time` < "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.$this->Data['Id'].') '.
+      'AND (`level`='.$Level.') ORDER BY `time` DESC LIMIT 1');
+    if($DbResult->num_rows > 0)
+    {
+      // Found one value, simply return it
+      $Row = $DbResult->fetch_array();
+      $Row['time'] = MysqlDateTimeToTime($Row['time']);
+      return(array($Row));
+    } else {
+      // Not found any value. Calculate it
+      //$Time = $Values[0]['time'] - 60;
+      //array_unshift($Values, array('time' => $Time, 'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
+      $Result[] = array('time' => ($Time - $this->TimeSegment($Level)), 'min' => 0,
+        'avg' => 0, 'max' => 0, 'continuity' => 0);
+
+      $DbResult = $this->Database->select($this->Data['DataTable'], '*',
+        '(`time` > "'.TimeToMysqlDateTime($Time).'") AND (`measure`='.$this->Data['Id'].') '.
+        'AND (`level`='.$Level.') ORDER BY `time` ASC LIMIT 1');
+      if($DbResult->num_rows > 0)
+      {
+        $Row = $DbResult->fetch_array();
+        array_push($Result, array('time' => (MysqlDateTimeToTime($Row['time']) - 10),
+          'min' => 0, 'avg' => 0, 'max' => 0, 'continuity' => 0));
+      }
+      return($Result);
+    }
+  }
+
+  function GetValues($TimeFrom, $TimeTo, $Level)
+  {
+  //$AbsoluteTime = GetTimeRange($this->DataId);
+
+//  if(($TimeFrom > $AbsoluteLeftTime) and ($TimeStart < $AbsoluteRightTime) and
+//    ($TimeTo > $AbsoluteLeftTime) and ($TimeTo < $AbsoluteRightTime))
+//  {
+
+    // Load values in time range
+    $Result = $this->Database->select($this->Data['DataTable'], '`time`, `min`, '.
+      '`avg`, `max`, `continuity`',
+      '(`time` > "'.TimeToMysqlDateTime($TimeFrom).'") AND '.
+      '(`time` < "'.TimeToMysqlDateTime($TimeTo).'") AND '.
+      '(`measure`='.$this->Data['Id'].') AND (`level`='.$Level.') ORDER BY `time`');
+    $Values = array();
+    while($Row = $Result->fetch_array())
+    {
+      $Values[] = array('time' => MysqlDateTimeToTime($Row['time']),
+        'min' => $Row['min'], 'avg' => $Row['avg'], 'max' => $Row['max'],
+        'continuity' => $Row['continuity']);
+    }
+   // array_pop($Values);
+
+    $Points = array();
+    if(count($Values) > 0)
+    {
+      $Values = array_merge(
+        $this->LoadLeftSideValue($Level, $TimeFrom),
+        $Values,
+        $this->LoadRightSideValue($Level, $TimeTo));
+
+      $StartIndex = 0;
+      $Points = array();
+      for($I = 0; $I < $this->DivisionCount; $I++)
+      {
+        $TimeStart = $TimeFrom + (($TimeTo - $TimeFrom) / $this->DivisionCount) * $I;
+        $TimeEnd = $TimeFrom + (($TimeTo - $TimeFrom) / $this->DivisionCount) * ($I + 1);
+
+        $EndIndex = $StartIndex;
+        while($Values[$EndIndex]['time'] < $TimeEnd) $EndIndex = $EndIndex + 1;
+        $SubValues = array_slice($Values, $StartIndex, $EndIndex - $StartIndex + 1);
+        $Points[] = $this->ComputeOneValue($TimeStart, $TimeEnd, $SubValues, $Level);
+        $StartIndex = $EndIndex - 1;
+      }
+    } else $Points[] = array('min' => 0, 'avg' => 0, 'max' => 0);
+    return($Points);
+  }
+
+  function RebuildMeasureCache()
+  {
+    echo('Velicina '.$this->Data['Name']."<br>\n");
+    if($this->Data['Continuity'] == 0) $this->Data['ContinuityEnabled'] = 0;  // non continuous
+      else $this->Data['ContinuityEnabled'] = 2;    // continuous graph
+
+    // Clear previous items
+    $DbResult = $this->Database->select($this->Data['DataTable'], 'COUNT(*)', '(`level`>0) AND (`measure`='.$this->Data['Id'].')');
+    $Row = $DbResult->fetch_array();
+    echo("Mazu starou cache (".$Row[0]." polozek)...");
+    $this->Database->delete($this->Data['DataTable'], '(`level`>0) AND (`measure`='.$this->Data['Id'].')');
+    echo("<br>\n");
+
+    for($Level = 1; $Level <= $this->MaxLevel; $Level++)
+    {
+      echo('Uroven '.$Level."<br>\n");
+      $TimeRange = $this->GetTimeRange($Level - 1);
+      $TimeSegment = $this->TimeSegment($Level);
+      $StartTime = $this->AlignTime($TimeRange['left'], $TimeSegment) - $TimeSegment;
+      $EndTime = $this->AlignTime($TimeRange['right'], $TimeSegment);
+      $BurstCount = 500;
+      echo('For 0 to '.round(($EndTime - $StartTime) / $TimeSegment / $BurstCount)."<br>\n");
+      for($I = 0; $I <= round(($EndTime - $StartTime) / $TimeSegment / $BurstCount); $I++)
+      {
+        echo($I.' ');
+        $StartTime2 = $StartTime + $I * $BurstCount * $TimeSegment;
+        $EndTime2 = $StartTime + ($I + 1) * $BurstCount * $TimeSegment;
+        $Values = array();
+        $DbResult = $this->Database->select($this->Data['DataTable'], '*', '(`time` > "'.TimeToMysqlDateTime($StartTime2).'") AND (`time` < "'.
+          TimeToMysqlDateTime($EndTime2).'") AND (`measure`='.$this->Data['Id'].') AND (`level`='.($Level - 1).') ORDER BY `time`');
+        while($Row = $DbResult->fetch_array())
+        {
+          $Row['time'] = MysqlDateTimeToTime($Row['time']);
+          $Values[] = $Row;
+        }
+
+        if(count($Values) > 0)
+        {
+          $Values = array_merge(
+            $this->LoadLeftSideValue($Level - 1, $StartTime2),
+            $Values,
+            $this->LoadRightSideValue($Level - 1, $EndTime2));
+
+          $StartIndex = 0;
+          for($B = 0; $B < $BurstCount; $B++)
+          {
+            echo('.');
+            $StartTime3 = $StartTime2 + (($EndTime2 - $StartTime2) / $BurstCount) * $B;
+            $EndTime3 = $StartTime2 + (($EndTime2 - $StartTime2) / $BurstCount) * ($B + 1);
+
+            $EndIndex = $StartIndex;
+            while($Values[$EndIndex]['time'] < $EndTime3) $EndIndex = $EndIndex + 1;
+            $SubValues = array_slice($Values, $StartIndex, $EndIndex - $StartIndex + 1);
+            if(count($SubValues) > 2)
+            {
+              $Point = $this->ComputeOneValue($StartTime3, $EndTime3, $SubValues, $Level);
+              $Continuity = $SubValues[1]['continuity'];
+              $this->Database->insert($this->Data['DataTable'], array('level' => $Level, 'measure' => $this->Data['Id'],
+                'min' => $Point['min'], 'avg' => $Point['avg'], 'max' => $Point['max'],
+                'continuity' => $Continuity, 'time' => TimeToMysqlDateTime($StartTime3 + ($EndTime3 - $StartTime3) / 2)));
+            }
+            $StartIndex = $EndIndex - 1;
+          }
+        }
+        // Load values in time range
+        //array_pop($NextValues);
+      }
+      echo("Uroven dokoncena<br>\n");
+      $DbResult = $this->Database->select($this->Data['DataTable'], 'COUNT(*)',
+        '(`level`='.$Level.') AND (`measure`='.$this->Data['Id'].')');
+      $Row = $DbResult->fetch_array();
+      echo("Vlozeno ".$Row[0]." polozek.<br>\n");
+    }
+  }
+
+  function RebuildAllMeasuresCache()
+  {
+    $Result = $this->Database->select('measure', 'Id');
+    while($Row = $Result->fetch_array())
+    {
+      $Measure = new Measure();
+      $Measure->Load($Row['Id']);
+      $Measure->RebuildMeasureCache();
+      echo('Velicina id '.$Row['Id'].' dokoncena<br/>');
+    }
+  }
+
+  function InitMeasureDataTable()
+  {
+    $this->Database->query('CREATE TABLE `data_'.$this->Data['Name'].'` (
+      `Time` TIMESTAMP NOT NULL ,
+      `Avg` '.$this->Data['DataType'].' NOT NULL ,
+      `Continuity` BOOL NOT NULL
+      ) ENGINE = MYISAM ;');
+
+    $this->Database->query('CREATE TABLE `data_'.$this->Data['Name'].'_cache` (
+`Time` TIMESTAMP NOT NULL ,
+`Level` TINYINT NOT NULL ,
+`Min` '.$this->Data['DataType'].' NOT NULL ,
+`Avg` '.$this->Data['DataType'].' NOT NULL ,
+`Max` '.$this->Data['DataType'].' NOT NULL ,
+`Continuity` BOOL NOT NULL
+) ENGINE = MYISAM ;');
+  }
+}
Index: /trunk/Packages/Common/AppModule.php
===================================================================
--- /trunk/Packages/Common/AppModule.php	(revision 69)
+++ /trunk/Packages/Common/AppModule.php	(revision 69)
@@ -0,0 +1,400 @@
+<?php
+
+/* This implementation will not support installation from remote source. Just
+ * installation of already presented modules mainly to persistence preparation.
+ */
+
+class ModuleType
+{
+  const System = 0;
+  const Normal = 1;
+  const Application = 2;
+}
+
+class ModuleAction
+{
+  const Start = 0;
+  const Stop = 1;
+  const Install = 2;
+  const Uninstall = 3;
+  const Upgrade = 4;
+  const Enable = 5;
+  const Disable = 6;
+}
+
+class ModuleCondition
+{
+  const All = 0;
+  const Enabled = 1;
+  const NotEnabled = 2;
+  const Installed = 3;
+  const NotInstalled = 4;
+  const Running = 5;
+  const NotRunning = 6;
+}
+
+class AppModule
+{
+  var $Id;
+  var $Name;
+  var $Title;
+  var $Version;
+  var $License;
+  var $Creator;
+  var $HomePage;
+  var $Description;
+  var $Running;
+  var $Enabled;
+  var $Installed;
+  var $InstalledVersion;
+  /** @var ModuleType */
+  var $Type;
+  var $Dependencies;
+  /** @var Database */
+  var $Database;
+  /** @var System */
+  var $System;
+  /** @var AppModuleManager */
+  var $Manager;
+  var $OnChange;
+
+  function __construct(System $System)
+  {
+    $this->System = &$System;
+    $this->Database = &$System->Database;
+    $this->Installed = false;
+    $this->Enabled = false;
+    $this->Running = false;
+    $this->HomePage = '';
+    $this->License = '';
+    $this->Version = '';
+    $this->Creator = '';
+    $this->Description = '';
+    $this->Dependencies = array();
+    $this->Type = ModuleType::Normal;
+  }
+
+  function Install()
+  {
+    if($this->Installed) return;
+    $List = array();
+    $this->Manager->EnumDependenciesCascade($this, $List, array(ModuleCondition::NotInstalled));
+    $this->Manager->Perform($List, array(ModuleAction::Install), array(ModuleCondition::NotInstalled));
+    $this->DoInstall();
+    $this->Installed = true;
+    $this->InstalledVersion = $this->Version;
+    $this->Manager->Modules[$this->Name] = $this;
+  }
+
+  function Uninstall()
+  {
+    if(!$this->Installed) return;
+    $this->Stop();
+    $this->Installed = false;
+    $List = array();
+    $this->Manager->EnumSuperiorDependenciesCascade($this, $List, array(ModuleCondition::Installed));
+    $this->Manager->Perform($List, array(ModuleAction::Uninstall), array(ModuleCondition::Installed));
+    $this->DoUninstall();
+  }
+
+  function Upgrade()
+  {
+    if(!$this->Installed) return;
+    if($this->InstalledVersion == $this->Version) return;
+    $List = array();
+    $this->Manager->EnumSuperiorDependenciesCascade($this, $List, array(ModuleCondition::Installed));
+    $this->Manager->Perform($List, array(ModuleAction::Upgrade), array(ModuleCondition::Installed));
+    $this->DoUpgrade();
+  }
+
+  function Reinstall()
+  {
+    $this->Uninstall();
+    // TODO: Install also back dependecies
+    $this->Install();
+  }
+
+  function Start()
+  {
+    if($this->Running) return;
+    if(!$this->Installed) return;
+    $List = array();
+    $this->Manager->EnumDependenciesCascade($this, $List, array(ModuleCondition::NotRunning));
+    $this->Manager->Perform($List, array(ModuleAction::Start), array(ModuleCondition::NotRunning));
+    $this->DoStart();
+    $this->Running = true;
+  }
+
+  function Stop()
+  {
+    if(!$this->Running) return;
+    $this->Running = false;
+    $List = array();
+    $this->Manager->EnumSuperiorDependenciesCascade($this, $List, array(ModuleCondition::Running));
+    $this->Manager->Perform($List, array(ModuleAction::Stop), array(ModuleCondition::Running));
+    $this->DoStop();
+  }
+
+  function Restart()
+  {
+    $this->Stop();
+    $this->Start();
+  }
+
+  function Enable()
+  {
+    if($this->Enabled) return;
+    if(!$this->Installed) return;
+    $List = array();
+    $this->Manager->EnumDependenciesCascade($this, $List, array(ModuleCondition::NotEnabled));
+    $this->Manager->Perform($List, array(ModuleAction::Enable), array(ModuleCondition::NotEnabled));
+    $this->Enabled = true;
+  }
+
+  function Disable()
+  {
+    if(!$this->Enabled) return;
+    $this->Stop();
+    $this->Enabled = false;
+    $List = array();
+    $this->Manager->EnumSuperiorDependenciesCascade($this, $List, array(ModuleCondition::Enabled));
+    $this->Manager->Perform($List, array(ModuleAction::Disable), array(ModuleCondition::Enabled));
+  }
+
+  protected function DoStart()
+  {
+  }
+
+  protected function DoStop()
+  {
+  }
+
+  protected function DoInstall()
+  {
+  }
+
+  protected function DoUninstall()
+  {
+  }
+
+  protected function DoUpgrade()
+  {
+
+  }
+}
+
+/* Manage installed modules */
+class AppModuleManager
+{
+  var $Modules;
+  var $System;
+  var $FileName;
+  var $ModulesDir;
+  var $OnLoadModules;
+
+  function __construct(System $System)
+  {
+    $this->Modules = array();
+    $this->System = &$System;
+    $this->FileName = dirname(__FILE__).'/../../Config/ModulesConfig.php';
+    $this->ModulesDir = dirname(__FILE__).'/../../Modules';
+  }
+
+  function Perform($List, $Actions, $Conditions = array(ModuleCondition::All))
+  {
+    foreach($List as $Index => $Module)
+    {
+      if(in_array(ModuleCondition::All, $Conditions) or
+        ($Module->Running and in_array(ModuleCondition::Running, $Conditions)) or
+        (!$Module->Running and in_array(ModuleCondition::NotRunning, $Conditions)) or
+        ($Module->Installed and in_array(ModuleCondition::Installed, $Conditions)) or
+        (!$Module->Installed and in_array(ModuleCondition::NotInstalled, $Conditions)) or
+        ($Module->Enabled and in_array(ModuleCondition::Enabled, $Conditions)) or
+        (!$Module->Enabled and in_array(ModuleCondition::NotEnabled, $Conditions)))
+      {
+        foreach($Actions as $Action)
+        {
+          if($Action == ModuleAction::Start) $Module->Start();
+          if($Action == ModuleAction::Stop) $Module->Stop();
+          if($Action == ModuleAction::Install) $Module->Install();
+          if($Action == ModuleAction::Uninstall) $Module->Uninstall();
+          if($Action == ModuleAction::Enable) $Module->Enable();
+          if($Action == ModuleAction::Disable) $Module->Disable();
+          if($Action == ModuleAction::Upgrade) $Module->Upgrade();
+        }
+      }
+    }
+  }
+
+  function EnumDependenciesCascade($Module, &$List, $Conditions = array(ModuleCondition::All))
+  {
+    foreach($Module->Dependencies as $Dependency)
+    {
+      if(!array_key_exists($Dependency, $this->Modules))
+        throw new Exception(sprintf(T('Module "%s" dependency "%s" not found'), $Module->Name, $Dependency));
+      $DepModule = $this->Modules[$Dependency];
+      if(in_array(ModuleCondition::All, $Conditions) or
+        ($Module->Running and in_array(ModuleCondition::Running, $Conditions)) or
+        (!$Module->Running and in_array(ModuleCondition::NotRunning, $Conditions)) or
+        ($Module->Enabled and in_array(ModuleCondition::Enabled, $Conditions)) or
+        (!$Module->Enabled and in_array(ModuleCondition::NotEnabled, $Conditions)) or
+        ($Module->Installed and in_array(ModuleCondition::Installed, $Conditions)) or
+        (!$Module->Installed and in_array(ModuleCondition::NotInstalled, $Conditions)))
+      {
+        array_push($List, $DepModule);
+        $this->EnumDependenciesCascade($DepModule, $List, $Conditions);
+      }
+    }
+  }
+
+  function EnumSuperiorDependenciesCascade($Module, &$List, $Conditions = array(ModuleCondition::All))
+  {
+    foreach($this->Modules as $RefModule)
+    {
+      if(in_array($Module->Name, $RefModule->Dependencies) and
+          (in_array(ModuleCondition::All, $Conditions) or
+          ($Module->Running and in_array(ModuleCondition::Running, $Conditions)) or
+          (!$Module->Running and in_array(ModuleCondition::NotRunning, $Conditions)) or
+          ($Module->Enabled and in_array(ModuleCondition::Enabled, $Conditions)) or
+          (!$Module->Enabled and in_array(ModuleCondition::NotEnabled, $Conditions)) or
+          ($Module->Installed and in_array(ModuleCondition::Installed, $Conditions)) or
+          (!$Module->Installed and in_array(ModuleCondition::NotInstalled, $Conditions))))
+      {
+        array_push($List, $RefModule);
+        $this->EnumSuperiorDependenciesCascade($RefModule, $List, $Conditions);
+      }
+    }
+  }
+
+  function Start()
+  {
+    $this->LoadModules();
+    if(file_exists($this->FileName)) $this->LoadState();
+    $this->StartEnabled();
+  }
+
+  function StartAll()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Start));
+  }
+
+  function StartEnabled()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Start), array(ModuleCondition::Enabled));
+  }
+
+  function StopAll()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Stop));
+  }
+
+  function InstallAll()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Install));
+    $this->SaveState();
+  }
+
+  function UninstallAll()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Uninstall));
+    $this->SaveState();
+  }
+
+  function EnableAll()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Enable));
+    $this->SaveState();
+  }
+
+  function DisableAll()
+  {
+    $this->Perform($this->Modules, array(ModuleAction::Disable));
+    $this->SaveState();
+  }
+
+  function ModulePresent($Name)
+  {
+    return(array_key_exists($Name, $this->Modules));
+  }
+
+  function ModuleEnabled($Name)
+  {
+    return(array_key_exists($Name, $this->Modules) and $this->Modules[$Name]->Enabled);
+  }
+
+  function ModuleRunning($Name)
+  {
+    return(array_key_exists($Name, $this->Modules) and $this->Modules[$Name]->Running);
+  }
+
+  /* @return Module */
+  function SearchModuleById($Id)
+  {
+    foreach($this->Modules as $Module)
+    {
+      //DebugLog($Module->Name.' '.$Module->Id);
+      if($Module->Id == $Id) return($Module->Name);
+    }
+    return('');
+  }
+
+  function LoadState()
+  {
+    $ConfigModules = array();
+    include($this->FileName);
+    foreach($ConfigModules as $Mod)
+    {
+      if(array_key_exists($Mod['Name'], $this->Modules))
+      {
+        $this->Modules[$Mod['Name']] = $this->Modules[$Mod['Name']];
+        $this->Modules[$Mod['Name']]->Enabled = $Mod['Enabled'];
+        $this->Modules[$Mod['Name']]->Installed = $Mod['Installed'];
+        $this->Modules[$Mod['Name']]->InstalledVersion = $Mod['Version'];
+      }
+    }
+  }
+
+  function SaveState()
+  {
+    $Data = array();
+    foreach($this->Modules as $Module)
+    {
+      $Data[] = array('Name' => $Module->Name, 'Enabled' => $Module->Enabled,
+        'Version' => $Module->Version, 'Installed' => $Module->Installed);
+    }
+    file_put_contents($this->FileName, "<?php \n\n\$ConfigModules = ".var_export($Data, true).";\n");
+  }
+
+  function RegisterModule(AppModule $Module)
+  {
+    $this->Modules[$Module->Name] = &$Module;
+    $Module->Manager = &$this;
+    $Module->OnChange = &$this->OnModuleChange;
+  }
+
+  function UnregisterModule(AppModule $Module)
+  {
+    unset($this->Modules[array_search($Module, $this->Modules)]);
+  }
+
+  function LoadModulesFromDir($Directory)
+  {
+    $List = scandir($Directory);
+    foreach($List as $Item)
+    {
+      if(is_dir($Directory.'/'.$Item) and ($Item != '.') and ($Item != '..') and ($Item != '.svn'))
+      {
+        include_once($Directory.'/'.$Item.'/'.$Item.'.php');
+        $ModuleName = 'Module'.$Item;
+        $this->RegisterModule(new $ModuleName($this->System));
+      }
+    }
+  }
+
+  function LoadModules()
+  {
+    if(method_exists($this->OnLoadModules[0], $this->OnLoadModules[1]))
+      $this->OnLoadModules();
+    else $this->LoadModulesFromDir($this->ModulesDir);
+  }
+}
Index: /trunk/Packages/Common/Application.php
===================================================================
--- /trunk/Packages/Common/Application.php	(revision 69)
+++ /trunk/Packages/Common/Application.php	(revision 69)
@@ -0,0 +1,22 @@
+<?php
+
+class Application extends System
+{
+  var $Name;
+  /** @var AppModuleManager */
+  var $ModuleManager;
+  var $Modules;
+
+  function __construct()
+  {
+    parent::__construct();
+    $this->Name = 'Application';
+    $this->ModuleManager = new AppModuleManager($this);
+    $this->Modules = array();
+  }
+
+  function Run()
+  {
+
+  }
+}
Index: /trunk/Packages/Common/Base.php
===================================================================
--- /trunk/Packages/Common/Base.php	(revision 69)
+++ /trunk/Packages/Common/Base.php	(revision 69)
@@ -0,0 +1,41 @@
+<?php
+
+class System
+{
+  /* @var Database */
+  var $Database;
+
+  function __construct()
+  {
+    $this->Database = new Database();
+  }
+}
+
+class Base
+{
+  /** @var Application */
+  var $System;
+  /* @var Database */
+  var $Database;
+
+  function __construct(Application $System)
+  {
+    $this->System = &$System;
+    $this->Database = &$System->Database;
+  }
+}
+
+class Model extends Base
+{
+
+}
+
+class View extends Base
+{
+
+}
+
+class Controller extends Base
+{
+
+}
Index: /trunk/Packages/Common/Common.php
===================================================================
--- /trunk/Packages/Common/Common.php	(revision 68)
+++ /trunk/Packages/Common/Common.php	(revision 69)
@@ -10,4 +10,13 @@
 include_once(dirname(__FILE__).'/VarDumper.php');
 include_once(dirname(__FILE__).'/Error.php');
+include_once(dirname(__FILE__).'/Base.php');
+include_once(dirname(__FILE__).'/Application.php');
+include_once(dirname(__FILE__).'/AppModule.php');
+include_once(dirname(__FILE__).'/Config.php');
+include_once(dirname(__FILE__).'/Page.php');
+include_once(dirname(__FILE__).'/Locale.php');
+include_once(dirname(__FILE__).'/Update.php');
+include_once(dirname(__FILE__).'/Setup.php');
+include_once(dirname(__FILE__).'/Table.php');
 
 class PackageCommon
@@ -15,4 +24,5 @@
   var $Name;
   var $Version;
+  var $ReleaseDate;
   var $License;
   var $Creator;
@@ -22,5 +32,6 @@
   {
     $this->Name = 'Common';
-    $this->Version = '1.1';
+    $this->Version = '1.2';
+    $this->ReleaseDate = strtotime('2016-01-22');
     $this->Creator = 'Chronos';
     $this->License = 'GNU/GPL';
@@ -28,2 +39,85 @@
   }
 }
+
+class Paging
+{
+  var $TotalCount;
+  var $ItemPerPage;
+  var $Around;
+  var $SQLLimit;
+  var $Page;
+
+  function __construct(System $System)
+  {
+    $this->ItemPerPage = $System->Config['Web']['ItemsPerPage'];
+    $this->Around = $System->Config['Web']['VisiblePagingItems'];
+  }
+
+  function Show()
+  {
+    $QueryItems = GetQueryStringArray($_SERVER['QUERY_STRING']);
+
+    $Result = '';
+    if(array_key_exists('all', $QueryItems))
+    {
+      $PageCount = 1;
+      $ItemPerPage = $this->TotalCount;
+    } else
+    {
+      $ItemPerPage = $this->ItemPerPage;
+      $Around = round($this->Around / 2);
+      $PageCount = floor($this->TotalCount / $ItemPerPage) + 1;
+    }
+
+    if(!array_key_exists('Page', $_SESSION)) $_SESSION['Page'] = 0;
+    if(array_key_exists('page', $_GET)) $_SESSION['Page'] = $_GET['page'] * 1;
+    if($_SESSION['Page'] < 0) $_SESSION['Page'] = 0;
+    if($_SESSION['Page'] >= $PageCount) $_SESSION['Page'] = $PageCount - 1;
+    $CurrentPage = $_SESSION['Page'];
+
+    $Result .= 'Počet položek: <strong>'.$this->TotalCount.'</strong> &nbsp; Stránky: ';
+
+    $Result = '';
+    if($PageCount > 1)
+    {
+      if($CurrentPage > 0)
+      {
+        $QueryItems['page'] = 0;
+        $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&lt;&lt;</a> ';
+        $QueryItems['page'] = ($CurrentPage - 1);
+        $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&lt;</a> ';
+      }
+      $PagesMax = $PageCount - 1;
+      $PagesMin = 0;
+      if($PagesMax > ($CurrentPage + $Around)) $PagesMax = $CurrentPage + $Around;
+      if($PagesMin < ($CurrentPage - $Around))
+      {
+        $Result.= ' ... ';
+        $PagesMin = $CurrentPage - $Around;
+      }
+      for($i = $PagesMin; $i <= $PagesMax; $i++)
+      {
+        if($i == $CurrentPage) $Result.= '<strong>'.($i + 1).'</strong> ';
+        else {
+         $QueryItems['page'] = $i;
+         $Result .= '<a href="?'.SetQueryStringArray($QueryItems).'">'.($i + 1).'</a> ';
+        }
+      }
+      if($PagesMax < ($PageCount - 1)) $Result .= ' ... ';
+      if($CurrentPage < ($PageCount - 1))
+      {
+        $QueryItems['page'] = ($CurrentPage + 1);
+        $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&gt;</a> ';
+        $QueryItems['page'] = ($PageCount - 1);
+        $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&gt;&gt;</a>';
+      }
+    }
+    $QueryItems['all'] = '1';
+    if($PageCount > 1) $Result.= ' <a href="?'.SetQueryStringArray($QueryItems).'">Vše</a>';
+
+    $Result = '<div style="text-align: center">'.$Result.'</div>';
+    $this->SQLLimit = ' LIMIT '.$CurrentPage * $ItemPerPage.', '.$ItemPerPage;
+    $this->Page = $CurrentPage;
+    return($Result);
+  }
+}
Index: /trunk/Packages/Common/Config.php
===================================================================
--- /trunk/Packages/Common/Config.php	(revision 69)
+++ /trunk/Packages/Common/Config.php	(revision 69)
@@ -0,0 +1,56 @@
+<?php
+
+class Config
+{
+  var $Data;
+
+  function __construct()
+  {
+    $this->Data = array();
+  }
+
+  function ReadValue($Name)
+  {
+    if(!is_array($Name)) $Name = explode('/', $Name);
+    $Last = array_pop($Name);
+    $Data = &$this->Data;
+    foreach($Name as $Item)
+    {
+      $Data = &$Data[$Item];
+    }
+    return($Data[$Last]);
+  }
+
+  function WriteValue($Name, $Value)
+  {
+    if(!is_array($Name)) $Name = explode('/', $Name);
+    $Last = array_pop($Name);
+    $Data = &$this->Data;
+    foreach($Name as $Item)
+    {
+      $Data = &$Data[$Item];
+    }
+    $Data[$Item] = $Value;
+  }
+
+  function LoadFromFile($FileName)
+  {
+    $ConfigData = array();
+    include $FileName;
+    foreach($this->Data as $Index => $Item)
+    {
+      if(array_key_exits($Index, $ConfigData))
+        $this->Data[$Index] = $ConfigData[$Index];
+    }
+  }
+
+  function SaveToFile($FileName)
+  {
+    file_put_contents($FileName, "<?php \n\n\$ConfigData = ".var_export($this->Data, true).";\n");
+  }
+
+  function GetAsArray()
+  {
+    return($this->Data);
+  }
+}
Index: /trunk/Packages/Common/Database.php
===================================================================
--- /trunk/Packages/Common/Database.php	(revision 68)
+++ /trunk/Packages/Common/Database.php	(revision 69)
@@ -2,5 +2,5 @@
 
 // Extended database class
-// Date: 2011-11-25
+// Date: 2016-01-11
 
 class DatabaseResult
@@ -27,20 +27,27 @@
 class Database
 {
-  var $Prefix = '';
+  var $Prefix;
   var $Functions;
   var $Type;
   var $PDO;
-  var $Error = '';
+  var $Error;
   var $insert_id;
-  var $LastQuery = '';
+  var $LastQuery;
   var $ShowSQLError;
   var $ShowSQLQuery;
+  var $LogSQLQuery;
+  var $LogFile;
 
   function __construct()
   {
+    $this->Prefix = '';
+    $this->Functions = array('NOW()', 'CURDATE()', 'CURTIME()', 'UUID()');
     $this->Type = 'mysql';  // mysql, pgsql
+    $this->Error = '';
+    $this->LastQuery = '';
     $this->ShowSQLError = false;
     $this->ShowSQLQuery = false;
-    $this->Functions = array('NOW()', 'CURDATE()', 'CURTIME()', 'UUID()');
+    $this->LogSQLQuery = false;
+    $this->LogFile = dirname(__FILE__).'/../../Query.log';
   }
 
@@ -77,8 +84,14 @@
   function query($Query)
   {
-    if(!$this->Connected()) throw new Exception('Not connected to database');
+    if(!$this->Connected()) throw new Exception(T('Not connected to database'));
+    if(($this->ShowSQLQuery == true) or ($this->LogSQLQuery == true)) $QueryStartTime = microtime();
     $this->LastQuery = $Query;
+    if(($this->ShowSQLQuery == true) or ($this->LogSQLQuery == true))
+      $Duration = ' ; '.round(microtime() - $QueryStartTime, 4). ' s';
+    if($this->LogSQLQuery == true)
+      file_put_contents($this->LogFile, $Query.$Duration."\n", FILE_APPEND);
     if($this->ShowSQLQuery == true)
-      echo('<div style="border-bottom-width: 1px; border-bottom-style: solid; padding-bottom: 3px; padding-top: 3px; font-size: 12px; font-family: Arial;">'.$Query.'</div>'."\n");
+      echo('<div style="border-bottom-width: 1px; border-bottom-style: solid; '.
+      'padding-bottom: 3px; padding-top: 3px; font-size: 12px; font-family: Arial;">'.$Query.$Duration.'</div>'."\n");
     $Result = new DatabaseResult();
     $Result->PDOStatement = $this->PDO->query($Query);
@@ -175,4 +188,9 @@
   }
 
+  function quote($Text)
+  {
+    return($this->PDO->quote($Text));
+  }
+
   public function __sleep()
   {
Index: /trunk/Packages/Common/Error.php
===================================================================
--- /trunk/Packages/Common/Error.php	(revision 68)
+++ /trunk/Packages/Common/Error.php	(revision 69)
@@ -74,5 +74,5 @@
     die();
   }
-  
+
   function ShowDefaultError($Message)
   {
@@ -112,5 +112,5 @@
 
     foreach($this->OnError as $OnError)
-      $OnError[0]->$OnError[1]($Error);
+      call_user_func($OnError, $Error);
   }
 }
Index: /trunk/Packages/Common/Image.php
===================================================================
--- /trunk/Packages/Common/Image.php	(revision 68)
+++ /trunk/Packages/Common/Image.php	(revision 69)
@@ -12,6 +12,6 @@
   var $FileName;
   var $Color;
-  
-  function __construct() 
+
+  function __construct()
   {
     $this->Color = COLOR_BLACK;
@@ -27,5 +27,5 @@
   var $Y;
 
-  function __construct() 
+  function __construct()
   {
     $this->Color = COLOR_BLACK;
@@ -40,5 +40,5 @@
   var $Color;
 
-  function __construct() 
+  function __construct()
   {
     $this->Color = COLOR_BLACK;
@@ -47,11 +47,11 @@
 }
 
-class Image 
+class Image
 {
-  var $Image;  
+  var $Image;
   var $Type;
   var $Font;
   var $Pen;
-  
+
   function __construct()
   {
@@ -59,42 +59,42 @@
     $this->Type = IMAGETYPE_PNG;
     $this->Pen = new Pen();
-    $this->Font = new Font();   
-    $this->Brush = new Brush();   
+    $this->Font = new Font();
+    $this->Brush = new Brush();
   }
-  
+
   function SaveToFile($FileName)
   {
-    if($this->Type == IMAGETYPE_JPEG) 
+    if($this->Type == IMAGETYPE_JPEG)
     {
       imagejpeg($this->Image, $FileName);
     } else
-    if($this->Type == IMAGETYPE_GIF) 
+    if($this->Type == IMAGETYPE_GIF)
     {
       imagegif($this->Image, $FileName);
     } else
-    if($this->Type == IMAGETYPE_PNG) 
+    if($this->Type == IMAGETYPE_PNG)
     {
       imagepng($this->Image, $FileName);
     }
   }
-  
+
   function LoadFromFile($FileName)
   {
     $ImageInfo = getimagesize($FileName);
     $this->Type = $ImageInfo[2];
-    if($this->Type == IMAGETYPE_JPEG) 
+    if($this->Type == IMAGETYPE_JPEG)
     {
       $this->Image = imagecreatefromjpeg($FileName);
     } else
-    if($this->Type == IMAGETYPE_GIF) 
+    if($this->Type == IMAGETYPE_GIF)
     {
       $this->Image = imagecreatefromgif($FileName);
     } else
-    if( $this->Type == IMAGETYPE_PNG) 
+    if( $this->Type == IMAGETYPE_PNG)
     {
       $this->Image = imagecreatefrompng($FileName);
-    }  
+    }
   }
-  
+
   function Output()
   {
@@ -103,36 +103,36 @@
 
   function SetSize($Width, $Height)
-  {	
+  {
     $NewImage = imagecreatetruecolor($Width, $Height);
-    imagecopy($NewImage, $this->Image, 0, 0, 0, 0, $this->GetWidth(), $this->GetHeight()); 	
+    imagecopy($NewImage, $this->Image, 0, 0, 0, 0, $this->GetWidth(), $this->GetHeight());
     imagedestroy($this->Image);
     $this->Image = $NewImage;
   }
 
-  function GetWidth() 
-  { 
+  function GetWidth()
+  {
     return(imagesx($this->Image));
   }
-  
-  function GetHeight() 
+
+  function GetHeight()
   {
     return(imagesy($this->Image));
   }
-  
+
   function TextOut($X, $Y, $Text)
   {
     imagettftext($this->Image, $this->Font->Size, 0, $X, $Y, $this->ConvertColor($this->Font->Color), $this->Font->FileName, $Text);
   }
-  
+
   function ConvertColor($Color)
   {
     return(imagecolorallocate($this->Image, ($Color >> 16) & 0xff, ($Color >> 8) & 0xff, $Color & 0xff));
   }
-  
+
   function FillRect($X1, $Y1, $X2, $Y2)
   {
     imagefilledrectangle($this->Image, $X1, $Y1, $X2, $Y2, $this->ConvertColor($this->Brush->Color));
   }
-  
+
   function Line($X1, $Y1, $X2, $Y2)
   {
Index: /trunk/Packages/Common/Locale.php
===================================================================
--- /trunk/Packages/Common/Locale.php	(revision 69)
+++ /trunk/Packages/Common/Locale.php	(revision 69)
@@ -0,0 +1,249 @@
+<?php
+
+class LocaleText
+{
+  var $Data;
+  var $Code;
+  var $Title;
+
+  function __construct()
+  {
+    $this->Data = array();
+    $this->Code = 'en';
+    $this->Title = 'English';
+  }
+
+  function Load()
+  {
+  }
+
+  function Translate($Text, $Group = '')
+  {
+    if(array_key_exists($Text, $this->Data[$Group]) and ($this->Data[$Group][$Text] != ''))
+      return($this->Data[$Group][$Text]);
+      else return($Text);
+  }
+
+  function TranslateReverse($Text, $Group = '')
+  {
+    $Key = array_search($Text, $this->Data[$Group]);
+    if(($Key === FALSE) or ($this->Data[$Group][$Key] == '')) return($Text);
+      else return($Key);
+  }
+}
+
+class LocaleFile extends Model
+{
+  var $Texts;
+  var $Dir;
+
+  function __construct(System $System)
+  {
+    parent::__construct($System);
+    $this->Texts = new LocaleText();
+  }
+
+  function Load($Language)
+  {
+    $FileName = $this->Dir.'/'.$Language.'.php';
+    if(file_exists($FileName)) {
+      include_once($FileName);
+      $ClassName = 'LocaleText'.$Language;
+      $this->Texts = new $ClassName();
+    } else throw new Exception(sprintf(T('Language file %s not found'), $FileName));
+    $this->Texts->Load();
+  }
+
+  function AnalyzeCode($Path)
+  {
+    // Search for php files
+    $FileList = scandir($Path);
+    foreach($FileList as $FileName)
+    {
+      $FullName = $Path.'/'.$FileName;
+      if(($FileName == '.') or ($FileName == '..')) ; // Skip virtual items
+      else if(substr($FileName, 0, 1) == '.') ; // Skip Linux hidden files
+      else if(is_dir($FullName)) $this->AnalyzeCode($FullName);
+      else if(file_exists($FullName))
+      {
+        if(substr($FullName, -4) == '.php')
+        {
+          $Content = file_get_contents($FullName);
+          // Search occurence of T() function
+          while(strpos($Content, 'T(') !== false)
+          {
+            $Previous = strtolower(substr($Content, strpos($Content, 'T(') - 1, 1));
+            $Content = substr($Content, strpos($Content, 'T(') + 2);
+            $Ord = ord($Previous);
+            //echo($Ord.',');
+            if(!(($Ord >= ord('a')) and ($Ord <= ord('z'))))
+            {
+              // Do for non-alpha previous character
+              $Original = substr($Content, 0, strpos($Content, ')'));
+              $Original2 = '';
+              if((substr($Original, 0, 1) == "'") and (substr($Original, -1, 1) == "'"))
+                $Original2 = substr($Original, 1, -1);
+              if((substr($Original, 0, 1) == '"') and (substr($Original, -1, 1) == '"'))
+                $Original2 = substr($Original, 1, -1);
+              if($Original2 != '')
+              {
+                if(!array_key_exists($Original2, $this->Texts->Data))
+                  $this->Texts->Data[$Original2] = '';
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  function SaveToFile($FileName)
+  {
+    $Content = '<?php'."\n".
+    ''."\n".
+    'class LocaleText'.$this->Texts->Code.' extends LocaleText'."\n".
+    '{'."\n".
+    '  function Load()'."\n".
+    '  {'."\n".
+    '    $this->Code = \''.$this->Texts->Code.'\';'."\n".
+    '    $this->Title = \''.$this->Texts->Title.'\';'."\n".
+    '    $this->Data = array('."\n";
+    foreach($this->Texts->Data as $Index => $Item)
+    {
+      $Content .= "      '".$Index."' => '".$Item."',\n";
+    }
+    $Content .= '    );'."\n".
+    '  }'."\n".
+    '}'."\n";
+    file_put_contents($FileName, $Content);
+  }
+
+  function LoadFromDatabase($Database, $LangCode)
+  {
+    $DbResult = $Database->select('Language', '*', 'Code='.$Database->quote($LangCode));
+    if($DbResult->num_rows > 0)
+    {
+      $Language = $DbResult->fetch_assoc();
+      $this->Texts->Data = array();
+      $DbResult = $Database->select('Locale', '`Original`, `Translated`', '`Language`='.$Language['Id']);
+      while($DbRow = $DbResult->fetch_assoc())
+        $this->Texts->Data[$DbRow['Original']] = $DbRow['Translated'];
+    }
+  }
+
+  function SaveToDatabase(Database $Database, $LangCode)
+  {
+    $DbResult = $Database->select('Language', '*', 'Code='.$Database->quote($LangCode));
+    if($DbResult->num_rows > 0)
+    {
+      $Language = $DbResult->fetch_assoc();
+      $Database->delete('Locale', '`Language`='.$Language['Id']);
+      foreach($this->Texts->Data as $Index => $Item)
+        $Database->query('INSERT INTO `Locale` (`Language`,`Original`,`Translated`) '.
+        'VALUES('.$Language['Id'].','.$Database->quote($Index).','.$Database->quote($Item).')');
+    }
+  }
+
+  function UpdateToDatabase(Database $Database, $LangCode)
+  {
+    $DbResult = $Database->select('Language', '*', '`Code`='.$Database->quote($LangCode));
+    if($DbResult->num_rows > 0)
+    {
+      $Language = $DbResult->fetch_assoc();
+      foreach($this->Texts->Data as $Index => $Item)
+      {
+        $DbResult = $Database->select('Locale', '*', '(`Original` ='.$Database->quote($Index).
+          ') AND (`Language`='.($Language['Id']).')');
+        if($DbResult->num_rows > 0)
+        $Database->update('Locale', '(`Language`='.($Language['Id']).') AND '.
+          '(`Original` ='.$Database->quote($Index).')', array('Translated' => $Item));
+        else $Database->insert('Locale', array('Language' => $Language['Id'],
+         'Original' => $Index, 'Translated' => $Item));
+      }
+    }
+  }
+}
+
+class LocaleManager extends Model
+{
+  var $CurrentLocale;
+  var $Codes;
+  var $Dir;
+  var $LangCode;
+  var $DefaultLangCode;
+  var $Available;
+
+  function __construct(System $System)
+  {
+    parent::__construct($System);
+    $this->Codes = array('en');
+    $this->CurrentLocale = new LocaleFile($System);
+    $this->LangCode = 'en';
+    $this->DefaultLangCode = 'en';
+    $this->Available = array();
+  }
+
+  function LoadAvailable()
+  {
+    $this->Available = array();
+    $FileList = scandir($this->Dir);
+    foreach($FileList as $FileName)
+    {
+      if(substr($FileName, -4) == '.php')
+      {
+        $Code = substr($FileName, 0, -4);
+        $Locale = new LocaleFile($this->System);
+        $Locale->Dir = $this->Dir;
+        $Locale->Load($Code);
+        $this->Available['Code'] = array('Code' => $Code, 'Title' => $Locale->Texts->Title);
+      }
+    }
+  }
+
+  function UpdateAll($Directory)
+  {
+    $Locale = new LocaleFile($this->System);
+    $Locale->AnalyzeCode($Directory);
+    $FileList = scandir($this->Dir);
+    foreach($FileList as $FileName)
+    {
+      if(substr($FileName, -4) == '.php')
+      {
+        $FileLocale = new LocaleFile($this->System);
+        $FileLocale->Dir = $this->Dir;
+        $FileLocale->Load(substr($FileName, 0, -4));
+
+        // Add new
+        foreach($Locale->Texts->Data as $Index => $Item)
+          if(!array_key_exists($Index, $FileLocale->Texts->Data))
+            $FileLocale->Texts->Data[$Index] = $Item;
+        // Remove old
+        foreach($FileLocale->Texts->Data as $Index => $Item)
+          if(!array_key_exists($Index, $Locale->Texts->Data))
+            unset($FileLocale->Texts->Data[$Index]);
+        $FileLocale->UpdateToDatabase($this->System->Database, $FileLocale->Texts->Code);
+        $FileName = $this->Dir.'/'.$FileLocale->Texts->Code.'.php';
+        $FileLocale->SaveToFile($FileName);
+      }
+    }
+    $this->CurrentLocale->Load($this->CurrentLocale->Texts->Code);
+  }
+
+  function LoadLocale($Code)
+  {
+    if(array_key_exists($Code, $this->Available))
+    {
+      $this->CurrentLocale->Dir = $this->Dir;
+      $this->CurrentLocale->Load($Code);
+    } else throw new Exception(sprintf(T('Unsupported locale code %s'), $Code));
+  }
+}
+
+// Short named global translation function
+function T($Text, $Group = '')
+{
+  global $GlobalLocaleManager;
+
+  if(isset($GlobalLocaleManager)) return($GlobalLocaleManager->CurrentLocale->Texts->Translate($Text, $Group));
+    else return($Text);
+}
Index: /trunk/Packages/Common/Mail.php
===================================================================
--- /trunk/Packages/Common/Mail.php	(revision 68)
+++ /trunk/Packages/Common/Mail.php	(revision 69)
@@ -13,5 +13,5 @@
   var $From;
   var $Recipients;
-  var $Bodies;	
+  var $Bodies;
   var $Attachments;
   var $AgentIdent;
@@ -19,13 +19,13 @@
   var $ReplyTo;
   var $Headers;
-  
+
   function __construct()
   {
-    $this->Priorities = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');           
+    $this->Priorities = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
     $this->Boundary = md5(date('r', time()));
     $this->AgentIdent = 'PHP/Mail';
     $this->Clear();
   }
-  
+
   function Clear()
   {
@@ -63,5 +63,5 @@
   function AddBody($Content, $MimeType = 'text/plain', $Charset = 'utf-8')
   {
-    $this->Bodies[] = array('Content' => $Content, 'Type' => strtolower($MimeType), 
+    $this->Bodies[] = array('Content' => $Content, 'Type' => strtolower($MimeType),
       'Charset' => strtolower($Charset));
   }
@@ -75,14 +75,14 @@
   {
     if(!intval($Priority)) return(false);
-		
+
     if(!isset($this->priorities[$Priority - 1])) return(false);
 
-    $this->xheaders['X-Priority'] = $this->priorities[$Priority - 1];	
+    $this->xheaders['X-Priority'] = $this->priorities[$Priority - 1];
     return(true);
   }
 
   function AttachFile($FileName, $FileType, $Disposition = DISPOSITION_ATTACHMENT)
-  {	
-    $this->Attachments[] = array('FileName' => $FileName, 'FileType' => $FileType, 
+  {
+    $this->Attachments[] = array('FileName' => $FileName, 'FileType' => $FileType,
       'Disposition' => $Disposition, 'Type' => 'File');
   }
@@ -90,5 +90,5 @@
   function AttachData($FileName, $FileType, $Data, $Disposition = DISPOSITION_ATTACHMENT)
   {
-    $this->Attachments[] = array('FileName' => $FileName, 'FileType' => $FileType, 
+    $this->Attachments[] = array('FileName' => $FileName, 'FileType' => $FileType,
       'Disposition' => $Disposition, 'Type' => 'Data', 'Data' => $Data);
   }
@@ -96,12 +96,12 @@
   function Send()
   {
-    if(count($this->Bodies) == 0) throw new Exception('Mail: Need at least one text body');
-    
+    if(count($this->Bodies) == 0) throw new Exception(T('Mail message need at least one text body'));
+
     $Body = $this->BuildAttachment($this->BuildBody());
-    
+
     $To = '';
     $this->Headers['CC'] = '';
     $this->Headers['BCC'] = '';
-    foreach($this->Recipients as $Index => $Recipient)      
+    foreach($this->Recipients as $Index => $Recipient)
     {
       if($Recipient['Type'] == 'SendCombined')
@@ -126,8 +126,8 @@
       }
     }
-    if($To == '') throw new Exception('Mail: Need at least one recipient address');
-    
+    if($To == '') throw new Exception(T('Mail message need at least one recipient address'));
+
     $this->Headers['Mime-Version'] = '1.0';
-    
+
     if($this->AgentIdent != '') $this->Headers['X-Mailer'] = $this->AgentIdent;
     if($this->ReplyTo != '') $this->Headers['Reply-To'] = $this->ReplyTo;
@@ -135,13 +135,13 @@
 
     $Headers = '';
-    foreach($this->Headers as $Header => $Value) 
+    foreach($this->Headers as $Header => $Value)
     {
       if(($Header != 'Subject') and ($Value != '')) $Headers .= $Header.': '.$Value."\n";
-    }   
+    }
 
     $this->Subject = strtr($this->Subject, "\r\n", '  ');
 
-    if($this->Subject == '') throw new Exception('Mail: Missing Subject');
-    
+    if($this->Subject == '') throw new Exception(T('Mail message missing Subject'));
+
 
     $res = mail($To, $this->Subject, $Body, $Headers);
@@ -152,5 +152,5 @@
   {
     if(ereg(".*<(.+)>", $Address, $regs)) $Address = $regs[1];
-    if(ereg("^[^@  ]+@([a-zA-Z0-9\-]+\.)+([a-zA-Z0-9\-]{2}|net|com|gov|mil|org|edu|int)\$", $Address)) 
+    if(ereg("^[^@  ]+@([a-zA-Z0-9\-]+\.)+([a-zA-Z0-9\-]{2}|net|com|gov|mil|org|edu|int)\$", $Address))
       return(true);
       else return(false);
@@ -159,11 +159,11 @@
   function CheckAdresses($Addresses)
   {
-    foreach($Addresses as $Address) 
-    {
-      if(!$this->ValidEmail($Address)) 
-	throw new Exception('Mail: Invalid address '.$Address);
-    }
-  } 
-  
+    foreach($Addresses as $Address)
+    {
+      if(!$this->ValidEmail($Address))
+  throw new Exception(sprintf(T('Mail message invalid address %s'), $Address));
+    }
+  }
+
   private function ContentEncoding($Charset)
   {
@@ -175,11 +175,11 @@
   {
     if(count($this->Attachments) > 0)
-    {        
+    {
       $this->Headers['Content-Type'] = "multipart/mixed;\n boundary=\"PHP-mixed-".$this->Boundary."\"";
       $Result = "This is a multi-part message in MIME format.\n".
-        "--PHP-mixed-".$this->Boundary."\n";    
+        "--PHP-mixed-".$this->Boundary."\n";
       $Result .= $Body;
-    
-      foreach($this->Attachments as $Attachment) 
+
+      foreach($this->Attachments as $Attachment)
       {
         $FileName = $Attachment['FileName'];
@@ -189,11 +189,11 @@
         if($Attachment['Type'] == 'File')
         {
-          if(!file_exists($FileName)) 
-            throw new Exception('Mail: Attached file '.$FileName.' can\'t be found');
+          if(!file_exists($FileName))
+            throw new Exception(sprintf(T('Mail message attached file %s can\'t be found'), $FileName));
           $Data = file_get_contents($FileName);
-        } else 
+        } else
         if($Attachment['Type'] == 'Data') $Data = $Attachment['Data'];
           else $Data = '';
-		
+
         $Result .= "\n".'--PHP-mixed-'.$this->Boundary."\n".
           "Content-Type: ".$ContentType."; name=\"".$BaseName."\"\n".
@@ -203,16 +203,16 @@
       }
     } else $Result = $Body;
-    return($Result);    
-  }
-  
+    return($Result);
+  }
+
   private function BuildBody()
   {
     $Result = '';
-    if(count($this->Bodies) > 1) 
+    if(count($this->Bodies) > 1)
     {
       $this->Headers['Content-Type'] = 'multipart/alternative; boundary="PHP-alt-'.$this->Boundary.'"';
       $Result .= 'Content-Type: multipart/alternative; boundary="PHP-alt-'.$this->Boundary.'"'.
         "\n\n";
-    } else 
+    } else
     {
       $this->Headers['Content-Type'] = $this->Bodies[0]['Type'].'; charset='.$this->Bodies[0]['Charset'];
@@ -220,13 +220,13 @@
     }
 
-        
+
     foreach($this->Bodies as $Body)
     {
-      if(count($this->Bodies) > 1) $Result .= "\n\n".'--PHP-alt-'.$this->Boundary."\n";           
+      if(count($this->Bodies) > 1) $Result .= "\n\n".'--PHP-alt-'.$this->Boundary."\n";
       $Result .= 'Content-Type: '.$Body['Type'].'; charset='.$Body['Charset'].
-        "\nContent-Transfer-Encoding: ".$this->ContentEncoding($Body['Charset'])."\n\n".$Body['Content']."\n";          
+        "\nContent-Transfer-Encoding: ".$this->ContentEncoding($Body['Charset'])."\n\n".$Body['Content']."\n";
     }
     return($Result);
-  }  
+  }
 }
 
Index: /trunk/Packages/Common/NetworkAddress.php
===================================================================
--- /trunk/Packages/Common/NetworkAddress.php	(revision 68)
+++ /trunk/Packages/Common/NetworkAddress.php	(revision 69)
@@ -60,49 +60,49 @@
 class NetworkAddressIPv6
 {
-	var $Address;
-	var $Prefix;
+  var $Address;
+  var $Prefix;
 
-	function __construct()
-	{
-		$this->Address = 0;
-		$this->Prefix = 0;
-	}
+  function __construct()
+  {
+    $this->Address = 0;
+    $this->Prefix = 0;
+  }
 
   function AddressToString()
   {
-  	return(inet_ntop($this->Address));
+    return(inet_ntop($this->Address));
   }
 
   function AddressFromString($Value)
   {
-  	$this->Address = inet_pton($Value);
+    $this->Address = inet_pton($Value);
   }
 
   function GetOctets()
   {
-  	$Result = array();
-  	$Data = array_reverse(unpack('C*', $this->Address));
-  	foreach($Data as $Item)
-  	{
+    $Result = array();
+    $Data = array_reverse(unpack('C*', $this->Address));
+    foreach($Data as $Item)
+    {
 
-  		$Result[] = dechex($Item & 15);
-  		$Result[] = dechex(($Item >> 4) & 15);
-  	}
-  	return($Result);
+      $Result[] = dechex($Item & 15);
+      $Result[] = dechex(($Item >> 4) & 15);
+    }
+    return($Result);
   }
 
   function EncodeMAC($MAC)
   {
-  	$MAC = explode(':', $MAC);
-  	$Data = unpack('C*', $this->Address);
-  	$Data[9] = hexdec($MAC[0]) ^ 0x02;
-  	$Data[10] = hexdec($MAC[1]);
-  	$Data[11] = hexdec($MAC[2]);
-  	$Data[12] = 0xff;
-  	$Data[13] = 0xfe;
-  	$Data[14] = hexdec($MAC[3]);
-  	$Data[15] = hexdec($MAC[4]);
-  	$Data[16] = hexdec($MAC[5]);
-  	$this->Address = pack_array('C*', $Data);
+    $MAC = explode(':', $MAC);
+    $Data = unpack('C*', $this->Address);
+    $Data[9] = hexdec($MAC[0]) ^ 0x02;
+    $Data[10] = hexdec($MAC[1]);
+    $Data[11] = hexdec($MAC[2]);
+    $Data[12] = 0xff;
+    $Data[13] = 0xfe;
+    $Data[14] = hexdec($MAC[3]);
+    $Data[15] = hexdec($MAC[4]);
+    $Data[16] = hexdec($MAC[5]);
+    $this->Address = pack_array('C*', $Data);
   }
 
Index: /trunk/Packages/Common/Page.php
===================================================================
--- /trunk/Packages/Common/Page.php	(revision 69)
+++ /trunk/Packages/Common/Page.php	(revision 69)
@@ -0,0 +1,32 @@
+<?php
+
+class Page extends View
+{
+  var $Title;
+  var $ParentClass;
+  var $RawPage;
+  var $OnSystemMessage;
+
+  function __construct(System $System)
+  {
+    parent::__construct($System);
+    $this->RawPage = false;
+    $this->OnSystemMessage = array();
+  }
+
+  function Show()
+  {
+    return('');
+  }
+
+  function GetOutput()
+  {
+    $Output = $this->Show();
+    return($Output);
+  }
+
+  function SystemMessage($Title, $Text)
+  {
+    return(call_user_func_array($this->OnSystemMessage, array($Title, $Text)));
+  }
+}
Index: /trunk/Packages/Common/RSS.php
===================================================================
--- /trunk/Packages/Common/RSS.php	(revision 68)
+++ /trunk/Packages/Common/RSS.php	(revision 69)
@@ -1,5 +1,5 @@
 <?php
 
-class RSS 
+class RSS
 {
   var $Charset;
@@ -9,5 +9,5 @@
   var $WebmasterEmail;
   var $Items;
-  
+
   function __construct()
   {
@@ -17,5 +17,5 @@
 
   function Generate()
-  { 
+  {
     $Result = '<?xml version="1.0" encoding="'.$this->Charset.'" ?>'."\n". //<?
   '<rss version="2.0">'."\n".
@@ -26,5 +26,5 @@
   "    <language>cs</language>\n".
   "    <webMaster>".$this->WebmasterEmail."</webMaster>\n".
-  "    <pubDate>".date('r')."</pubDate>\n".  
+  "    <pubDate>".date('r')."</pubDate>\n".
   "    <ttl>20</ttl>\n";
     foreach($this->Items as $Item)
@@ -33,6 +33,6 @@
         '      <title>'.htmlspecialchars($Item['Title'])."</title>\n".
         '      <description>'.htmlspecialchars($Item['Description'])."</description>\n".
-	'      <pubDate>'.date('r',$Item['Time'])."</pubDate>\n".
-	'      <link>'.$Item['Link']."</link>\n".
+  '      <pubDate>'.date('r',$Item['Time'])."</pubDate>\n".
+  '      <link>'.$Item['Link']."</link>\n".
         "    </item>\n";
     }
Index: /trunk/Packages/Common/Setup.php
===================================================================
--- /trunk/Packages/Common/Setup.php	(revision 69)
+++ /trunk/Packages/Common/Setup.php	(revision 69)
@@ -0,0 +1,447 @@
+<?php
+
+class PageSetup extends Page
+{
+  var $UpdateManager;
+  var $ConfigDefinition;
+  var $Config;
+  var $DatabaseRevision;
+  var $Revision;
+  var $Updates;
+  var $ConfigDir;
+
+  function __construct($System)
+  {
+    parent::__construct($System);
+    $this->Title = T('Application setup');
+    //$this->ParentClass = 'PagePortal';
+    $this->ConfigDir = dirname(dirname(dirname(__FILE__))).'/Config';
+    $this->YesNo = array(false => T('No'), true => T('Yes'));
+  }
+
+  function LoginPanel()
+  {
+    $Output = '<h3>Přihlášení k instalaci</h3>'.
+      '<form action="?" method="post">'.
+      '<table>'.
+      '<tr><td>Systémové heslo:</td><td> <input type="password" name="SystemPassword" value=""/></td></tr>'.
+      '</table>'.
+      '<input type="submit" name="login" value="'.T('Login').'"/>'.
+      '</form>';
+    return($Output);
+  }
+
+  function ControlPanel()
+  {
+    $Output = '<h3>'.T('Instance management').'</h3>';
+
+    $Output .= 'Je připojení k databázi: '.$this->YesNo[$this->UpdateManager->Database->Connected()].'<br/>';
+    if($this->UpdateManager->Database->Connected())
+    {
+      $Output .= 'Je instalováno: '.$this->YesNo[$this->UpdateManager->IsInstalled()].'<br/>';
+      if($this->UpdateManager->IsInstalled())
+        $Output .= 'Je aktuální: '.$this->YesNo[$this->UpdateManager->IsUpToDate()].'<br/>'.
+        'Verze databáze: '.$this->UpdateManager->GetDbVersion().'<br/>';
+      $Output .= 'Verze databáze kódu: '.$this->UpdateManager->Revision.'<br/>';
+      if($this->UpdateManager->IsInstalled())
+      {
+        if(!$this->UpdateManager->IsUpToDate())
+          $Output .= '<a href="?action=upgrade">'.T('Upgrade').'</a> ';
+        $Output .= '<a href="?action=insert_sample_data">Vložit vzorová data</a> ';
+        $Output .= '<a href="?action=reload_modules">Obnovit seznam modulů</a> ';
+        $Output .= '<a href="?action=uninstall">Odinstalovat</a> ';
+        $Output .= '<a href="?action=modules">Správa modulů</a> ';
+        $Output .= '<a href="?action=models">Přegenerovat modely</a> ';
+      } else $Output .= '<a href="?action=install">Instalovat</a> ';
+    }
+    $Output .= '<a href="?action=configure">Nastavit</a> ';
+    $Output .= '<a href="?action=logout">Odhlásit</a> ';
+    $Output .= '<a href="'.$this->System->Link('/').'">'.T('Go to main page').'</a> ';
+    $Output .= '';
+    return($Output);
+  }
+
+  function Show()
+  {
+    global $ConfigDefinition, $DatabaseRevision, $Config, $Updates;
+
+    $this->UpdateManager = $this->System->Setup->UpdateManager;
+    $DefaultConfig = new DefaultConfig();
+    $this->ConfigDefinition = $DefaultConfig->Get();
+    $this->DatabaseRevision = $DatabaseRevision;
+    $this->Config = &$Config;
+
+    $Output = '';
+    if(isset($this->Config))
+    {
+      if(!array_key_exists('SystemPassword', $_SESSION)) $_SESSION['SystemPassword'] = '';
+      if(array_key_exists('login', $_POST)) $_SESSION['SystemPassword'] = $_POST['SystemPassword'];
+      if(sha1($_SESSION['SystemPassword']) != $this->Config['SystemPassword'])
+      {
+        $Output .= $this->LoginPanel();
+      } else
+      {
+        if(array_key_exists('action', $_GET)) $Action = $_GET['action'];
+          else $Action = '';
+        if($Action == 'logout')
+        {
+          $_SESSION['SystemPassword'] = '';
+          $Output .= 'Odhlášen';
+          $Output .= $this->LoginPanel();
+        } else
+        if($Action == 'models')
+        {
+          $this->System->FormManager->UpdateSQLMeta();
+        } else
+        if($Action == 'upgrade')
+        {
+          $Output .= '<h3>Povýšení</h3>';
+          try {
+            $Output .= $this->System->Setup->Upgrade();
+          } catch (Exception $E) {
+            $Output .= $this->SystemMessage('Chyba aktualizace',
+              'Došlo k chybě v SQL dotazu při aktualizaci: <br/>'.$E->getMessage());
+          }
+          $Output .= $this->ControlPanel();
+        } else
+        if($Action == 'install')
+        {
+          $Output .= '<h3>Instalace</h3>';
+          $this->System->Setup->Install();
+          $this->System->ModuleManager->LoadModules();
+          $this->System->ModuleManager->SaveState();
+          //$Output .= $this->System->Setup->Upgrade();
+          $Output .= $this->ControlPanel();
+        } else
+        if($Action == 'uninstall')
+        {
+          $Output .= '<h3>Odinstalace</h3>';
+          $this->System->Setup->Uninstall();
+          $Output .= $this->ControlPanel();
+        } else
+        if($Action == 'reload_modules')
+        {
+          $Output .= '<h3>Znovunačtení seznamu modulů</h3>';
+          $this->System->ModuleManager->LoadModules();
+          $this->System->ModuleManager->SaveState();
+          $Output .= $this->ControlPanel();
+        } else
+        if($Action == 'insert_sample_data')
+        {
+          $Output .= '<h3>Vložení vzorových dat</h3>';
+          $this->System->Setup->InsertSampleData();
+          $Output .= $this->ControlPanel();
+        } else
+        if($Action == 'modules')
+        {
+          $Output .= $this->ShowModules();
+        } else
+        if($Action == 'configure_save')
+        {
+           $Output .= $this->ConfigSave($this->Config);
+           $Output .= $this->ControlPanel();
+        } else
+        if($Action == 'configure')
+        {
+          $Output .= $this->PrepareConfig($this->Config);
+        } else
+        {
+          $Output .= $this->ControlPanel();
+        }
+      }
+    } else
+    {
+      if(array_key_exists('configure_save', $_POST))
+      {
+        $Output .= $this->ConfigSave(array());
+        $Output .= 'Pokračujte k přihlášení <a href="">zde</a>';
+      } else {
+        $Output .= $this->PrepareConfig(array());
+      }
+    }
+    return($Output);
+  }
+
+  function ShowModules()
+  {
+    $Output = '';
+    if(array_key_exists('op', $_GET)) $Operation = $_GET['op'];
+      else $Operation = '';
+    if($Operation == 'install')
+    {
+      $this->System->ModuleManager->Modules[$_GET['name']]->Install();
+      $this->System->ModuleManager->SaveState();
+      $Output .= 'Modul '.$_GET['name'].' instalován<br/>';
+    } else
+    if($Operation == 'uninstall')
+    {
+      $this->System->ModuleManager->Modules[$_GET['name']]->Uninstall();
+      $this->System->ModuleManager->SaveState();
+      $Output .= 'Modul '.$_GET['name'].' odinstalován<br/>';
+    } else
+    if($Operation == 'enable')
+    {
+      $this->System->ModuleManager->Modules[$_GET['name']]->Enable();
+      $this->System->ModuleManager->SaveState();
+      $Output .= 'Modul '.$_GET['name'].' povolen<br/>';
+    } else
+    if($Operation == 'disable')
+    {
+      $this->System->ModuleManager->Modules[$_GET['name']]->Disable();
+      $this->System->ModuleManager->SaveState();
+      $Output .= 'Modul '.$_GET['name'].' zakázán<br/>';
+    } else
+    if($Operation == 'upgrade')
+    {
+      $this->System->ModuleManager->Modules[$_GET['name']]->Upgrade();
+      $this->System->ModuleManager->SaveState();
+      $Output .= 'Modul '.$_GET['name'].' povýšen<br/>';
+    }
+    $Output .= '<h3>Správa modulů</h3>';
+    $Output .= $this->ShowList();
+    return($Output);
+  }
+
+  function ShowList()
+  {
+    $Output = '';
+
+    $Pageing = new Paging($this->System);
+    $Pageing->TotalCount = count($this->System->ModuleManager->Modules);
+    $Table = new VisualTable($this->System);
+    $Table->SetColumns(array(
+      array('Name' => 'Name', 'Title' => 'Jméno'),
+      array('Name' => 'Creator', 'Title' => 'Tvůrce'),
+      array('Name' => 'Version', 'Title' => 'Verze'),
+      array('Name' => 'License', 'Title' => 'Licence'),
+      array('Name' => 'Installed', 'Title' => 'Instalováno'),
+      array('Name' => 'Enabled', 'Title' => 'Povoleno'),
+      array('Name' => 'Description', 'Title' => 'Popis'),
+      array('Name' => 'Dependencies', 'Title' => 'Závislosti'),
+      array('Name' => '', 'Title' => 'Akce'),
+    ));
+    foreach($this->System->ModuleManager->Modules as $Module)
+    {
+      if(($Module->Dependencies) > 0) $Dependencies = implode(',', $Module->Dependencies);
+       else $Dependencies = '&nbsp;';
+      $Actions = '';
+      if($Module->Installed == true)
+      {
+        $Actions .= ' <a href="?action=modules&amp;op=uninstall&amp;name='.$Module->Name.'">Odinstalovat</a>';
+        if($Module->Enabled == true) $Actions .= ' <a href="?action=modules&amp;op=disable&amp;name='.$Module->Name.'">Zakázat</a>';
+        else $Actions .= ' <a href="?action=modules&amp;op=enable&amp;name='.$Module->Name.'">Povolit</a>';
+        if($Module->InstalledVersion != $Module->Version) $Actions .= ' <a href="?action=modules&amp;op=upgrade&amp;name='.$Module->Name.'">Povýšit</a>';
+      } else $Actions .= ' <a href="?action=modules&amp;op=install&amp;name='.$Module->Name.'">Instalovat</a>';
+
+      $Table->Table->Cells[] = array($Module->Name,
+        $Module->Creator, $Module->Version,
+        $Module->License, $this->YesNo[$Module->Installed],
+        $this->YesNo[$Module->Enabled], $Module->Description,
+        $Dependencies, $Actions);
+    }
+    $Output .= $Pageing->Show();
+    $Output .= $Table->Show();
+    $Output .= $Pageing->Show();
+    //$Output .= '<p><a href="?A=SaveToDb">Uložit do databáze</a></p>';
+    return($Output);
+  }
+
+  function PrepareConfig($Config)
+  {
+    $Output = '';
+    if(!file_exists($this->ConfigDir.'/Config.php') and !is_writable($this->ConfigDir))
+      $Output .= 'Varování: Konfigurační soubor nebude možné zapsat, protože složka "'.$this->ConfigDir.'" není povolená pro zápis!';
+    if(file_exists($this->ConfigDir.'/Config.php') and !is_writable($this->ConfigDir.'/Config.php'))
+      $Output .= 'Varování: Konfigurační soubor nebude možné zapsat, protože soubor "'.$this->ConfigDir.'/Config.php" není povolen pro zápis!';
+    $Output .= '<h3>Nastavení systému</h3>'.
+        '<form action="?action=configure_save" method="post">'.
+        '<table>';
+    foreach($this->ConfigDefinition as $Def)
+    {
+      $PathParts = explode('/', $Def['Name']);
+      $TempConfig = &$Config;
+      foreach($PathParts as $Part)
+        if(array_key_exists($Part, $TempConfig))
+        {
+          $TempConfig = &$TempConfig[$Part];
+        }
+      if(!is_array($TempConfig)) $Value = $TempConfig;
+        else $Value = $Def['Default'];
+      $Output .= '<tr><td>'.$Def['Title'].'</td><td>';
+      if($Def['Type'] == 'String') $Output .= '<input type="text" name="'.$Def['Name'].'" value="'.$Value.'"/>';
+      if($Def['Type'] == 'Password') $Output .= '<input type="password" name="'.$Def['Name'].'"/>';
+      if($Def['Type'] == 'PasswordEncoded') $Output .= '<input type="password" name="'.$Def['Name'].'"/>';
+      if($Def['Type'] == 'Integer') $Output .= '<input type="text" name="'.$Def['Name'].'" value="'.$Value.'"/>';
+      if($Def['Type'] == 'Float') $Output .= '<input type="text" name="'.$Def['Name'].'" value="'.$Value.'"/>';
+      if($Def['Type'] == 'Boolean') $Output .= '<input type="text" name="'.$Def['Name'].'" value="'.$Value.'"/>';
+      if($Def['Type'] == 'Array') $Output .= '<input type="text" name="'.$Def['Name'].'" value="'.implode(',', $Value).'"/>';
+    }
+    $Output .= '</td></tr>'.
+        '<tr><td colspan="2"><input type="submit" name="configure_save" value="'.T('Save').'"/></td></tr>'.
+        '</table>'.
+        '</form>';
+    return($Output);
+  }
+
+  function ConfigSave($DefaultConfig)
+  {
+    $Config = $DefaultConfig;
+    foreach($this->ConfigDefinition as $Def)
+    {
+      $Value = null;
+      if($Def['Type'] == 'String') if(array_key_exists($Def['Name'], $_POST))
+        $Value = $_POST[$Def['Name']];
+      if($Def['Type'] == 'Password') if(array_key_exists($Def['Name'], $_POST) and ($_POST[$Def['Name']] != ''))
+        $Value = $_POST[$Def['Name']];
+      if($Def['Type'] == 'PasswordEncoded') if(array_key_exists($Def['Name'], $_POST) and ($_POST[$Def['Name']] != ''))
+        $Value = sha1($_POST[$Def['Name']]);
+      if($Def['Type'] == 'Integer') if(array_key_exists($Def['Name'], $_POST))
+        $Value = $_POST[$Def['Name']];
+      if($Def['Type'] == 'Float') if(array_key_exists($Def['Name'], $_POST))
+        $Value = $_POST[$Def['Name']];
+      if($Def['Type'] == 'Boolean') if(array_key_exists($Def['Name'], $_POST))
+        $Value = $_POST[$Def['Name']];
+      if($Def['Type'] == 'Array') if(array_key_exists($Def['Name'], $_POST))
+        $Value = explode(',', $_POST[$Def['Name']]);
+      if(!is_null($Value))
+      {
+        $PathParts = explode('/', $Def['Name']);
+        $TempConfig = &$Config;
+        foreach($PathParts as $Part)
+        {
+          $TempConfig = &$TempConfig[$Part];
+        }
+        if(!is_array($TempConfig)) $TempConfig = $Value;
+        else $Value = $Def['Default'];
+      }
+    }
+    $ConfigText = $this->CreateConfig($Config);
+    file_put_contents($this->ConfigDir.'/Config.php', $ConfigText);
+    $Output = 'Konfigurace nastavena<br/>';
+    return($Output);
+  }
+
+  function CreateConfig($Config)
+  {
+    $Output = "<?php\n\n".
+      "\$IsDeveloper = array_key_exists('REMOTE_ADDR', \$_SERVER) and  in_array(\$_SERVER['REMOTE_ADDR'], array('127.0.0.1'));\n\n";
+
+    foreach($this->ConfigDefinition as $Def)
+    {
+      $PathParts = explode('/', $Def['Name']);
+      $Output .= "\$Config";
+      foreach($PathParts as $Part)
+        $Output .= "['".$Part."']";
+      $TempConfig = &$Config;
+      $Output .= ' = ';
+      foreach($PathParts as $Part)
+        if(array_key_exists($Part, $TempConfig))
+        {
+          $TempConfig = &$TempConfig[$Part];
+        }
+      if(!is_array($TempConfig)) $Value = $TempConfig;
+        else $Value = $Def['Default'];
+      if($Def['Type'] == 'Array')
+      {
+        $Output .= ' array(';
+        foreach($Value as $Index => $Item)
+          $Output .= '\''.$Item.'\', ';
+        $Output .= ')';
+      }
+      else $Output .= "'".$Value."'";
+      $Output .= ";\n";
+    }
+    $Output .= "\n\n";
+    return($Output);
+  }
+}
+
+class PageSetupRedirect extends Page
+{
+  function Show()
+  {
+    $Output = '';
+    if(!$this->Database->Connected()) $Output .= T('Can\'t connect to database').'<br>';
+    else {
+      if(!$this->System->Setup->UpdateManager->IsInstalled())
+        $Output .= T('System requires database initialization').'<br>';
+      else
+      if(!$this->System->Setup->UpdateManager->IsUpToDate())
+        $Output .= T('System requires database upgrade').'<br>';
+    }
+    $Output .= sprintf(T('Front page was not configured. Continue to %s'), '<a href="'.$this->System->Link('/setup/').'">'.T('setup').'</a>');
+    return($Output);
+  }
+}
+
+class Setup extends Model
+{
+  var $UpdateManager;
+
+  function Start()
+  {
+    global $DatabaseRevision;
+
+    $this->System->RegisterPage('', 'PageSetupRedirect');
+    $this->System->RegisterPage('setup', 'PageSetup');
+
+    // Check database persistence structure
+    $this->UpdateManager = new UpdateManager();
+    $this->UpdateManager->Database = &$this->Database;
+    $this->UpdateManager->Revision = $DatabaseRevision;
+    $this->UpdateManager->InstallMethod = 'FullInstall';
+  }
+
+  function Stop()
+  {
+    unset($this->UpdateManager);
+    $this->System->UnregisterPage('');
+    $this->System->UnregisterPage('setup');
+  }
+
+  function CheckState()
+  {
+    return($this->Database->Connected() and $this->UpdateManager->IsInstalled() and
+      $this->UpdateManager->IsUpToDate());
+  }
+
+  function Install()
+  {
+    global $DatabaseRevision;
+
+    $this->Database->query('CREATE TABLE IF NOT EXISTS `'.$this->UpdateManager->VersionTable.'` (
+  `Id` int(11) NOT NULL AUTO_INCREMENT,
+  `Revision` int(11) NOT NULL,
+  PRIMARY KEY (`Id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;');
+    $this->Database->query("INSERT INTO `'.$this->UpdateManager->VersionTable.'` (`Id`, `Revision`) VALUES
+      (1, '.$DatabaseRevision.');");
+    $this->Database->query("CREATE TABLE IF NOT EXISTS `Module` (
+  `Id` int(11) NOT NULL AUTO_INCREMENT,
+  `Name` varchar(255) NOT NULL,
+  `Title` varchar(255) NOT NULL,
+  PRIMARY KEY (`Id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;");
+  }
+
+  function Uninstall()
+  {
+    $this->System->ModuleManager->UninstallAll();
+    $this->Database->query('DROP TABLE `Module`');
+    $this->Database->query('DROP TABLE `'.$this->UpdateManager->VersionTable.'`');
+  }
+
+  function IsInstalled()
+  {
+    $DbResult = $this->Database->query('SHOW TABLES LIKE "'.$this->UpdateManager->VersionTable.'"');
+    return($DbResult->num_rows > 0);
+  }
+
+  function Upgrade()
+  {
+    $Updates = new Updates();
+    $this->UpdateManager->Trace = $Updates->Get();
+    $Output = $this->UpdateManager->Upgrade();
+    return($Output);
+  }
+}
Index: /trunk/Packages/Common/Table.php
===================================================================
--- /trunk/Packages/Common/Table.php	(revision 69)
+++ /trunk/Packages/Common/Table.php	(revision 69)
@@ -0,0 +1,192 @@
+<?php
+
+class Control
+{
+  var $Name;
+
+  function Show()
+  {
+    return('');
+  }
+}
+
+class Table
+{
+  function GetCell($Y, $X)
+  {
+    return('');
+  }
+
+  function BeginRead()
+  {
+  }
+
+  function EndRead()
+  {
+  }
+
+  function RowsCount()
+  {
+    return(0);
+  }
+}
+
+class TableMemory extends Table
+{
+  var $Cells;
+
+  function GetCell($Y, $X)
+  {
+    return($this->Cells[$Y][$X]);
+  }
+
+  function RowsCount()
+  {
+    return(count($this->Cells));
+  }
+}
+
+class TableSQL extends Table
+{
+  var $Query;
+  var $Database;
+  var $Cells;
+
+  function GetCell($Y, $X)
+  {
+    return($this->Cells[$Y][$X]);
+  }
+
+  function BeginRead()
+  {
+    $this->Cells = array();
+    $DbResult = $this->Database->query($this->Query);
+    while($DbRow = $DbResult->fetch_row())
+    {
+      $this->Cells[] = $DbRow;
+    }
+  }
+
+  function EndRead()
+  {
+    $this->Cells = array();
+  }
+
+  function RowsCount()
+  {
+    return(count($this->Cells));
+  }
+}
+
+class TableColumn
+{
+  var $Name;
+  var $Title;
+}
+
+class VisualTable extends Control
+{
+  var $Cells;
+  var $Columns;
+  var $OrderSQL;
+  var $OrderColumn;
+  var $OrderDirection;
+  var $OrderArrowImage;
+  var $DefaultColumn;
+  var $DefaultOrder;
+  var $Table;
+  var $Style;
+
+  function __construct(System $System)
+  {
+    $this->Columns = array();
+    $this->Table = new TableMemory();
+    $this->OrderDirSQL = array('ASC', 'DESC');
+    $this->OrderArrowImage = array($System->Link('/images/sort_asc.png'),
+      $System->Link('/images/sort_desc.png'));
+    $this->DefaultOrder = 0;
+    $this->Style = 'BaseTable';
+  }
+
+  function SetColumns($Columns)
+  {
+    $this->Columns = array();
+    foreach($Columns as $Column)
+    {
+      $NewCol = new TableColumn();
+      $NewCol->Name = $Column['Name'];
+      $NewCol->Title = $Column['Title'];
+      $this->Columns[] = $NewCol;
+    }
+    if(count($this->Columns) > 0)
+      $this->DefaultColumn = $this->Columns[0]->Name;
+      else $this->DefaultColumn = '';
+  }
+
+  function Show()
+  {
+    $Output = '<table class="'.$this->Style.'">';
+    $Output .= $this->GetOrderHeader();
+    $this->Table->BeginRead();
+    for($Y = 0; $Y < $this->Table->RowsCount(); $Y++)
+    {
+      $Output .= '<tr>';
+
+      for($X = 0; $X < count($this->Columns); $X++)
+      {
+        $Cell = $this->Table->GetCell($Y, $X);
+        if($Cell == '') $Output .= '<td>&nbsp;</td>';
+          else $Output .= '<td>'.$Cell.'</td>';
+      }
+      $Output .= '</tr>';
+    }
+    $this->Table->EndRead();
+    $Output .= '</table>';
+    return($Output);
+  }
+
+  function GetOrderHeader()
+  {
+    if(array_key_exists('OrderCol', $_GET)) $_SESSION['OrderCol'] = $_GET['OrderCol'];
+    if(array_key_exists('OrderDir', $_GET)) $_SESSION['OrderDir'] = $_GET['OrderDir'];
+    if(!array_key_exists('OrderCol', $_SESSION)) $_SESSION['OrderCol'] = $this->DefaultColumn;
+    if(!array_key_exists('OrderDir', $_SESSION)) $_SESSION['OrderDir'] = $this->DefaultOrder;
+
+    // Check OrderCol
+    $Found = false;
+    foreach($this->Columns as $Column)
+    {
+      if($Column->Name == $_SESSION['OrderCol'])
+      {
+        $Found = true;
+        break;
+      }
+    }
+    if($Found == false)
+    {
+      $_SESSION['OrderCol'] = $this->DefaultColumn;
+      $_SESSION['OrderDir'] = $this->DefaultOrder;
+    }
+    // Check OrderDir
+    if(($_SESSION['OrderDir'] != 0) and ($_SESSION['OrderDir'] != 1))
+      $_SESSION['OrderDir'] = 0;
+
+    $Result = '';
+    $QueryItems = GetQueryStringArray($_SERVER['QUERY_STRING']);
+    foreach($this->Columns as $Index => $Column)
+    {
+      $QueryItems['OrderCol'] = $Column->Name;
+      $QueryItems['OrderDir'] = 1 - $_SESSION['OrderDir'];
+      if($Column->Name == $_SESSION['OrderCol'])
+        $ArrowImage = '<img style="vertical-align: middle; border: 0px;" src="'.$this->OrderArrowImage[$_SESSION['OrderDir']].'" alt="order arrow">';
+        else $ArrowImage = '';
+      if($Column->Name == '') $Result .= '<th>'.$Column->Title.'</th>';
+        else $Result .= '<th><a href="?'.SetQueryStringArray($QueryItems).'">'.$Column->Title.$ArrowImage.'</a></th>';
+    }
+    $this->OrderSQL = ' ORDER BY `'.$_SESSION['OrderCol'].'` '.$this->OrderDirSQL[$_SESSION['OrderDir']];
+    $this->OrderColumn = $_SESSION['OrderCol'];
+    $this->OrderDirection = $_SESSION['OrderDir'];
+
+    return('<tr>'.$Result.'</tr>');
+  }
+}
Index: /trunk/Packages/Common/UTF8.php
===================================================================
--- /trunk/Packages/Common/UTF8.php	(revision 68)
+++ /trunk/Packages/Common/UTF8.php	(revision 69)
@@ -21,5 +21,5 @@
   ISO8859-1: iso1
   Windows1257: win1257
-  
+
   example:  $new_string=to_utf8($some_string,"win1250");
 */
@@ -28,498 +28,498 @@
 /*
   translation table - actually, it's array where key is hexadecimal number of
-  character in ISO8859-2/Windows1250 and value is its two byte representation in UTF-8 
+  character in ISO8859-2/Windows1250 and value is its two byte representation in UTF-8
 */
 
-class Encoding 
+class Encoding
 {
   function __construct()
   {
-  	$this->CharTable = array(
+    $this->CharTable = array(
   'iso2' => array(
-	0x80=>"\xc2\x80",
-	0x81=>"\xc2\x81",
-	0x82=>"\xc2\x82",
-	0x83=>"\xc2\x83",
-	0x84=>"\xc2\x84",
-	0x85=>"\xc2\x85",
-	0x86=>"\xc2\x86",
-	0x87=>"\xc2\x87",
-	0x88=>"\xc2\x88",
-	0x89=>"\xc2\x89",
-	0x8A=>"\xc2\x8a",
-	0x8B=>"\xc2\x8b",
-	0x8C=>"\xc2\x8c",
-	0x8D=>"\xc2\x8d",
-	0x8E=>"\xc2\x8e",
-	0x8F=>"\xc2\x8f",
-	0x90=>"\xc2\x90",
-	0x91=>"\xc2\x91",
-	0x92=>"\xc2\x92",
-	0x93=>"\xc2\x93",
-	0x94=>"\xc2\x94",
-	0x95=>"\xc2\x95",
-	0x96=>"\xc2\x96",
-	0x97=>"\xc2\x97",
-	0x98=>"\xc2\x98",
-	0x99=>"\xc2\x99",
-	0x9A=>"\xc2\x9a",
-	0x9B=>"\xc2\x9b",
-	0x9C=>"\xc2\x9c",
-	0x9D=>"\xc2\x9d",
-	0x9E=>"\xc2\x9e",
-	0x9F=>"\xc2\x9f",
-	0xA0=>"\xc2\xa0",
-	0xA1=>"\xc4\x84",
-	0xA2=>"\xcb\x98",
-	0xA3=>"\xc5\x81",
-	0xA4=>"\xc2\xa4",
-	0xA5=>"\xc4\xbd",
-	0xA6=>"\xc5\x9a",
-	0xA7=>"\xc2\xa7",
-	0xA8=>"\xc2\xa8",
-	0xA9=>"\xc5\xa0",
-	0xAA=>"\xc5\x9e",
-	0xAB=>"\xc5\xa4",
-	0xAC=>"\xc5\xb9",
-	0xAD=>"\xc2\xad",
-	0xAE=>"\xc5\xbd",
-	0xAF=>"\xc5\xbb",
-	0xB0=>"\xc2\xb0",
-	0xB1=>"\xc4\x85",
-	0xB2=>"\xcb\x9b",
-	0xB3=>"\xc5\x82",
-	0xB4=>"\xc2\xb4",
-	0xB5=>"\xc4\xbe",
-	0xB6=>"\xc5\x9b",
-	0xB7=>"\xcb\x87",
-	0xB8=>"\xc2\xb8",
-	0xB9=>"\xc5\xa1",
-	0xBA=>"\xc5\x9f",
-	0xBB=>"\xc5\xa5",
-	0xBC=>"\xc5\xba",
-	0xBD=>"\xcb\x9d",
-	0xBE=>"\xc5\xbe",
-	0xBF=>"\xc5\xbc",
-	0xC0=>"\xc5\x94",
-	0xC1=>"\xc3\x81",
-	0xC2=>"\xc3\x82",
-	0xC3=>"\xc4\x82",
-	0xC4=>"\xc3\x84",
-	0xC5=>"\xc4\xb9",
-	0xC6=>"\xc4\x86",
-	0xC7=>"\xc3\x87",
-	0xC8=>"\xc4\x8c",
-	0xC9=>"\xc3\x89",
-	0xCA=>"\xc4\x98",
-	0xCB=>"\xc3\x8b",
-	0xCC=>"\xc4\x9a",
-	0xCD=>"\xc3\x8d",
-	0xCE=>"\xc3\x8e",
-	0xCF=>"\xc4\x8e",
-	0xD0=>"\xc4\x90",
-	0xD1=>"\xc5\x83",
-	0xD2=>"\xc5\x87",
-	0xD3=>"\xc3\x93",
-	0xD4=>"\xc3\x94",
-	0xD5=>"\xc5\x90",
-	0xD6=>"\xc3\x96",
-	0xD7=>"\xc3\x97",
-	0xD8=>"\xc5\x98",
-	0xD9=>"\xc5\xae",
-	0xDA=>"\xc3\x9a",
-	0xDB=>"\xc5\xb0",
-	0xDC=>"\xc3\x9c",
-	0xDD=>"\xc3\x9d",
-	0xDE=>"\xc5\xa2",
-	0xDF=>"\xc3\x9f",
-	0xE0=>"\xc5\x95",
-	0xE1=>"\xc3\xa1",
-	0xE2=>"\xc3\xa2",
-	0xE3=>"\xc4\x83",
-	0xE4=>"\xc3\xa4",
-	0xE5=>"\xc4\xba",
-	0xE6=>"\xc4\x87",
-	0xE7=>"\xc3\xa7",
-	0xE8=>"\xc4\x8d",
-	0xE9=>"\xc3\xa9",
-	0xEA=>"\xc4\x99",
-	0xEB=>"\xc3\xab",
-	0xEC=>"\xc4\x9b",
-	0xED=>"\xc3\xad",
-	0xEE=>"\xc3\xae",
-	0xEF=>"\xc4\x8f",
-	0xF0=>"\xc4\x91",
-	0xF1=>"\xc5\x84",
-	0xF2=>"\xc5\x88",
-	0xF3=>"\xc3\xb3",
-	0xF4=>"\xc3\xb4",
-	0xF5=>"\xc5\x91",
-	0xF6=>"\xc3\xb6",
-	0xF7=>"\xc3\xb7",
-	0xF8=>"\xc5\x99",
-	0xF9=>"\xc5\xaf",
-	0xFA=>"\xc3\xba",
-	0xFB=>"\xc5\xb1",
-	0xFC=>"\xc3\xbc",
-	0xFD=>"\xc3\xbd",
-	0xFE=>"\xc5\xa3",
-	0xFF=>"\xcb\x99"
+  0x80=>"\xc2\x80",
+  0x81=>"\xc2\x81",
+  0x82=>"\xc2\x82",
+  0x83=>"\xc2\x83",
+  0x84=>"\xc2\x84",
+  0x85=>"\xc2\x85",
+  0x86=>"\xc2\x86",
+  0x87=>"\xc2\x87",
+  0x88=>"\xc2\x88",
+  0x89=>"\xc2\x89",
+  0x8A=>"\xc2\x8a",
+  0x8B=>"\xc2\x8b",
+  0x8C=>"\xc2\x8c",
+  0x8D=>"\xc2\x8d",
+  0x8E=>"\xc2\x8e",
+  0x8F=>"\xc2\x8f",
+  0x90=>"\xc2\x90",
+  0x91=>"\xc2\x91",
+  0x92=>"\xc2\x92",
+  0x93=>"\xc2\x93",
+  0x94=>"\xc2\x94",
+  0x95=>"\xc2\x95",
+  0x96=>"\xc2\x96",
+  0x97=>"\xc2\x97",
+  0x98=>"\xc2\x98",
+  0x99=>"\xc2\x99",
+  0x9A=>"\xc2\x9a",
+  0x9B=>"\xc2\x9b",
+  0x9C=>"\xc2\x9c",
+  0x9D=>"\xc2\x9d",
+  0x9E=>"\xc2\x9e",
+  0x9F=>"\xc2\x9f",
+  0xA0=>"\xc2\xa0",
+  0xA1=>"\xc4\x84",
+  0xA2=>"\xcb\x98",
+  0xA3=>"\xc5\x81",
+  0xA4=>"\xc2\xa4",
+  0xA5=>"\xc4\xbd",
+  0xA6=>"\xc5\x9a",
+  0xA7=>"\xc2\xa7",
+  0xA8=>"\xc2\xa8",
+  0xA9=>"\xc5\xa0",
+  0xAA=>"\xc5\x9e",
+  0xAB=>"\xc5\xa4",
+  0xAC=>"\xc5\xb9",
+  0xAD=>"\xc2\xad",
+  0xAE=>"\xc5\xbd",
+  0xAF=>"\xc5\xbb",
+  0xB0=>"\xc2\xb0",
+  0xB1=>"\xc4\x85",
+  0xB2=>"\xcb\x9b",
+  0xB3=>"\xc5\x82",
+  0xB4=>"\xc2\xb4",
+  0xB5=>"\xc4\xbe",
+  0xB6=>"\xc5\x9b",
+  0xB7=>"\xcb\x87",
+  0xB8=>"\xc2\xb8",
+  0xB9=>"\xc5\xa1",
+  0xBA=>"\xc5\x9f",
+  0xBB=>"\xc5\xa5",
+  0xBC=>"\xc5\xba",
+  0xBD=>"\xcb\x9d",
+  0xBE=>"\xc5\xbe",
+  0xBF=>"\xc5\xbc",
+  0xC0=>"\xc5\x94",
+  0xC1=>"\xc3\x81",
+  0xC2=>"\xc3\x82",
+  0xC3=>"\xc4\x82",
+  0xC4=>"\xc3\x84",
+  0xC5=>"\xc4\xb9",
+  0xC6=>"\xc4\x86",
+  0xC7=>"\xc3\x87",
+  0xC8=>"\xc4\x8c",
+  0xC9=>"\xc3\x89",
+  0xCA=>"\xc4\x98",
+  0xCB=>"\xc3\x8b",
+  0xCC=>"\xc4\x9a",
+  0xCD=>"\xc3\x8d",
+  0xCE=>"\xc3\x8e",
+  0xCF=>"\xc4\x8e",
+  0xD0=>"\xc4\x90",
+  0xD1=>"\xc5\x83",
+  0xD2=>"\xc5\x87",
+  0xD3=>"\xc3\x93",
+  0xD4=>"\xc3\x94",
+  0xD5=>"\xc5\x90",
+  0xD6=>"\xc3\x96",
+  0xD7=>"\xc3\x97",
+  0xD8=>"\xc5\x98",
+  0xD9=>"\xc5\xae",
+  0xDA=>"\xc3\x9a",
+  0xDB=>"\xc5\xb0",
+  0xDC=>"\xc3\x9c",
+  0xDD=>"\xc3\x9d",
+  0xDE=>"\xc5\xa2",
+  0xDF=>"\xc3\x9f",
+  0xE0=>"\xc5\x95",
+  0xE1=>"\xc3\xa1",
+  0xE2=>"\xc3\xa2",
+  0xE3=>"\xc4\x83",
+  0xE4=>"\xc3\xa4",
+  0xE5=>"\xc4\xba",
+  0xE6=>"\xc4\x87",
+  0xE7=>"\xc3\xa7",
+  0xE8=>"\xc4\x8d",
+  0xE9=>"\xc3\xa9",
+  0xEA=>"\xc4\x99",
+  0xEB=>"\xc3\xab",
+  0xEC=>"\xc4\x9b",
+  0xED=>"\xc3\xad",
+  0xEE=>"\xc3\xae",
+  0xEF=>"\xc4\x8f",
+  0xF0=>"\xc4\x91",
+  0xF1=>"\xc5\x84",
+  0xF2=>"\xc5\x88",
+  0xF3=>"\xc3\xb3",
+  0xF4=>"\xc3\xb4",
+  0xF5=>"\xc5\x91",
+  0xF6=>"\xc3\xb6",
+  0xF7=>"\xc3\xb7",
+  0xF8=>"\xc5\x99",
+  0xF9=>"\xc5\xaf",
+  0xFA=>"\xc3\xba",
+  0xFB=>"\xc5\xb1",
+  0xFC=>"\xc3\xbc",
+  0xFD=>"\xc3\xbd",
+  0xFE=>"\xc5\xa3",
+  0xFF=>"\xcb\x99"
   ),
   'win1250' => array(
-	0x80=>"\xc2\x80",
-	0x81=>"\xc2\x81",
-	0x82=>"\xe2\x80\x9a",
-	0x83=>"\xc2\x83",
-	0x84=>"\xe2\x80\x9e",
-	0x85=>"\xe2\x80\xa6",
-	0x86=>"\xe2\x80\xa0",
-	0x87=>"\xe2\x80\xa1",
-	0x88=>"\xc2\x88",
-	0x89=>"\xe2\x80\xb0",
-	0x8a=>"\xc5\xa0",
-	0x8b=>"\xe2\x80\xb9",
-	0x8c=>"\xc5\x9a",
-	0x8d=>"\xc5\xa4",
-	0x8e=>"\xc5\xbd",
-	0x8f=>"\xc5\xb9",
-	0x90=>"\xc2\x90",
-	0x91=>"\xe2\x80\x98",
-	0x92=>"\xe2\x80\x99",
-	0x93=>"\xe2\x80\x9c",
-	0x94=>"\xe2\x80\x9d",
-	0x95=>"\xe2\x80\xa2",
-	0x96=>"\xe2\x80\x93",
-	0x97=>"\xe2\x80\x94",
-	0x98=>"\xe2\x80\x98",
-	0x99=>"\xe2\x84\xa2",
-	0x9a=>"\xc5\xa1",
-	0x9b=>"\xe2\x80\xba",
-	0x9c=>"\xc5\x9b",
-	0x9d=>"\xc5\xa5",
-	0x9e=>"\xc5\xbe",
-	0x9f=>"\xc5\xba",
-	0xa0=>"\xc2\xa0",
-	0xa1=>"\xcb\x87",
-	0xa2=>"\xcb\x98",
-	0xa3=>"\xc5\x81",
-	0xa4=>"\xc2\xa4",
-	0xa5=>"\xc4\x84",
-	0xa6=>"\xc2\xa6",
-	0xa7=>"\xc2\xa7",
-	0xa8=>"\xc2\xa8",
-	0xa9=>"\xc2\xa9",
-	0xaa=>"\xc5\x9e",
-	0xab=>"\xc2\xab",
-	0xac=>"\xc2\xac",
-	0xad=>"\xc2\xad",
-	0xae=>"\xc2\xae",
-	0xaf=>"\xc5\xbb",
-	0xb0=>"\xc2\xb0",
-	0xb1=>"\xc2\xb1",
-	0xb2=>"\xcb\x9b",
-	0xb3=>"\xc5\x82",
-	0xb4=>"\xc2\xb4",
-	0xb5=>"\xc2\xb5",
-	0xb6=>"\xc2\xb6",
-	0xb7=>"\xc2\xb7",
-	0xb8=>"\xc2\xb8",
-	0xb9=>"\xc4\x85",
-	0xba=>"\xc5\x9f",
-	0xbb=>"\xc2\xbb",
-	0xbc=>"\xc4\xbd",
-	0xbd=>"\xcb\x9d",
-	0xbe=>"\xc4\xbe",
-	0xbf=>"\xc5\xbc",
-	0xc0=>"\xc5\x94",
-	0xc1=>"\xc3\x81",
-	0xc2=>"\xc3\x82",
-	0xc3=>"\xc4\x82",
-	0xc4=>"\xc3\x84",
-	0xc5=>"\xc4\xb9",
-	0xc6=>"\xc4\x86",
-	0xc7=>"\xc3\x87",
-	0xc8=>"\xc4\x8c",
-	0xc9=>"\xc3\x89",
-	0xca=>"\xc4\x98",
-	0xcb=>"\xc3\x8b",
-	0xcc=>"\xc4\x9a",
-	0xcd=>"\xc3\x8d",
-	0xce=>"\xc3\x8e",
-	0xcf=>"\xc4\x8e",
-	0xd0=>"\xc4\x90",
-	0xd1=>"\xc5\x83",
-	0xd2=>"\xc5\x87",
-	0xd3=>"\xc3\x93",
-	0xd4=>"\xc3\x94",
-	0xd5=>"\xc5\x90",
-	0xd6=>"\xc3\x96",
-	0xd7=>"\xc3\x97",
-	0xd8=>"\xc5\x98",
-	0xd9=>"\xc5\xae",
-	0xda=>"\xc3\x9a",
-	0xdb=>"\xc5\xb0",
-	0xdc=>"\xc3\x9c",
-	0xdd=>"\xc3\x9d",
-	0xde=>"\xc5\xa2",
-	0xdf=>"\xc3\x9f",
-	0xe0=>"\xc5\x95",
-	0xe1=>"\xc3\xa1",
-	0xe2=>"\xc3\xa2",
-	0xe3=>"\xc4\x83",
-	0xe4=>"\xc3\xa4",
-	0xe5=>"\xc4\xba",
-	0xe6=>"\xc4\x87",
-	0xe7=>"\xc3\xa7",
-	0xe8=>"\xc4\x8d",
-	0xe9=>"\xc3\xa9",
-	0xea=>"\xc4\x99",
-	0xeb=>"\xc3\xab",
-	0xec=>"\xc4\x9b",
-	0xed=>"\xc3\xad",
-	0xee=>"\xc3\xae",
-	0xef=>"\xc4\x8f",
-	0xf0=>"\xc4\x91",
-	0xf1=>"\xc5\x84",
-	0xf2=>"\xc5\x88",
-	0xf3=>"\xc3\xb3",
-	0xf4=>"\xc3\xb4",
-	0xf5=>"\xc5\x91",
-	0xf6=>"\xc3\xb6",
-	0xf7=>"\xc3\xb7",
-	0xf8=>"\xc5\x99",
-	0xf9=>"\xc5\xaf",
-	0xfa=>"\xc3\xba",
-	0xfb=>"\xc5\xb1",
-	0xfc=>"\xc3\xbc",
-	0xfd=>"\xc3\xbd",
-	0xfe=>"\xc5\xa3",
-	0xff=>"\xcb\x99"
+  0x80=>"\xc2\x80",
+  0x81=>"\xc2\x81",
+  0x82=>"\xe2\x80\x9a",
+  0x83=>"\xc2\x83",
+  0x84=>"\xe2\x80\x9e",
+  0x85=>"\xe2\x80\xa6",
+  0x86=>"\xe2\x80\xa0",
+  0x87=>"\xe2\x80\xa1",
+  0x88=>"\xc2\x88",
+  0x89=>"\xe2\x80\xb0",
+  0x8a=>"\xc5\xa0",
+  0x8b=>"\xe2\x80\xb9",
+  0x8c=>"\xc5\x9a",
+  0x8d=>"\xc5\xa4",
+  0x8e=>"\xc5\xbd",
+  0x8f=>"\xc5\xb9",
+  0x90=>"\xc2\x90",
+  0x91=>"\xe2\x80\x98",
+  0x92=>"\xe2\x80\x99",
+  0x93=>"\xe2\x80\x9c",
+  0x94=>"\xe2\x80\x9d",
+  0x95=>"\xe2\x80\xa2",
+  0x96=>"\xe2\x80\x93",
+  0x97=>"\xe2\x80\x94",
+  0x98=>"\xe2\x80\x98",
+  0x99=>"\xe2\x84\xa2",
+  0x9a=>"\xc5\xa1",
+  0x9b=>"\xe2\x80\xba",
+  0x9c=>"\xc5\x9b",
+  0x9d=>"\xc5\xa5",
+  0x9e=>"\xc5\xbe",
+  0x9f=>"\xc5\xba",
+  0xa0=>"\xc2\xa0",
+  0xa1=>"\xcb\x87",
+  0xa2=>"\xcb\x98",
+  0xa3=>"\xc5\x81",
+  0xa4=>"\xc2\xa4",
+  0xa5=>"\xc4\x84",
+  0xa6=>"\xc2\xa6",
+  0xa7=>"\xc2\xa7",
+  0xa8=>"\xc2\xa8",
+  0xa9=>"\xc2\xa9",
+  0xaa=>"\xc5\x9e",
+  0xab=>"\xc2\xab",
+  0xac=>"\xc2\xac",
+  0xad=>"\xc2\xad",
+  0xae=>"\xc2\xae",
+  0xaf=>"\xc5\xbb",
+  0xb0=>"\xc2\xb0",
+  0xb1=>"\xc2\xb1",
+  0xb2=>"\xcb\x9b",
+  0xb3=>"\xc5\x82",
+  0xb4=>"\xc2\xb4",
+  0xb5=>"\xc2\xb5",
+  0xb6=>"\xc2\xb6",
+  0xb7=>"\xc2\xb7",
+  0xb8=>"\xc2\xb8",
+  0xb9=>"\xc4\x85",
+  0xba=>"\xc5\x9f",
+  0xbb=>"\xc2\xbb",
+  0xbc=>"\xc4\xbd",
+  0xbd=>"\xcb\x9d",
+  0xbe=>"\xc4\xbe",
+  0xbf=>"\xc5\xbc",
+  0xc0=>"\xc5\x94",
+  0xc1=>"\xc3\x81",
+  0xc2=>"\xc3\x82",
+  0xc3=>"\xc4\x82",
+  0xc4=>"\xc3\x84",
+  0xc5=>"\xc4\xb9",
+  0xc6=>"\xc4\x86",
+  0xc7=>"\xc3\x87",
+  0xc8=>"\xc4\x8c",
+  0xc9=>"\xc3\x89",
+  0xca=>"\xc4\x98",
+  0xcb=>"\xc3\x8b",
+  0xcc=>"\xc4\x9a",
+  0xcd=>"\xc3\x8d",
+  0xce=>"\xc3\x8e",
+  0xcf=>"\xc4\x8e",
+  0xd0=>"\xc4\x90",
+  0xd1=>"\xc5\x83",
+  0xd2=>"\xc5\x87",
+  0xd3=>"\xc3\x93",
+  0xd4=>"\xc3\x94",
+  0xd5=>"\xc5\x90",
+  0xd6=>"\xc3\x96",
+  0xd7=>"\xc3\x97",
+  0xd8=>"\xc5\x98",
+  0xd9=>"\xc5\xae",
+  0xda=>"\xc3\x9a",
+  0xdb=>"\xc5\xb0",
+  0xdc=>"\xc3\x9c",
+  0xdd=>"\xc3\x9d",
+  0xde=>"\xc5\xa2",
+  0xdf=>"\xc3\x9f",
+  0xe0=>"\xc5\x95",
+  0xe1=>"\xc3\xa1",
+  0xe2=>"\xc3\xa2",
+  0xe3=>"\xc4\x83",
+  0xe4=>"\xc3\xa4",
+  0xe5=>"\xc4\xba",
+  0xe6=>"\xc4\x87",
+  0xe7=>"\xc3\xa7",
+  0xe8=>"\xc4\x8d",
+  0xe9=>"\xc3\xa9",
+  0xea=>"\xc4\x99",
+  0xeb=>"\xc3\xab",
+  0xec=>"\xc4\x9b",
+  0xed=>"\xc3\xad",
+  0xee=>"\xc3\xae",
+  0xef=>"\xc4\x8f",
+  0xf0=>"\xc4\x91",
+  0xf1=>"\xc5\x84",
+  0xf2=>"\xc5\x88",
+  0xf3=>"\xc3\xb3",
+  0xf4=>"\xc3\xb4",
+  0xf5=>"\xc5\x91",
+  0xf6=>"\xc3\xb6",
+  0xf7=>"\xc3\xb7",
+  0xf8=>"\xc5\x99",
+  0xf9=>"\xc5\xaf",
+  0xfa=>"\xc3\xba",
+  0xfb=>"\xc5\xb1",
+  0xfc=>"\xc3\xbc",
+  0xfd=>"\xc3\xbd",
+  0xfe=>"\xc5\xa3",
+  0xff=>"\xcb\x99"
   ),
   'iso1' => array(
-	0xA0=>"\xc2\xa0",
-	0xA1=>"\xc2\xa1",
-	0xA2=>"\xc2\xa2",
-	0xA3=>"\xc2\xa3",
-	0xA4=>"\xc2\xa4",
-	0xA5=>"\xc2\xa5",
-	0xA6=>"\xc2\xa6",
-	0xA7=>"\xc2\xa7",
-	0xA8=>"\xc2\xa8",
-	0xA9=>"\xc2\xa9",
-	0xAA=>"\xc2\xaa",
-	0xAB=>"\xc2\xab",
-	0xAC=>"\xc2\xac",
-	0xAD=>"\xc2\xad",
-	0xAE=>"\xc2\xae",
-	0xAF=>"\xc2\xaf",
-	0xB0=>"\xc2\xb0",
-	0xB1=>"\xc2\xb1",
-	0xB2=>"\xc2\xb2",
-	0xB3=>"\xc2\xb3",
-	0xB4=>"\xc2\xb4",
-	0xB5=>"\xc2\xb5",
-	0xB6=>"\xc2\xb6",
-	0xB7=>"\xc2\xb7",
-	0xB8=>"\xc2\xb8",
-	0xB9=>"\xc2\xb9",
-	0xBA=>"\xc2\xba",
-	0xBB=>"\xc2\xbb",
-	0xBC=>"\xc2\xbc",
-	0xBD=>"\xc2\xbd",
-	0xBE=>"\xc2\xbe",
-	0xBF=>"\xc2\xbf",
-	0xC0=>"\xc3\x80",
-	0xC1=>"\xc3\x81",
-	0xC2=>"\xc3\x82",
-	0xC3=>"\xc3\x83",
-	0xC4=>"\xc3\x84",
-	0xC5=>"\xc3\x85",
-	0xC6=>"\xc3\x86",
-	0xC7=>"\xc3\x87",
-	0xC8=>"\xc3\x88",
-	0xC9=>"\xc3\x89",
-	0xCA=>"\xc3\x8a",
-	0xCB=>"\xc3\x8b",
-	0xCC=>"\xc3\x8c",
-	0xCD=>"\xc3\x8d",
-	0xCE=>"\xc3\x8e",
-	0xCF=>"\xc3\x8f",
-	0xD0=>"\xc3\x90",
-	0xD1=>"\xc3\x91",
-	0xD2=>"\xc3\x92",
-	0xD3=>"\xc3\x93",
-	0xD4=>"\xc3\x94",
-	0xD5=>"\xc3\x95",
-	0xD6=>"\xc3\x96",
-	0xD7=>"\xc3\x97",
-	0xD8=>"\xc3\x98",
-	0xD9=>"\xc3\x99",
-	0xDA=>"\xc3\x9a",
-	0xDB=>"\xc3\x9b",
-	0xDC=>"\xc3\x9c",
-	0xDD=>"\xc3\x9d",
-	0xDE=>"\xc3\x9e",
-	0xDF=>"\xc3\x9f",
-	0xE0=>"\xc3\xa0",
-	0xE1=>"\xc3\xa1",
-	0xE2=>"\xc3\xa2",
-	0xE3=>"\xc3\xa3",
-	0xE4=>"\xc3\xa4",
-	0xE5=>"\xc3\xa5",
-	0xE6=>"\xc3\xa6",
-	0xE7=>"\xc3\xa7",
-	0xE8=>"\xc3\xa8",
-	0xE9=>"\xc3\xa9",
-	0xEA=>"\xc3\xaa",
-	0xEB=>"\xc3\xab",
-	0xEC=>"\xc3\xac",
-	0xED=>"\xc3\xad",
-	0xEE=>"\xc3\xae",
-	0xEF=>"\xc3\xaf",
-	0xF0=>"\xc3\xb0",
-	0xF1=>"\xc3\xb1",
-	0xF2=>"\xc3\xb2",
-	0xF3=>"\xc3\xb3",
-	0xF4=>"\xc3\xb4",
-	0xF5=>"\xc3\xb5",
-	0xF6=>"\xc3\xb6",
-	0xF7=>"\xc3\xb7",
-	0xF8=>"\xc3\xb8",
-	0xF9=>"\xc3\xb9",
-	0xFA=>"\xc3\xba",
-	0xFB=>"\xc3\xbb",
-	0xFC=>"\xc3\xbc",
-	0xFD=>"\xc3\xbd",
-	0xFE=>"\xc3\xbe"
+  0xA0=>"\xc2\xa0",
+  0xA1=>"\xc2\xa1",
+  0xA2=>"\xc2\xa2",
+  0xA3=>"\xc2\xa3",
+  0xA4=>"\xc2\xa4",
+  0xA5=>"\xc2\xa5",
+  0xA6=>"\xc2\xa6",
+  0xA7=>"\xc2\xa7",
+  0xA8=>"\xc2\xa8",
+  0xA9=>"\xc2\xa9",
+  0xAA=>"\xc2\xaa",
+  0xAB=>"\xc2\xab",
+  0xAC=>"\xc2\xac",
+  0xAD=>"\xc2\xad",
+  0xAE=>"\xc2\xae",
+  0xAF=>"\xc2\xaf",
+  0xB0=>"\xc2\xb0",
+  0xB1=>"\xc2\xb1",
+  0xB2=>"\xc2\xb2",
+  0xB3=>"\xc2\xb3",
+  0xB4=>"\xc2\xb4",
+  0xB5=>"\xc2\xb5",
+  0xB6=>"\xc2\xb6",
+  0xB7=>"\xc2\xb7",
+  0xB8=>"\xc2\xb8",
+  0xB9=>"\xc2\xb9",
+  0xBA=>"\xc2\xba",
+  0xBB=>"\xc2\xbb",
+  0xBC=>"\xc2\xbc",
+  0xBD=>"\xc2\xbd",
+  0xBE=>"\xc2\xbe",
+  0xBF=>"\xc2\xbf",
+  0xC0=>"\xc3\x80",
+  0xC1=>"\xc3\x81",
+  0xC2=>"\xc3\x82",
+  0xC3=>"\xc3\x83",
+  0xC4=>"\xc3\x84",
+  0xC5=>"\xc3\x85",
+  0xC6=>"\xc3\x86",
+  0xC7=>"\xc3\x87",
+  0xC8=>"\xc3\x88",
+  0xC9=>"\xc3\x89",
+  0xCA=>"\xc3\x8a",
+  0xCB=>"\xc3\x8b",
+  0xCC=>"\xc3\x8c",
+  0xCD=>"\xc3\x8d",
+  0xCE=>"\xc3\x8e",
+  0xCF=>"\xc3\x8f",
+  0xD0=>"\xc3\x90",
+  0xD1=>"\xc3\x91",
+  0xD2=>"\xc3\x92",
+  0xD3=>"\xc3\x93",
+  0xD4=>"\xc3\x94",
+  0xD5=>"\xc3\x95",
+  0xD6=>"\xc3\x96",
+  0xD7=>"\xc3\x97",
+  0xD8=>"\xc3\x98",
+  0xD9=>"\xc3\x99",
+  0xDA=>"\xc3\x9a",
+  0xDB=>"\xc3\x9b",
+  0xDC=>"\xc3\x9c",
+  0xDD=>"\xc3\x9d",
+  0xDE=>"\xc3\x9e",
+  0xDF=>"\xc3\x9f",
+  0xE0=>"\xc3\xa0",
+  0xE1=>"\xc3\xa1",
+  0xE2=>"\xc3\xa2",
+  0xE3=>"\xc3\xa3",
+  0xE4=>"\xc3\xa4",
+  0xE5=>"\xc3\xa5",
+  0xE6=>"\xc3\xa6",
+  0xE7=>"\xc3\xa7",
+  0xE8=>"\xc3\xa8",
+  0xE9=>"\xc3\xa9",
+  0xEA=>"\xc3\xaa",
+  0xEB=>"\xc3\xab",
+  0xEC=>"\xc3\xac",
+  0xED=>"\xc3\xad",
+  0xEE=>"\xc3\xae",
+  0xEF=>"\xc3\xaf",
+  0xF0=>"\xc3\xb0",
+  0xF1=>"\xc3\xb1",
+  0xF2=>"\xc3\xb2",
+  0xF3=>"\xc3\xb3",
+  0xF4=>"\xc3\xb4",
+  0xF5=>"\xc3\xb5",
+  0xF6=>"\xc3\xb6",
+  0xF7=>"\xc3\xb7",
+  0xF8=>"\xc3\xb8",
+  0xF9=>"\xc3\xb9",
+  0xFA=>"\xc3\xba",
+  0xFB=>"\xc3\xbb",
+  0xFC=>"\xc3\xbc",
+  0xFD=>"\xc3\xbd",
+  0xFE=>"\xc3\xbe"
   ),
   'win1257' => array(
-	0x80=>"\xe2\x82\xac",
-	0x81=>"\xc0\x4",
-	0x82=>"\xe2\x80\x9a",
-	0x83=>"\xc0\x4",
-	0x84=>"\xe2\x80\x9e",
-	0x85=>"\xe2\x80\xa6",
-	0x86=>"\xe2\x80\xa0",
-	0x87=>"\xe2\x80\xa1",
-	0x88=>"\xc0\x4",
-	0x89=>"\xe2\x80\xb0",
-	0x8A=>"\xc0\x4",
-	0x8B=>"\xe2\x80\xb9",
-	0x8C=>"\xc0\x4",
-	0x8D=>"\xc2\xa8",
-	0x8E=>"\xcb\x87",
-	0x8F=>"\xc2\xb8",
-	0x90=>"\xc0\x4",
-	0x91=>"\xe2\x80\x98",
-	0x92=>"\xe2\x80\x99",
-	0x93=>"\xe2\x80\x9c",
-	0x94=>"\xe2\x80\x9d",
-	0x95=>"\xe2\x80\xa2",
-	0x96=>"\xe2\x80\x93",
-	0x97=>"\xe2\x80\x94",
-	0x98=>"\xc0\x4",
-	0x99=>"\xe2\x84\xa2",
-	0x9A=>"\xc0\x4",
-	0x9B=>"\xe2\x80\xba",
-	0x9C=>"\xc0\x4",
-	0x9D=>"\xc2\xaf",
-	0x9E=>"\xcb\x9b",
-	0x9F=>"\xc0\x4",
-	0xA0=>"\xc2\xa0",
-	0xA1=>"\xc0\x4",
-	0xA2=>"\xc2\xa2",
-	0xA3=>"\xc2\xa3",
-	0xA4=>"\xc2\xa4",
-	0xA5=>"\xc0\x4",
-	0xA6=>"\xc2\xa6",
-	0xA7=>"\xc2\xa7",
-	0xA8=>"\xc3\x98",
-	0xA9=>"\xc2\xa9",
-	0xAA=>"\xc5\x96",
-	0xAB=>"\xc2\xab",
-	0xAC=>"\xc2\xac",
-	0xAD=>"\xc2\xad",
-	0xAE=>"\xc2\xae",
-	0xAF=>"\xc3\x86",
-	0xB0=>"\xc2\xb0",
-	0xB1=>"\xc2\xb1",
-	0xB2=>"\xc2\xb2",
-	0xB3=>"\xc2\xb3",
-	0xB4=>"\xc2\xb4",
-	0xB5=>"\xc2\xb5",
-	0xB6=>"\xc2\xb6",
-	0xB7=>"\xc2\xb7",
-	0xB8=>"\xc3\xb8",
-	0xB9=>"\xc2\xb9",
-	0xBA=>"\xc5\x97",
-	0xBB=>"\xc2\xbb",
-	0xBC=>"\xc2\xbc",
-	0xBD=>"\xc2\xbd",
-	0xBE=>"\xc2\xbe",
-	0xBF=>"\xc3\xa6",
-	0xC0=>"\xc4\x84",
-	0xC1=>"\xc4\xae",
-	0xC2=>"\xc4\x80",
-	0xC3=>"\xc4\x86",
-	0xC4=>"\xc3\x84",
-	0xC5=>"\xc3\x85",
-	0xC6=>"\xc4\x98",
-	0xC7=>"\xc4\x92",
-	0xC8=>"\xc4\x8c",
-	0xC9=>"\xc3\x89",
-	0xCA=>"\xc5\xb9",
-	0xCB=>"\xc4\x96",
-	0xCC=>"\xc4\xa2",
-	0xCD=>"\xc4\xb6",
-	0xCE=>"\xc4\xaa",
-	0xCF=>"\xc4\xbb",
-	0xD0=>"\xc5\xa0",
-	0xD1=>"\xc5\x83",
-	0xD2=>"\xc5\x85",
-	0xD3=>"\xc3\x93",
-	0xD4=>"\xc5\x8c",
-	0xD5=>"\xc3\x95",
-	0xD6=>"\xc3\x96",
-	0xD7=>"\xc3\x97",
-	0xD8=>"\xc5\xb2",
-	0xD9=>"\xc5\x81",
-	0xDA=>"\xc5\x9a",
-	0xDB=>"\xc5\xaa",
-	0xDC=>"\xc3\x9c",
-	0xDD=>"\xc5\xbb",
-	0xDE=>"\xc5\xbd",
-	0xDF=>"\xc3\x9f",
-	0xE0=>"\xc4\x85",
-	0xE1=>"\xc4\xaf",
-	0xE2=>"\xc4\x81",
-	0xE3=>"\xc4\x87",
-	0xE4=>"\xc3\xa4",
-	0xE5=>"\xc3\xa5",
-	0xE6=>"\xc4\x99",
-	0xE7=>"\xc4\x93",
-	0xE8=>"\xc4\x8d",
-	0xE9=>"\xc3\xa9",
-	0xEA=>"\xc5\xba",
-	0xEB=>"\xc4\x97",
-	0xEC=>"\xc4\xa3",
-	0xED=>"\xc4\xb7",
-	0xEE=>"\xc4\xab",
-	0xEF=>"\xc4\xbc",
-	0xF0=>"\xc5\xa1",
-	0xF1=>"\xc5\x84",
-	0xF2=>"\xc5\x86",
-	0xF3=>"\xc3\xb3",
-	0xF4=>"\xc5\x8d",
-	0xF5=>"\xc3\xb5",
-	0xF6=>"\xc3\xb6",
-	0xF7=>"\xc3\xb7",
-	0xF8=>"\xc5\xb3",
-	0xF9=>"\xc5\x82",
-	0xFA=>"\xc5\x9b",
-	0xFB=>"\xc5\xab",
-	0xFC=>"\xc3\xbc",
-	0xFD=>"\xc5\xbc",
-	0xFE=>"\xc5\xbe",
-	0xFF=>"\xcb\x99"
+  0x80=>"\xe2\x82\xac",
+  0x81=>"\xc0\x4",
+  0x82=>"\xe2\x80\x9a",
+  0x83=>"\xc0\x4",
+  0x84=>"\xe2\x80\x9e",
+  0x85=>"\xe2\x80\xa6",
+  0x86=>"\xe2\x80\xa0",
+  0x87=>"\xe2\x80\xa1",
+  0x88=>"\xc0\x4",
+  0x89=>"\xe2\x80\xb0",
+  0x8A=>"\xc0\x4",
+  0x8B=>"\xe2\x80\xb9",
+  0x8C=>"\xc0\x4",
+  0x8D=>"\xc2\xa8",
+  0x8E=>"\xcb\x87",
+  0x8F=>"\xc2\xb8",
+  0x90=>"\xc0\x4",
+  0x91=>"\xe2\x80\x98",
+  0x92=>"\xe2\x80\x99",
+  0x93=>"\xe2\x80\x9c",
+  0x94=>"\xe2\x80\x9d",
+  0x95=>"\xe2\x80\xa2",
+  0x96=>"\xe2\x80\x93",
+  0x97=>"\xe2\x80\x94",
+  0x98=>"\xc0\x4",
+  0x99=>"\xe2\x84\xa2",
+  0x9A=>"\xc0\x4",
+  0x9B=>"\xe2\x80\xba",
+  0x9C=>"\xc0\x4",
+  0x9D=>"\xc2\xaf",
+  0x9E=>"\xcb\x9b",
+  0x9F=>"\xc0\x4",
+  0xA0=>"\xc2\xa0",
+  0xA1=>"\xc0\x4",
+  0xA2=>"\xc2\xa2",
+  0xA3=>"\xc2\xa3",
+  0xA4=>"\xc2\xa4",
+  0xA5=>"\xc0\x4",
+  0xA6=>"\xc2\xa6",
+  0xA7=>"\xc2\xa7",
+  0xA8=>"\xc3\x98",
+  0xA9=>"\xc2\xa9",
+  0xAA=>"\xc5\x96",
+  0xAB=>"\xc2\xab",
+  0xAC=>"\xc2\xac",
+  0xAD=>"\xc2\xad",
+  0xAE=>"\xc2\xae",
+  0xAF=>"\xc3\x86",
+  0xB0=>"\xc2\xb0",
+  0xB1=>"\xc2\xb1",
+  0xB2=>"\xc2\xb2",
+  0xB3=>"\xc2\xb3",
+  0xB4=>"\xc2\xb4",
+  0xB5=>"\xc2\xb5",
+  0xB6=>"\xc2\xb6",
+  0xB7=>"\xc2\xb7",
+  0xB8=>"\xc3\xb8",
+  0xB9=>"\xc2\xb9",
+  0xBA=>"\xc5\x97",
+  0xBB=>"\xc2\xbb",
+  0xBC=>"\xc2\xbc",
+  0xBD=>"\xc2\xbd",
+  0xBE=>"\xc2\xbe",
+  0xBF=>"\xc3\xa6",
+  0xC0=>"\xc4\x84",
+  0xC1=>"\xc4\xae",
+  0xC2=>"\xc4\x80",
+  0xC3=>"\xc4\x86",
+  0xC4=>"\xc3\x84",
+  0xC5=>"\xc3\x85",
+  0xC6=>"\xc4\x98",
+  0xC7=>"\xc4\x92",
+  0xC8=>"\xc4\x8c",
+  0xC9=>"\xc3\x89",
+  0xCA=>"\xc5\xb9",
+  0xCB=>"\xc4\x96",
+  0xCC=>"\xc4\xa2",
+  0xCD=>"\xc4\xb6",
+  0xCE=>"\xc4\xaa",
+  0xCF=>"\xc4\xbb",
+  0xD0=>"\xc5\xa0",
+  0xD1=>"\xc5\x83",
+  0xD2=>"\xc5\x85",
+  0xD3=>"\xc3\x93",
+  0xD4=>"\xc5\x8c",
+  0xD5=>"\xc3\x95",
+  0xD6=>"\xc3\x96",
+  0xD7=>"\xc3\x97",
+  0xD8=>"\xc5\xb2",
+  0xD9=>"\xc5\x81",
+  0xDA=>"\xc5\x9a",
+  0xDB=>"\xc5\xaa",
+  0xDC=>"\xc3\x9c",
+  0xDD=>"\xc5\xbb",
+  0xDE=>"\xc5\xbd",
+  0xDF=>"\xc3\x9f",
+  0xE0=>"\xc4\x85",
+  0xE1=>"\xc4\xaf",
+  0xE2=>"\xc4\x81",
+  0xE3=>"\xc4\x87",
+  0xE4=>"\xc3\xa4",
+  0xE5=>"\xc3\xa5",
+  0xE6=>"\xc4\x99",
+  0xE7=>"\xc4\x93",
+  0xE8=>"\xc4\x8d",
+  0xE9=>"\xc3\xa9",
+  0xEA=>"\xc5\xba",
+  0xEB=>"\xc4\x97",
+  0xEC=>"\xc4\xa3",
+  0xED=>"\xc4\xb7",
+  0xEE=>"\xc4\xab",
+  0xEF=>"\xc4\xbc",
+  0xF0=>"\xc5\xa1",
+  0xF1=>"\xc5\x84",
+  0xF2=>"\xc5\x86",
+  0xF3=>"\xc3\xb3",
+  0xF4=>"\xc5\x8d",
+  0xF5=>"\xc3\xb5",
+  0xF6=>"\xc3\xb6",
+  0xF7=>"\xc3\xb7",
+  0xF8=>"\xc5\xb3",
+  0xF9=>"\xc5\x82",
+  0xFA=>"\xc5\x9b",
+  0xFB=>"\xc5\xab",
+  0xFC=>"\xc3\xbc",
+  0xFD=>"\xc5\xbc",
+  0xFE=>"\xc5\xbe",
+  0xFF=>"\xcb\x99"
   ),
 );
@@ -529,5 +529,5 @@
   {
     $Result = '';
-    for($I = 0; $I < strlen($String); $I++) 
+    for($I = 0; $I < strlen($String); $I++)
     {
        if(ord($String[$I]) < 128) $Result .= $String[$I];
Index: /trunk/Packages/Common/Update.php
===================================================================
--- /trunk/Packages/Common/Update.php	(revision 69)
+++ /trunk/Packages/Common/Update.php	(revision 69)
@@ -0,0 +1,83 @@
+<?php
+
+class UpdateManager
+{
+  var $Revision;
+  var $Trace;
+  var $VersionTable;
+  /* @var Database */
+  var $Database;
+  var $InstallMethod;
+
+  function __construct()
+  {
+    $this->Revision = 0;
+    $this->Trace = array();
+    $this->VersionTable = 'SystemVersion';
+    $this->InstallMethod = 'FullInstall';
+    $this->InsertSampleDataMethod = 'InsertSampleData';
+  }
+
+  function GetDbVersion()
+  {
+    $DbResult = $this->Database->select($this->VersionTable, '*', 'Id=1');
+    $Version = $DbResult->fetch_assoc();
+    return($Version['Revision']);
+  }
+
+  function IsInstalled()
+  {
+    $DbResult = $this->Database->query('SHOW TABLES LIKE "'.$this->VersionTable.'"');
+    return($DbResult->num_rows > 0);
+  }
+
+  function IsUpToDate()
+  {
+    return($this->Revision <= $this->GetDbVersion());
+  }
+
+  function Upgrade()
+  {
+    $DbRevision = $this->GetDbVersion();
+    $Output = 'Počáteční revize databáze: '.$DbRevision.'<br/>';
+    while($this->Revision > $DbRevision)
+    {
+      $TraceItem = $this->Trace[$DbRevision];
+      $Output .= 'Aktualizace na verzi '.$TraceItem['Revision'].':<br/>';
+      // Show applied SQL queries immediatelly
+      echo($Output);
+      $Output = '';
+      $RevUpdate = $TraceItem['Function'];
+      $RevUpdate($this);
+      $DbRevision = $TraceItem['Revision'];
+      $this->Database->query('UPDATE `'.$this->VersionTable.'` SET `Revision`= '.
+        $TraceItem['Revision'].' WHERE `Id`=1');
+    }
+    return($Output);
+  }
+
+  function Install()
+  {
+    $InstallMethod = $this->InstallMethod;
+    $InstallMethod($this);
+    $this->Update();
+  }
+
+  function Uninstall()
+  {
+
+  }
+
+  function InsertSampleData()
+  {
+    $InstallMethod = $this->InsertSampleDataMethod;
+    $InstallMethod($this);
+  }
+
+  function Execute($Query)
+  {
+    echo($Query.';<br/>');
+    flush();
+    return($this->Database->query($Query));
+  }
+}
Index: unk/Update.php
===================================================================
--- /trunk/Update.php	(revision 68)
+++ 	(revision )
@@ -1,71 +1,0 @@
-<?php
-
-class UpdateManager
-{
-  var $Revision;
-  var $Trace;
-  var $VersionTable;
-  var $Database;
-  var $InstallMethod;
-
-  function __construct()
-  {
-    $this->Revision = 0;
-    $this->Trace = array();
-    $this->VersionTable = 'SystemVersion';
-    $this->InstallMethod = 'FullInstall';
-  }
-
-  function GetDbVersion()
-  {
-    $DbResult = $this->Database->select($this->VersionTable, '*', '`Id`=1');
-    $Version = $DbResult->fetch_assoc();
-    return($Version['Revision']);
-  }
-
-  function IsInstalled()
-  {
-    $DbResult = $this->Database->query('SHOW TABLES LIKE "'.$this->VersionTable.'"');
-    return($DbResult->num_rows > 0);
-  }
-
-  function IsUpToDate()
-  {
-    return($this->Revision <= $this->GetDbVersion());
-  }
-
-  function Update()
-  {
-    $DbRevision = $this->GetDbVersion();
-    $Output = 'Počáteční revize databáze: '.$DbRevision.'<br/>';
-    while($this->Revision > $DbRevision)
-    {
-      $TraceItem = $this->Trace[$DbRevision];
-      $Output .= 'Aktualizace na verzi: '.$TraceItem['Revision'].'<br/>';
-      $RevUpdate = $TraceItem['Function'];
-      $RevUpdate($this);
-      $DbRevision = $TraceItem['Revision'];
-      $this->Database->query('UPDATE `'.$this->VersionTable.'` SET `Revision`= '.$TraceItem['Revision'].' WHERE `Id`=1');
-    }
-    return($Output);
-  }
-
-  function Install()
-  {
-    $InstallMethod = $this->InstallMethod;
-    $InstallMethod($this);
-    $this->Update();
-  }
-
-  function Uninstall()
-  {
-
-  }
-
-  function Execute($Query)
-  {
-    echo($Query.'<br/>');
-    flush();
-    return($this->Database->query($Query));
-  }
-}
Index: unk/UpdateTrace.php
===================================================================
--- /trunk/UpdateTrace.php	(revision 68)
+++ 	(revision )
@@ -1,51 +1,0 @@
-<?php
-
-class UpdateTrace
-{
-  var $Manager;
-
-  function FullInstall($Manager)
-  {
-    $this->Manager->Execute('CREATE TABLE IF NOT EXISTS `SystemVersion` (
-  `Id` int(11) NOT NULL AUTO_INCREMENT,
-  `Revision` int(11) NOT NULL,
-  PRIMARY KEY (`Id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
-    $this->Manager->Execute('INSERT INTO `SystemVersion` (`Id`, `Version`) VALUES (NULL, 65);');
-  }
-
-  function UpdateTo67($Manager)
-  {
-    $this->Manager->Execute('RENAME TABLE `measure` TO `Measure`;');
-    $this->Manager->Execute('CREATE TABLE IF NOT EXISTS `Permission` (
-  `Id` int(11) NOT NULL AUTO_INCREMENT,
-  `Measure` int(11) NOT NULL,
-  `Address` varchar(255) NOT NULL,
-  `Operation` varchar(255) NOT NULL,
-  PRIMARY KEY (`Id`),
-  KEY `Measure` (`Measure`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
-    $this->Manager->Execute('ALTER TABLE `Permission` ADD FOREIGN KEY (`Measure`) '.
-    'REFERENCES `Measure`(`Id`) ON DELETE RESTRICT ON UPDATE RESTRICT;');
-    $DbResult = $this->Manager->Execute('SELECT `Id`, `PermissionAdd` FROM `Measure`');
-    while($DbRow = $DbResult->fetch_assoc())
-    {
-	  $this->Manager->Execute('INSERT INTO `Permission` (`Measure`, `Operation`) VALUES ('.
-	    $DbRow['Id'].', "add");');
-	}
-    $DbResult = $this->Manager->Execute('SELECT `Id`, `PermissionView` FROM `Measure`');
-    while($DbRow = $DbResult->fetch_assoc())
-    {
-	  $this->Manager->Execute('INSERT INTO `Permission` (`Measure`, `Operation`) VALUES ('.
-	    $DbRow['Id'].', "view");');
-	}
-	//$this->Manager->Execute('');
-  }
-
-  function __construct()
-  {
-    $this->Updates = array(
-      65 => array('Revision' => 67, 'Function' => 'UpdateTo67'),
-    );
-  }
-}
Index: unk/Version.php
===================================================================
--- /trunk/Version.php	(revision 68)
+++ 	(revision )
@@ -1,10 +1,0 @@
-<?php
-
-// Subversion revision have to be updated always to next revision number
-// before commiting new changes.
-// Database revision have to be updated if database structure is changed
-// and system will need database update.
-
-$Revision = 67;
-$DatabaseRevision = 67;
-$ReleaseTime = strtotime('2016-01-03');
Index: /trunk/add.php
===================================================================
--- /trunk/add.php	(revision 68)
+++ /trunk/add.php	(revision 69)
@@ -1,8 +1,10 @@
 <?php
 
-include('Global.php');
+include_once('Application/Core.php');
 
-$Application = new Application();
-$Application->Start();
+$Core = new Core();
+$Core->ShowPage = false;
+$Core->Run();
+
 
 if(array_key_exists('MeasureId', $_GET) and array_key_exists('Value', $_GET))
@@ -10,5 +12,5 @@
   $MeasureId = addslashes($_GET['MeasureId']);
   $Value = addslashes($_GET['Value']);
-  $Measure = new Measure($Application->Database);
+  $Measure = new Measure($Core->Database);
   $Measure->Load($MeasureId);
   $HostName = gethostbyaddr($_SERVER['REMOTE_ADDR']);
Index: unk/config.sample.php
===================================================================
--- /trunk/config.sample.php	(revision 68)
+++ 	(revision )
@@ -1,55 +1,0 @@
-<?php
-
-// Toto je ukázkový konfigurační soubor. Zkopírujte si jej jako config.php do stejné složky a upravte podle potřeby.
-
-$IsDeveloper = in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1'));
-
-$Config = array(
-  'Database' => array(
-    'Host' => 'localhost',
-    'User' => 'root',
-    'Password' => '',
-    'Database' => 'statistic',
-    'Prefix' => '',
-    'Charset' => 'utf8',
-  ),
-  'AddNewValueUrl' => 'http://localhost/dev/statistic/add.php',
-  'Debug' => 0,
-  'Web' => array(
-    'Title' => 'Statistika',
-    'Charset' => 'utf-8',
-    'AdminEmail' => 'admin@domain',
-    'ShowError' => 0,
-    'ShowSQLError' => $IsDeveloper,
-    'ShowSQLQuery' => $IsDeveloper,
-    'ErrorLogFile' => 'php_script_error.log',
-  ),
-  'DivisionCount' => 500,
-  'LevelReducing' => 5,
-  'MaxLevel' => 4,
-  'ReferenceTime' => 0,
-  'ImageWidth' => 750,
-  'ImageHeight' => 200,
-  'ValueToImageHeigthCoefficient' => 0.9,
-  'FontFileName' => './arial.ttf',
-  'FontSize' => 10,
-  'DefaultVariables' => array(
-    'TimeStart' => time() - 3600*24,
-    'TimeEnd' => time(),
-    'Measure' => 1,
-    'Period' => 'day',
-    'TimeSpecify' => 0,
-    'Differential' => 0,
-  ),
-);
-
-// For back compatibility during development 
-$DivisionCount = $Config['DivisionCount'];
-$ReferenceTime = $Config['ReferenceTime'];
-$LevelReducing = $Config['LevelReducing'];
-$MaxLevel = $Config['MaxLevel'];
-$DefaultWidth = $Config['ImageWidth'];
-$DefaultHeight = $Config['ImageHeight'];
-$K = $Config['ValueToImageHeigthCoefficient']; 
-$FontFile = $Config['FontFileName'];
-$FontSize = $Config['FontSize'];
Index: /trunk/index.php
===================================================================
--- /trunk/index.php	(revision 68)
+++ /trunk/index.php	(revision 69)
@@ -1,247 +1,6 @@
 <?php
-include('Global.php');
 
-class MainView
-{
-  var $Months;
-  var $GrafTimeRanges;
+include_once('Application/Core.php');
 
-  function __construct($Database)
-  {
-    $this->Database = &$Database;
-    $this->Months = array('', 'Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září',
-      'Říjen', 'Listopad', 'Prosinec');
-
-    $this->GrafTimeRanges = array(
-      'hour' => array(
-        'caption' => 'Hodina',
-        'period' => 3600,
-      ),
-      'day' => array(
-        'caption' => 'Den',
-        'period' => 3600 * 24,
-      ),
-      'week' => array(
-        'caption' => 'Týden',
-        'period' => 3600 * 24 * 7,
-      ),
-      'month' => array(
-        'caption' => 'Měsíc',
-        'period' => 3600 * 24 * 30,
-      ),
-      'year' => array(
-        'caption' => 'Rok',
-        'period' => 3600 * 24 * 365,
-      ),
-      'years' => array(
-        'caption' => 'Desetiletí',
-        'period' => 3600 * 24 * 365 * 10,
-      ),
-    );
-  }
-
-  function EditTime($Time)
-  {
-    $Output = '<form style="display: inline;" action="?Operation=SetTime&amp;Time='.$Time.'" method="post">';
-    $TimeParts = getdate($_SESSION[$Time]);
-
-    // Day selection
-    $Output .= '<select name="Day">';
-    for($I = 1; $I < 32; $I++)
-    {
-      if($I == $TimeParts['mday']) $Selected = ' selected="1"'; else $Selected = '';
-      $Output .= '<option value="'.$I.'"'.$Selected.'>'.$I.'</option>';
-    }
-    $Output .= '</select>. ';
-
-    // Month selection
-    $Output .= '<select name="Month">';
-    foreach($this->Months as $Index => $Month)
-    {
-      if($Index == $TimeParts['mon']) $Selected = ' selected="1"'; else $Selected = '';
-      if($Index > 0) $Output .= '<option value="'.$Index.'"'.$Selected.'>'.$Month.'</option>';
-    }
-    $Output .= '</select>. ';
-
-    // Year selection
-    $Output .= '<select name="Year">';
-    for($I = 2000; $I <= date("Y"); $I++)
-    {
-      if($I == $TimeParts['year']) $Selected = ' selected="1"'; else $Selected = '';
-      $Output .= '<option value="'.$I.'"'.$Selected.'>'.$I.'</option>';
-    }
-    $Output .= '</select> &nbsp;&nbsp; ';
-
-    // Hour selection
-    $Output .= '<select name="Hour">';
-    for($I = 0; $I < 24; $I++)
-    {
-      if($I == $TimeParts['hours']) $Selected = ' selected="1"'; else $Selected = '';
-      $Output .= '<option value="'.$I.'"'.$Selected.'>'.$I.'</option>';
-    }
-    $Output .= '</select> : ';
-
-    // Minute selection
-    $Output .= '<select name="Minute">';
-    for($I = 0; $I < 60; $I++)
-    {
-      if($I == $TimeParts['minutes']) $Selected = ' selected="1"'; else $Selected = '';
-      $Output .= '<option value="'.$I.'"'.$Selected.'>'.$I.'</option>';
-    }
-    $Output .= '</select> ';
-
-    $Output .= '<input type="submit" value="Nastavit">';
-    $Output .= '</form>';
-    $Output .= ' <form style="display: inline;" action="?Operation=SetTimeNow&amp;Time='.$Time.'" method="post">';
-    $Output .= '<input type="submit" value="Aktuální čas">';
-    $Output .= '</form>';
-
-    return($Output);
-  }
-
-  /* Produce table with available measures */
-  function ShowMeasureTable()
-  {
-    $PrefixMultiplier = new PrefixMultiplier();
-    $Output = '<table border="1" cellspacing="0" cellpadding="2" style="font-size: small; margin: 0px auto;">';
-    $Output .= '<tr><th>Měřená veličina</th><th>Poslední hodnota</th><th>Čas posledního měření</th><th>Interpolace</th><th>Poznámky</th>';
-    if(array_key_exists('Debug', $_GET)) $Output .= '<th>Počet položek</th><th>Čas vykonání</th>';
-    $Output .= '</tr>';
-    $Result = $this->Database->select('measure', '*', '(Enabled=1) AND ((PermissionView="all") OR (PermissionView="'.gethostbyaddr($_SERVER['REMOTE_ADDR']).'")) ORDER BY Description');
-    while($Measure = $Result->fetch_array())
-    {
-      $StopWatchStart = GetMicrotime();
-      if(array_key_exists('Debug', $_GET))
-      {
-        $DbResult = $this->Database->select($Measure['DataTable'], 'COUNT(*)', 'measure='.$Measure['Id']);
-        $RowCount = $DbResult->fetch_array();
-        $RowCount = $RowCount[0];
-      }
-      $Result2 = $this->Database->select($Measure['DataTable'], 'time, avg', '(measure='.$Measure['Id'].') AND (level=0) ORDER BY time DESC LIMIT 1');
-      if($Result2->num_rows > 0)
-      {
-        $Row = $Result2->fetch_array();
-        $LastMeasureTime = date('j.n.Y G:i:s', MysqlDateTimeToTime($Row['time']));
-        $LastMeasureValue = $PrefixMultiplier->Add($Row['avg'], $Measure['Unit']);
-      } else {
-        $LastMeasureTime = '&nbsp;';
-        $LastMeasureValue = '&nbsp;';
-      }
-      if($Measure['Continuity'] == 1) $Interpolate = 'Ano'; else $Interpolate = 'Ne';
-      if($Measure['Info'] == '') $Measure['Info'] = '&nbsp;';
-      $GenerationTime = floor((GetMicrotime() - $StopWatchStart) * 1000  ) / 1000;
-      $Output .= '<tr><td style="text-align: left"><a href="?Measure='.$Measure['Id'].'&amp;Differential=0">'.$Measure['Description'].'</a></td><td align="center">'.$LastMeasureValue.'</td><td align="center">'.$LastMeasureTime.'</td><td align="center">'.$Interpolate.'</td><td>'.$Measure['Info'].'</td>';
-      if(array_key_exists('Debug', $_GET)) $Output .= '<td>'.$RowCount.'</td><td>'.$GenerationTime.'</td>';
-      $Output .= '</tr>';
-    }
-    $Output .= '</table>';
-    return($Output);
-  }
-
-  function ShowGraph()
-  {
-    $Output = '<strong>Graf:</strong><br>';
-    $Output .= '<img alt="Graf" src="Graph.php?Measure='.$_SESSION['Measure'].'&amp;From='.
-      $_SESSION['TimeStart'].'&amp;To='.$_SESSION['TimeEnd'].
-      '&amp;Width=750&amp;Height=200&amp;Differential='.
-      $_SESSION['Differential'].'" width="750" height="200"><br>';
-    $Output .= '<a href="?Measure='.$_SESSION['Measure'].'&amp;TimeStart='.
-      $_SESSION['TimeStart'].'&amp;TimeEnd='.$_SESSION['TimeEnd'].
-      '&amp;TimeSpecify=1&amp;Differential='.$_SESSION['Differential'].
-      '">Odkaz na vybraný graf</a><br/>';
-    return($Output);
-  }
-
-  function ShowTimeRange()
-  {
-    $Output = '';
-    if($_SESSION['TimeSpecify'] == 0)
-    {
-      $_SESSION['TimeEnd'] = time() - 60;
-      $_SESSION['TimeStart'] = $_SESSION['TimeEnd'] - $this->GrafTimeRanges[$_SESSION['Period']]['period'];
-    }
-
-    if(!array_key_exists('Operation', $_GET)) $_GET['Operation'] = '';
-    switch($_GET['Operation'])
-    {
-      case 'SetTime':
-        if(array_key_exists('Time', $_GET) and array_key_exists('Month', $_POST) and array_key_exists('Day', $_POST) and
-        array_key_exists('Year', $_POST) and array_key_exists('Hour', $_POST) and array_key_exists('Minute', $_POST))
-        {
-          if(($_GET['Time'] == 'TimeStart') or ($_GET['Time'] == 'TimeEnd'))
-          {
-            $_SESSION[$_GET['Time']] = mktime($_POST['Hour'], $_POST['Minute'], 0, $_POST['Month'],
-                $_POST['Day'], $_POST['Year']);
-            $$_GET['Time'] = $_SESSION[$_GET['Time']];
-          }
-        }
-        break;
-      case 'SetTimeNow':
-        if(array_key_exists('Time', $_GET))
-        {
-          if(($_GET['Time'] == 'TimeStart') or ($_GET['Time'] == 'TimeEnd'))
-          {
-            $_SESSION[$_GET['Time']] = time();
-            $$_GET['Time'] = $_SESSION[$_GET['Time']];
-          }
-        }
-        break;
-
-    }
-    $Output .= '<strong>Časový úsek:</strong><br>';
-
-    // Show graph time range menu
-    if($_SESSION['TimeSpecify'] == 0)
-    {
-      $Output .= 'Délka úseku: ';
-      foreach($this->GrafTimeRanges as $Index => $Item)
-        $Output .= '<a href="?Period='.$Index.'">'.$Item['caption'].'</a>&nbsp;';
-      $Output .= '<br>';
-      $Output .= '<a href="?TimeSpecify=1">Přesnější nastavení...</a><br>';
-    } else {
-      $Output .= '<table cellspacing="0" cellpadding="2" border="0" style="margin: 0px auto;">';
-      $Output .= '<tr><td>Počátek:</td><td>'.$this->EditTime('TimeStart').'</td></tr>';
-      $Output .= '<tr><td>Konec:</td><td>'.$this->EditTime('TimeEnd').'</td></tr>';
-      $Output .= '</table>';
-      $Output .= '<a href="?TimeSpecify=0">Jednoduché nastavení...</a><br>';
-    }
-    $Output .= '<br/>';
-    return($Output);
-
-  }
-
-  function Show()
-  {
-    global $Config;
-
-    $Debug = 0;
-    foreach($Config['DefaultVariables'] as $Index => $Variable)
-    {
-      if(!array_key_exists($Index, $_SESSION)) $_SESSION[$Index] = $Variable;
-      if(array_key_exists($Index, $_GET)) $_SESSION[$Index] = $_GET[$Index];
-      if(array_key_exists($Index, $_POST)) $_SESSION[$Index] = $_POST[$Index];
-      //$$Index = $_SESSION[$Index];
-    }
-
-    $Output = '<div style="text-align: center"><div class="Title">'.$Config['Web']['Title'].'</div>';
-
-    $Result = $this->Database->select('measure', 'Id', '(`Enabled`=1) AND '.
-        '((`PermissionView`="all") OR (`PermissionView`="'.gethostbyaddr($_SERVER['REMOTE_ADDR']).'")) '.
-        'AND (`Id`='.($_SESSION['Measure'] * 1).')');
-    if($Result->num_rows == 0)
-      $_SESSION['Measure'] = $Config['DefaultVariables']['Measure'];
-
-    $Output .= $this->ShowTimeRange();
-    $Output .= $this->ShowGraph();
-    $Output .= '<br/>';
-    $Output .= $this->ShowMeasureTable();
-    $Output .= '</div>';
-
-    ShowPage($Output);
-  }
-}
-
-$Application = new Application();
-$Application->Start();
-$MainView = new MainView($Application->Database);
-$MainView->Show();
+$Core = new Core();
+$Core->Run();
Index: /trunk/style/style.css
===================================================================
--- /trunk/style/style.css	(revision 68)
+++ /trunk/style/style.css	(revision 69)
@@ -321,2 +321,30 @@
 	width: auto;
 }
+
+.BaseTable
+{
+  margin: 2px auto 2px auto;
+  border-width: 1px;
+  border-color: black;
+  border-style: solid;
+  border-collapse: collapse;
+}
+
+.BaseTable tr td
+{
+  border-width: 1px;
+  border-color: black;
+  border-style: solid;
+  padding: 2px;
+  text-align: center;
+}
+
+.BaseTable tr th
+{
+  border-width: 1px;
+  border-color: black;
+  border-style: solid;
+  padding: 2px;
+  background-color: #F0F0F0;
+  text-align: center;
+}
