diff --git a/config/config.ini.php b/config/config.ini.php
index d850e8e2d6599505a0c19fad9760f7757e87b151..782d20195c29f4311b8174b9275d796d67320e68 100755
--- a/config/config.ini.php
+++ b/config/config.ini.php
@@ -7,7 +7,7 @@ host			= localhost
 username		= root
 password		= nintendo
 dbname			= piwiktrunk
-adapter			= PDO_MYSQL
+adapter			= PDO_MYSQL ; PDO_MYSQL or MYSQLI
 tables_prefix	= piwik_
 profiler 		= true
 
@@ -15,6 +15,10 @@ profiler 		= true
 dbname			= piwiktests
 tables_prefix	= piwiktests_
 
+[LogStats]
+; set to 0 if you want to stop tracking the visitors. Useful if you need to stop all the connections on the DB.
+record_statistics	= 1
+
 
 [log]
 
diff --git a/index.php b/index.php
index 6f4246f90b4819870e9d298e598c5c1438dfbd4d..5aa67895f5f4f66e85ea34a42e52559a3b645f5d 100755
--- a/index.php
+++ b/index.php
@@ -1,16 +1,11 @@
 <?php
-/*
+/**
  * PHP Configuration init
  */
 error_reporting(E_ALL|E_NOTICE);
 date_default_timezone_set('Europe/London');
 define('PIWIK_INCLUDE_PATH', '.');
 
-require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php";
-set_error_handler('Piwik_ErrorHandler');
-require_once PIWIK_INCLUDE_PATH . "/modules/ExceptionHandler.php";
-set_exception_handler('Piwik_ExceptionHandler');
-
 set_include_path(PIWIK_INCLUDE_PATH 
 					. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs/'
 					. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/'
@@ -22,7 +17,15 @@ assert_options(ASSERT_ACTIVE, 	1);
 assert_options(ASSERT_WARNING, 	1);
 assert_options(ASSERT_BAIL, 	1);
 
-/*
+/**
+ * Error / exception handling functions
+ */
+require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php";
+set_error_handler('Piwik_ErrorHandler');
+require_once PIWIK_INCLUDE_PATH . "/modules/ExceptionHandler.php";
+set_exception_handler('Piwik_ExceptionHandler');
+
+/**
  * Zend classes
  */
 include "Zend/Exception.php";
@@ -35,7 +38,7 @@ Zend_Loader::loadClass('Zend_Debug');
 Zend_Loader::loadClass('Zend_Auth');
 Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable');
 
-/*
+/**
  * Piwik classes
  */
 Zend_Loader::loadClass('Piwik_Access');
@@ -103,4 +106,21 @@ function main()
 	$api->UsersManager->addUser("login", "password", "email@geage.com");
 }
 
-?>
\ No newline at end of file
+?>
+
+
+
+<!-- Piwik -->
+<a href="http://piwik.org" title="Web analytics" onclick="window.open(this.href);return(false);">
+<script language="javascript" src="piwik.js" type="text/javascript"></script>
+<script type="text/javascript">
+<!--
+	piwik_action_name = '';
+	piwik_idsite = 1;
+	piwik_url = "http://localhost/dev/piwiktrunk/piwik.php";
+	piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+//-->
+</script><object>
+<noscript><p>Web analytics<img src="http://localhost/dev/piwiktrunk/piwik.php" style="border:0" /></p>
+</noscript></object></a>
+<!-- /Piwik --> 
\ No newline at end of file
diff --git a/libs/Event/Dispatcher.php b/libs/Event/Dispatcher.php
new file mode 100755
index 0000000000000000000000000000000000000000..1fa91f85a5203e823b272eef05beca117b5e4cd2
--- /dev/null
+++ b/libs/Event/Dispatcher.php
@@ -0,0 +1,478 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2005, Bertrand Mansion                                  |
+// | All rights reserved.                                                  |
+// |                                                                       |
+// | Redistribution and use in source and binary forms, with or without    |
+// | modification, are permitted provided that the following conditions    |
+// | are met:                                                              |
+// |                                                                       |
+// | o Redistributions of source code must retain the above copyright      |
+// |   notice, this list of conditions and the following disclaimer.       |
+// | o Redistributions in binary form must reproduce the above copyright   |
+// |   notice, this list of conditions and the following disclaimer in the |
+// |   documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote      |
+// |   products derived from this software without specific prior written  |
+// |   permission.                                                         |
+// |                                                                       |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
+// |                                                                       |
+// +-----------------------------------------------------------------------+
+// | Author: Bertrand Mansion <bmansion@mamasam.com>                       |
+// |         Stephan Schmidt <schst@php.net>                               |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Dispatcher.php,v 1.4 2005/09/22 15:37:10 schst Exp $
+
+require_once 'Event/Notification.php';
+
+/**
+ * Pseudo 'static property' for Notification object
+ * @global array $GLOBALS["_Event_Dispatcher"]
+ */
+$GLOBALS['_Event_Dispatcher'] = array(
+                                  'NotificationClass' => 'Event_Notification'
+                                     );
+
+/**
+ * Registers a global observer
+ */
+define('EVENT_DISPATCHER_GLOBAL', '');
+
+/**
+ * Dispatch notifications using PHP callbacks
+ *
+ * The Event_Dispatcher acts acts as a notification dispatch table.
+ * It is used to notify other objects of interesting things, if
+ * they meet certain criteria. This information is encapsulated 
+ * in {@link Event_Notification} objects. Client objects register 
+ * themselves with the Event_Dispatcher as observers of specific
+ * notifications posted by other objects. When an event occurs,
+ * an object posts an appropriate notification to the Event_Dispatcher.
+ * The Event_Dispatcher dispatches a message to each
+ * registered observer, passing the notification as the sole argument.
+ *
+ * The Event_Dispatcher is actually a combination of three design
+ * patterns: the Singleton, {@link http://c2.com/cgi/wiki?MediatorPattern Mediator},
+ * and Observer patterns. The idea behind Event_Dispatcher is borrowed from 
+ * {@link http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/index.html Apple's Cocoa framework}.
+ *
+ * @category   Event
+ * @package    Event_Dispatcher
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Stephan Schmidt <schst@php.net>
+ * @copyright  1997-2005 The PHP Group
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/Event_Dispatcher
+ */
+class Event_Dispatcher
+{
+    /**
+     * Registered observer callbacks
+     * @var array
+     * @access private
+     */
+    var $_ro = array();
+    
+    /**
+     * Pending notifications
+     * @var array
+     * @access private
+     */
+    var $_pending = array();
+
+    /**
+     * Nested observers
+     * @var array
+     * @access private
+     */
+    var $_nestedDispatchers = array();
+
+    /**
+     * Name of the dispatcher
+     * @var string
+     * @access private
+     */
+    var $_name = null;
+
+    /**
+     * Class used for notifications
+     * @var string
+     * @access private
+     */
+    var $_notificationClass = null;
+
+    /**
+     * PHP4 constructor
+     *
+     * Please use {@link getInstance()} instead.
+     *
+     * @access  private
+     * @param   string      Name of the notification dispatcher.
+     */
+    function Event_Dispatcher($name)
+    {
+        Event_Dispatcher::__construct($name);
+    }
+
+    /**
+     * PHP5 constructor
+     *
+     * Please use {@link getInstance()} instead.
+     *
+     * @access  private
+     * @param   string      Name of the notification dispatcher.
+     */
+    function __construct($name)
+    {
+        $this->_name = $name;
+        $this->_notificationClass = $GLOBALS['_Event_Dispatcher']['NotificationClass'];
+    }
+
+    /**
+     * Returns a notification dispatcher singleton
+     *
+     * There is usually no need to have more than one notification
+     * center for an application so this is the recommended way
+     * to get a Event_Dispatcher object.
+     *
+     * @param string    Name of the notification dispatcher.
+     *                  The default notification dispatcher is named __default.
+     * 
+     * @return object Event_Dispatcher
+     */
+    function &getInstance($name = '__default')
+    {
+        static $dispatchers = array();
+
+        if (!isset($dispatchers[$name])) {
+            $dispatchers[$name] = new Event_Dispatcher($name);
+        }
+
+        return $dispatchers[$name];
+    }
+
+    /**
+     * Registers an observer callback
+     *
+     * This method registers a {@link http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback callback}
+     * which is called when the notification corresponding to the
+     * criteria given at registration time is posted.
+     * The criteria are the notification name and eventually the 
+     * class of the object posted with the notification.
+     *
+     * If there are any pending notifications corresponding to the criteria
+     * given here, the callback will be called straight away.
+     *
+     * If the notification name is empty, the observer will receive all the
+     * posted notifications. Same goes for the class name.
+     *
+     * @access  public
+     * @param   mixed       A PHP callback
+     * @param   string      Expected notification name, serves as a filter
+     * @param   string      Expected contained object class, serves as a filter
+     * @return void
+     */
+    function addObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
+    {
+        if (is_array($callback)) {
+            if (is_object($callback[0])) {
+                // Note : PHP4 does not allow correct object comparison so
+                // only the class name is used for registration checks.
+                $reg = get_class($callback[0]).'::'.$callback[1];
+            } else {
+                $reg = $callback[0].'::'.$callback[1];
+            }
+        } else {
+            $reg = $callback;
+        }
+
+        $this->_ro[$nName][$reg] = array(
+                                    'callback' => $callback,
+                                    'class'    => $class
+                                    );
+
+        // Post eventual pending notifications for this observer
+        if (isset($this->_pending[$nName])) {
+            foreach (array_keys($this->_pending[$nName]) as $k) {
+                $notification =& $this->_pending[$nName][$k];
+                if (!$notification->isNotificationCancelled()) {
+                    $objClass = get_class($notification->getNotificationObject());
+                    if (empty($class) || strcasecmp($class, $objClass) == 0) {
+                        call_user_func_array($callback, array(&$notification));
+                        $notification->increaseNotificationCount();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates and posts a notification object
+     *
+     * The purpose of the optional associated object is generally to pass
+     * the object posting the notification to the observers, so that the 
+     * observers can query the posting object for more information about
+     * the event.
+     *
+     * Notifications are by default added to a pending notification list.
+     * This way, if an observer is not registered by the time they are 
+     * posted, it will still be notified when it is added as an observer.
+     * This behaviour can be turned off in order to make sure that only
+     * the registered observers will be notified.
+     *
+     * The info array serves as a container for any kind of useful 
+     * information. It is added to the notification object and posted along.
+     *
+     * @access  public
+     * @param   object      Notification associated object
+     * @param   string      Notification name
+     * @param   array       Optional user information
+     * @param   bool        Whether the notification is pending
+     * @param   bool        Whether you want the notification to bubble up
+     * @return  object  The notification object
+     */
+    function &post(&$object, $nName, $info = array(), $pending = true, $bubble = true)
+    {
+        $notification =& new $this->_notificationClass($object, $nName, $info);
+        return $this->postNotification($notification, $pending, $bubble);
+    }
+
+    /**
+     * Posts the {@link Event_Notification} object
+     *
+     * @access  public
+     * @param   object      The Notification object
+     * @param   bool        Whether to post the notification immediately
+     * @param   bool        Whether you want the notification to bubble up
+     * @see Event_Dispatcher::post()
+     * @return  object  The notification object
+     */
+    function &postNotification(&$notification, $pending = true, $bubble = true)
+    {
+        $nName = $notification->getNotificationName();
+        if ($pending === true) {
+            $this->_pending[$nName][] =& $notification;
+        }
+        $objClass = get_class($notification->getNotificationObject());
+
+        // Find the registered observers
+        if (isset($this->_ro[$nName])) {
+            foreach (array_keys($this->_ro[$nName]) as $k) {
+                $rObserver =& $this->_ro[$nName][$k];
+                if ($notification->isNotificationCancelled()) {
+                    return $notification;
+                }
+                if (empty($rObserver['class']) ||
+                	strcasecmp($rObserver['class'], $objClass) == 0) {
+                    call_user_func_array($rObserver['callback'], array(&$notification));
+                    $notification->increaseNotificationCount();
+                }
+            }
+        }
+
+        // Notify globally registered observers
+        if (isset($this->_ro[EVENT_DISPATCHER_GLOBAL])) {
+            foreach (array_keys($this->_ro[EVENT_DISPATCHER_GLOBAL]) as $k) {
+                $rObserver =& $this->_ro[EVENT_DISPATCHER_GLOBAL][$k];
+                if ($notification->isNotificationCancelled()) {
+                    return $notification;
+                }
+                if (empty($rObserver['class']) || 
+                	strcasecmp($rObserver['class'], $objClass) == 0) {
+                    call_user_func_array($rObserver['callback'], array(&$notification));
+                    $notification->increaseNotificationCount();
+                }
+            }
+        }
+
+        if ($bubble === false) {
+            return $notification;
+        }
+        
+        // Notify in nested dispatchers
+        foreach (array_keys($this->_nestedDispatchers) as $nested) {
+            $notification =& $this->_nestedDispatchers[$nested]->postNotification($notification, $pending);
+        }
+
+        return $notification;
+    }
+
+    /**
+     * Removes a registered observer that correspond to the given criteria
+     *
+     * @access  public
+     * @param   mixed       A PHP callback
+     * @param   string      Notification name
+     * @param   string      Contained object class
+     * @return  bool    True if an observer was removed, false otherwise
+     */
+    function removeObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
+    {
+        if (is_array($callback)) {
+            if (is_object($callback[0])) {
+                $reg = get_class($callback[0]).'::'.$callback[1];
+            } else {
+                $reg = $callback[0].'::'.$callback[1];
+            }
+        } else {
+            $reg = $callback;
+        }
+
+        $removed = false;
+        if (isset($this->_ro[$nName][$reg])) {
+            if (!empty($class)) {
+                if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) {
+                    unset($this->_ro[$nName][$reg]);
+                    $removed = true;
+                }
+            } else {
+                unset($this->_ro[$nName][$reg]);
+                $removed = true;
+            }
+        }
+
+        if (isset($this->_ro[$nName]) && count($this->_ro[$nName]) == 0) {
+            unset($this->_ro[$nName]);
+        }
+        return $removed;
+    }
+
+   /**
+    * Check, whether the specified observer has been registered with the
+    * dispatcher
+    *
+     * @access  public
+     * @param   mixed       A PHP callback
+     * @param   string      Notification name
+     * @param   string      Contained object class
+     * @return  bool        True if the observer has been registered, false otherwise
+    */
+    function observerRegistered($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
+    {
+        if (is_array($callback)) {
+            if (is_object($callback[0])) {
+                $reg = get_class($callback[0]).'::'.$callback[1];
+            } else {
+                $reg = $callback[0].'::'.$callback[1];
+            }
+        } else {
+            $reg = $callback;
+        }
+
+        if (!isset($this->_ro[$nName][$reg])) {
+            return false;
+        }
+        if (empty($class)) {
+            return true;
+        }
+        if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) {
+            return true;
+        }
+        return false;
+    }
+
+   /**
+    * Get all observers, that have been registered for a notification
+    *
+     * @access  public
+     * @param   string      Notification name
+     * @param   string      Contained object class
+     * @return  array       List of all observers
+    */
+    function getObservers($nName = EVENT_DISPATCHER_GLOBAL, $class = null)
+    {
+        $observers = array();        
+        if (!isset($this->_ro[$nName])) {
+            return $observers;
+        }
+        foreach ($this->_ro[$nName] as $reg => $observer) {
+        	if ($class == null || $observer['class'] == null ||  strcasecmp($observer['class'], $class) == 0) {
+        		$observers[] = $reg;
+        	}
+        }
+        return $observers;
+    }
+    
+    /**
+     * Get the name of the dispatcher.
+     *
+     * The name is the unique identifier of a dispatcher.
+     *
+     * @access   public
+     * @return   string     name of the dispatcher
+     */
+    function getName()
+    {
+        return $this->_name;
+    }
+
+    /**
+     * add a new nested dispatcher
+     *
+     * Notifications will be broadcasted to this dispatcher as well, which
+     * allows you to create event bubbling.
+     *
+     * @access   public
+     * @param    Event_Dispatcher    The nested dispatcher
+     */
+    function addNestedDispatcher(&$dispatcher)
+    {
+        $name = $dispatcher->getName();
+        $this->_nestedDispatchers[$name] =& $dispatcher;
+    }
+
+   /**
+    * remove a nested dispatcher
+    *
+    * @access   public
+    * @param    Event_Dispatcher    Dispatcher to remove
+    * @return   boolean
+    */
+    function removeNestedDispatcher($dispatcher)
+    {
+        if (is_object($dispatcher)) {
+            $dispatcher = $dispatcher->getName();
+        }
+        if (!isset($this->_nestedDispatchers[$dispatcher])) {
+            return false;
+        }
+        unset($this->_nestedDispatchers[$dispatcher]);
+        return true;
+    }
+
+    /**
+     * Changes the class used for notifications
+     *
+     * You may call this method on an object to change it for a single
+     * dispatcher or statically, to set the default for all dispatchers
+     * that will be created.
+     *
+     * @access   public
+     * @param    string     name of the notification class
+     * @return   boolean
+     */
+    function setNotificationClass($class)
+    {
+        if (isset($this) && is_a($this, 'Event_Dispatcher')) {
+            $this->_notificationClass = $class;
+            return true;
+        }
+        $GLOBALS['_Event_Dispatcher']['NotificationClass'] = $class;
+        return true;
+    }
+
+}
+?>
\ No newline at end of file
diff --git a/libs/Event/Notification.php b/libs/Event/Notification.php
new file mode 100755
index 0000000000000000000000000000000000000000..ca3f3833f77d1bd53f9279c4a13d86da70afee8b
--- /dev/null
+++ b/libs/Event/Notification.php
@@ -0,0 +1,194 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2005, Bertrand Mansion                                  |
+// | All rights reserved.                                                  |
+// |                                                                       |
+// | Redistribution and use in source and binary forms, with or without    |
+// | modification, are permitted provided that the following conditions    |
+// | are met:                                                              |
+// |                                                                       |
+// | o Redistributions of source code must retain the above copyright      |
+// |   notice, this list of conditions and the following disclaimer.       |
+// | o Redistributions in binary form must reproduce the above copyright   |
+// |   notice, this list of conditions and the following disclaimer in the |
+// |   documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote      |
+// |   products derived from this software without specific prior written  |
+// |   permission.                                                         |
+// |                                                                       |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
+// |                                                                       |
+// +-----------------------------------------------------------------------+
+// | Author: Bertrand Mansion <bmansion@mamasam.com>                       |
+// |         Stephan Schmidt <schst@php.net>                               |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Notification.php,v 1.2 2005/02/14 10:35:18 mansion Exp $
+
+/**
+ * Default state of the notification
+ */
+define('EVENT_NOTIFICATION_STATE_DEFAULT', 0);
+
+/**
+ * Notification has been cancelled
+ */
+define('EVENT_NOTIFICATION_STATE_CANCELLED', 1);
+
+/**
+ * A Notification object
+ *
+ * The Notification object can be easily subclassed and serves as a container
+ * for the information about the notification. It holds an object which is 
+ * usually a reference to the object that posted the notification,
+ * a notification name used to identify the notification and some user
+ * information which can be anything you need.
+ * 
+ * @category   Event
+ * @package    Event_Dispatcher
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Stephan Schmidt <schst@php.net>
+ * @copyright  1997-2005 The PHP Group
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/Event_Dispatcher
+ */
+class Event_Notification
+{
+    /**
+     * name of the notofication
+     * @var string
+     * @access private
+     */
+    var $_notificationName;
+    
+    /**
+     * object of interesed (the sender of the notification, in most cases)
+     * @var object
+     * @access private
+     */
+    var $_notificationObject;
+    
+    /**
+     * additional information about the notification
+     * @var mixed
+     * @access private
+     */
+    var $_notificationInfo = array();
+
+    /**
+     * state of the notification
+     *
+     * This may be:
+     * - EVENT_NOTIFICATION_STATE_DEFAULT
+     * - EVENT_NOTIFICATION_STATE_CANCELLED
+     *
+     * @var integer
+     * @access private
+     */
+    var $_notificationState = EVENT_NOTIFICATION_STATE_DEFAULT;
+    
+    /**
+     * amount of observers that received this notification
+     * @var mixed
+     * @access private
+     */
+    var $_notificationCount = 0;
+
+    /**
+     * Constructor
+     *
+     * @access  public
+     * @param   object      The object of interest for the notification,
+     *                      usually is the posting object
+     * @param   string      Notification name
+     * @param   array       Free information array
+     */
+    function Event_Notification(&$object, $name, $info = array())
+    {
+        $this->_notificationObject =& $object;
+        $this->_notificationName   = $name;
+        $this->_notificationInfo   = $info;
+    }
+
+    /**
+     * Returns the notification name
+     * @return  string Notification name
+     */
+    function getNotificationName()
+    {
+        return $this->_notificationName;
+    }
+
+    /**
+     * Returns the contained object
+     * @return  object Contained object
+     */
+    function &getNotificationObject()
+    {
+        return $this->_notificationObject;
+    }
+
+    /**
+     * Returns the user info array
+     * @return  array user info
+     */
+    function getNotificationInfo()
+    {
+        return $this->_notificationInfo;    
+    }
+
+   /**
+    * Increase the internal count
+    *
+    * @access   public
+    */
+    function increaseNotificationCount()
+    {
+        ++$this->_notificationCount;
+    }
+    
+   /**
+    * Get the number of posted notifications
+    *
+    * @access   public
+    * @return   int
+    */
+    function getNotificationCount()
+    {
+        return $this->_notificationCount;
+    }
+    
+   /**
+    * Cancel the notification
+    *
+    * @access   public
+    * @return   void
+    */
+    function cancelNotification()
+    {
+        $this->_notificationState = EVENT_NOTIFICATION_STATE_CANCELLED;
+    }
+
+   /**
+    * Checks whether the notification has been cancelled
+    *
+    * @access   public
+    * @return   boolean
+    */
+    function isNotificationCancelled()
+    {
+        return ($this->_notificationState === EVENT_NOTIFICATION_STATE_CANCELLED);
+    }
+}
+?>
\ No newline at end of file
diff --git a/libs/Zend/Db/Adapter/Mysqli.php b/libs/Zend/Db/Adapter/Mysqli.php
index 51b8f29c27d18f7fbdb1e3bc7bcad928231b19af..903a8041be48554af979c01dbf8103e9d2b51382 100755
--- a/libs/Zend/Db/Adapter/Mysqli.php
+++ b/libs/Zend/Db/Adapter/Mysqli.php
@@ -243,9 +243,18 @@ class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract
             $port = null;
         }
 
+		if(!class_exists("mysqli"))
+		{
+            /**
+             * @see Zend_Db_Adapter_Mysqli_Exception
+             */
+            require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+            throw new Zend_Db_Adapter_Mysqli_Exception("The extension mysqli is not installed.");
+		}
+	
         // Suppress connection warnings here.
         // Throw an exception instead.
-        @$this->_connection = new mysqli(
+        $this->_connection = new mysqli(
             $this->_config['host'],
             $this->_config['username'],
             $this->_config['password'],
diff --git a/modules/ExceptionHandler.php b/modules/ExceptionHandler.php
index f6fd67e0f112ff2d3f1409ea26a5fd75e139c397..edcfc212854a3aecd1ac590f0b7c481f209a1e3f 100644
--- a/modules/ExceptionHandler.php
+++ b/modules/ExceptionHandler.php
@@ -5,9 +5,11 @@ function Piwik_ExceptionHandler(Exception $exception)
 	try	{
 		Zend_Registry::get('logger_exception')->log($exception);
 	} catch(Exception $e) {
-		print("<br> -------------------------- <br>An exception occured while dealing with an uncaught exception... <br>");
+		print("<br> <b>Exception</b>: '". $exception->getMessage()."'");
+		
+		print("<br> -------------------------- <br>
+			This exception occured and also raised this exception: ");
 		print("'" . $e->getMessage()."'");
-		print("<br> The initial exception was: <br>'". $exception->getMessage()."'");
 	}
 }
 ?>
diff --git a/piwik.js b/piwik.js
new file mode 100644
index 0000000000000000000000000000000000000000..032b5224020920c5038713f60e35b0a731e5ec82
--- /dev/null
+++ b/piwik.js
@@ -0,0 +1,103 @@
+//-- Web analytics by Piwik -- http://piwik.org
+//-- Copyleft 2007, All rights reversed.
+
+var _pk_use_title_as_name = 0;
+
+//-- Beginning script
+function _pk_plug_normal(_pk_pl) {
+	if (_pk_tm.indexOf(_pk_pl) != -1 && (navigator.mimeTypes[_pk_pl].enabledPlugin != null)) 
+		return '1';
+	return '0';
+}
+
+function _pk_plug_ie(_pk_pl){
+	_pk_find = false;
+	document.write('<SCR' + 'IPT LANGUAGE=VBScript>\n on error resume next \n _pk_find = IsObject(CreateObject("' + _pk_pl + '")) </SCR' + 'IPT>\n');
+	if (_pk_find) return '1';
+	return '0';
+}
+
+var _pk_jav = '0'; if(navigator.javaEnabled()) _pk_jav='1';
+var _pk_agent = navigator.userAgent.toLowerCase();
+var _pk_moz = (navigator.appName.indexOf("Netscape") != -1);
+var _pk_ie = (_pk_agent.indexOf("msie") != -1);
+var _pk_win = ((_pk_agent.indexOf("win") != -1) || (_pk_agent.indexOf("32bit") != -1));
+var _pk_cookie = (navigator.cookieEnabled)? '1' : '0';
+if((typeof (navigator.cookieEnabled) == "undefined") && (_pk_cookie == '0')) {
+	document.cookie="_pk_testcookie"
+	_pk_cookie=(document.cookie.indexOf("_pk_testcookie")!=-1)? '1' : '0';
+}
+
+var _pk_dir='0',_pk_fla='0',_pk_pdf='0',_pk_qt = '0',_pk_rea = '0',_pk_wma='0'; 
+if (_pk_win && _pk_ie){
+	_pk_dir = _pk_plug_ie("SWCtl.SWCtl.1");
+	_pk_fla = _pk_plug_ie("ShockwaveFlash.ShockwaveFlash.1");
+	if (_pk_plug_ie("PDF.PdfCtrl.1") == '1' || _pk_plug_ie('PDF.PdfCtrl.5') == '1' || _pk_plug_ie('PDF.PdfCtrl.6') == '1') 
+		_pk_pdf = '1';
+	_pk_qt = _pk_plug_ie("Quicktime.Quicktime"); // Old : "QuickTimeCheckObject.QuickTimeCheck.1"
+	_pk_rea = _pk_plug_ie("rmocx.RealPlayer G2 Control.1");
+	_pk_wma = _pk_plug_ie("wmplayer.ocx"); // Old : "MediaPlayer.MediaPlayer.1"
+} else {
+	var _pk_tm = '';
+	for (var i=0; i < navigator.mimeTypes.length; i++)
+		_pk_tm += navigator.mimeTypes[i].type.toLowerCase();
+	_pk_dir = _pk_plug_normal("application/x-director");
+	_pk_fla = _pk_plug_normal("application/x-shockwave-flash");
+	_pk_pdf = _pk_plug_normal("application/pdf");
+	_pk_qt  = _pk_plug_normal("video/quicktime");
+	_pk_rea = _pk_plug_normal("audio/x-pn-realaudio-plugin");
+	_pk_wma = _pk_plug_normal("application/x-mplayer2");
+}
+	
+var _pk_rtu = '';
+try {
+	_pk_rtu = top.document.referrer;
+} catch(e1) {
+	if(parent){ 
+		try{ _pk_rtu = parent.document.referrer; } catch(e2) { _pk_rtu=''; }
+	}
+}
+if(_pk_rtu == '') {
+	_pk_rtu = document.referrer;
+}
+
+function _pk_escape(_pk_str){
+	if (typeof(encodeURIComponent) == 'function') {
+		return encodeURIComponent(_pk_str);
+	} else {
+		return escape(_pk_str);
+	}
+}
+var _pk_called;
+
+function _pk_getUrlLog( _pk_action_name, _pk_site, _pk_pkurl )
+{
+	var _pk_url = document.location.href;
+	var _pk_da = new Date();
+	var _pk_src = _pk_pkurl
+		+'?url='+_pk_escape(_pk_url)
+		+'&aname='+_pk_escape(_pk_action_name)
+		+'&id='+_pk_site
+		+'&res='+screen.width+'x'+screen.height
+		+'&col='+screen.colorDepth
+		+'&h='+_pk_da.getHours()+'&m='+_pk_da.getMinutes()+'&s='+_pk_da.getSeconds()
+		+'&flash='+_pk_fla
+		+'&director='+_pk_dir
+		+'&quicktime='+_pk_qt
+		+'&realplayer='+_pk_rea
+		+'&pdf='+_pk_pdf
+		+'&windowsmedia='+_pk_wma
+		+'&java='+_pk_jav
+		+'&cookie='+_pk_cookie
+		+'&urlref='+_pk_escape(_pk_rtu);
+	return _pk_src;
+}
+
+function piwik_log( _pk_action_name, _pk_site, _pk_pkurl )
+{
+	if(_pk_called && (!_pk_action_name || _pk_action_name=="")) return;
+	var _pk_src = _pk_getUrlLog(_pk_action_name, _pk_site, _pk_pkurl );
+	document.writeln('<img src="'+_pk_src+'" alt="Piwik" style="border:0" />');
+	if(!_pk_action_name || _pk_action_name=="") _pk_called=1;
+}
+
diff --git a/piwik.php b/piwik.php
index 58436c979c5c608f970795679a0bceba65d58f3d..ebadb700a354aa499b2fdc6b9c3ba7134b787cc2 100644
--- a/piwik.php
+++ b/piwik.php
@@ -1,5 +1,123 @@
 <?php
 
+error_reporting(E_ALL|E_NOTICE);
+define('PIWIK_INCLUDE_PATH', '.');
+
+@ignore_user_abort(true);
+@set_time_limit(0);
+set_include_path(PIWIK_INCLUDE_PATH 
+					. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs/'
+					. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/'
+					. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/modules'
+					. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/models'
+					. PATH_SEPARATOR . get_include_path() );
+
+require_once "Event/Dispatcher.php";
+
+function printDebug( $str = '' )
+{
+	print($str . "<br>\n");
+}
+
+
+
+/*
+ * Some benchmarks
+ * 
+ * - with the config parsing + db connection
+ * Requests per second:    471.91 [#/sec] (mean)
+ * 
+ * 
+ */
+
+/**
+ * Simple database PDO wrapper
+ * 
+ */
+	
+//Constante com caminhos para includes
+define("PATH_INCLUDES", $_SERVER['DOCUMENT_ROOT']."/conn");
+
+class Piwik_LogStats_Db 
+{
+	private $connection;
+	private $username;
+	private $password;
+	
+	public function __construct( $host, $username, $password, $dbname) 
+	{
+		$this->dsn = "mysql:dbname=$dbname;host=$host";
+		$this->username = $username;
+		$this->password = $password;
+	}
+
+	public function connect() 
+	{
+		try {
+			$pdoConnect = new PDO($this->dsn, $this->username, $this->password);
+			$pdoConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+			$this->connection = $pdoConnect;
+		} catch (PDOException $e) {
+			throw new Exception("Error connecting database: ".$e->getMessage());
+		}
+	}
+
+	public function fetchAll( $query )
+	{
+		try {
+			$sth = $this->connexion->prepare($query);
+			$sth->execute();
+			return $sth->fetchAll(PDO::FETCH_ASSOC);
+		} catch (PDOException $e) {
+			throw new Exception("Error connecting database: ".$e->getMessage());
+		}
+	}
+	
+	public function query($query) 
+	{
+		if (!$this->connection->query($query)) {
+			throw new Exception($this->connection->errorInfo());
+		} else {
+			return true;
+		}
+	}
+}
+
+class Piwik_LogStats_Config
+{
+	static private $instance = null;
+	
+	static public function getInstance()
+	{
+		if (self::$instance == null)
+		{			
+			$c = __CLASS__;
+			self::$instance = new $c();
+		}
+		return self::$instance;
+	}
+	
+	public $config = array();
+	
+	private function __construct()
+	{
+		$pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
+		$this->config = parse_ini_file($pathIniFile, true);
+	}
+	
+	public function __get( $name )
+	{
+		if(isset($this->config[$name]))
+		{
+			return $this->config[$name];
+		}
+		else
+		{
+			throw new Exception("The config element $name is not available in the configuration (check the configuration file).");
+		}
+	}
+}
+
 
 /**
  * Plugin specification for a statistics logging plugin
@@ -22,7 +140,189 @@
  * - register to hooks in other plugins
  * - generally a plugin method can modify data (filter) and add/remove data 
  * 
+ * 
+ */ 
+ 
+class Piwik_PluginsManager
+{
+	public $dispatcher;
+	private $pluginsPath;
+	
+	static private $instance = null;
+	
+	static public function getInstance()
+	{
+		if (self::$instance == null)
+		{			
+			$c = __CLASS__;
+			self::$instance = new $c();
+		}
+		return self::$instance;
+	}
+	
+	private function __construct()
+	{
+		$this->pluginsPath = 'plugins/';
+		$this->pluginsCategory = 'LogsStats/';
+		
+		$this->dispatcher = Event_Dispatcher::getInstance();
+		$this->loadPlugins();
+	}
+	
+	/**
+	 * Load the plugins classes installed.
+	 * Register the observers for every plugin.
+	 * 
+	 */
+	public function loadPlugins()
+	{
+		$defaultPlugins = array(
+			array( 'fileName' => 'Provider', 'className' => 'Piwik_Plugin_LogStats_Provider' ),
+		//	'Piwik_Plugin_LogStats_UserSettings',
+		);
+		
+		foreach($defaultPlugins as $pluginInfo)
+		{
+			$pluginFileName = $pluginInfo['fileName'];
+			$pluginClassName = $pluginInfo['className'];
+			/*
+			// TODO make sure the plugin name is secure
+			$path = PIWIK_INCLUDE_PATH 
+					. $this->pluginsPath 
+					. $this->pluginsCategory
+					. $pluginFileName . ".php";
+			
+			if(is_file($path))
+			{
+				throw new Exception("The plugin file $path couldn't be found.");
+			}
+			
+			require_once $path;
+			*/
+			
+			$newPlugin = new $pluginClassName;
+			
+			$this->addPluginObservers( $newPlugin );
+		}
+	}
+	
+	/**
+	 * For the given plugin, add all the observers of this plugin.
+	 */
+	private function addPluginObservers( Piwik_Plugin $plugin )
+	{
+		$hooks = $plugin->getListHooksRegistered();
+		
+		foreach($hooks as $hookName => $methodToCall)
+		{
+			$this->dispatcher->addObserver( array( $plugin, $methodToCall) );
+		}
+	}
+	
+}
+
+/**
+ * Post an event to the dispatcher which will notice the observers
  */
+function Piwik_PostEvent( $eventName, $object = null, $info = array() )
+{
+	printDebug("Dispatching event $eventName...");
+	Piwik_PluginsManager::getInstance()->dispatcher->post( $object, $eventName, $info, false, false );
+}
+
+
+abstract class Piwik_Plugin
+{
+	/**
+	 * Returns the plugin details
+	 */
+	abstract function getInformation();
+	
+	/**
+	 * Returns the list of hooks registered with the methods names
+	 */
+	abstract function getListHooksRegistered();
+	
+	/**
+	 * Returns the names of the required plugins
+	 */
+	public function getListRequiredPlugins()
+	{
+		return array();
+	}
+	 
+	/**
+	 * Install the plugin
+	 * - create tables
+	 * - update existing tables
+	 * - etc.
+	*/
+	public function install()
+	{
+		return;
+	}
+	  
+	/**
+	 * Remove the created resources during the install
+	 */
+	public function uninstall()
+	{
+		return;
+	}
+}
+
+
+
+class Piwik_Plugin_LogStats_Provider extends Piwik_Plugin
+{	
+	public function __construct()
+	{
+	}
+
+	public function getInformation()
+	{
+		$info = array(
+			'name' => 'LogProvider',
+			'description' => 'Log in the DB the hostname looked up from the IP',
+			'author' => 'Piwik',
+			'homepage' => 'http://piwik.org/plugins/LogProvider',
+			'version' => '0.1',
+		);
+		
+		return $info;
+	}
+	
+	function install()
+	{
+		// add column hostname / hostname ext in the visit table
+	}
+	
+	function uninstall()
+	{
+		// add column hostname / hostname ext in the visit table
+	}
+	
+	function getListHooksRegistered()
+	{
+		$hooks = array(
+			'LogsStats.NewVisitor' => 'detectHostname'
+		);
+		return $hooks;
+	}
+	
+	function detectHostname( $notification )
+	{
+		$object = $notification->getNotificationObject();
+		var_dump($object);printDebug();
+	}
+}
+/*
+class Piwik_Plugin_LogStats_UserSettings extends Piwik_Plugin
+{
+	
+}*/
+
+Piwik_PostEvent( 'LogsStats.NewVisitor' );
 
 /**
  * To maximise the performance of the logging module, we use different techniques.
@@ -55,7 +355,6 @@
 
 /**
  * Configuration options for the statsLogEngine module:
- * - record_logs ; defines if the logs are saved
  * - use_cookie  ; defines if we try to get/set a cookie to help recognize a unique visitor
  * - 
  */
@@ -92,12 +391,27 @@ class Piwik_LogStats_Action
 
 class Piwik_LogStats_Visit
 {
+	
+	function __construct()
+	{
+	}
+	
 	// test if the visitor is excluded because of
 	// - IP
 	// - cookie
 	// - configuration option?
-	public function isExcluded()
-	{}
+	private function isExcluded()
+	{
+		$excluded = 0;
+		
+		if($excluded)
+		{
+			printDebug("Visitor excluded.");
+			return true;
+		}
+		
+		return false;
+	}
 	
 	/**
 	 * Handles the visitor.
@@ -126,28 +440,62 @@ class Piwik_LogStats_Visit
 	 * 2) If the visitor doesn't have a cookie, we try to look for a similar visitor configuration.
 	 * 	  We search for a visitor with the same plugins/OS/Browser/Resolution for today for this website.
 	 */
-	 
+	private function recognizeTheVisitor()
+	{
+		
+	}
+	
+	private function isVisitorKnown()
+	{
+		
+	}
+	
 	/**
 	 * Once we have the visitor information, we have to define if the visit is a new or a known visit.
 	 * 
 	 * 1) When the last action was done more than 30min ago, 
 	 * 	  or if the visitor is new, then this is a new visit.
-	 *    
-	 * 	  In the case of a new visit, then the time spent 
-	 *    during the last action of the previous visit is unknown.
-	 * 
-	 *    In the case of a new visit but with a known visitor, 
-	 *    we can set the 'returning visitor' flag.
-	 * 
+	 *	
 	 * 2) If the last action is less than 30min ago, then the same visit is going on. 
-	 *    Because the visit goes on, we can get the time spent during the last action.
+	 *	Because the visit goes on, we can get the time spent during the last action.
+	 *
+	 * NB:
+	 *  - In the case of a new visit, then the time spent 
+	 *	during the last action of the previous visit is unknown.
+	 * 
+	 *	- In the case of a new visit but with a known visitor, 
+	 *	we can set the 'returning visitor' flag.
+	 *
 	 */
 	 
 	/**
 	 * In all the cases we set a cookie to the visitor with the new information.
 	 */
 	public function handle()
-	{}
+	{
+		if(!$this->isExcluded())
+		{
+			$this->recognizeTheVisitor();
+			
+			// known visitor
+			if($this->isVisitorKnown())
+			{
+				if($this->isLastActionInTheSameVisit())
+				{
+					$this->handleKnownVisit();
+				}
+				else
+				{
+					$this->handleNewVisit();
+				}
+			}
+			// new visitor
+			else
+			{
+				$this->handleNewVisit();
+			}
+		}
+	}
 	
 	/**
 	 * In the case of a known visit, we have to do the following actions:
@@ -157,7 +505,9 @@ class Piwik_LogStats_Visit
 	 * 2) Update the visit information
 	 */
 	private function handleKnownVisit()
-	{}
+	{
+		printDebug("Visit known.");
+	}
 	
 	/**
 	 * In the case of a new visit, we have to do the following actions:
@@ -167,41 +517,99 @@ class Piwik_LogStats_Visit
 	 * 2) Insert the visit information
 	 */
 	private function handleNewVisit()
-	{}
+	{
+		printDebug("New Visit.");
+	}
 }
 
 
 class Piwik_LogStats
-{
-	// load the configuration file
-	function loadConfigFile() 
-	{}
+{	
+	private $stateValid;
+	
+	const NOT_SPECIFIED   = 1;
+	const LOGGING_DISABLE = 10;
+	const NO_GET_VARIABLE = 11;
 	
 	// create the database object
 	function connectDatabase()
-	{}
+	{
+		$configDb = Piwik_LogStats_Config::getInstance()->database;
+		$db = new Piwik_LogStats_Db( 	$configDb['host'], 
+										$configDb['username'], 
+										$configDb['password'], 
+										$configDb['dbname']
+							);  
+		$db->connect();
+		$this->stateValid = self::NOT_SPECIFIED;
+	}
 	
-	// in case of any error during the logging, 
-	// log the errors in DB or file depending on the config file
-	function logMessage()
-	{}
-
-	// set some php configuration 
-	function init()
-	{}
+	private function initProcess()
+	{
+		
+		$saveStats = Piwik_LogStats_Config::getInstance()->LogStats['record_statistics'];
+		if($saveStats == 0)
+		{
+			$this->setState(self::LOGGING_DISABLE);
+		}
+		
+		if( count($_GET) == 0)
+		{
+			$this->setState(self::NO_GET_VARIABLE);			
+		}
+	}
+	
+	private function processVisit()
+	{
+		return $this->stateValid === true;
+	}
+	private function getState()
+	{
+		return $this->stateValid;
+	}
+	private function setState( $value )
+	{
+		$this->stateValid = $value;
+	}
 	
 	// main algorithm 
 	// => input : variables filtered
 	// => action : read cookie, read database, database logging, cookie writing
 	function main()
-	{}	
+	{
+		$this->initProcess();
+		
+		if( $this->processVisit() )
+		{
+			$visit = new Piwik_LogStats_Visit;
+			$visit->handle();
+		}
+		$this->endProcess();
+	}	
 	
 	// display the logo or pixel 1*1 GIF
 	// or a marketing page if no parameters in the url
 	// or redirect to a url (transmit the cookie as well)
 	// or load a URL (rss feed) (transmit the cookie as well)
-	public function endProcess()
-	{}
+	private function endProcess()
+	{
+		switch($this->getState())
+		{
+			case self::LOGGING_DISABLE:
+				printDebug("Logging disabled, display transparent logo");
+			break;
+			case self::NO_GET_VARIABLE:
+				printDebug("No get variables => piwik page");
+			break;
+			case self::NOT_SPECIFIED:
+			default:
+				printDebug("Unknown state => default behaviour");
+			break;
+		}
+		printDebug("End of the page.");
+	}
 }
 
+$process = new Piwik_LogStats;
+$process->main();
 ?>
diff --git a/tests/config_test.php b/tests/config_test.php
index 62e33b03f19ab8045bc859e780699771f9e03314..f36342e7ba992fc26b202fe5526c694abd7e9962 100755
--- a/tests/config_test.php
+++ b/tests/config_test.php
@@ -28,12 +28,13 @@ set_include_path(PATH_TEST_TO_ROOT
 					. PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/core/models'
 					. PATH_SEPARATOR . get_include_path());
 
+require_once "Zend/Exception.php";
+require_once "Zend/Loader.php";
+
 
 require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php";
 //set_error_handler('Piwik_ErrorHandler');
 
-require_once "Zend/Exception.php";
-require_once "Zend/Loader.php";
 
 Zend_Loader::loadClass('Zend_Registry');
 Zend_Loader::loadClass('Zend_Config_Ini');