[pytango] 01/04: Imported Upstream version 8.1.4

Frédéric-Emmanuel Picca picca at moszumanska.debian.org
Wed Oct 1 04:20:00 UTC 2014


This is an automated email from the git hooks/post-receive script.

picca pushed a commit to branch master
in repository pytango.

commit ea8f1e02a733d5ac453306c51b4ab3274403d2bb
Author: Picca Frédéric-Emmanuel <picca at debian.org>
Date:   Tue Sep 30 18:43:31 2014 +0200

    Imported Upstream version 8.1.4
---
 PKG-INFO                                           |    2 +-
 doc/_static/PowerSupplyDS.py                       |   80 +
 doc/_static/arrow03.png                            |  Bin 0 -> 5669 bytes
 doc/_static/banner1.png                            |  Bin 335135 -> 237697 bytes
 doc/_static/banner2.png                            |  Bin 89184 -> 73015 bytes
 doc/_static/banner3.png                            |  Bin 108825 -> 83303 bytes
 doc/_static/database.png                           |  Bin 0 -> 13726 bytes
 doc/_static/device.png                             |  Bin 0 -> 65909 bytes
 doc/_static/ipython_db.html                        |   25 +
 doc/_static/ipython_motor.html                     |   56 +
 doc/_static/ipython_serial.html                    |   56 +
 doc/_static/ipython_tango.html                     |  314 ++
 doc/_static/itango.ico                             |  Bin 0 -> 16446 bytes
 doc/_static/itango.png                             |  Bin 0 -> 15517 bytes
 doc/{itango => _static}/itango00.png               |  Bin
 doc/{itango => _static}/itango01.png               |  Bin
 doc/{itango => _static}/itango02.png               |  Bin
 doc/{itango => _static}/itango03.png               |  Bin
 doc/{itango => _static}/itango04.png               |  Bin
 doc/{itango => _static}/itango05.png               |  Bin
 doc/{itango => _static}/itango06.png               |  Bin
 doc/_static/jive_powersupply.png                   |  Bin 0 -> 14023 bytes
 doc/_static/jssor.css                              |   23 +
 doc/_static/jssor.js                               | 2806 ++++++++++++++
 doc/_static/jssor.slider.js                        | 4081 ++++++++++++++++++++
 doc/_static/motor.png                              |  Bin 0 -> 13299 bytes
 doc/_static/serial.png                             |  Bin 0 -> 6323 bytes
 doc/_static/starter.png                            |  Bin 0 -> 15763 bytes
 doc/_templates/index.html                          |  154 +-
 doc/_templates/indexsidebar.html                   |   14 +-
 doc/_templates/layout.html                         |    2 +
 doc/api.rst                                        |    8 +-
 doc/{client => client_api}/attribute_proxy.rst     |    0
 doc/{client => client_api}/device_proxy.rst        |    0
 doc/client_api/green.rst                           |   21 +
 doc/{client => client_api}/group.rst               |    0
 doc/{client => client_api}/index.rst               |    1 +
 doc/client_api/miscellaneous.rst                   |  144 +
 doc/{client => client_api}/other.rst               |    0
 doc/conf.py                                        |    4 +
 doc/contents.rst                                   |   11 +-
 doc/data_types.rst                                 |    2 +-
 doc/exception.rst                                  |   48 +-
 doc/faq.rst                                        |  132 +-
 doc/{client/miscellaneous.rst => green.rst}        |  176 +-
 doc/{server/index.rst => howto.rst}                | 1084 ++++--
 doc/{itango/highlights.rst => itango.rst}          |  102 +-
 doc/itango/features.rst                            |   40 -
 doc/itango/index.rst                               |   32 -
 doc/quicktour.rst                                  |  629 +--
 doc/quicktour_old.rst                              |  439 ---
 doc/revision.rst                                   |  529 +--
 doc/server/server.rst                              |  109 -
 doc/{server => server_api}/attribute.rst           |    0
 doc/{server => server_api}/device.rst              |    4 +-
 doc/{server => server_api}/device_class.rst        |    0
 doc/server_api/index.rst                           |   14 +
 doc/{server => server_api}/logging.rst             |    0
 doc/server_api/server.rst                          |  303 ++
 doc/{server => server_api}/util.rst                |    0
 doc/start.rst                                      |   25 +-
 doc/tep/tep-0001.rst                               |   45 +-
 doc/utilities.rst                                  |   23 +
 setup.py                                           |   70 +-
 src/boost/cpp/attr_conf_event_data.cpp             |   45 +-
 src/boost/cpp/attribute_proxy.cpp                  |   19 +-
 src/boost/cpp/base_types.cpp                       |   73 +-
 src/boost/cpp/callback.cpp                         |    5 +
 src/boost/cpp/callback.h                           |    6 +-
 src/boost/cpp/data_ready_event_data.cpp            |   39 +-
 src/boost/cpp/device_attribute.cpp                 |    2 +-
 src/boost/cpp/device_attribute.h                   |   10 +-
 src/boost/cpp/device_proxy.cpp                     |   12 +
 src/boost/cpp/event_data.cpp                       |   45 +-
 src/boost/cpp/fast_from_py.h                       |    3 +-
 src/boost/cpp/fast_from_py_numpy.hpp               |    1 -
 src/boost/cpp/server/attribute.cpp                 |  153 +-
 src/boost/cpp/server/attribute.h                   |    7 +
 src/boost/cpp/server/command.cpp                   |   19 +-
 src/boost/cpp/server/device_class.cpp              |   77 +-
 src/boost/cpp/server/device_impl.cpp               |   83 +-
 src/boost/cpp/server/device_impl.h                 |    2 +-
 src/boost/cpp/server/dserver.cpp                   |    7 -
 src/boost/cpp/server/tango_util.cpp                |   28 +-
 src/boost/cpp/server/wattribute.cpp                |  108 +-
 src/boost/cpp/to_py_numpy.hpp                      |    2 -
 src/boost/python/__init__.py                       |   68 +-
 src/boost/python/attr_data.py                      |    9 +-
 src/boost/python/client.py                         |  232 +-
 src/boost/python/codec.py                          |   22 +
 src/boost/python/connection.py                     |    8 +-
 src/boost/python/constants.py~                     |    0
 src/boost/python/databaseds/DataBaseds             |    5 +-
 src/boost/python/databaseds/create_db.sql          |    6 +
 src/boost/python/databaseds/database.py            |  750 +++-
 src/boost/python/databaseds/db_access.py           |  517 ---
 src/boost/python/databaseds/db_access/__init__.py  |   14 +
 src/boost/python/databaseds/db_access/sqlite3.py   | 1575 ++++++++
 src/boost/python/db.py                             |    2 +-
 src/boost/python/device_class.py                   |  744 ++--
 src/boost/python/device_proxy.py                   |   23 +-
 src/boost/python/device_server.py                  |  129 +-
 src/boost/python/group.py                          |    4 +-
 src/boost/python/ipython/__init__.py               |   10 +-
 src/boost/python/ipython/common.py                 |   17 +-
 .../python/ipython/ipython_00_10/ipy_install.py    |   19 +-
 .../python/ipython/ipython_00_11/ipy_install.py    |   15 +-
 .../python/ipython/ipython_00_11/ipython_00_11.py  |    2 +-
 .../python/ipython/ipython_10_00/ipy_install.py    |   17 +-
 .../python/ipython/ipython_10_00/ipython_10_00.py  |  127 +-
 src/boost/python/ipython/resource/serial.png       |  Bin 6365 -> 6323 bytes
 src/boost/python/pytango_init.py                   |    4 +-
 src/boost/python/release.py                        |    2 +-
 src/boost/python/server.py                         | 1700 +++++---
 src/boost/python/server.py.single_threaded.v1      | 1080 ++++++
 src/boost/python/server.py.single_threaded.v2      | 1096 ++++++
 src/boost/python/server.py.single_threaded.v3      | 1124 ++++++
 src/boost/python/server.py.single_threaded.v4      | 1102 ++++++
 src/boost/python/utils.py                          |  262 +-
 119 files changed, 19180 insertions(+), 3859 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index cbeb299..385f2eb 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: PyTango
-Version: 8.1.1
+Version: 8.1.4
 Summary: A python binding for the Tango control system
 Home-page: http://www.tinyurl.com/PyTango/
 Author: Tiago Coutinho
diff --git a/doc/_static/PowerSupplyDS.py b/doc/_static/PowerSupplyDS.py
new file mode 100644
index 0000000..e5b8e0d
--- /dev/null
+++ b/doc/_static/PowerSupplyDS.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""Demo power supply tango device server"""
+
+import time
+import numpy
+
+from PyTango import AttrQuality, AttrWriteType, DispLevel, DevState, DebugIt
+from PyTango.server import Device, DeviceMeta, attribute, command, run
+from PyTango.server import device_property
+
+
+class PowerSupply(Device):
+    __metaclass__ = DeviceMeta
+
+    voltage = attribute(label="Voltage", dtype=float,
+                        display_level=DispLevel.OPERATOR,
+                        access=AttrWriteType.READ,
+                        unit="V",format="8.4f",
+                        doc="the power supply voltage")
+
+    current = attribute(label="Current", dtype=float,
+                        display_level=DispLevel.EXPERT,
+                        access=AttrWriteType.READ_WRITE,
+                        unit="A",format="8.4f",
+                        min_value=0.0, max_value=8.5,
+                        min_alarm=0.1, max_alarm=8.4,
+                        min_warning=0.5, max_warning=8.0,
+                        fget="get_current",
+                        fset="set_current",
+                        doc="the power supply current")
+
+    noise = attribute(label="Noise",
+                      dtype=((int,),),
+                      max_dim_x=1024, max_dim_y=1024)
+
+    host = device_property(dtype=str)
+    port = device_property(dtype=int, default_value=9788)
+    
+    def init_device(self):
+        Device.init_device(self)
+        self.__current = 0.0
+        self.set_state(DevState.STANDBY)
+    
+    def read_voltage(self):
+        self.info_stream("read_voltage(%s, %d)", self.host, self.port)
+        return 9.99, time.time(), AttrQuality.ATTR_WARNING
+    
+    def get_current(self):
+        return self.__current
+
+    def set_current(self, current):
+        # should set the power supply current
+        self.__current = current
+
+    @DebugIt()
+    def read_noise(self):
+        return numpy.random.random_integers(1000, size=(100, 100))
+
+    @command
+    def TurnOn(self):
+        # turn on the actual power supply here
+        self.set_state(DevState.ON)
+
+    @command
+    def TurnOff(self):
+        # turn off the actual power supply here
+        self.set_state(DevState.OFF)
+
+    @command(dtype_in=float, doc_in="Ramp target current",
+             dtype_out=bool, doc_out="True if ramping went well, False otherwise")
+    def Ramp(self, target_current):
+        # should do the ramping
+        return True
+    
+    
+if __name__ == "__main__":
+    run([PowerSupply])
+
diff --git a/doc/_static/arrow03.png b/doc/_static/arrow03.png
new file mode 100644
index 0000000..1855c0b
Binary files /dev/null and b/doc/_static/arrow03.png differ
diff --git a/doc/_static/banner1.png b/doc/_static/banner1.png
index 75ae5d5..d05a270 100644
Binary files a/doc/_static/banner1.png and b/doc/_static/banner1.png differ
diff --git a/doc/_static/banner2.png b/doc/_static/banner2.png
index 4eaf882..fc69431 100644
Binary files a/doc/_static/banner2.png and b/doc/_static/banner2.png differ
diff --git a/doc/_static/banner3.png b/doc/_static/banner3.png
index 6299619..4981397 100644
Binary files a/doc/_static/banner3.png and b/doc/_static/banner3.png differ
diff --git a/doc/_static/database.png b/doc/_static/database.png
new file mode 100644
index 0000000..ad598bb
Binary files /dev/null and b/doc/_static/database.png differ
diff --git a/doc/_static/device.png b/doc/_static/device.png
new file mode 100644
index 0000000..1b52f7d
Binary files /dev/null and b/doc/_static/device.png differ
diff --git a/doc/_static/ipython_db.html b/doc/_static/ipython_db.html
new file mode 100644
index 0000000..1c9b793
--- /dev/null
+++ b/doc/_static/ipython_db.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> db</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#8b0000;">Result [</span><span style=" font-weight:600; color:#8b0000;">1</span><span style=" color:#8b0000;">]:</span> </p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<table border="0" style=" margin:0px;" width="100%" cellspacing="2" cellpadding="2">
+<tr>
+<td width="140" rowspan="2" style=" vertical-align:middle;">
+<p align="center" style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="database.png" height="128" style="vertical-align: middle;" /></p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">bcu01ctrl.esrf.fr:10000</span></p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">TANGO Database sys/database/2<br /><br />Running since 2013-11-12 17:08:54<br /><br />Devices defined = 32<br />Devices exported = 4<br />Device servers defined = 10<br />Device servers exported = 2<br /><br />Device properties defined = 17 [History lgth = 30]<br />Class properties defined = 63 [History lgth = 56]<br />Device attribute properties defined = 4 [History lgth = 7]<br />Class attribute properties defined = 0 [Histor [...]
+<br/>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/ipython_motor.html b/doc/_static/ipython_motor.html
new file mode 100644
index 0000000..5c60d85
--- /dev/null
+++ b/doc/_static/ipython_motor.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> energy = Motor("energy")</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> energy</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#8b0000;">Result [</span><span style=" font-weight:600; color:#8b0000;">2</span><span style=" color:#8b0000;">]:</span> </p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<table border="0" style=" margin:0px;" width="100%" cellspacing="2" cellpadding="2">
+<tr>
+<td width="140" rowspan="7" style=" vertical-align:middle;">
+<p align="center" style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="motor.png" height="128" style="vertical-align: middle;" /></p></td>
+<td width="140">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Name:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">motor/icepapctrl/34</span></p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Alias:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">energy</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Database:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">bcu01ctrl.esrf.fr:10000</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Type:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Motor</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Sardana/energy</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server host:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">bcu01ctrl.esrf.fr</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Documentation:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.sardana-controls.org"><span style=" text-decoration: underline; color:#0000ff;">www.sardana-controls.org</span></a></p></td></tr></table>
+<br/>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">3</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/ipython_serial.html b/doc/_static/ipython_serial.html
new file mode 100644
index 0000000..c672f42
--- /dev/null
+++ b/doc/_static/ipython_serial.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> serial_line = Serial("pc01/tty/1")</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> serial_line</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#8b0000;">Result [</span><span style=" font-weight:600; color:#8b0000;">2</span><span style=" color:#8b0000;">]:</span> </p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<table border="0" style=" margin:0px;" width="100%" cellspacing="2" cellpadding="2">
+<tr>
+<td width="140" rowspan="7" style=" vertical-align:middle;">
+<p align="center" style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="serial.png" height="128" style="vertical-align: middle;" /></p></td>
+<td width="140">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Name:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">pc01/tty/1</span></p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Alias:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">-----</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Database:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">bcu01ctrl.esrf.fr:10000</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Type:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Serial</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Serial/pc01</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server host:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">pc01.esrf.fr</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Documentation:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.esrf.fr/computing/cs/tango/tango_doc/ds_doc/tango-ds/Communication/SerialLine/index.html"><span style=" text-decoration: underline; color:#0000ff;">www.tango-controls.org/devices/Serial</span></a></p></td></tr></table>
+<br/>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">3</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/ipython_tango.html b/doc/_static/ipython_tango.html
new file mode 100644
index 0000000..f9d825a
--- /dev/null
+++ b/doc/_static/ipython_tango.html
@@ -0,0 +1,314 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> tango_test = TangoTest("sys/tg_test/1")</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> wave, img = tango_test.wave, tango_test.short_image_ro</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">3</span><span style=" color:#000080;">]:</span> fig, (axis1, axis2) = plt.subplots(1, 2, figsize=(9, 3));</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">       ...:</span> axis2.imshow(img); axis2.set_title("Beam");</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">       ...:</span> axis1.plot(wave); axis1.set_title("Sine");</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">       ...:</span> </p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAAgkAAADSCAYAAADABFM9AAAABHNCSVQICAgI
+fAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XtcVWXa//HP
+2ggiAoIcFVBMKxVByUNaajSlBjadtNFSnx7tYE6NnWuapvEwpTU9zkyT1Thl
+zfPE1Oh0mPqFUU1FTc2kpeZpOpoUG0NOAiLKcf3+uBFPgCKw1wa+79drvWBv
+1l7rWlvX2te+131ft2Xbto2IiIjIMVxOByAiIiLeSUmCiIiINEpJgoiIiDRK
+SYKIiIg0SkmCiIiINEpJgoiIiDRKSYKcsr/85S9MmTLF6TBERKSdKEmQE3r3
+3XcZNWoUPXv2JCQkhLFjx/Lpp58ya9Ys3nzzTafDExEvEh8fT0BAAEFBQQQG
+BnLhhReya9cup8OSU6QkQZpVXFzMpZdeys9//nPKy8spKCjgoYcewt/f3+nQ
+RMQLWZbF66+/zr59+ygqKqJ///789Kc/dTosOUVKEqRZX3zxBX5+fkyfPh3L
+svD19SUlJYVhw4bx5z//mQkTJjSs63K5WLVqFWeeeSaBgYFcd911HFnQ87HH
+HiM+Pp7g4GDOO+88du7c6cQhiYiHdO/enWnTpvHNN98AcODAARYsWEBkZCSh
+oaFcc801HDhwAICSkhKmTJlCeHg4QUFBXHjhhXz33XcN20pJSeH+++9n/Pjx
+BAUFcckll1BYWMisWbPo1asXiYmJfPvtt44cZ2emJEGaNXToUGpra5k3bx5v
+vvkmRUVFza6fmZnJ5s2b+fzzz3nttdd4/fXXAXj++ed57LHHeO+99ygrKyM1
+NZXp06d74hBExMMOfTmoqKhgzZo1nHvuuQDccsst5Ofns3PnTnbv3k1ZWRn3
+3ntvw2sWLlzInj17yM/Pp0+fPsyfP/+o7a5du5a//vWv5Obmkp2dzbhx41iw
+YAF79+5l1KhR3H///Z490K7AFjmBbdu22bNnz7ZjYmJsl8tlp6am2j/88IP9
+7LPP2uPHj29Yz7Is+6OPPmp4/JOf/MR+4IEHbNu27ZSUFHv16tUNf6utrbUD
+AgLsr776ynMHIiLtrn///nZgYKAdEhJi+/n52TExMfa2bdvsyspK29/f3965
+c2fDuv/617/sPn36NLqdbdu22T169Gh4nJKSYi9btqzh8V133WWnpaU1PM7I
+yLATEhLa4Yi6NrUkyAkNGzaM5557DrfbzZdffklhYSE33XQTlmUdt250dHTD
+7wEBAVRWVgLgdru55ZZbCA0NJTQ0lLCwMAAKCgo8cxAi4hGWZfHqq6+yd+9e
+KisreeKJJ5g4cSKFhYVUVlYycuTIhutAamoqZWVlAJSWlvLf//3fxMTEEBIS
+wrnnnktlZeVRtyyjoqIafvfz8yMyMvKox4euN9J2lCRIiwwaNIi5c+eyY8eO
+Fr2uT58+PPvss+zdu7dh2b9/P+ecc047RSoi3uCSSy6hW7dufPDBB/j6+vL1
+1183XANKSkooLy8H4JFHHiE3N5ctW7ZQUlLCRx99hG3bRyUJR2rsS4q0PSUJ
+0qyvvvqKxx9/nPz8fABycnJ44YUXGD169Alfe+QJfsMNN7Bs2bKGDkzl5eX8
+/e9/b7/ARcQxR36wv/766xQVFTF48GDmzJnDHXfcQUlJCQB5eXm88847gOm/
+4OvrS1BQEGVlZfz6179udrtNJQ/StpQkSLMCAwN55513SEpKomfPnowcOZKB
+Awfy2GOPAUdn88dm9pZlNTw3e/ZsbrjhBlJTUwkODubMM89UkiDSSf34xz9u
+qJNw55138tRTTzFixAhWrlxJaGgoQ4YMaRjltH37dgBuu+02SktLCQ0NZezY
+sVxwwQWNXlOO/L25v0vbsOxWpmPz5s0jIyODyMhItm3b1ug6Cxcu5J133qF7
+9+6sXr2a5OTk1uxSREREPKDVLQlz584lMzOzyb+/9NJLfP/99+zYsYPVq1cz
+d+7c1u5SRMSjMjMzSUxMZOjQoTz88MNOhyPiMa1OEiZMmEBoaGiTf1+3bh1z
+5swBIDk5mZqaGtxud2t3KyLiEZWVlSxYsIDMzEy2bt3Kiy++yObNm50OS8Qj
+2r1PgtvtJi4uruFxbGyskgQR6TDWr19PQkICMTExdOvWjRkzZpCRkeF0WCIe
+0c0TOzm220NjnUuCggZRXq4yvSLeYODAgQ0jUbq6xr7oZGVlHbWOZUUDezwb
+mMhJicK280751e2eJMTGxpKTk8PZZ58NmBMuNjb2uPXKy3eyYYPNoEEQEgKH
+8gjbhtJS+O47+Owz+PhjePttqKmBadPg+uth8OD2i3/x4sUsXry4/XZwihRX
+yyiullEv8cNO7r3YA5x3xOP4+sXTsoAUB/arGBqXhefjyK5fDnm/VVtr9yQh
+LS2N9PR0pk+fzqZNm/Dx8SEmJqbRdRsbem9ZJmkICYHhw+Gaa0zisGMH/PWv
+cP75MHQo3HsvXHDB4eRCRKQtHPqic0hOTs5RLQuHpXgsJpGmxXN0gtq6JKHV
+fRKuuuoqzjnnHL788kvi4uJ45plnWLVqFatWrQJg2rRpxMTEkJCQwHXXXcez
+zz7b2l1iWTBsGDzwgGlhmDsXbroJUlJMS4OISFsZPXo027dvJzc3l+rqatau
+XUtqaqrTYYl4RKtbEl544YUTrrNy5crW7qZJfn4wezbMnAnPPQdXXAFTp8JD
+D0H99ACtkpKS0vqNtAPF1TKKS06Vv78/Tz75JFOmTKGuro45c+Zw1llnOR1W
+E+KdDgDFcKR4pwNotVYXU2orlmW1SZnN0lL45S/hxRfhqafg4ovbIDiRLqat
+zseuwvRbWOR0GCKNWNKqc7nTlWXu1QseewzWrIGbb4af/hQOHnQ6KhERkY6n
+0yUJh0ycCFu2QGEhnHce7N7tdEQiIiIdS6dNEsC0KqxZA5ddBmPGwL//7XRE
+IiIiHUenThLAjIS4915YtQouvRReesnpiERERDoGj1Rc9AZTp8Kbb5qfpaUw
+b57TEYmIiHi3LpMkACQnQ1YWTJ4Me/fCHXc4HZGIiIj36lJJAsAZZ8A//wkX
+Xgi1tXD33U5HJCIi4p26XJIAEBcH775rRkAEBJihkiIiInK0LpkkAMTEwD/+
+YYZHBgSoj4KIiMixumySADBggEkUUlKgd28zVFJERESMLp0kgOmj8NprkJoK
+ffuaegoiIiLSBeoknIxRo+CZZ0xLwq5dTkcjIiLiHbp8S8IhP/4xZGebOgr/
++heEhDgdkYiIiLPUknCEn/0MLrgAZs2CujqnoxEREXGWkoRj/Pa3sG8fLFni
+dCQiIiLOUpJwDF9f+NvfTB+FV191OhoRERHnKEloRFQUvPgiXH89fPWV09GI
+iIg4Q0lCE84+GxYvhpkzobLS6WhEREQ8T0lCMxYsgPh4uOcepyMRERHxPCUJ
+zbAsePppeOUVeP11p6MRERHxLCUJJ9C7N6Snw3XXQW6u09GIiIh4jpKEkzBh
+Avz0pzB7tpleWkREpCtQknCS7rsPamrg0UedjkRERMQzlCScJB8f+POfYdky
++OILp6MRERFpf0oSWmDgQFOJ8ZprTKuCiHQu8fHxJCUlkZyczJj6KWGLi4uZ
+NGkSSUlJTJkyhZKSEoejFPEcJQkttGABBAbC//yP05GISFuzLIusrCw2b97M
+hg0bAFi0aBFTp05l69atpKamsmjRIoejFPEcJQkt5HKZks0rVsD27U5HIyJt
+zbbtox6vW7eOOXPmADB79mwyMjKcCEvEEUoSTkH//vDQQ7rtINLZWJbVcGth
+5cqVABQUFBAWFgZAeHg4+fn5ToYo4lHdnA6go5o3D154wYx2uOMOp6MRkbbw
+8ccfExkZSUFBARdddBGDBw9uwauzjvg9vn4R8bTs+qVtKEk4RZYFf/wjjB0L
+V1wBAwY4HZGItFZkZCQAERERTJ8+nU8++YSIiAgKCwsJDw+noKCgYZ3jpXgs
+TpGmxXN0gvp+q7am2w2tMGgQ3HmnKbR0zG1MEelgKioqqKioAGD//v1kZmaS
+kJBAWloa6enpAKSnp5OWluZkmCIeZdnH9tJxiGVZx3UY6giqq2HkSPjFL8yM
+kSKdQUc9H1tj165dXHbZZViWRUVFBTNnzmTp0qUUFxczY8YM9uzZQ3R0NGvX
+riUkJOSo11qWBWjUg3ijJa06l1udJGRmZnLXXXdRW1vLNddcwz3HTJmYlZXF
+pZdeymmnnQbAtGnT+OUvf3l8IB34orR+PVx2GezYYeZ6EOnoOvL56AQlCeK9
+WpcktKpPQmVlJQsWLODDDz8kKiqKcePGMXnyZJKTk49a77zzzuO1115rza68
+2tlnw/TpZkrpp55yOhoREZG20ao+CevXrychIYGYmBi6devGjBkzGh1D3BW+
+kTz4IGRkmFYFERGRzqBVSYLb7SYuLq7hcWxsLG63+6h1LMvi3//+N4mJiVxw
+wQVs2bKlNbv0WsHB8PDDcNNNmilSREQ6h1bdbjD34Zo3cuRI3G43/v7+vPXW
+W1x22WXs2rWr0XUXL17c8HtKSgopKSmtCc/jZs+GVatg9Wq44QanoxE5eVlZ
+WWRlZTkdhoh4mVYlCbGxseTk5DQ8zsnJOaplASAwMLDh98mTJ+Pn50deXh7R
+0dHHbe/IJKEjsixYuRKmTIFp06C+SJuI1zs2KV+yZIlzwYiI12jV7YbRo0ez
+fft2cnNzqa6uZu3ataSmph61TmFhYcPvGzduZP/+/c0UI+n4RoyAK6+ERgZw
+iIiIdCitaknw9/fnySefZMqUKdTV1TFnzhzOOussVq1aBcD8+fN54YUX+NOf
+/gSAn58fzz//PC5X567h9Otfw5AhcP31cNZZTkcjIiJyalRMqZ2sXg1PPw0f
+fWRmjhTpSDrb+djeVCdBvFfr6iTo46udzJ0LdXVQX81VRESkw1GS0E5cLvj9
+7+G++2D/fqejERERaTklCe1o3Dg491xYscLpSERERFpOSUI7e+ghePRRyM11
+OhIREZGWUZLQzuLjTWElDYkUEZGORkmCB9x7L2RmwqZNTkciIiJy8pQkeEBw
+MCxZArffDhpVJiIiHYWSBA+ZNw+KiuDVV52ORERE5OQoSfCQbt3gt7+Fu+6C
+qiqnoxERETkxJQkeNGkSnHEGPP6405GIiIicmJIED3vkEVi+HEpKnI5ERESk
+eUoSPGzoUPjxj+E3v3E6EhERkeYpSXDA4sWwapUKLImIiHdTkuCAuDi47joz
+LFJERMRbKUlwyM9/Dn//O3zxhdORiIiINE5JgkNCQ+Huu+EXv3A6EhERkcYp
+SXDQzTfDp5/Cv//tdCQiXce8efOIiooiMTGx4bni4mImTZpEUlISU6ZMoeSI
+4UfLly9n6NChJCYm8tZbbzkRsohjlCQ4yN8fli41LQoq1yziGXPnziUzM/Oo
+5xYtWsTUqVPZunUrqampLFq0CICNGzfy8ssvs23bNjIzM5k/fz5VqoYmXYiS
+BIfNmQN798LrrzsdiUjXMGHCBEJDQ496bt26dcyZMweA2bNnk5GRAUBGRgYz
+Z87Ex8eHmJgYEhIS2LBhg8djFnGKkgSH+fjAQw+Zjoy1tU5HI9I1FRQUEBYW
+BkB4eDj5+fkA5ObmEhsb27BebGwsbrfbkRhFnKAkwQtMnQphYfC//+t0JCJy
+6rKOWLIdjEO6tmyO/r/YOt1avQVpNcuChx+GGTPg6qtNXwUR8ZyIiAgKCwsJ
+Dw+noKCAyMhIwLQc5OTkNKzndruJi4trYisp7R+oyAnF1y+HvN+qraklwUuM
+GwcjRphKjCLiWWlpaaSnpwOQnp5OWlpaw/Nr1qyhpqYGt9vN9u3bGTNmjJOh
+iniUZdve0a/esiy8JBTHbN0KkyfDN99AYKDT0UhX1pnPx6uuuor333+fwsJC
+oqKiWLp0KZdeeikzZsxgz549REdHs3btWkJCQgBYtmwZ6enpuFwuVqxYwZQp
+U47bpmVZwCIPH0lH4Q+EnnAtqASK2zmWrmhJq85lJQle5uqrISEB7rvP6Uik
+K9P52DJKEo7kwtzJDgSi63/2OYnXVQC5wIH6nzZQ3U4xdiVKEjqVr782tx6+
++gp693Y6GumqdD62jJKEQ8IxrQZRQBAQDZY/uE6iJcGuhLpiTJLgBg7W/8xr
+t2i7htYlCeq46GVOPx2uuAIeeQSWL3c6GhGRk+GLSQoigNOBOPAJAL+eEABE
+nsQmDvjBniCoq4GqaLC/xbQm9MAkC2pVcIJaErxQTo7pxLhjB0RHOx2NdEU6
+H1uma7ck+AN9gTOAaPDpA739oLtl7jJ0x/z0OcFm9mAaEQ79LKiEuiKwS4Ft
+mBYF9VloObUkdDpxcXDNNfDgg/DYY05HIyLSlG7AMEwWcBp07wX9XBCDyR1i
+MI0McZhkoTnf1P/MBmqB77tDXl8o7YFpUYgGPgXK2vwopGlqSfBSBQUweDBs
+3Ajx8U5HI12NzseW6ZotCf5AEhADrkHQoycMBkIwXRMGArGYJOFQstCc7Pqf
+u4DdQAnwAyZ5KKmC2mzMbYdtwN42PZLOTS0JnVJEBNx0EyxZAs8+63Q0IiJH
+8sG0IESCzyCI6wm9McnBMEy/xQGYL//+Nt36VIBfXbNbrAkJhDrLJBl9gCJM
+A4I/sNsPsk+D6hrMx5ZaFDxFSYIXu+MO05Hx889hyBCnoxERgcN9EOJMC0J0
+T5McnIlpMRhU/7NPHVZgDb1j8on0z8fX1XzHw9yeMZQVhlIT5Icd5mtaECwO
+tzDUdIMfBsFBH0wG8j1qUWh/ShK8WK9ecOed8Ktfwd/+5nQ0IiJgkoTTwTUQ
+uvU0rQbDgDBgOBACrqQD9AzeR1hwEX3ZDdDwszGFhBMYUE5RvzDKqoPZ0zuK
+um49TauCxeFPqmI/qB4ItTWY+xElmOYGaS+tThIyMzO56667qK2t5ZprruGe
+e+45bp2FCxfyzjvv0L17d1avXk1ycnJrd9tl3HwzDBpk+iaMHOl0NCLStfXG
+fIs/Dfy7m9aDfpiRjyPMn7rH7aNnaBn9e35HOIX0pogeHCCWpmfPLCCSCgII
+oYRC33DsXhYVw3pS1jMc/F0mDzgIVAFfuKAkBuyB9U/80N4H3aW1KkmorKxk
+wYIFfPjhh0RFRTFu3DgmT558VBLw0ksv8f3337Njxw42b97M3Llz+eyzz1od
+eFcREAC//KVZ3njD6WhEpGsLAQZBtwjo6zIPT+NwC0K/CqJjcoliD3HkEEk+
+QZQRTiF9m/kwD6eIMoIIIYIAKgjwryDbP54afKjYH2aSERsoBQpdUBsCZYMw
+QyLzUGtC+2lVkrB+/XoSEhKIiYkBYMaMGWRkZByVJKxbt445c+YAkJyc3DBR
+ypFztEvzrrvOFFf64AOYONHpaESka+oFDAKrFwRbpkBSf8wIhhCwhhykT/Ru
+IsnnDL4iiDL68T3R7CGUvYSylx4cOG6rtfjQi1IqCCCACrpTSSDlALhC68g+
+rQd1tj8Uuczkhvsw0zyU94S6/pgejjnHbVfaRquShGOnTY2NjSUrK+uE6yhJ
+aBk/PzPK4Re/gH/+00wtLSLiCKsX9KpPEgZhaiiFQ8/e++jrs5t+fE8g+xjC
+F4Syl77sJoICguvKCK8uPG5zB13+hPiWsJu+AIRQwudY7CMIgIrIAPYU9cMe
+BtRg+ioWAbk9OfG4SmmtViUJ1kl+Wh07RrOp1y1evLjh95SUFFJSUk41tE5n
+1ix46CFzy6F+FluRNpOVlXVcgi9ytEFm6dXTTM8QgmlcCABXbAWxvdz0opQI
+8unPd0RQQD++J8reQ9+9+fjuq6Vb4fG3BXr4VxAU+T0BYRWEuErYxQDi6vsv
+VNKdGN9cyuKCqdgXZvYZWr/fmG6Q0x/sYkzmUO6h96FraVWSEBsbS07O4Wae
+nJyco1oNjlzn7LPPBmi2FeHIJEGO5uMDDzxgZoe86CJwuZyOSDqTY5PyJUuW
+OBeMeL9emD6MoWBF1hAd9QN92U0vSgmmjCjy6cf39KaYmNI8/Ivq4Gug4PhN
+ufzBb5hN38pCavv64HLVUYsPZQQRXF8LoSwkmJ2xPakr8jejKEKB7zx3uF1Z
+qz5qRo8ezfbt28nNzaW6upq1a9eSmpp61DppaWn85S9/AWDTpk34+Pg09GGQ
+lrn8cpMsaDikiHjWAOB06BZu+gWE0NCS4Bt0kCDXPgAiyCcWNzHkEmTvo19x
+Lv7f1cEXmCThP8Bnxyx5wHbo9r1N7J49BNeVEcUeYthNX3KxsAmwKggILIde
+NgSb/RIPBAXR0MIh7aJVLQn+/v48+eSTTJkyhbq6OubMmcNZZ53FqlWrAJg/
+fz7Tpk3jvffeIyEhge7du/OsygeeMsuCZcvgZz+DadOgm6pciIgTQmhoSege
+UkEvVynR/EAMufQhj27U0Msuxb+sBnKB7Zg7AjswoxaPVIUpd2CBby+bkPAS
+fnD1JZo8SgmmlBD2EURQcBn7Q0Oxe/uYfaslwSNa/TGTmpp6XOvB/Pnzj3q8
+cuXK1u5G6k2aBH37wp//bEY9iIh4Axc2LuqwsIkkn6jSQlw5mCShFPgSKIaD
+uw6/xi8aXD6YgkkRwPfQK/ggfWNy2ecKrN9e8+WcpX3pu2gHY1mwfDlceSXM
+ng3+/k5HJCLSjCpMOYNCKNsBX+07/KeYYjNNA73RVAxeSt3fOqCxY031xSee
+cDoSEZGTZINda3KBjZjKBnW1oIYC76YkoYN64AEzJLJM2beIdDBqAO04lCR0
+UMOGmaGQK1Y4HYmIyEnwAd9AiLZgtAVnWuAfgDIGL6c+CR3YkiUwapSZBCoi
+wuloREQa0QMzs3QxBLjgzIDDf3JFYfoj9MVMNy1eRy0JHdiAAXDVVWZYpIiI
+kw7iTwUBHMSfPUSxp1c4dXEcrqtwOhAOPsMOL1YEZvKmMBrmgiiN9me3K4YD
+9KjfXg/q9FHlGLUkdHC//CUkJMBtt0G/fk5HIyJdQh7gBiKhIqIX+T0jCfQp
+p4gwigklggLyXZGERpYSVFoFQ4EAoDvH10mIB6KBcKiMtsjrFk0VvuwllGJ6
+s5dQCgmnqCACe7fLDKlsetZpaWNKEjq46GiYP9/celi92uloRKRz2gX0hpre
+kB1pWgaqzFJ3oBv7KoOoDOhOPlEEU4YfVfhSjTugLzEDfyAosBLLB1Mp8Vj+
+wCA4GO5DbmgU+6wg3MSSS192E8NB/CmvDqS6vDtUWVCNWbKBffuAb+oXaQ9q
+w+kE7r4bXnsNvvjC6UhEvN+8efOIiooiMTGx4bnFixcTGxtLcnIyycnJvPHG
+Gw1/W758OUOHDiUxMZG33nrLiZC9zx7MN/ofwM7zYe/e3hQRRiHhFBBBEWH8
+QB/yiCYvMJLyaD+qhlkwjOOWumFwIM6FOyKaAiuCXQygmN7kE0kBERTTm8Ly
+cOw9fvADphVht3OH3tWoJaETCAmBO++E++/XvA4iJzJ37lx+9rOf8V//9V8N
+z1mWxe23387tt99+1LobN27k5ZdfZtu2beTl5TF+/Hi+/PJL/Pz8PB22F/gG
+CIPSnhDSEyowpZb3QV1OAN8H9MMvpIoiK5zuVOJLNQfoQRV+7OsZRFCPfYT1
+Ljpuqwctfwq7meTiB/qwj0C+ox+5xFJOILsqBnAgJ8TsrwRTvbEKyK0B+zvM
+pBCaAbK9KEnoJH72Mzj9dPj0UzPiQUQaN2HCBLKzs497/tgp7QEyMjKYOXNm
+w8R0CQkJbNiwgfHjx3sgUi9VtwfyB0CedXhGRgsO9A6iKDgMfKA7lVTRnYF8
+Qw3dKKUXka588l2Rx23OxqKQcMoJZA9RfEc/igg3rRBEU1LUGzvPZSaH2o1p
+xfgBqNsLHPTkkXdJShI6iYAA04nxF78AtYiKtNzjjz/O008/zciRI/nDH/5A
+7969yc3N5Uc/+lHDOrGxsbjdXbXXXCnmW7s/HKyC/O7QEzPEMRTsr/3Y3S0O
+u5+Fq1sdceQAkE8ZYRRRTmCTWy4gnDKCKSScH+hLIeG47Vjy90RRt6uHSQz2
+ADsxpRr314JdhpnlKad9D7uLU5LQiVx7LfzP/8B778H55zsdjUjHcdNNN/Gr
+X/0KMP0TFi5cSHp6egu3knXE7/H1S2eTB/SG2hj4IRgCephOjJ8BI8AO7U5+
+XV9KwkKoCe1GGcFEkk8BkZQR3ORWC4ngIP4UEcZeQtm5byAH9vekMifY7HIH
+phUhD8irhYoyTKeI3PY/5A4nu35pG0oSOhE/P1i6FO69F/79bzMZlIicWHj4
+4Uo+8+fP5/z6LDs2NpacnMPfVN1uN3FxcU1sJaUdI/QWBzCtCT2hegR83cN8
+ivQAtpo1agp7UJvgYmfNIIpCwij3DcSXavYR1ORWS+nFfnpSWBNOeWUQpXtD
+qfuqh0kKPsf8/Br4CtOKwc76J4rb8Vg7qniOTlDfb9XWlCR0MlddBQ8/bEY7
+XHqp09GIdAz5+flERpr75S+99BIJCQkApKWlceONN3LrrbeSl5fH9u3bGTNm
+jJOheoEKIAfsXlBrwZ5QsDG1EGzgdLBrunOgnx8H84IpiwsmKmgPe31Cm92q
+uySWg/lB1Fb4Qp7L5AC7Mf0PPsfcbjh4AOq+NvunsD0PUuopSehkXC548EHT
+mnDxxeDj43REIt7lqquu4v3336ewsJC4uDiWLFnCe++9x9atW6mqqqJ///6s
+ri86MnLkSC6//HKSkpJwuVysWrUKX19fh4/AaTWYugk9wa6GgjPBNxS2A0WY
+WR33Avst7Bgf9peEs6tvEJZf89M91mX3ML9kY1oO9gLbMLlAHpB7EGq/AvIx
+TQoH2uHY5FiW3ViXXgdYltVo72JpOduG8eNNkaUjRnmJnDSdjy1jWRawyOkw
+PMwXU1N5AFinQfdQOA3TRyHCPE0c4IeZm+FEo0az63/uxLQelGCSg51AxaEW
+hFzMfQ0lCCdvSavOZbUkdEKWBb/5DVx9NVx5JfTo4XREItL5VGOaDwC7Ag6e
+AV/2htj6lpYSzJd+Xw4nC83ZWf8z+4ifhXWwvxgziqEYJQiepyShkzr3XBg5
+Ev7wB7jnHqejEZHO60tgP1ADtVHwfT8oDoAeLnPLwB/z+X7w0aOvAAAS+UlE
+QVSiW5/FQBnmNaVAaTnY+fVPfA18jxIEz9Pthk7sq6/gnHPg8881lbS0jM7H
+lumatxuO1RMzU9NAoB9YgeATbEY+hJ3EDAAHbSi0wa6C2lJMvwc3hzslyKlp
+3e0GJQmd3M03m86Mf/iD05FIR6LzsWWUJBwSiJnFKQoIAqLBCgAr7MQvtQ+C
+XYBpLXBjqim6Mc0LcuqUJEgzCgpgyBBTN+H0052ORjoKnY8toyShMT0xPRjr
+k4UTqsB0TDyIWg7akjouSjMiIuD2282QyBdfdDoaEek69tcvvpj+BCdSU7++
+eBMlCV3ArbfC4MHwr3+ZPgoiIp5TjemJKB3RSfQmkY4uIAB+/WsznbRakEVE
+5GQpSegiZs+Gigp4+WWnI5GmlJQ4HYGIyNGUJHQRPj7wyCOmZkJVldPRyLGq
+q2HcOFi/3ulIREQOU5LQhUyaBIMGwZNPOh2JHOuPf4S4OOjycweJiFfREMgu
+ZscOOP98+M9/4IjZccVBxcWmY+m778KwYU5HY+h8bBkNgRTv1bohkGpJ6GIS
+EmDGDPjVr5yORA5ZuhSuuMJ7EgQRkUPUktAFFRebAktvvw1JSU5H07V98YWZ
+sfM//4HISKejOUznY8uoJUG8l1oSpIV694ZFi+CWWzQk0km2DQsXwn33eVeC
+ICJyiJKELuqGG6CoSEMinfT3v0NurplfQ0TEG51yxcXi4mJmzJjBnj176NOn
+D2vWrCEkJOS49eLj4wkODsbHxwdfX182bNjQqoClbXTrBo8+CvPmQVoa9Ojh
+dERdy4EDplz200+Dr6/T0YiINO6UWxIWLVrE1KlT2bp1K6mpqSxa1Pj9OMuy
+yMrKYvPmzUoQvMz558PIkbBihdORdD2/+Q2MGgUXXOB0JCIiTTvljosDBw5k
+w4YNhIWFUVhYyNixY/nmm2+OW2/AgAF8+umnhIU1P1WoOko5Y9cu82H12Wdm
+nL60v+xsk5xt3gz9+jkdTeN0PraMOi6K93Ko42JBQUHDB394eDj5+fmNrmdZ
+FpMmTSIpKYmVK1ee6u6knQwYYDrP3XKL05F0HXfcYSbd8tYEQUTkkGb7JEya
+NIm8vOPn9X7wwQdPegcff/wxkZGRFBQUcNFFFzF48GAuvPDCRtddvHhxw+8p
+KSmkpKSc9H7k1N1zjxkK+frrcPHFTkfTuf3jH6YF4S9/cTqSo2VlZZGVleV0
+GCLiZVp1u2H9+vWEh4dTUFDAuHHjGr3dcKTly5cDcO+99x4fiJo3HfWPf8D1
+15uKjAEBTkfTOR08CMOHmzk0LrnE6Wiap/OxZXS7QbyXQ7cb0tLSSE9PByA9
+PZ20tLTj1qmoqKCiogKA/fv3k5mZSUJCwqnuUtrRhRfC2LHwwANOR9J5PfQQ
+DB3q/QmCiMghp9yScOQQyOjoaNauXUtISAi7d+/m+uuvJyMjg2+//ZbLL78c
+y7KoqKhg5syZLF26tPFA9M3FcT/8YG47vP+++TCTtvPll3DuueZWQ0foIKrz
+sWXUkiDeq3UtCSrLLEdZuRJefBHeew8sy+loOgfbhh/9CC67rON0EO2s52NO
+Tg6zZs1i7969VFVVce2113L33Xc3W/dl+fLlPPfcc/j4+LBixQomT5583HaV
+JIj3UllmaUMLFkB5Ofzf/zkdSefxf/8H+/apsqI38PPz44knnmDbtm1s3LiR
+p59+mi1btjRZ92Xjxo28/PLLbNu2jczMTObPn09VVZXDRyHiOUoS5Cg+PrBq
+Fdx9N+zZ43Q0HV9hoXkvV60y7604KyoqimH1020GBgaSlJREbm4u69atY86c
+OQDMnj2bjIwMADIyMpg5cyY+Pj7ExMSQkJCgonDSpShJkOOMHGnKNeubb+vd
+eitcfbV5T8W7ZGdn88knnzB+/Pgm677k5uYSGxvb8JrY2Fjcbrcj8Yo44ZTn
+bpDObdEiGDHC9E+YPt3paDqmV1+F9ethyxanI5FjlZeXM336dB599FGCg4Pb
+aKtZR/weX7+IeFp2/dI2lCRIo/z94ZlnTIJw/vlwgqracoziYtO/Y80a1Z3w
+NtXV1UybNo1Zs2Zx2WWXARAREUFhYWFD3ZfI+rm7Y2NjycnJaXit2+0mrsnh
+KSntHLnIyYjn6AT1/VZtTbcbpEnnnAMzZpgmc2mZhQvhJz+BCROcjkSOZNs2
+1157LUOHDuW2225reL6pui9paWmsWbOGmpoa3G4327dvZ8yYMY7ELuIEDYGU
+Zu3fb2on/P738OMfOx1Nx/Dqq2Z+hi1boGdPp6M5NZ31fPzwww+ZOHEiSUlJ
+9cMWzRDHMWPGNFr3BWDZsmWkp6fjcrlYsWIFU6ZMOW67GgIp3kt1EqSdffAB
+zJxpZoqsb4WVJhQVQWIi/PWvMHGi09GcOp2PLaMkQbyX6iRIO5s4Ea65xox4
+0OdG02zbzH8xc2bHThBERA5RkiAnZckSU7b5j390OhLv9dRT8O23UD+PmYhI
+h6fRDXJS/PzM9Mbjx5vRDoMHOx2Rd/niC7jvPnNrpnt3p6MREWkbakmQkzZ4
+sJklctYsUGXawyor4aqr4MEHYcgQp6MREWk7ShKkRebPh9hYuOcepyPxHr/4
+BQwYYPojiIh0JrrdIC1iWfDss6bM8PjxMG2a0xE567XX4G9/M1NAa9ZMEels
+1JIgLda7t/lgvPFG+Pprp6NxzjffwHXXwdq1qkgpIp2TkgQ5JaNGwdKlpmxz
+RYXT0XheRQVccQUsXgxjxzodjYhI+1CSIKfsxhtN4aDrruta9RNs2/TNGD7c
+zM8gItJZKUmQU2ZZpjbA1193rdoAK1bAtm2wapX6IYhI56aOi9IqPXqYuQrG
+jDHD/y6/3OmI2tff/w6/+x18/LFmdxSRzk9JgrRa377wyiuQlmaGAo4Y4XRE
+7WPTJjPMcd06aHK2YBGRTkS3G6RNjB4NTzwBU6ea0sSdTU4OXHopPPmkOVYR
+ka5ALQnSZq68EvLzYcoU+OijzjNjZH4+TJoEt91mRnOIiHQVShKkTd10E+zZ
+A6mp8N57EBzsdEStU1oKF11kEqDbb3c6GhERz9LtBmlzS5aYjoxpaVBW5nQ0
+p66iAi65BM45x9SEEBHpapQkSJuzLHj8cRg2zHwLLy11OqKWKyszsQ8YAH/4
+g4Y6ikjXpCRB2oXLZToyjhhh+iiUlDgd0ckrLjZ9EBIS4JlnzLGIiHRFuvxJ
+u3G5TIvC2WfDeeeB2+10RCeWlwc/+hGce65JcpQgiEhXpkugtCvLgt//HmbN
+gnHjYMsWpyNq2tatJqGZNs1UVdQtBhHp6jS6QdqdZcHdd0P//qYZ/89/Np0a
+vcn/+38wbx6sXAkzZjgdjYiId1CSIB4zY4apVDhjBsyeDb/+NXRz+H9gbS08
+8AD86U/w+uumJUFERAzdbhCPOuccU95482ZISTGVDJ3idsMFF8AHH8AnnyhB
+EBE5lpIE8biICDP/wcUXw1lnmVLHdXWe239dHfzxj5CcbG5/vPWWmX9CRESO
+piRBHOFywc9/Du+/D889B+PHm5kV29vHH8OECfC//wtZWXDffeDj0/77Fe+Q
+k5PDxIkTSUxM5Mwzz+Q3v/kNAIsXLyY2Npbk5GSSk5N54403Gl6zfPlyhg4d
+SmJiIm+99ZZToYs44pSThL/97W8kJCTg4+PDpk2bmlwvMzOTxMREhg4dysMP
+P3yqu3NMVlaW0yE0qrPENXQofPihmV3xyivNyIJm/judsqeeymL6dLOPa681
++0xIaPv9tJS3/jt2Vn5+fjzxxBNs27aNjRs38vTTT7NlyxYsy+L2229n8+bN
+bN68mdTUVAA2btzIyy+/zLZt28jMzGT+/PlUVVU5fBTNyXY6ABTDkbKdDqDV
+TjlJSExM5JVXXmHixIlNrlNZWcmCBQvIzMxk69atvPjii2zevPlUd+kIb72I
+d6a4XC6YOxe++srUJ7j0Ujj/fFizxpRGPlUVFWYbP/oR3HFHFmPHwpdfmlEM
+3tJ64K3/jp1VVFQUw4YNAyAwMJCkpCRyc3MBsG37uPUzMjKYOXMmPj4+xMTE
+kJCQwIYNGzwac8tkOx0AiuFI2U4H0GqnnCQMHjyYM844o9l11q9fT0JCAjEx
+MXTr1o0ZM2aQkZFxqruUTq5HDzOJ0rffwg03wLPPmr4CV15pChtt3w7NfYmr
+qoIdO0wfh5/8xLz2mWfguuvgllvgzjshIMBzxyPeLTs7m08++YQJEyYA8Pjj
+jzNkyBBmz55NcXExALm5ucTGxja8JjY2FndHqAom0kbatU+C2+0mLi6u4bFO
+MDkZvr5w1VWQmWm++V98sRl9cMUVZlbJQYNg7FjT6XDyZFOkadAg87fLL4f1
+600dhi+/hDffhKuv9p6WA/EO5eXlXHnllTz66KMEBQVx0003sXPnTv7zn/8w
+cOBAFi5ceApbzTpiyW67YEVaJJuj/y+2kt2MCy+80B42bNhxy2uvvdawTkpK
+ir1x48ZGX//888/bN954Y8PjF154wZ4/f36j6w4cONAGtGjR4gXLwIEDm7s0
+dGhVVVX25MmT7d/+9reN/j03N9c+44wzbNu27aVLl9qPPPJIw9+mTp1qf/jh
+h8e9Zvjw4Y7/m2nR0tgyfPjwVp0vzZayefvtt5v78wnFxsaSc8RA+JycnKNa
+Fo70zTfftGpfIiInYts21157LUOHDuW2225reD4/P5/IyEgAXnrpJRLqe7Wm
+paVx4403cuutt5KXl8f27dsZM2bMcdv97LPPPHMAIh7WJvXu7EY6/ACMHj2a
+7du3k5ubS2RkJGvXrmXVqlVtsUsRkRb76KOPSE9PJykpieTkZACWLVvG888/
+z9atW6mqqqJ///6sXr0agJEjR3L55ZeTlJSEy+Vi1apV+Pr6OnkIIh5l2U19
+wp/AK6+8wsKFCyksLKRXr14NY4t3797N9ddf39BB8Y033uCuu+6irq6OOXPm
+cO+997bpAYiIiEj7OOUkQURERDo3xysuelOxpfj4+IZmyEP3HYuLi5k0aRJJ
+SUlMmTKFkpKSdo9j3rx5REVFkZiY2PBcc3F4qiJcY3F5Q6W6pqroOf2eeWt1
+v4MHDzJ69GiSk5M544wzGu7NO/1+dVROXcOcuF55y7XJG65F3nDd8cg1plXd
+Hlvp4MGDdnx8vO12u+3q6mp71KhR9qZNmxyLJz4+3i4qKjrquZtvvtn+3e9+
+Z9u2bf/ud7+zFy5c2O5xfPDBB/amTZvsYcOGnTCOTz/91B41apRdU1Nju91u
+Oz4+3q6srPRYXIsXL7ZXrFhx3LqejCsvL8/etm2bbdu2vW/fPvv000+3P/vs
+M8ffs6bi8ob3rKKiwrZt266urrbPPvts+91333X8/eqInLyGOXG98pZrkzdc
+i7zhuuOJa4yjLQneWGzJPubuy7p165gzZw4As2fP9kh8EyZMIDQ09KTi8GRF
+uMbigsY7rnoyrqaq6Dn9nnlzdb8ePXoAUFVVRW1tLZGRkY6/Xx2R09cwT1+v
+vOXa5A3XIm+47njiGuNokuBtxZYsy2poJlq5ciUABQUFhIWFARAeHk5+fr4j
+sTUVhzdUhPOmSnWHquiNHz/eq94zb6vuV1dXx4gRI4iKiuL8888nISHBq96v
+jsLJa5i3XK+86f+NU+eVN1x32usa42iSYFmWk7s/zscff8ymTZt45513ePbZ
+Z/nHP/7hdEher20q1bWN8vJypk+fzqOPPkpwcLBjcRyrfar7tY7L5eKzzz7D
+7XbzwQcf8N5773k8hs7AyWuYrldHc+q88obrTnteYxxNElpSbMkTDhVTiYiI
+YPr06XzyySdERERQWFgImIz50Dqe1lQcx76Hx36zaW/h4eFYloVlWcyfP59P
+PvnEkbiqq6uZNm0as2bN4rLLLgO84z07FNfVV1/dEJe3vGcAvXr1YurUqaxf
+v94r3q+OxslrmLdcr7zl/40T55U3XHfa+xrjaJJwZLGl6upq1q5d2zBFq6dV
+VFRQUT/l4P79+8nMzCQhIYG0tDTS09MBSE9PJy0tzZH4moojLS2NNWvWUFNT
+g9vtbrIiXHs5sjnz2Ep1norLbqKKntPvWVNxOf2eFRUVsW/fPgAOHDjA22+/
+TWJiouPvV0fk1DXMm65X3vL/xtPnlTdcdzxyjWlV18o2sG7dOjshIcEeMmSI
+vWzZMsfi+Pbbb+2kpCR7+PDh9umnn27ff//9tm3bdlFRkX3hhRfaiYmJ9qRJ
+k+y9e/e2eywzZ860+/TpY/v6+tqxsbH2M88802wcDz74oD1kyBA7ISHBzszM
+9Fhcq1evtmfPnm0nJSXZgwcPtqdMmWK73W6Px/XPf/7TtizLHj58uD1ixAh7
+xIgR9htvvOH4e9ZYXOvWrXP8Pdu6das9YsQIe/jw4faZZ55pL1myxLbt5v+v
+e+rfsiNy4hrm1PXKW65N3nAt8obrjieuMSqmJCIiIo1yvJiSiIiIeCclCSIi
+ItIoJQkiIiLSKCUJIiIi0iglCSIiItIoJQkiIiLSKCUJIiIi0qj/D8+9rBqN
+ky3xAAAAAElFTkSuQmCC
+" /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">4</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/itango.ico b/doc/_static/itango.ico
new file mode 100644
index 0000000..eb8e2dc
Binary files /dev/null and b/doc/_static/itango.ico differ
diff --git a/doc/_static/itango.png b/doc/_static/itango.png
new file mode 100644
index 0000000..b4cd925
Binary files /dev/null and b/doc/_static/itango.png differ
diff --git a/doc/itango/itango00.png b/doc/_static/itango00.png
similarity index 100%
rename from doc/itango/itango00.png
rename to doc/_static/itango00.png
diff --git a/doc/itango/itango01.png b/doc/_static/itango01.png
similarity index 100%
rename from doc/itango/itango01.png
rename to doc/_static/itango01.png
diff --git a/doc/itango/itango02.png b/doc/_static/itango02.png
similarity index 100%
rename from doc/itango/itango02.png
rename to doc/_static/itango02.png
diff --git a/doc/itango/itango03.png b/doc/_static/itango03.png
similarity index 100%
rename from doc/itango/itango03.png
rename to doc/_static/itango03.png
diff --git a/doc/itango/itango04.png b/doc/_static/itango04.png
similarity index 100%
rename from doc/itango/itango04.png
rename to doc/_static/itango04.png
diff --git a/doc/itango/itango05.png b/doc/_static/itango05.png
similarity index 100%
rename from doc/itango/itango05.png
rename to doc/_static/itango05.png
diff --git a/doc/itango/itango06.png b/doc/_static/itango06.png
similarity index 100%
rename from doc/itango/itango06.png
rename to doc/_static/itango06.png
diff --git a/doc/_static/jive_powersupply.png b/doc/_static/jive_powersupply.png
new file mode 100644
index 0000000..f3cdc79
Binary files /dev/null and b/doc/_static/jive_powersupply.png differ
diff --git a/doc/_static/jssor.css b/doc/_static/jssor.css
new file mode 100644
index 0000000..908ed85
--- /dev/null
+++ b/doc/_static/jssor.css
@@ -0,0 +1,23 @@
+ /* jssor slider arrow navigator skin 03 css */
+/*
+.jssora03l              (normal)
+.jssora03r              (normal)
+.jssora03l:hover        (normal mouseover)
+.jssora03r:hover        (normal mouseover)
+.jssora03ldn            (mousedown)
+.jssora03rdn            (mousedown)
+*/
+.jssora03l, .jssora03r, .jssora03ldn, .jssora03rdn
+{
+	position: absolute;
+	cursor: pointer;
+	display: block;
+    background: url(../img/a03.png) no-repeat;
+    overflow:hidden;
+}
+.jssora03l { background-position: -3px -33px; }
+.jssora03r { background-position: -63px -33px; }
+.jssora03l:hover { background-position: -123px -33px; }
+.jssora03r:hover { background-position: -183px -33px; }
+.jssora03ldn { background-position: -243px -33px; }
+.jssora03rdn { background-position: -303px -33px; }
\ No newline at end of file
diff --git a/doc/_static/jssor.js b/doc/_static/jssor.js
new file mode 100644
index 0000000..255b990
--- /dev/null
+++ b/doc/_static/jssor.js
@@ -0,0 +1,2806 @@
+/*
+* Jssor 18.0
+* http://www.jssor.com/
+* 
+* TERMS OF USE - Jssor
+* 
+* Copyright 2014 Jssor
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+* 
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+* 
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/*! Jssor */
+
+//$JssorDebug$
+var $JssorDebug$ = new function () {
+
+    this.$DebugMode = true;
+
+    // Methods
+
+    this.$Log = function (msg, important) {
+        var console = window.console || {};
+        var debug = this.$DebugMode;
+
+        if (debug && console.log) {
+            console.log(msg);
+        } else if (debug && important) {
+            alert(msg);
+        }
+    };
+
+    this.$Error = function (msg, e) {
+        var console = window.console || {};
+        var debug = this.$DebugMode;
+
+        if (debug && console.error) {
+            console.error(msg);
+        } else if (debug) {
+            alert(msg);
+        }
+
+        if (debug) {
+            // since we're debugging, fail fast by crashing
+            throw e || new Error(msg);
+        }
+    };
+
+    this.$Fail = function (msg) {
+        throw new Error(msg);
+    };
+
+    this.$Assert = function (value, msg) {
+        var debug = this.$DebugMode;
+        if (debug) {
+            if (!value)
+                throw new Error("Assert failed " + msg || "");
+        }
+    };
+
+    this.$Trace = function (msg) {
+        var console = window.console || {};
+        var debug = this.$DebugMode;
+
+        if (debug && console.log) {
+            console.log(msg);
+        }
+    };
+
+    this.$Execute = function (func) {
+        var debug = this.$DebugMode;
+        if (debug)
+            func();
+    };
+
+    this.$LiveStamp = function (obj, id) {
+        var debug = this.$DebugMode;
+        if (debug) {
+            var stamp = document.createElement("DIV");
+            stamp.setAttribute("id", id);
+
+            obj.$Live = stamp;
+        }
+    };
+
+    this.$C_AbstractMethod = function () {
+        ///	<summary>
+        ///		Tells compiler the method is abstract, it should be implemented by subclass.
+        ///	</summary>
+
+        throw new Error("The method is abstract, it should be implemented by subclass.");
+    };
+
+    function C_AbstractClass (instance) {
+        ///	<summary>
+        ///		Tells compiler the class is abstract, it should be implemented by subclass.
+        ///	</summary>
+
+        if(instance.constructor === C_AbstractClass.caller)
+            throw new Error("Cannot create instance of an abstract class.");
+    }
+
+    this.$C_AbstractClass = C_AbstractClass;
+};
+
+//$JssorPoint$
+var $JssorPoint$ = function (x, y) {
+    var _ThisPoint = this;
+
+    // Properties
+    _ThisPoint.x = x;
+    _ThisPoint.y = y;
+
+    _ThisPoint.$Plus = function (point) {
+        return new $JssorPoint$(x + point.x, y + point.y);
+    };
+
+    _ThisPoint.$Minus = function (point) {
+        return new $JssorPoint$(x - point.x, y - point.y);
+    };
+
+    _ThisPoint.$Times = function (factor) {
+        return new $JssorPoint$(x * factor, y * factor);
+    };
+
+    _ThisPoint.$Divide = function (factor) {
+        return new $JssorPoint$(x / factor, y / factor);
+    };
+
+    _ThisPoint.$Negate = function () {
+        return new $JssorPoint$(-x, -y);
+    };
+
+    _ThisPoint.$DistanceTo = function (point) {
+        return Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
+    };
+
+    _ThisPoint.$Apply = function (func) {
+        return new $JssorPoint$(func(x), func(y));
+    };
+
+    _ThisPoint.$Equals = function (point) {
+        return (point instanceof $JssorPoint$) && (x === point.x) && (y === point.y);
+    };
+
+    _ThisPoint.$ToString = function () {
+        return "(" + x + "," + y + ")";
+    };
+};
+
+//$JssorEasing$
+var $JssorEasing$ = window.$JssorEasing$ = {
+    $EaseLinear: function (t) {
+        return t;
+    },
+    $EaseGoBack: function (t) {
+        return 1 - Math.abs((t *= 2) - 1);
+    },
+    $EaseSwing: function (t) {
+        return -Math.cos(t * Math.PI) / 2 + .5;
+    },
+    $EaseInQuad: function (t) {
+        return t * t;
+    },
+    $EaseOutQuad: function (t) {
+        return -t * (t - 2);
+    },
+    $EaseInOutQuad: function (t) {
+        return (t *= 2) < 1 ? 1 / 2 * t * t : -1 / 2 * (--t * (t - 2) - 1);
+    },
+    $EaseInCubic: function (t) {
+        return t * t * t;
+    },
+    $EaseOutCubic: function (t) {
+        return (t -= 1) * t * t + 1;
+    },
+    $EaseInOutCubic: function (t) {
+        return (t *= 2) < 1 ? 1 / 2 * t * t * t : 1 / 2 * ((t -= 2) * t * t + 2);
+    },
+    $EaseInQuart: function (t) {
+        return t * t * t * t;
+    },
+    $EaseOutQuart: function (t) {
+        return -((t -= 1) * t * t * t - 1);
+    },
+    $EaseInOutQuart: function (t) {
+        return (t *= 2) < 1 ? 1 / 2 * t * t * t * t : -1 / 2 * ((t -= 2) * t * t * t - 2);
+    },
+    $EaseInQuint: function (t) {
+        return t * t * t * t * t;
+    },
+    $EaseOutQuint: function (t) {
+        return (t -= 1) * t * t * t * t + 1;
+    },
+    $EaseInOutQuint: function (t) {
+        return (t *= 2) < 1 ? 1 / 2 * t * t * t * t * t : 1 / 2 * ((t -= 2) * t * t * t * t + 2);
+    },
+    $EaseInSine: function (t) {
+        return 1 - Math.cos(t * Math.PI / 2);
+    },
+    $EaseOutSine: function (t) {
+        return Math.sin(t * Math.PI / 2);
+    },
+    $EaseInOutSine: function (t) {
+        return -1 / 2 * (Math.cos(Math.PI * t) - 1);
+    },
+    $EaseInExpo: function (t) {
+        return t == 0 ? 0 : Math.pow(2, 10 * (t - 1));
+    },
+    $EaseOutExpo: function (t) {
+        return t == 1 ? 1 : -Math.pow(2, -10 * t) + 1;
+    },
+    $EaseInOutExpo: function (t) {
+        return t == 0 || t == 1 ? t : (t *= 2) < 1 ? 1 / 2 * Math.pow(2, 10 * (t - 1)) : 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
+    },
+    $EaseInCirc: function (t) {
+        return -(Math.sqrt(1 - t * t) - 1);
+    },
+    $EaseOutCirc: function (t) {
+        return Math.sqrt(1 - (t -= 1) * t);
+    },
+    $EaseInOutCirc: function (t) {
+        return (t *= 2) < 1 ? -1 / 2 * (Math.sqrt(1 - t * t) - 1) : 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
+    },
+    $EaseInElastic: function (t) {
+        if (!t || t == 1)
+            return t;
+        var p = .3, s = .075;
+        return -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * 2 * Math.PI / p));
+    },
+    $EaseOutElastic: function (t) {
+        if (!t || t == 1)
+            return t;
+        var p = .3, s = .075;
+        return Math.pow(2, -10 * t) * Math.sin((t - s) * 2 * Math.PI / p) + 1;
+    },
+    $EaseInOutElastic: function (t) {
+        if (!t || t == 1)
+            return t;
+        var p = .45, s = .1125;
+        return (t *= 2) < 1 ? -.5 * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * 2 * Math.PI / p) : Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * 2 * Math.PI / p) * .5 + 1;
+    },
+    $EaseInBack: function (t) {
+        var s = 1.70158;
+        return t * t * ((s + 1) * t - s);
+    },
+    $EaseOutBack: function (t) {
+        var s = 1.70158;
+        return (t -= 1) * t * ((s + 1) * t + s) + 1;
+    },
+    $EaseInOutBack: function (t) {
+        var s = 1.70158;
+        return (t *= 2) < 1 ? 1 / 2 * t * t * (((s *= 1.525) + 1) * t - s) : 1 / 2 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);
+    },
+    $EaseInBounce: function (t) {
+        return 1 - $JssorEasing$.$EaseOutBounce(1 - t)
+    },
+    $EaseOutBounce: function (t) {
+        return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
+    },
+    $EaseInOutBounce: function (t) {
+        return t < 1 / 2 ? $JssorEasing$.$EaseInBounce(t * 2) * .5 : $JssorEasing$.$EaseOutBounce(t * 2 - 1) * .5 + .5;
+    },
+    $EaseInWave: function (t) {
+        return 1 - Math.cos(t * Math.PI * 2)
+    },
+    $EaseOutWave: function (t) {
+        return Math.sin(t * Math.PI * 2);
+    },
+    $EaseOutJump: function (t) {
+        return 1 - (((t *= 2) < 1) ? (t = 1 - t) * t * t : (t -= 1) * t * t);
+    },
+    $EaseInJump: function (t) {
+        return ((t *= 2) < 1) ? t * t * t : (t = 2 - t) * t * t;
+    }
+};
+
+var $JssorDirection$ = window.$JssorDirection$ = {
+    $TO_LEFT: 0x0001,
+    $TO_RIGHT: 0x0002,
+    $TO_TOP: 0x0004,
+    $TO_BOTTOM: 0x0008,
+    $HORIZONTAL: 0x0003,
+    $VERTICAL: 0x000C,
+    $LEFTRIGHT: 0x0003,
+    $TOPBOTOM: 0x000C,
+    $TOPLEFT: 0x0005,
+    $TOPRIGHT: 0x0006,
+    $BOTTOMLEFT: 0x0009,
+    $BOTTOMRIGHT: 0x000A,
+    $AROUND: 0x000F,
+
+    $GetDirectionHorizontal: function (direction) {
+        return direction & 0x0003;
+    },
+    $GetDirectionVertical: function (direction) {
+        return direction & 0x000C;
+    },
+    $ChessHorizontal: function (direction) {
+        return (~direction & 0x0003) + (direction & 0x000C);
+    },
+    $ChessVertical: function (direction) {
+        return (~direction & 0x000C) + (direction & 0x0003);
+    },
+    $IsToLeft: function (direction) {
+        return (direction & 0x0003) == 0x0001;
+    },
+    $IsToRight: function (direction) {
+        return (direction & 0x0003) == 0x0002;
+    },
+    $IsToTop: function (direction) {
+        return (direction & 0x000C) == 0x0004;
+    },
+    $IsToBottom: function (direction) {
+        return (direction & 0x000C) == 0x0008;
+    },
+    $IsHorizontal: function (direction) {
+        return (direction & 0x0003) > 0;
+    },
+    $IsVertical: function (direction) {
+        return (direction & 0x000C) > 0;
+    }
+};
+
+var $JssorKeyCode$ = {
+    $BACKSPACE: 8,
+    $COMMA: 188,
+    $DELETE: 46,
+    $DOWN: 40,
+    $END: 35,
+    $ENTER: 13,
+    $ESCAPE: 27,
+    $HOME: 36,
+    $LEFT: 37,
+    $NUMPAD_ADD: 107,
+    $NUMPAD_DECIMAL: 110,
+    $NUMPAD_DIVIDE: 111,
+    $NUMPAD_ENTER: 108,
+    $NUMPAD_MULTIPLY: 106,
+    $NUMPAD_SUBTRACT: 109,
+    $PAGE_DOWN: 34,
+    $PAGE_UP: 33,
+    $PERIOD: 190,
+    $RIGHT: 39,
+    $SPACE: 32,
+    $TAB: 9,
+    $UP: 38
+};
+
+var $JssorAlignment$ = {
+    $TopLeft: 0x11,
+    $TopCenter: 0x12,
+    $TopRight: 0x14,
+    $MiddleLeft: 0x21,
+    $MiddleCenter: 0x22,
+    $MiddleRight: 0x24,
+    $BottomLeft: 0x41,
+    $BottomCenter: 0x42,
+    $BottomRight: 0x44,
+
+    $IsTop: function (aligment) {
+        return aligment & 0x10 > 0;
+    },
+    $IsMiddle: function (alignment) {
+        return alignment & 0x20 > 0;
+    },
+    $IsBottom: function (alignment) {
+        return alignment & 0x40 > 0;
+    },
+    $IsLeft: function (alignment) {
+        return alignment & 0x01 > 0;
+    },
+    $IsCenter: function (alignment) {
+        return alignment & 0x02 > 0;
+    },
+    $IsRight: function (alignment) {
+        return alignment & 0x04 > 0;
+    }
+};
+
+var $JssorMatrix$;
+
+var $JssorAnimator$;
+
+// $Jssor$ is a static class, so make it singleton instance
+var $Jssor$ = window.$Jssor$ = new function () {
+    // Fields
+    var _This = this;
+
+    var REGEX_WHITESPACE_GLOBAL = /\S+/g;
+
+    var ROWSER_UNKNOWN = 0;
+    var BROWSER_IE = 1;
+    var BROWSER_FIREFOX = 2;
+    var BROWSER_FIREFOX = 3;
+    var BROWSER_CHROME = 4;
+    var BROWSER_OPERA = 5;
+
+    //var arrActiveX = ["Msxml2.XMLHTTP", "Msxml3.XMLHTTP", "Microsoft.XMLHTTP"];
+
+    var browser = 0;
+    var browserRuntimeVersion = 0;
+    var browserEngineVersion = 0;
+    var browserJavascriptVersion = 0;
+    var webkitVersion = 0;
+
+    var app = navigator.appName;
+    var ver = navigator.appVersion;
+    var ua = navigator.userAgent;
+
+    var _DocElmt = document.documentElement;
+    var _TransformProperty;
+
+    function DetectBrowser() {
+        if (!browser) {
+            if (app == "Microsoft Internet Explorer" &&
+                !!window.attachEvent && !!window.ActiveXObject) {
+
+                var ieOffset = ua.indexOf("MSIE");
+                browser = BROWSER_IE;
+                browserEngineVersion = ParseFloat(ua.substring(ieOffset + 5, ua.indexOf(";", ieOffset)));
+
+                //check IE javascript version
+                /*@cc_on
+                browserJavascriptVersion = @_jscript_version;
+                @*/
+
+                // update: for intranet sites and compat view list sites, IE sends
+                // an IE7 User-Agent to the server to be interoperable, and even if
+                // the page requests a later IE version, IE will still report the
+                // IE7 UA to JS. we should be robust to self
+                //var docMode = document.documentMode;
+                //if (typeof docMode !== "undefined") {
+                //    browserRuntimeVersion = docMode;
+                //}
+
+                browserRuntimeVersion = document.documentMode || browserEngineVersion;
+
+            }
+            else if (app == "Netscape" && !!window.addEventListener) {
+
+                var ffOffset = ua.indexOf("Firefox");
+                var saOffset = ua.indexOf("Safari");
+                var chOffset = ua.indexOf("Chrome");
+                var webkitOffset = ua.indexOf("AppleWebKit");
+
+                if (ffOffset >= 0) {
+                    browser = BROWSER_FIREFOX;
+                    browserRuntimeVersion = ParseFloat(ua.substring(ffOffset + 8));
+                }
+                else if (saOffset >= 0) {
+                    var slash = ua.substring(0, saOffset).lastIndexOf("/");
+                    browser = (chOffset >= 0) ? BROWSER_CHROME : BROWSER_FIREFOX;
+                    browserRuntimeVersion = ParseFloat(ua.substring(slash + 1, saOffset));
+                }
+
+                if (webkitOffset >= 0)
+                    webkitVersion = ParseFloat(ua.substring(webkitOffset + 12));
+            }
+            else {
+                var match = /(opera)(?:.*version|)[ \/]([\w.]+)/i.exec(ua);
+                if (match) {
+                    browser = BROWSER_OPERA;
+                    browserRuntimeVersion = ParseFloat(match[2]);
+                }
+            }
+        }
+    }
+
+    function IsBrowserIE() {
+        DetectBrowser();
+        return browser == BROWSER_IE;
+    }
+
+    function IsBrowserIeQuirks() {
+        return IsBrowserIE() && (browserRuntimeVersion < 6 || document.compatMode == "BackCompat");   //Composite to "CSS1Compat"
+    }
+
+    function IsBrowserFireFox() {
+        DetectBrowser();
+        return browser == BROWSER_FIREFOX;
+    }
+
+    function IsBrowserSafari() {
+        DetectBrowser();
+        return browser == BROWSER_FIREFOX;
+    }
+
+    function IsBrowserChrome() {
+        DetectBrowser();
+        return browser == BROWSER_CHROME;
+    }
+
+    function IsBrowserOpera() {
+        DetectBrowser();
+        return browser == BROWSER_OPERA;
+    }
+
+    function IsBrowserBadTransform() {
+        return IsBrowserSafari() && (webkitVersion > 534) && (webkitVersion < 535);
+    }
+
+    function IsBrowserSafeHWA() {
+        return IsBrowserSafari() && (webkitVersion < 535);
+    }
+
+    function IsBrowserIe9Earlier() {
+        return IsBrowserIE() && browserRuntimeVersion < 9; 
+    }
+
+    function GetTransformProperty(elmt) {
+
+        if (!_TransformProperty) {
+            // Note that in some versions of IE9 it is critical that
+            // msTransform appear in this list before MozTransform
+
+            each(['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (property) {
+                if (elmt.style[property] != undefined) {
+                    _TransformProperty = property;
+                    return true;
+                }
+            });
+
+            _TransformProperty = _TransformProperty || "transform";
+        }
+
+        return _TransformProperty;
+    }
+
+    // Helpers
+    function getOffsetParent(elmt, isFixed) {
+        // IE and Opera "fixed" position elements don't have offset parents.
+        // regardless, if it's fixed, its offset parent is the body.
+        if (isFixed && elmt != document.body) {
+            return document.body;
+        } else {
+            return elmt.offsetParent;
+        }
+    }
+
+    function toString(obj) {
+        return Object.prototype.toString.call(obj);
+    }
+
+    // [[Class]] -> type pairs
+    var class2type;
+
+    function each(object, callback) {
+        if (toString(object) == "[object Array]") {
+            for (var i = 0; i < object.length; i++) {
+                if (callback(object[i], i, object)) {
+                    return true;
+                }
+            }
+        }
+        else {
+            for (var name in object) {
+                if (callback(object[name], name, object)) {
+                    return true;
+                }
+            }
+        }
+    }
+
+    function GetClass2Type() {
+        if (!class2type) {
+            class2type = {};
+            each(["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object"], function (name) {
+                class2type["[object " + name + "]"] = name.toLowerCase();
+            });
+        }
+
+        return class2type;
+    }
+
+    function type(obj) {
+        return obj == null ? String(obj) : GetClass2Type()[toString(obj)] || "object";
+    }
+
+    function isPlainObject(obj) {
+        // Must be an Object.
+        // Because of IE, we also have to check the presence of the constructor property.
+        // Make sure that DOM nodes and window objects don't pass through, as well
+        if (!obj || type(obj) !== "object" || obj.nodeType || _This.$IsWindow(obj)) {
+            return false;
+        }
+
+        var hasOwn = Object.prototype.hasOwnProperty;
+
+        try {
+            // Not own constructor property must be Object
+            if (obj.constructor &&
+				!hasOwn.call(obj, "constructor") &&
+				!hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
+                return false;
+            }
+        } catch (e) {
+            // IE8,9 Will throw exceptions on certain host objects #9897
+            return false;
+        }
+
+        // Own properties are enumerated firstly, so to speed up,
+        // if last one is own, then all properties are own.
+
+        var key;
+        for (key in obj) { }
+
+        return key === undefined || hasOwn.call(obj, key);
+    }
+
+    function Delay(code, delay) {
+        setTimeout(code, delay || 0);
+    }
+
+    function RemoveByReg(str, reg) {
+        var m = reg.exec(str);
+
+        if (m) {
+            var header = str.substr(0, m.index);
+            var tailer = str.substr(m.lastIndex + 1, str.length - (m.lastIndex + 1));
+            str = header + tailer;
+        }
+
+        return str;
+    }
+
+    function BuildNewCss(oldCss, removeRegs, replaceValue) {
+        var css = (!oldCss || oldCss == "inherit") ? "" : oldCss;
+
+        each(removeRegs, function (removeReg) {
+            var m = removeReg.exec(css);
+
+            if (m) {
+                var header = css.substr(0, m.index);
+                var tailer = css.substr(m.lastIndex + 1, css.length - (m.lastIndex + 1));
+                css = header + tailer;
+            }
+        });
+
+        css = replaceValue + (css.indexOf(" ") != 0 ? " " : "") + css;
+
+        return css;
+    }
+
+    function SetStyleFilterIE(elmt, value) {
+        if (browserRuntimeVersion < 9) {
+            elmt.style.filter = value;
+        }
+    }
+
+    function SetStyleMatrixIE(elmt, matrix, offset) {
+        //matrix is not for ie9+ running in ie8- mode
+        if (browserJavascriptVersion < 9) {
+            var oldFilterValue = elmt.style.filter;
+            var matrixReg = new RegExp(/[\s]*progid:DXImageTransform\.Microsoft\.Matrix\([^\)]*\)/g);
+            var matrixValue = matrix ? "progid:DXImageTransform.Microsoft.Matrix(" + "M11=" + matrix[0][0] + ", M12=" + matrix[0][1] + ", M21=" + matrix[1][0] + ", M22=" + matrix[1][1] + ", SizingMethod='auto expand')" : "";
+
+            var newFilterValue = BuildNewCss(oldFilterValue, [matrixReg], matrixValue);
+
+            SetStyleFilterIE(elmt, newFilterValue);
+
+            _This.$CssMarginTop(elmt, offset.y);
+            _This.$CssMarginLeft(elmt, offset.x);
+        }
+    }
+
+    // Methods
+
+    _This.$IsBrowserIE = IsBrowserIE;
+
+    _This.$IsBrowserIeQuirks = IsBrowserIeQuirks;
+
+    _This.$IsBrowserFireFox = IsBrowserFireFox;
+
+    _This.$IsBrowserSafari = IsBrowserSafari;
+
+    _This.$IsBrowserChrome = IsBrowserChrome;
+
+    _This.$IsBrowserOpera = IsBrowserOpera;
+
+    _This.$IsBrowserBadTransform = IsBrowserBadTransform;
+
+    _This.$IsBrowserSafeHWA = IsBrowserSafeHWA;
+
+    _This.$IsBrowserIe9Earlier = IsBrowserIe9Earlier;
+
+    _This.$BrowserVersion = function () {
+        return browserRuntimeVersion;
+    };
+
+    _This.$BrowserEngineVersion = function () {
+        return browserEngineVersion || browserRuntimeVersion;
+    };
+
+    _This.$WebKitVersion = function () {
+        return webkitVersion;
+    };
+
+    _This.$Delay = Delay;
+
+    _This.$Inherit = function (instance, baseClass) {
+        baseClass.apply(instance, [].slice.call(arguments, 2));
+        return Extend({}, instance);
+    };
+
+    function Construct(instance, constructor) {
+        instance.constructor === Construct.caller && instance.$Construct && instance.$Construct();
+    }
+
+    _This.$Construct = Construct;
+
+    _This.$GetElement = function (elmt) {
+        if (_This.$IsString(elmt)) {
+            elmt = document.getElementById(elmt);
+        }
+
+        return elmt;
+    };
+
+    function GetEvent(event) {
+        return event || window.event;
+    }
+
+    GetEvent = GetEvent;
+
+    _This.$EventSrc = function (event) {
+        event = GetEvent(event);
+        return event.target || event.srcElement || document;
+    };
+
+    _This.$EventDst = function (event) {
+        event = GetEvent(event);
+        return event.relatedTarget || event.toElement;
+    };
+
+    _This.$MousePosition = function (event) {
+        event = GetEvent(event);
+        var body = document.body;
+
+        return {
+            x: event.pageX || event.clientX + (_DocElmt.scrollLeft || body.scrollLeft || 0) - (_DocElmt.clientLeft || body.clientLeft || 0) || 0,
+            y: event.pageY || event.clientY + (_DocElmt.scrollTop || body.scrollTop || 0) - (_DocElmt.clientTop || body.clientTop || 0) || 0
+        };
+    };
+
+    _This.$PageScroll = function () {
+        var body = document.body;
+
+        return {
+            x: (window.pageXOffset || _DocElmt.scrollLeft || body.scrollLeft || 0) - (_DocElmt.clientLeft || body.clientLeft || 0),
+            y: (window.pageYOffset || _DocElmt.scrollTop || body.scrollTop || 0) - (_DocElmt.clientTop || body.clientTop || 0)
+        };
+    };
+
+    _This.$WindowSize = function () {
+        var body = document.body;
+
+        return {
+            x: body.clientWidth || _DocElmt.clientWidth,
+            y: body.clientHeight || _DocElmt.clientHeight
+        };
+    };
+
+    //_This.$GetElementPosition = function (elmt) {
+    //    elmt = _This.$GetElement(elmt);
+    //    var result = new $JssorPoint$();
+
+    //    // technique from:
+    //    // http://www.quirksmode.org/js/findpos.html
+    //    // with special check for "fixed" elements.
+
+    //    while (elmt) {
+    //        result.x += elmt.offsetLeft;
+    //        result.y += elmt.offsetTop;
+
+    //        var isFixed = _This.$GetElementStyle(elmt).position == "fixed";
+
+    //        if (isFixed) {
+    //            result = result.$Plus(_This.$PageScroll(window));
+    //        }
+
+    //        elmt = getOffsetParent(elmt, isFixed);
+    //    }
+
+    //    return result;
+    //};
+
+    //_This.$GetMouseScroll = function (event) {
+    //    event = GetEvent(event);
+    //    var delta = 0; // default value
+
+    //    // technique from:
+    //    // http://blog.paranoidferret.com/index.php/2007/10/31/javascript-tutorial-the-scroll-wheel/
+
+    //    if (typeof (event.wheelDelta) == "number") {
+    //        delta = event.wheelDelta;
+    //    } else if (typeof (event.detail) == "number") {
+    //        delta = event.detail * -1;
+    //    } else {
+    //        $JssorDebug$.$Fail("Unknown event mouse scroll, no known technique.");
+    //    }
+
+    //    // normalize value to [-1, 1]
+    //    return delta ? delta / Math.abs(delta) : 0;
+    //};
+
+    //_This.$MakeAjaxRequest = function (url, callback) {
+    //    var async = typeof (callback) == "function";
+    //    var req = null;
+
+    //    if (async) {
+    //        var actual = callback;
+    //        var callback = function () {
+    //            Delay($Jssor$.$CreateCallback(null, actual, req), 1);
+    //        };
+    //    }
+
+    //    if (window.ActiveXObject) {
+    //        for (var i = 0; i < arrActiveX.length; i++) {
+    //            try {
+    //                req = new ActiveXObject(arrActiveX[i]);
+    //                break;
+    //            } catch (e) {
+    //                continue;
+    //            }
+    //        }
+    //    } else if (window.XMLHttpRequest) {
+    //        req = new XMLHttpRequest();
+    //    }
+
+    //    if (!req) {
+    //        $JssorDebug$.$Fail("Browser doesn't support XMLHttpRequest.");
+    //    }
+
+    //    if (async) {
+    //        req.onreadystatechange = function () {
+    //            if (req.readyState == 4) {
+    //                // prevent memory leaks by breaking circular reference now
+    //                req.onreadystatechange = new Function();
+    //                callback();
+    //            }
+    //        };
+    //    }
+
+    //    try {
+    //        req.open("GET", url, async);
+    //        req.send(null);
+    //    } catch (e) {
+    //        $JssorDebug$.$Log(e.name + " while making AJAX request: " + e.message);
+
+    //        req.onreadystatechange = null;
+    //        req = null;
+
+    //        if (async) {
+    //            callback();
+    //        }
+    //    }
+
+    //    return async ? null : req;
+    //};
+
+    //_This.$ParseXml = function (string) {
+    //    var xmlDoc = null;
+
+    //    if (window.ActiveXObject) {
+    //        try {
+    //            xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+    //            xmlDoc.async = false;
+    //            xmlDoc.loadXML(string);
+    //        } catch (e) {
+    //            $JssorDebug$.$Log(e.name + " while parsing XML (ActiveX): " + e.message);
+    //        }
+    //    } else if (window.DOMParser) {
+    //        try {
+    //            var parser = new DOMParser();
+    //            xmlDoc = parser.parseFromString(string, "text/xml");
+    //        } catch (e) {
+    //            $JssorDebug$.$Log(e.name + " while parsing XML (DOMParser): " + e.message);
+    //        }
+    //    } else {
+    //        $JssorDebug$.$Fail("Browser doesn't support XML DOM.");
+    //    }
+
+    //    return xmlDoc;
+    //};
+
+    function Css(elmt, name, value) {
+        ///	<summary>
+        ///		access css
+        ///     $Jssor$.$Css(elmt, name);         //get css value
+        ///     $Jssor$.$Css(elmt, name, value);  //set css value
+        ///	</summary>
+        ///	<param name="elmt" type="HTMLElement">
+        ///		the element to access css
+        ///	</param>
+        ///	<param name="name" type="String">
+        ///		the name of css property
+        ///	</param>
+        ///	<param name="value" optional="true">
+        ///		the value to set
+        ///	</param>
+        if (value != undefined) {
+            elmt.style[name] = value;
+        }
+        else {
+            var style = elmt.currentStyle || elmt.style;
+            value = style[name];
+
+            if (value == "" && window.getComputedStyle) {
+                style = elmt.ownerDocument.defaultView.getComputedStyle(elmt, null);
+
+                style && (value = style.getPropertyValue(name) || style[name]);
+            }
+
+            return value;
+        }
+    }
+
+    function CssN(elmt, name, value, isDimensional) {
+        ///	<summary>
+        ///		access css as numeric
+        ///     $Jssor$.$CssN(elmt, name);         //get css value
+        ///     $Jssor$.$CssN(elmt, name, value);  //set css value
+        ///	</summary>
+        ///	<param name="elmt" type="HTMLElement">
+        ///		the element to access css
+        ///	</param>
+        ///	<param name="name" type="String">
+        ///		the name of css property
+        ///	</param>
+        ///	<param name="value" type="Number" optional="true">
+        ///		the value to set
+        ///	</param>
+        if (value != undefined) {
+            isDimensional && (value += "px");
+            Css(elmt, name, value);
+        }
+        else {
+            return ParseFloat(Css(elmt, name));
+        }
+    }
+
+    function CssP(elmt, name, value) {
+        ///	<summary>
+        ///		access css in pixel as numeric, like 'top', 'left', 'width', 'height'
+        ///     $Jssor$.$CssP(elmt, name);         //get css value
+        ///     $Jssor$.$CssP(elmt, name, value);  //set css value
+        ///	</summary>
+        ///	<param name="elmt" type="HTMLElement">
+        ///		the element to access css
+        ///	</param>
+        ///	<param name="name" type="String">
+        ///		the name of css property
+        ///	</param>
+        ///	<param name="value" type="Number" optional="true">
+        ///		the value to set
+        ///	</param>
+        return CssN(elmt, name, value, true);
+    }
+
+    function CssProxy(name, numericOrDimension) {
+        ///	<summary>
+        ///		create proxy to access css, CssProxy(name[, numericOrDimension]);
+        ///	</summary>
+        ///	<param name="elmt" type="HTMLElement">
+        ///		the element to access css
+        ///	</param>
+        ///	<param name="numericOrDimension" type="Number" optional="true">
+        ///		not set: access original css, 1: access css as numeric, 2: access css in pixel as numeric
+        ///	</param>
+        var isDimensional = numericOrDimension & 2;
+        var cssAccessor = numericOrDimension ? CssN : Css;
+        return function (elmt, value) {
+            return cssAccessor(elmt, name, value, isDimensional);
+        };
+    }
+
+    function GetStyleOpacity(elmt) {
+        if (IsBrowserIE() && browserEngineVersion < 9) {
+            var match = /opacity=([^)]*)/.exec(elmt.style.filter || "");
+            return match ? (ParseFloat(match[1]) / 100) : 1;
+        }
+        else
+            return ParseFloat(elmt.style.opacity || "1");
+    }
+
+    function SetStyleOpacity(elmt, opacity, ie9EarlierForce) {
+
+        if (IsBrowserIE() && browserEngineVersion < 9) {
+            //var filterName = "filter"; // browserEngineVersion < 8 ? "filter" : "-ms-filter";
+            var finalFilter = elmt.style.filter || "";
+
+            // for CSS filter browsers (IE), remove alpha filter if it's unnecessary.
+            // update: doing _This always since IE9 beta seems to have broken the
+            // behavior if we rely on the programmatic filters collection.
+            var alphaReg = new RegExp(/[\s]*alpha\([^\)]*\)/g);
+
+            // important: note the lazy star! _This protects against
+            // multiple filters; we don't want to delete the other ones.
+            // update: also trimming extra whitespace around filter.
+
+            var ieOpacity = Math.round(100 * opacity);
+            var alphaFilter = "";
+            if (ieOpacity < 100 || ie9EarlierForce) {
+                alphaFilter = "alpha(opacity=" + ieOpacity + ") ";
+                //elmt.style["-ms-filter"] = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + ieOpacity + ") ";
+            }
+
+            var newFilterValue = BuildNewCss(finalFilter, [alphaReg], alphaFilter);
+
+            SetStyleFilterIE(elmt, newFilterValue);
+        }
+
+            //if (!IsBrowserIE() || browserEngineVersion >= 9) 
+        else {
+            elmt.style.opacity = opacity == 1 ? "" : Math.round(opacity * 100) / 100;
+        }
+    }
+
+    function SetStyleTransformInternal(elmt, transform) {
+        var rotate = transform.$Rotate || 0;
+        var scale = transform.$Scale == undefined ? 1 : transform.$Scale;
+
+        if (IsBrowserIe9Earlier()) {
+            var matrix = _This.$CreateMatrix(rotate / 180 * Math.PI, scale, scale);
+            SetStyleMatrixIE(elmt, (!rotate && scale == 1) ? null : matrix, _This.$GetMatrixOffset(matrix, transform.$OriginalWidth, transform.$OriginalHeight));
+        }
+        else {
+            //rotate(15deg) scale(.5) translateZ(0)
+            var transformProperty = GetTransformProperty(elmt);
+            if (transformProperty) {
+                var transformValue = "rotate(" + rotate % 360 + "deg) scale(" + scale + ")";
+                if (IsBrowserChrome() && webkitVersion > 535)
+                    transformValue += " perspective(2000px)";
+
+                elmt.style[transformProperty] = transformValue;
+            }
+        }
+    }
+
+    _This.$SetStyleTransform = function (elmt, transform) {
+        if (IsBrowserBadTransform()) {
+            Delay(_This.$CreateCallback(null, SetStyleTransformInternal, elmt, transform));
+        }
+        else {
+            SetStyleTransformInternal(elmt, transform);
+        }
+    };
+
+    _This.$SetStyleTransformOrigin = function (elmt, transformOrigin) {
+        var transformProperty = GetTransformProperty(elmt);
+
+        if (transformProperty)
+            elmt.style[transformProperty + "Origin"] = transformOrigin;
+    };
+
+    _This.$CssScale = function (elmt, scale) {
+
+        if (IsBrowserIE() && browserEngineVersion < 9 || (browserEngineVersion < 10 && IsBrowserIeQuirks())) {
+            elmt.style.zoom = (scale == 1) ? "" : scale;
+        }
+        else {
+            var transformProperty = GetTransformProperty(elmt);
+
+            if (transformProperty) {
+                //rotate(15deg) scale(.5)
+                var transformValue = "scale(" + scale + ")";
+
+                var oldTransformValue = elmt.style[transformProperty];
+                var scaleReg = new RegExp(/[\s]*scale\(.*?\)/g);
+
+                var newTransformValue = BuildNewCss(oldTransformValue, [scaleReg], transformValue);
+
+                elmt.style[transformProperty] = newTransformValue;
+            }
+        }
+    };
+
+    _This.$EnableHWA = function (elmt) {
+        if (!elmt.style[GetTransformProperty(elmt)] || elmt.style[GetTransformProperty(elmt)] == "none")
+            elmt.style[GetTransformProperty(elmt)] = "perspective(2000px)";
+    };
+
+    _This.$DisableHWA = function (elmt) {
+        //if (force || elmt.style[GetTransformProperty(elmt)] == "perspective(2000px)")
+        elmt.style[GetTransformProperty(elmt)] = "none";
+    };
+
+    var ie8OffsetWidth = 0;
+    var ie8OffsetHeight = 0;
+    //var ie8WindowResizeCallbackHandlers;
+    //var ie8LastVerticalScrollbar;
+    //var toggleInfo = "";
+
+    //function Ie8WindowResizeFilter(window, handler) {
+
+    //    var trigger = true;
+
+    //    var checkElement = (IsBrowserIeQuirks() ? window.document.body : window.document.documentElement);
+    //    if (checkElement) {
+    //        //check vertical bar
+    //        //var hasVerticalBar = checkElement.scrollHeight > checkElement.clientHeight;
+    //        //var verticalBarToggle = hasVerticalBar != ie8LastVerticalScrollbar;
+    //        //ie8LastVerticalScrollbar = hasVerticalBar;
+
+    //        var widthChange = checkElement.offsetWidth - ie8OffsetWidth;
+    //        var heightChange = checkElement.offsetHeight - ie8OffsetHeight;
+    //        if (widthChange || heightChange) {
+
+    //            ie8OffsetWidth += widthChange;
+    //            ie8OffsetHeight += heightChange;
+    //        }
+    //        else
+    //            trigger = false;
+    //    }
+
+    //    trigger && handler();
+    //}
+
+    //_This.$OnWindowResize = function (window, handler) {
+
+    //    if (IsBrowserIE() && browserEngineVersion < 9) {
+    //        if (!ie8WindowResizeCallbackHandlers) {
+    //            ie8WindowResizeCallbackHandlers = [handler];
+    //            handler = _This.$CreateCallback(null, Ie8WindowResizeFilter, window);
+    //        }
+    //        else {
+    //            ie8WindowResizeCallbackHandlers.push(handler);
+    //            return;
+    //        }
+    //    }
+
+    //    _This.$AddEvent(window, "resize", handler);
+    //};
+
+    _This.$WindowResizeFilter = function (window, handler) {
+        return IsBrowserIe9Earlier() ? function () {
+
+            var trigger = true;
+
+            var checkElement = (IsBrowserIeQuirks() ? window.document.body : window.document.documentElement);
+            if (checkElement) {
+                //check vertical bar
+                //var hasVerticalBar = checkElement.scrollHeight > checkElement.clientHeight;
+                //var verticalBarToggle = hasVerticalBar != ie8LastVerticalScrollbar;
+                //ie8LastVerticalScrollbar = hasVerticalBar;
+
+                var widthChange = checkElement.offsetWidth - ie8OffsetWidth;
+                var heightChange = checkElement.offsetHeight - ie8OffsetHeight;
+                if (widthChange || heightChange) {
+                    ie8OffsetWidth += widthChange;
+                    ie8OffsetHeight += heightChange;
+                }
+                else
+                    trigger = false;
+            }
+
+            trigger && handler();
+
+        } : handler;
+    };
+
+    _This.$MouseOverOutFilter = function (handler, target) {
+        ///	<param name="target" type="HTMLDomElement">
+        ///		The target element to detect mouse over/out events. (for ie < 9 compatibility)
+        ///	</param>
+
+        $JssorDebug$.$Execute(function () {
+            if (!target) {
+                throw new Error("Null reference, parameter \"target\".");
+            }
+        });
+
+        return function (event) {
+            event = GetEvent(event);
+
+            var eventName = event.type;
+            var related = event.relatedTarget || (eventName == "mouseout" ? event.toElement : event.fromElement);
+
+            if (!related || (related !== target && !_This.$IsChild(target, related))) {
+                handler(event);
+            }
+        };
+    };
+
+    _This.$AddEvent = function (elmt, eventName, handler, useCapture) {
+        elmt = _This.$GetElement(elmt);
+
+        // technique from:
+        // http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
+
+        if (elmt.addEventListener) {
+            if (eventName == "mousewheel") {
+                elmt.addEventListener("DOMMouseScroll", handler, useCapture);
+            }
+            // we are still going to add the mousewheel -- not a mistake!
+            // _This is for opera, since it uses onmousewheel but needs addEventListener.
+            elmt.addEventListener(eventName, handler, useCapture);
+        }
+        else if (elmt.attachEvent) {
+            elmt.attachEvent("on" + eventName, handler);
+            if (useCapture && elmt.setCapture) {
+                elmt.setCapture();
+            }
+        }
+
+        $JssorDebug$.$Execute(function () {
+            if (!elmt.addEventListener && !elmt.attachEvent) {
+                $JssorDebug$.$Fail("Unable to attach event handler, no known technique.");
+            }
+        });
+
+    };
+
+    _This.$RemoveEvent = function (elmt, eventName, handler, useCapture) {
+        elmt = _This.$GetElement(elmt);
+
+        // technique from:
+        // http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
+
+        if (elmt.removeEventListener) {
+            if (eventName == "mousewheel") {
+                elmt.removeEventListener("DOMMouseScroll", handler, useCapture);
+            }
+            // we are still going to remove the mousewheel -- not a mistake!
+            // _This is for opera, since it uses onmousewheel but needs removeEventListener.
+            elmt.removeEventListener(eventName, handler, useCapture);
+        }
+        else if (elmt.detachEvent) {
+            elmt.detachEvent("on" + eventName, handler);
+            if (useCapture && elmt.releaseCapture) {
+                elmt.releaseCapture();
+            }
+        }
+    };
+
+    _This.$FireEvent = function (elmt, eventName) {
+        //var document = elmt.document;
+
+        $JssorDebug$.$Execute(function () {
+            if (!document.createEvent && !document.createEventObject) {
+                $JssorDebug$.$Fail("Unable to fire event, no known technique.");
+            }
+
+            if (!elmt.dispatchEvent && !elmt.fireEvent) {
+                $JssorDebug$.$Fail("Unable to fire event, no known technique.");
+            }
+        });
+
+        var evento;
+
+        if (document.createEvent) {
+            evento = document.createEvent("HTMLEvents");
+            evento.initEvent(eventName, false, false);
+            elmt.dispatchEvent(evento);
+        }
+        else {
+            var ieEventName = "on" + eventName;
+            evento = document.createEventObject();
+            //event.eventType = ieEventName;
+            //event.eventName = ieEventName;
+
+            elmt.fireEvent(ieEventName, evento);
+        }
+    };
+
+    _This.$AddEventBrowserMouseUp = function (handler, userCapture) {
+        _This.$AddEvent((IsBrowserIe9Earlier()) ? document : window, "mouseup", handler, userCapture);
+    };
+
+    _This.$RemoveEventBrowserMouseUp = function (handler, userCapture) {
+        _This.$RemoveEvent((IsBrowserIe9Earlier()) ? document : window, "mouseup", handler, userCapture);
+    };
+
+    //_This.$AddEventBrowserMouseDown = function (handler, userCapture) {
+    //    _This.$AddEvent((IsBrowserIe9Earlier()) ? document : window, "mousedown", handler, userCapture);
+    //};
+
+    //_This.$RemoveEventBrowserMouseDown = function (handler, userCapture) {
+    //    _This.$RemoveEvent((IsBrowserIe9Earlier()) ? document : window, "mousedown", handler, userCapture);
+    //};
+
+    _This.$CancelEvent = function (event) {
+        event = GetEvent(event);
+
+        // technique from:
+        // http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
+
+        if (event.preventDefault) {
+            event.preventDefault();     // W3C for preventing default
+        }
+
+        event.cancel = true;            // legacy for preventing default
+        event.returnValue = false;      // IE for preventing default
+    };
+
+    _This.$StopEvent = function (event) {
+        event = GetEvent(event);
+
+        // technique from:
+        // http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
+
+        if (event.stopPropagation) {
+            event.stopPropagation();    // W3C for stopping propagation
+        }
+
+        event.cancelBubble = true;      // IE for stopping propagation
+    };
+
+    _This.$CreateCallback = function (object, method) {
+        // create callback args
+        var initialArgs = [].slice.call(arguments, 2);
+
+        // create closure to apply method
+        var callback = function () {
+            // concatenate new args, but make a copy of initialArgs first
+            var args = initialArgs.concat([].slice.call(arguments, 0));
+
+            return method.apply(object, args);
+        };
+
+        //$JssorDebug$.$LiveStamp(callback, "callback_" + ($Jssor$.$GetNow() & 0xFFFFFF));
+
+        return callback;
+    };
+
+    var _Freeer;
+    _This.$FreeElement = function (elmt) {
+        if (!_Freeer)
+            _Freeer = _This.$CreateDiv();
+
+        if (elmt) {
+            $Jssor$.$AppendChild(_Freeer, elmt);
+            $Jssor$.$ClearInnerHtml(_Freeer);
+        }
+    };
+
+    _This.$InnerText = function (elmt, text) {
+        if (text == undefined)
+            return elmt.textContent || elmt.innerText;
+
+        var textNode = document.createTextNode(text);
+        _This.$ClearInnerHtml(elmt);
+        elmt.appendChild(textNode);
+    };
+    
+    _This.$InnerHtml = function (elmt, html) {
+        if (html == undefined)
+            return elmt.innerHTML;
+
+        elmt.innerHTML = html;
+    };
+
+    _This.$GetClientRect = function (elmt) {
+        var rect = elmt.getBoundingClientRect();
+
+        return { x: rect.left, y: rect.top, w: rect.right - rect.left, h: rect.bottom - rect.top };
+    };
+
+    _This.$ClearInnerHtml = function (elmt) {
+        elmt.innerHTML = "";
+    };
+
+    _This.$EncodeHtml = function (text) {
+        var div = _This.$CreateDiv();
+        _This.$InnerText(div, text);
+        return _This.$InnerHtml(div);
+    };
+
+    _This.$DecodeHtml = function (html) {
+        var div = _This.$CreateDiv();
+        _This.$InnerHtml(div, html);
+        return _This.$InnerText(div);
+    };
+
+    _This.$SelectElement = function (elmt) {
+        var userSelection;
+        if (window.getSelection) {
+            //W3C default
+            userSelection = window.getSelection();
+        }
+        var theRange = null;
+        if (document.createRange) {
+            theRange = document.createRange();
+            theRange.selectNode(elmt);
+        }
+        else {
+            theRange = document.body.createTextRange();
+            theRange.moveToElementText(elmt);
+            theRange.select();
+        }
+        //set user selection
+        if (userSelection)
+            userSelection.addRange(theRange);
+    };
+
+    _This.$DeselectElements = function () {
+        if (document.selection) {
+            document.selection.empty();
+        } else if (window.getSelection) {
+            window.getSelection().removeAllRanges();
+        }
+    };
+
+    _This.$Children = function (elmt) {
+        var children = [];
+
+        for (var tmpEl = elmt.firstChild; tmpEl; tmpEl = tmpEl.nextSibling) {
+            if (tmpEl.nodeType == 1) {
+                children.push(tmpEl);
+            }
+        }
+
+        return children;
+    };
+
+    function FindFirstChild(elmt, attrValue, attrName, deep) {
+        if (!attrName)
+            attrName = "u";
+
+        for (elmt = elmt ? elmt.firstChild : null; elmt; elmt = elmt.nextSibling) {
+            if (elmt.nodeType == 1) {
+                if (AttributeEx(elmt, attrName) == attrValue)
+                    return elmt;
+
+                if (deep) {
+                    var childRet = FindFirstChild(elmt, attrValue, attrName, deep);
+                    if (childRet)
+                        return childRet;
+                }
+            }
+        }
+    }
+
+    _This.$FindFirstChild = FindFirstChild;
+
+    function FindChildren(elmt, attrValue, attrName, deep) {
+        if (!attrName)
+            attrName = "u";
+
+        var ret = [];
+
+        for (elmt = elmt ? elmt.firstChild : null; elmt; elmt = elmt.nextSibling) {
+            if (elmt.nodeType == 1) {
+                if (AttributeEx(elmt, attrName) == attrValue)
+                    ret.push(elmt);
+
+                if (deep) {
+                    var childRet = FindChildren(elmt, attrValue, attrName, deep);
+                    if (childRet.length)
+                        ret = ret.concat(childRet);
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    _This.$FindChildren = FindChildren;
+
+    function FindFirstChildByTag(elmt, tagName, deep) {
+
+        for (elmt = elmt ? elmt.firstChild : null; elmt; elmt = elmt.nextSibling) {
+            if (elmt.nodeType == 1) {
+                if (elmt.tagName == tagName)
+                    return elmt;
+
+                if (deep) {
+                    var childRet = FindFirstChildByTag(elmt, tagName, deep);
+                    if (childRet)
+                        return childRet;
+                }
+            }
+        }
+    }
+
+    _This.$FindFirstChildByTag = FindFirstChildByTag;
+
+    function FindChildrenByTag(elmt, tagName, deep) {
+        var ret = [];
+
+        for (elmt = elmt ? elmt.firstChild : null; elmt; elmt = elmt.nextSibling) {
+            if (elmt.nodeType == 1) {
+                if (!tagName || elmt.tagName == tagName)
+                    ret.push(elmt);
+
+                if (deep) {
+                    var childRet = FindChildrenByTag(elmt, tagName, true);
+                    if (childRet.length)
+                        ret = ret.concat(childRet);
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    _This.$FindChildrenByTag = FindChildrenByTag;
+
+    _This.$GetElementsByTag = function (elmt, tagName) {
+        return elmt.getElementsByTagName(tagName);
+    };
+
+    function Extend(target) {
+        for (var i = 1; i < arguments.length; i++) {
+
+            var options = arguments[i];
+
+            // Only deal with non-null/undefined values
+            if (options) {
+                // Extend the base object
+                for (var name in options) {
+                    target[name] = options[name];
+                }
+            }
+        }
+
+        // Return the modified object
+        return target;
+    }
+
+    _This.$Extend = Extend;
+
+    function Unextend(target, options) {
+        $JssorDebug$.$Assert(options);
+
+        var unextended = {};
+
+        // Extend the base object
+        for (var name in target) {
+            if (target[name] != options[name]) {
+                unextended[name] = target[name];
+            }
+        }
+
+        // Return the modified object
+        return unextended;
+    }
+
+    _This.$Unextend = Unextend;
+
+    _This.$IsUndefined = function (obj) {
+        return type(obj) == "undefined";
+    };
+
+    _This.$IsFunction = function (obj) {
+        return type(obj) == "function";
+    };
+
+    _This.$IsArray = function (obj) {
+        return type(obj) == "array";
+    };
+
+    _This.$IsString = function (obj) {
+        return type(obj) == "string";
+    };
+
+    _This.$IsNumeric = function (obj) {
+        return !isNaN(ParseFloat(obj)) && isFinite(obj);
+    };
+
+    _This.$IsWindow = function (obj) {
+        return obj && obj == obj.window;
+    };
+
+    _This.$Type = type;
+
+    // args is for internal usage only
+    _This.$Each = each;
+
+    _This.$IsPlainObject = isPlainObject;
+
+    function CreateElement(tagName) {
+        return document.createElement(tagName);
+    }
+
+    _This.$CreateElement = CreateElement;
+
+    _This.$CreateDiv = function () {
+        return CreateElement("DIV", document);
+    };
+
+    _This.$CreateSpan = function () {
+        return CreateElement("SPAN", document);
+    };
+
+    _This.$EmptyFunction = function () { };
+
+    function Attribute(elmt, name, value) {
+        if (value == undefined)
+            return elmt.getAttribute(name);
+
+        elmt.setAttribute(name, value);
+    }
+
+    function AttributeEx(elmt, name) {
+        return Attribute(elmt, name) || Attribute(elmt, "data-" + name);
+    }
+
+    _This.$Attribute = Attribute;
+    _This.$AttributeEx = AttributeEx;
+
+    function ClassName(elmt, className) {
+        if (className == undefined)
+            return elmt.className;
+
+        elmt.className = className;
+    }
+
+    _This.$ClassName = ClassName;
+
+    function ToHash(array) {
+        var hash = {};
+
+        each(array, function (item) {
+            hash[item] = item;
+        });
+
+        return hash;
+    }
+
+    _This.$ToHash = ToHash;
+
+    function Join(separator, strings) {
+        ///	<param name="separator" type="String">
+        ///		The element to show the dialog around
+        ///	</param>
+        ///	<param name="strings" type="Array" value="['1']">
+        ///		The element to show the dialog around
+        ///	</param>
+
+        var joined = "";
+
+        each(strings, function (str) {
+            joined && (joined += separator);
+            joined += str;
+        });
+
+        return joined;
+    }
+
+    _This.$Join = Join;
+
+    _This.$AddClass = function (elmt, className) {
+        var newClassName = ClassName(elmt) + " " + className;
+        ClassName(elmt, Join(" ", ToHash(newClassName.match(REGEX_WHITESPACE_GLOBAL))));
+    };
+
+    _This.$RemoveClass = function (elmt, className) {
+        ClassName(elmt, Join(" ", _This.$Unextend(ToHash(ClassName(elmt).match(REGEX_WHITESPACE_GLOBAL)), ToHash(className.match(REGEX_WHITESPACE_GLOBAL)))));
+    };
+
+    _This.$ParentNode = function (elmt) {
+        return elmt.parentNode;
+    };
+
+    _This.$HideElement = function (elmt) {
+        _This.$CssDisplay(elmt, "none");
+    };
+
+    _This.$HideElements = function (elmts) {
+        for (var i = 0; i < elmts.length; i++) {
+            _This.$HideElement(elmts[i]);
+        }
+    };
+
+    _This.$ShowElement = function (elmt, show) {
+        _This.$CssDisplay(elmt, show == false ? "none" : "");
+    };
+
+    _This.$ShowElements = function (elmts) {
+        for (var i = 0; i < elmts.length; i++) {
+            _This.$ShowElement(elmts[i]);
+        }
+    };
+
+    _This.$RemoveAttribute = function (elmt, attrbuteName) {
+        elmt.removeAttribute(attrbuteName);
+    };
+
+    _This.$CanClearClip = function () {
+        return IsBrowserIE() && browserRuntimeVersion < 10;
+    };
+
+    _This.$SetStyleClip = function (elmt, clip) {
+        if (clip) {
+            elmt.style.clip = "rect(" + Math.round(clip.$Top) + "px " + Math.round(clip.$Right) + "px " + Math.round(clip.$Bottom) + "px " + Math.round(clip.$Left) + "px)";
+        }
+        else {
+            var cssText = elmt.style.cssText;
+            var clipRegs = [
+                new RegExp(/[\s]*clip: rect\(.*?\)[;]?/i),
+                new RegExp(/[\s]*cliptop: .*?[;]?/i),
+                new RegExp(/[\s]*clipright: .*?[;]?/i),
+                new RegExp(/[\s]*clipbottom: .*?[;]?/i),
+                new RegExp(/[\s]*clipleft: .*?[;]?/i)
+            ];
+
+            var newCssText = BuildNewCss(cssText, clipRegs, "");
+
+            $Jssor$.$CssCssText(elmt, newCssText);
+        }
+    };
+
+    _This.$GetNow = function () {
+        return new Date().getTime();
+    };
+
+    _This.$AppendChild = function (elmt, child) {
+        elmt.appendChild(child);
+    };
+
+    _This.$AppendChildren = function (elmt, children) {
+        each(children, function (child) {
+            _This.$AppendChild(elmt, child);
+        });
+    };
+
+    _This.$InsertBefore = function (elmt, child, refObject) {
+        elmt.insertBefore(child, refObject);
+    };
+
+    _This.$InsertAdjacentHtml = function (elmt, where, text) {
+        elmt.insertAdjacentHTML(where, text);
+    };
+
+    _This.$RemoveChild = function (elmt, child) {
+        elmt.removeChild(child);
+    };
+
+    _This.$RemoveChildren = function (elmt, children) {
+        each(children, function (child) {
+            _This.$RemoveChild(elmt, child);
+        });
+    };
+
+    _This.$ClearChildren = function (elmt) {
+        _This.$RemoveChildren(elmt, _This.$Children(elmt));
+    };
+
+    _This.$ParseInt = function (str, radix) {
+        return parseInt(str, radix || 10);
+    };
+
+    function ParseFloat(str) {
+        return parseFloat(str);
+    }
+
+    _This.$ParseFloat = ParseFloat;
+
+    _This.$IsChild = function (elmtA, elmtB) {
+        var body = document.body;
+        while (elmtB && elmtA != elmtB && body != elmtB) {
+            try {
+                elmtB = elmtB.parentNode;
+            } catch (e) {
+                // Firefox sometimes fires events for XUL elements, which throws
+                // a "permission denied" error. so this is not a child.
+                return false;
+            }
+        }
+        return elmtA == elmtB;
+    };
+
+    function CloneNode(elmt, deep) {
+        return elmt.cloneNode(deep);
+    }
+
+    _This.$CloneNode = CloneNode;
+
+    function TranslateTransition(transition) {
+        if (transition) {
+            var flyDirection = transition.$FlyDirection;
+
+            if (flyDirection & 1) {
+                transition.x = transition.$ScaleHorizontal || 1;
+            }
+            if (flyDirection & 2) {
+                transition.x = -transition.$ScaleHorizontal || -1;
+            }
+            if (flyDirection & 4) {
+                transition.y = transition.$ScaleVertical || 1;
+            }
+            if (flyDirection & 8) {
+                transition.y = -transition.$ScaleVertical || -1;
+            }
+
+            TranslateTransition(transition.$Brother);
+        }
+    }
+
+    _This.$TranslateTransitions = function (transitions) {
+        ///	<summary>
+        ///		For backward compatibility only.
+        ///	</summary>
+        if (transitions) {
+            for (var i = 0; i < transitions.length; i++) {
+                TranslateTransition(transitions[i]);
+            }
+            for (var name in transitions) {
+                TranslateTransition(transitions[name]);
+            }
+        }
+    };
+
+    function LoadImageCallback(callback, image, abort) {
+        image.onload = null;
+        image.abort = null;
+
+        if (callback)
+            callback(image, abort);
+    }
+
+    _This.$LoadImage = function (src, callback) {
+        if (IsBrowserOpera() && browserRuntimeVersion < 11.6 || !src) {
+            LoadImageCallback(callback, null);
+        }
+        else {
+            var image = new Image();
+            image.onload = _This.$CreateCallback(null, LoadImageCallback, callback, image);
+            image.onabort = _This.$CreateCallback(null, LoadImageCallback, callback, image, true);
+            image.src = src;
+        }
+    };
+
+    _This.$LoadImages = function (imageElmts, mainImageElmt, callback) {
+
+        var _ImageLoading = imageElmts.length + 1;
+
+        function LoadImageCompleteEventHandler(image, abort) {
+            _ImageLoading--;
+            if (mainImageElmt && image && image.src == mainImageElmt.src)
+                mainImageElmt = image;
+            !_ImageLoading && callback && callback(mainImageElmt);
+        }
+
+        each(imageElmts, function (imageElmt) {
+            $Jssor$.$LoadImage(imageElmt.src, LoadImageCompleteEventHandler);
+        });
+
+        LoadImageCompleteEventHandler();
+    };
+
+    _This.$BuildElement = function (template, tagName, replacer, createCopy) {
+        if (createCopy)
+            template = CloneNode(template, true);
+
+        var templateHolders = $Jssor$.$GetElementsByTag(template, tagName);
+        for (var j = templateHolders.length - 1; j > -1; j--) {
+            var templateHolder = templateHolders[j];
+            var replaceItem = CloneNode(replacer, true);
+            ClassName(replaceItem, ClassName(templateHolder));
+            $Jssor$.$CssCssText(replaceItem, templateHolder.style.cssText);
+
+            var thumbnailPlaceHolderParent = $Jssor$.$ParentNode(templateHolder);
+            $Jssor$.$InsertBefore(thumbnailPlaceHolderParent, replaceItem, templateHolder);
+            $Jssor$.$RemoveChild(thumbnailPlaceHolderParent, templateHolder);
+        }
+
+        return template;
+    };
+
+    var _MouseDownButtons;
+    function JssorButtonEx(elmt) {
+        var _Self = this;
+
+        var _OriginClassName;
+
+        var _IsMouseDown;   //class name 'dn'
+        var _IsActive;      //class name 'av'
+        var _IsDisabled;    //class name 'ds'
+
+        function Highlight() {
+            var className = _OriginClassName;
+
+            if (_IsDisabled) {
+                className += 'ds';
+            }
+            else if (_IsMouseDown) {
+                className += 'dn';
+            }
+            else if (_IsActive) {
+                className += "av";
+            }
+
+            ClassName(elmt, className);
+        }
+
+        function OnMouseDown(event) {
+            if (_IsDisabled) {
+                _This.$CancelEvent(event);
+            }
+            else {
+                _MouseDownButtons.push(_Self);
+
+                _IsMouseDown = true;
+
+                Highlight();
+            }
+        }
+
+        _Self.$MouseUp = function () {
+            ///	<summary>
+            ///		Internal member function, do not use it.
+            ///	</summary>
+            ///	<private />
+
+            _IsMouseDown = false;
+
+            Highlight();
+        };
+
+        _Self.$Activate = function (activate) {
+            if (activate != undefined) {
+                _IsActive = activate;
+
+                Highlight();
+            }
+            else {
+                return _IsActive;
+            }
+        };
+
+        _Self.$Enable = function (enable) {
+            if (enable != undefined) {
+                _IsDisabled = !enable;
+
+                Highlight();
+            }
+            else {
+                return !_IsDisabled;
+            }
+        };
+
+        //JssorButtonEx Constructor
+        {
+            elmt = _This.$GetElement(elmt);
+
+            if (!_MouseDownButtons) {
+                _This.$AddEventBrowserMouseUp(function () {
+                    var oldMouseDownButtons = _MouseDownButtons;
+                    _MouseDownButtons = [];
+
+                    each(oldMouseDownButtons, function (button) {
+                        button.$MouseUp();
+                    });
+                });
+
+                _MouseDownButtons = [];
+            }
+
+            _OriginClassName = ClassName(elmt);
+
+            $Jssor$.$AddEvent(elmt, "mousedown", OnMouseDown);
+        }
+    }
+
+    _This.$Buttonize = function (elmt) {
+        return new JssorButtonEx(elmt);
+    };
+
+    _This.$Css = Css;
+    _This.$CssN = CssN;
+    _This.$CssP = CssP;
+
+    _This.$CssOverflow = CssProxy("overflow");
+
+    _This.$CssTop = CssProxy("top", 2);
+    _This.$CssLeft = CssProxy("left", 2);
+    _This.$CssWidth = CssProxy("width", 2);
+    _This.$CssHeight = CssProxy("height", 2);
+    _This.$CssMarginLeft = CssProxy("marginLeft", 2);
+    _This.$CssMarginTop = CssProxy("marginTop", 2);
+    _This.$CssPosition = CssProxy("position");
+    _This.$CssDisplay = CssProxy("display");
+    _This.$CssZIndex = CssProxy("zIndex", 1);
+    _This.$CssFloat = function (elmt, float) {
+        return Css(elmt, IsBrowserIE() ? "styleFloat" : "cssFloat", float);
+    };
+    _This.$CssOpacity = function (elmt, opacity, ie9EarlierForce) {
+        if (opacity != undefined) {
+            SetStyleOpacity(elmt, opacity, ie9EarlierForce);
+        }
+        else {
+            return GetStyleOpacity(elmt);
+        }
+    };
+    _This.$CssCssText = function (elmt, text) {
+        if (text != undefined) {
+            elmt.style.cssText = text;
+        }
+        else {
+            return elmt.style.cssText;
+        }
+    };
+
+    var _StyleGetter = {
+        $Opacity: _This.$CssOpacity,
+        $Top: _This.$CssTop,
+        $Left: _This.$CssLeft,
+        $Width: _This.$CssWidth,
+        $Height: _This.$CssHeight,
+        $Position: _This.$CssPosition,
+        $Display: _This.$CssDisplay,
+        $ZIndex: _This.$CssZIndex
+    };
+
+    //var _StyleGetter = {
+    //    $Opacity: _This.$GetStyleOpacity,
+    //    $Top: _This.$GetStyleTop,
+    //    $Left: _This.$GetStyleLeft,
+    //    $Width: _This.$GetStyleWidth,
+    //    $Height: _This.$GetStyleHeight,
+    //    $Position: _This.$GetStylePosition,
+    //    $Display: _This.$GetStyleDisplay,
+    //    $ZIndex: _This.$GetStyleZIndex
+    //};
+
+    var _StyleSetterReserved;
+
+    //var _StyleSetterReserved = {
+    //    $Opacity: _This.$SetStyleOpacity,
+    //    $Top: _This.$SetStyleTop,
+    //    $Left: _This.$SetStyleLeft,
+    //    $Width: _This.$SetStyleWidth,
+    //    $Height: _This.$SetStyleHeight,
+    //    $Display: _This.$SetStyleDisplay,
+    //    $Clip: _This.$SetStyleClip,
+    //    $MarginLeft: _This.$SetStyleMarginLeft,
+    //    $MarginTop: _This.$SetStyleMarginTop,
+    //    $Transform: _This.$SetStyleTransform,
+    //    $Position: _This.$SetStylePosition,
+    //    $ZIndex: _This.$SetStyleZIndex
+    //};
+
+    function StyleSetter() {
+        if (!_StyleSetterReserved) {
+            _StyleSetterReserved = Extend({
+                $MarginTop: _This.$CssMarginTop,
+                $MarginLeft: _This.$CssMarginLeft,
+                $Clip: _This.$SetStyleClip,
+                $Transform: _This.$SetStyleTransform
+            }, _StyleGetter);
+        }
+        return _StyleSetterReserved;
+    }
+
+    function StyleSetterEx() {
+        StyleSetter();
+
+        //For Compression Only
+        _StyleSetterReserved.$Transform = _StyleSetterReserved.$Transform;
+
+        return _StyleSetterReserved;
+    }
+
+    _This.$StyleSetter = StyleSetter;
+
+    _This.$StyleSetterEx = StyleSetterEx;
+
+    _This.$GetStyles = function (elmt, originStyles) {
+        StyleSetter();
+
+        var styles = {};
+
+        each(originStyles, function (value, key) {
+            if (_StyleGetter[key]) {
+                styles[key] = _StyleGetter[key](elmt);
+            }
+        });
+
+        return styles;
+    };
+
+    _This.$SetStyles = function (elmt, styles) {
+        var styleSetter = StyleSetter();
+
+        each(styles, function (value, key) {
+            styleSetter[key] && styleSetter[key](elmt, value);
+        });
+    };
+
+    _This.$SetStylesEx = function (elmt, styles) {
+        StyleSetterEx();
+
+        _This.$SetStyles(elmt, styles);
+    };
+
+    $JssorMatrix$ = new function () {
+        var _ThisMatrix = this;
+
+        function Multiply(ma, mb) {
+            var acs = ma[0].length;
+            var rows = ma.length;
+            var cols = mb[0].length;
+
+            var matrix = [];
+
+            for (var r = 0; r < rows; r++) {
+                var row = matrix[r] = [];
+                for (var c = 0; c < cols; c++) {
+                    var unitValue = 0;
+
+                    for (var ac = 0; ac < acs; ac++) {
+                        unitValue += ma[r][ac] * mb[ac][c];
+                    }
+
+                    row[c] = unitValue;
+                }
+            }
+
+            return matrix;
+        }
+
+        _ThisMatrix.$ScaleX = function (matrix, sx) {
+            return _ThisMatrix.$ScaleXY(matrix, sx, 0);
+        };
+
+        _ThisMatrix.$ScaleY = function (matrix, sy) {
+            return _ThisMatrix.$ScaleXY(matrix, 0, sy);
+        };
+
+        _ThisMatrix.$ScaleXY = function (matrix, sx, sy) {
+            return Multiply(matrix, [[sx, 0], [0, sy]]);
+        };
+
+        _ThisMatrix.$TransformPoint = function (matrix, p) {
+            var pMatrix = Multiply(matrix, [[p.x], [p.y]]);
+
+            return new $JssorPoint$(pMatrix[0][0], pMatrix[1][0]);
+        };
+    };
+
+    _This.$CreateMatrix = function (alpha, scaleX, scaleY) {
+        var cos = Math.cos(alpha);
+        var sin = Math.sin(alpha);
+        //var r11 = cos;
+        //var r21 = sin;
+        //var r12 = -sin;
+        //var r22 = cos;
+
+        //var m11 = cos * scaleX;
+        //var m12 = -sin * scaleY;
+        //var m21 = sin * scaleX;
+        //var m22 = cos * scaleY;
+
+        return [[cos * scaleX, -sin * scaleY], [sin * scaleX, cos * scaleY]];
+    };
+
+    _This.$GetMatrixOffset = function (matrix, width, height) {
+        var p1 = $JssorMatrix$.$TransformPoint(matrix, new $JssorPoint$(-width / 2, -height / 2));
+        var p2 = $JssorMatrix$.$TransformPoint(matrix, new $JssorPoint$(width / 2, -height / 2));
+        var p3 = $JssorMatrix$.$TransformPoint(matrix, new $JssorPoint$(width / 2, height / 2));
+        var p4 = $JssorMatrix$.$TransformPoint(matrix, new $JssorPoint$(-width / 2, height / 2));
+
+        return new $JssorPoint$(Math.min(p1.x, p2.x, p3.x, p4.x) + width / 2, Math.min(p1.y, p2.y, p3.y, p4.y) + height / 2);
+    };
+};
+
+//for backward compatibility
+//var $JssorUtils$ = window.$JssorUtils$ = $Jssor$;
+
+//$JssorObject$
+var $JssorObject$ = window.$JssorObject$ = function () {
+    var _ThisObject = this;
+    // Fields
+
+    var _Listeners = []; // dictionary of eventName --> array of handlers
+    var _Listenees = [];
+
+    // Private Methods
+    function AddListener(eventName, handler) {
+
+        $JssorDebug$.$Execute(function () {
+            if (eventName == undefined || eventName == null)
+                throw new Error("param 'eventName' is null or empty.");
+
+            if (typeof (handler) != "function") {
+                throw "param 'handler' must be a function.";
+            }
+
+            $Jssor$.$Each(_Listeners, function (listener) {
+                if (listener.$EventName == eventName && listener.$Handler === handler) {
+                    throw new Error("The handler listened to the event already, cannot listen to the same event of the same object with the same handler twice.");
+                }
+            });
+        });
+
+        _Listeners.push({ $EventName: eventName, $Handler: handler });
+    }
+
+    function RemoveListener(eventName, handler) {
+
+        $JssorDebug$.$Execute(function () {
+            if (eventName == undefined || eventName == null)
+                throw new Error("param 'eventName' is null or empty.");
+
+            if (typeof (handler) != "function") {
+                throw "param 'handler' must be a function.";
+            }
+        });
+
+        $Jssor$.$Each(_Listeners, function (listener, index) {
+            if (listener.$EventName == eventName && listener.$Handler === handler) {
+                _Listeners.splice(index, 1);
+            }
+        });
+    }
+
+    function ClearListeners() {
+        _Listeners = [];
+    }
+
+    function ClearListenees() {
+
+        $Jssor$.$Each(_Listenees, function (listenee) {
+            $Jssor$.$RemoveEvent(listenee.$Obj, listenee.$EventName, listenee.$Handler);
+        });
+
+        _Listenees = [];
+    }
+
+    //Protected Methods
+    _ThisObject.$Listen = function (obj, eventName, handler, useCapture) {
+
+        $JssorDebug$.$Execute(function () {
+            if (!obj)
+                throw new Error("param 'obj' is null or empty.");
+
+            if (eventName == undefined || eventName == null)
+                throw new Error("param 'eventName' is null or empty.");
+
+            if (typeof (handler) != "function") {
+                throw "param 'handler' must be a function.";
+            }
+
+            $Jssor$.$Each(_Listenees, function (listenee) {
+                if (listenee.$Obj === obj && listenee.$EventName == eventName && listenee.$Handler === handler) {
+                    throw new Error("The handler listened to the event already, cannot listen to the same event of the same object with the same handler twice.");
+                }
+            });
+        });
+
+        $Jssor$.$AddEvent(obj, eventName, handler, useCapture);
+        _Listenees.push({ $Obj: obj, $EventName: eventName, $Handler: handler });
+    };
+
+    _ThisObject.$Unlisten = function (obj, eventName, handler) {
+
+        $JssorDebug$.$Execute(function () {
+            if (!obj)
+                throw new Error("param 'obj' is null or empty.");
+
+            if (eventName == undefined || eventName == null)
+                throw new Error("param 'eventName' is null or empty.");
+
+            if (typeof (handler) != "function") {
+                throw "param 'handler' must be a function.";
+            }
+        });
+
+        $Jssor$.$Each(_Listenees, function (listenee, index) {
+            if (listenee.$Obj === obj && listenee.$EventName == eventName && listenee.$Handler === handler) {
+                $Jssor$.$RemoveEvent(obj, eventName, handler);
+                _Listenees.splice(index, 1);
+            }
+        });
+    };
+
+    _ThisObject.$UnlistenAll = ClearListenees;
+
+    // Public Methods
+    _ThisObject.$On = _ThisObject.addEventListener = AddListener;
+
+    _ThisObject.$Off = _ThisObject.removeEventListener = RemoveListener;
+
+    _ThisObject.$TriggerEvent = function (eventName) {
+
+        var args = [].slice.call(arguments, 1);
+
+        $Jssor$.$Each(_Listeners, function (listener) {
+            try {
+                listener.$EventName == eventName && listener.$Handler.apply(window, args);
+            } catch (e) {
+                // handler threw an error, ignore, go on to next one
+                $JssorDebug$.$Error(e.name + " while executing " + eventName +
+                        " handler: " + e.message, e);
+            }
+        });
+    };
+
+    _ThisObject.$Destroy = function () {
+        ClearListenees();
+        ClearListeners();
+
+        for (var name in _ThisObject)
+            delete _ThisObject[name];
+    };
+
+    $JssorDebug$.$C_AbstractClass(_ThisObject);
+};
+
+$JssorAnimator$ = function (delay, duration, options, elmt, fromStyles, toStyles) {
+    delay = delay || 0;
+
+    var _ThisAnimator = this;
+    var _AutoPlay;
+    var _Hiden;
+    var _CombineMode;
+    var _PlayToPosition;
+    var _PlayDirection;
+    var _NoStop;
+    var _TimeStampLastFrame = 0;
+
+    var _SubEasings;
+    var _SubRounds;
+    var _SubDurings;
+    var _Callback;
+
+    var _Position_Current = 0;
+    var _Position_Display = 0;
+    var _Hooked;
+
+    var _Position_InnerBegin = delay;
+    var _Position_InnerEnd = delay + duration;
+    var _Position_OuterBegin;
+    var _Position_OuterEnd;
+    var _LoopLength;
+
+    var _NestedAnimators = [];
+    var _StyleSetter;
+
+    function GetPositionRange(position, begin, end) {
+        var range = 0;
+
+        if (position < begin)
+            range = -1;
+
+        else if (position > end)
+            range = 1;
+
+        return range;
+    }
+
+    function GetInnerPositionRange(position) {
+        return GetPositionRange(position, _Position_InnerBegin, _Position_InnerEnd);
+    }
+
+    function GetOuterPositionRange(position) {
+        return GetPositionRange(position, _Position_OuterBegin, _Position_OuterEnd);
+    }
+
+    function Shift(offset) {
+        _Position_OuterBegin += offset;
+        _Position_OuterEnd += offset;
+        _Position_InnerBegin += offset;
+        _Position_InnerEnd += offset;
+
+        _Position_Current += offset;
+        _Position_Display += offset;
+
+        $Jssor$.$Each(_NestedAnimators, function (animator) {
+            animator, animator.$Shift(offset);
+        });
+    }
+
+    function Locate(position, relative) {
+        var offset = position - _Position_OuterBegin + delay * relative;
+
+        Shift(offset);
+
+        //$JssorDebug$.$Execute(function () {
+        //    _ThisAnimator.$Position_InnerBegin = _Position_InnerBegin;
+        //    _ThisAnimator.$Position_InnerEnd = _Position_InnerEnd;
+        //    _ThisAnimator.$Position_OuterBegin = _Position_OuterBegin;
+        //    _ThisAnimator.$Position_OuterEnd = _Position_OuterEnd;
+        //});
+
+        return _Position_OuterEnd;
+    }
+
+    function GoToPosition(positionOuter, force) {
+        var trimedPositionOuter = positionOuter;
+
+        if (_LoopLength && (trimedPositionOuter >= _Position_OuterEnd || trimedPositionOuter <= _Position_OuterBegin)) {
+            trimedPositionOuter = ((trimedPositionOuter - _Position_OuterBegin) % _LoopLength + _LoopLength) % _LoopLength + _Position_OuterBegin;
+        }
+
+        if (!_Hooked || _NoStop || force || _Position_Current != trimedPositionOuter) {
+
+            var positionToDisplay = Math.min(trimedPositionOuter, _Position_OuterEnd);
+            positionToDisplay = Math.max(positionToDisplay, _Position_OuterBegin);
+
+            if (!_Hooked || _NoStop || force || positionToDisplay != _Position_Display) {
+                if (toStyles) {
+                    var currentStyles = toStyles;
+
+                    if (fromStyles) {
+                        var interPosition = (positionToDisplay - _Position_InnerBegin) / (duration || 1);
+                        if (options.$Optimize && $Jssor$.$IsBrowserChrome() && duration) {
+                            interPosition = Math.round(interPosition / 16 * duration) * 16 / duration;
+                        }
+                        if (options.$Reverse)
+                            interPosition = 1 - interPosition;
+
+                        currentStyles = {};
+
+                        for (var key in toStyles) {
+                            var round = _SubRounds[key] || 1;
+                            var during = _SubDurings[key] || [0, 1];
+                            var propertyInterPosition = (interPosition - during[0]) / during[1];
+                            propertyInterPosition = Math.min(Math.max(propertyInterPosition, 0), 1);
+                            propertyInterPosition = propertyInterPosition * round;
+                            var floorPosition = Math.floor(propertyInterPosition);
+                            if (propertyInterPosition != floorPosition)
+                                propertyInterPosition -= floorPosition;
+
+                            var easing = _SubEasings[key] || _SubEasings.$Default;
+                            var easingValue = easing(propertyInterPosition);
+                            var currentPropertyValue;
+                            var value = fromStyles[key];
+                            var toValue = toStyles[key];
+
+                            if ($Jssor$.$IsNumeric(toValue)) {
+                                currentPropertyValue = value + (toValue - value) * easingValue;
+                            }
+                            else {
+                                currentPropertyValue = $Jssor$.$Extend({ $Offset: {} }, fromStyles[key]);
+
+                                $Jssor$.$Each(toValue.$Offset, function (rectX, n) {
+                                    var offsetValue = rectX * easingValue;
+                                    currentPropertyValue.$Offset[n] = offsetValue;
+                                    currentPropertyValue[n] += offsetValue;
+                                });
+                            }
+                            currentStyles[key] = currentPropertyValue;
+                        }
+                    }
+
+                    if (fromStyles.$Zoom) {
+                        currentStyles.$Transform = { $Rotate: currentStyles.$Rotate || 0, $Scale: currentStyles.$Zoom, $OriginalWidth: options.$OriginalWidth, $OriginalHeight: options.$OriginalHeight };
+                    }
+
+                    if (toStyles.$Clip && options.$Move) {
+                        var styleFrameNClipOffset = currentStyles.$Clip.$Offset;
+
+                        var offsetY = (styleFrameNClipOffset.$Top || 0) + (styleFrameNClipOffset.$Bottom || 0);
+                        var offsetX = (styleFrameNClipOffset.$Left || 0) + (styleFrameNClipOffset.$Right || 0);
+
+                        currentStyles.$Left = (currentStyles.$Left || 0) + offsetX;
+                        currentStyles.$Top = (currentStyles.$Top || 0) + offsetY;
+                        currentStyles.$Clip.$Left -= offsetX;
+                        currentStyles.$Clip.$Right -= offsetX;
+                        currentStyles.$Clip.$Top -= offsetY;
+                        currentStyles.$Clip.$Bottom -= offsetY;
+                    }
+
+                    if (currentStyles.$Clip && $Jssor$.$CanClearClip() && !currentStyles.$Clip.$Top && !currentStyles.$Clip.$Left && (currentStyles.$Clip.$Right == options.$OriginalWidth) && (currentStyles.$Clip.$Bottom == options.$OriginalHeight))
+                        currentStyles.$Clip = null;
+
+                    $Jssor$.$Each(currentStyles, function (value, key) {
+                        _StyleSetter[key] && _StyleSetter[key](elmt, value);
+                    });
+                }
+
+                _ThisAnimator.$OnInnerOffsetChange(_Position_Display - _Position_InnerBegin, positionToDisplay - _Position_InnerBegin);
+            }
+
+            _Position_Display = positionToDisplay;
+
+            $Jssor$.$Each(_NestedAnimators, function (animator, i) {
+                var nestedAnimator = positionOuter < _Position_Current ? _NestedAnimators[_NestedAnimators.length - i - 1] : animator;
+                nestedAnimator.$GoToPosition(positionOuter, force);
+            });
+
+            var positionOld = _Position_Current;
+            var positionNew = positionOuter;
+
+            _Position_Current = trimedPositionOuter;
+            _Hooked = true;
+
+            _ThisAnimator.$OnPositionChange(positionOld, positionNew);
+        }
+    }
+
+    function Join(animator, combineMode) {
+        ///	<summary>
+        ///		Combine another animator as nested animator
+        ///	</summary>
+        ///	<param name="animator" type="$JssorAnimator$">
+        ///		An instance of $JssorAnimator$
+        ///	</param>
+        ///	<param name="combineMode" type="int">
+        ///		0: parallel - place the animator parallel to this animator.
+        ///		1: chain - chain the animator at the _Position_InnerEnd of this animator.
+        ///	</param>
+        $JssorDebug$.$Execute(function () {
+            if (combineMode !== 0 && combineMode !== 1)
+                $JssorDebug$.$Fail("Argument out of range, the value of 'combineMode' should be either 0 or 1.");
+        });
+
+        if (combineMode)
+            animator.$Locate(_Position_OuterEnd, 1);
+
+        _Position_OuterEnd = Math.max(_Position_OuterEnd, animator.$GetPosition_OuterEnd());
+        _NestedAnimators.push(animator);
+    }
+
+    function PlayFrame() {
+        if (_AutoPlay) {
+            var now = $Jssor$.$GetNow();
+            //var timeOffset = Math.min(now - _TimeStampLastFrame, $Jssor$.$IsBrowserOpera() ? 80 : 20);
+            var timeOffset = Math.min(now - _TimeStampLastFrame, 80);
+            var timePosition = _Position_Current + timeOffset * _PlayDirection;
+            _TimeStampLastFrame = now;
+
+            if (timePosition * _PlayDirection >= _PlayToPosition * _PlayDirection)
+                timePosition = _PlayToPosition;
+
+            GoToPosition(timePosition);
+
+            if (!_NoStop && timePosition * _PlayDirection >= _PlayToPosition * _PlayDirection) {
+                Stop(_Callback);
+            }
+            else {
+                $Jssor$.$Delay(PlayFrame, options.$Interval);
+            }
+        }
+    }
+
+    function PlayToPosition(toPosition, callback, noStop) {
+        if (!_AutoPlay) {
+            _AutoPlay = true;
+            _NoStop = noStop
+            _Callback = callback;
+            toPosition = Math.max(toPosition, _Position_OuterBegin);
+            toPosition = Math.min(toPosition, _Position_OuterEnd);
+            _PlayToPosition = toPosition;
+            _PlayDirection = _PlayToPosition < _Position_Current ? -1 : 1;
+            _ThisAnimator.$OnStart();
+            _TimeStampLastFrame = $Jssor$.$GetNow();
+            PlayFrame();
+        }
+    }
+
+    function Stop(callback) {
+        if (_AutoPlay) {
+            _NoStop = _AutoPlay = _Callback = false;
+            _ThisAnimator.$OnStop();
+
+            if (callback)
+                callback();
+        }
+    }
+
+    _ThisAnimator.$Play = function (positionLength, callback, noStop) {
+        PlayToPosition(positionLength ? _Position_Current + positionLength : _Position_OuterEnd, callback, noStop);
+    };
+
+    _ThisAnimator.$PlayToPosition = PlayToPosition;
+
+    _ThisAnimator.$PlayToBegin = function (callback, noStop) {
+        PlayToPosition(_Position_OuterBegin, callback, noStop);
+    };
+
+    _ThisAnimator.$PlayToEnd = function (callback, noStop) {
+        PlayToPosition(_Position_OuterEnd, callback, noStop);
+    };
+
+    _ThisAnimator.$Stop = Stop;
+
+    _ThisAnimator.$Continue = function (toPosition) {
+        PlayToPosition(toPosition);
+    };
+
+    _ThisAnimator.$GetPosition = function () {
+        return _Position_Current;
+    };
+
+    _ThisAnimator.$GetPlayToPosition = function () {
+        return _PlayToPosition;
+    };
+
+    _ThisAnimator.$GetPosition_Display = function () {
+        return _Position_Display;
+    };
+
+    _ThisAnimator.$GoToPosition = GoToPosition;
+
+    _ThisAnimator.$GoToBegin = function () {
+        GoToPosition(_Position_OuterBegin, true);
+    };
+
+    _ThisAnimator.$GoToEnd = function () {
+        GoToPosition(_Position_OuterEnd, true);
+    };
+
+    _ThisAnimator.$Move = function (offset) {
+        GoToPosition(_Position_Current + offset);
+    };
+
+    _ThisAnimator.$CombineMode = function () {
+        return _CombineMode;
+    };
+
+    _ThisAnimator.$GetDuration = function () {
+        return duration;
+    };
+
+    _ThisAnimator.$IsPlaying = function () {
+        return _AutoPlay;
+    };
+
+    _ThisAnimator.$IsOnTheWay = function () {
+        return _Position_Current > _Position_InnerBegin && _Position_Current <= _Position_InnerEnd;
+    };
+
+    _ThisAnimator.$SetLoopLength = function (length) {
+        _LoopLength = length;
+    };
+
+    _ThisAnimator.$Locate = Locate;
+
+    _ThisAnimator.$Shift = Shift;
+
+    _ThisAnimator.$Join = Join;
+
+    _ThisAnimator.$Combine = function (animator) {
+        ///	<summary>
+        ///		Combine another animator parallel to this animator
+        ///	</summary>
+        ///	<param name="animator" type="$JssorAnimator$">
+        ///		An instance of $JssorAnimator$
+        ///	</param>
+        Join(animator, 0);
+    };
+
+    _ThisAnimator.$Chain = function (animator) {
+        ///	<summary>
+        ///		Chain another animator at the _Position_InnerEnd of this animator
+        ///	</summary>
+        ///	<param name="animator" type="$JssorAnimator$">
+        ///		An instance of $JssorAnimator$
+        ///	</param>
+        Join(animator, 1);
+    };
+
+    _ThisAnimator.$GetPosition_InnerBegin = function () {
+        ///	<summary>
+        ///		Internal member function, do not use it.
+        ///	</summary>
+        ///	<private />
+        ///	<returns type="int" />
+        return _Position_InnerBegin;
+    };
+
+    _ThisAnimator.$GetPosition_InnerEnd = function () {
+        ///	<summary>
+        ///		Internal member function, do not use it.
+        ///	</summary>
+        ///	<private />
+        ///	<returns type="int" />
+        return _Position_InnerEnd;
+    };
+
+    _ThisAnimator.$GetPosition_OuterBegin = function () {
+        ///	<summary>
+        ///		Internal member function, do not use it.
+        ///	</summary>
+        ///	<private />
+        ///	<returns type="int" />
+        return _Position_OuterBegin;
+    };
+
+    _ThisAnimator.$GetPosition_OuterEnd = function () {
+        ///	<summary>
+        ///		Internal member function, do not use it.
+        ///	</summary>
+        ///	<private />
+        ///	<returns type="int" />
+        return _Position_OuterEnd;
+    };
+
+    _ThisAnimator.$OnPositionChange = _ThisAnimator.$OnStart = _ThisAnimator.$OnStop = _ThisAnimator.$OnInnerOffsetChange = $Jssor$.$EmptyFunction;
+    _ThisAnimator.$Version = $Jssor$.$GetNow();
+
+    //Constructor  1
+    {
+        options = $Jssor$.$Extend({
+            $Interval: 16
+        }, options);
+
+        //Sodo statement, for development time intellisence only
+        $JssorDebug$.$Execute(function () {
+            options = $Jssor$.$Extend({
+                $LoopLength: undefined,
+                $Setter: undefined,
+                $Easing: undefined
+            }, options);
+        });
+
+        _LoopLength = options.$LoopLength;
+
+        _StyleSetter = $Jssor$.$Extend({}, $Jssor$.$StyleSetter(), options.$Setter);
+
+        _Position_OuterBegin = _Position_InnerBegin = delay;
+        _Position_OuterEnd = _Position_InnerEnd = delay + duration;
+
+        var _SubRounds = options.$Round || {};
+        var _SubDurings = options.$During || {};
+        _SubEasings = $Jssor$.$Extend({ $Default: $Jssor$.$IsFunction(options.$Easing) && options.$Easing || $JssorEasing$.$EaseSwing }, options.$Easing);
+    }
+};
+
+function $JssorPlayerClass$() {
+
+    var _ThisPlayer = this;
+    var _PlayerControllers = [];
+
+    function PlayerController(playerElement) {
+        var _SelfPlayerController = this;
+        var _PlayerInstance;
+        var _PlayerInstantces = [];
+
+        function OnPlayerInstanceDataAvailable(event) {
+            var srcElement = $Jssor$.$EventSrc(event);
+            _PlayerInstance = srcElement.pInstance;
+
+            $Jssor$.$RemoveEvent(srcElement, "dataavailable", OnPlayerInstanceDataAvailable);
+            $Jssor$.$Each(_PlayerInstantces, function (playerInstance) {
+                if (playerInstance != _PlayerInstance) {
+                    playerInstance.$Remove();
+                }
+            });
+
+            playerElement.pTagName = _PlayerInstance.tagName;
+            _PlayerInstantces = null;
+        }
+
+        function HandlePlayerInstance(playerInstanceElement) {
+            var playerHandler;
+
+            if (!playerInstanceElement.pInstance) {
+                var playerHandlerAttribute = $Jssor$.$AttributeEx(playerInstanceElement, "pHandler");
+
+                if ($JssorPlayer$[playerHandlerAttribute]) {
+                    $Jssor$.$AddEvent(playerInstanceElement, "dataavailable", OnPlayerInstanceDataAvailable);
+                    playerHandler = new $JssorPlayer$[playerHandlerAttribute](playerElement, playerInstanceElement);
+                    _PlayerInstantces.push(playerHandler);
+
+                    $JssorDebug$.$Execute(function () {
+                        if ($Jssor$.$Type(playerHandler.$Remove) != "function") {
+                            $JssorDebug$.$Fail("'pRemove' interface not implemented for player handler '" + playerHandlerAttribute + "'.");
+                        }
+                    });
+                }
+            }
+
+            return playerHandler;
+        }
+
+        _SelfPlayerController.$InitPlayerController = function () {
+            if (!playerElement.pInstance && !HandlePlayerInstance(playerElement)) {
+
+                var playerInstanceElements = $Jssor$.$Children(playerElement);
+
+                $Jssor$.$Each(playerInstanceElements, function (playerInstanceElement) {
+                    HandlePlayerInstance(playerInstanceElement);
+                });
+            }
+        };
+    }
+
+    _ThisPlayer.$EVT_SWITCH = 21;
+
+    _ThisPlayer.$FetchPlayers = function (elmt) {
+        elmt = elmt || document.body;
+
+        var playerElements = $Jssor$.$FindChildren(elmt, "player", null, true);
+
+        $Jssor$.$Each(playerElements, function (playerElement) {
+            if (!_PlayerControllers[playerElement.pId]) {
+                playerElement.pId = _PlayerControllers.length;
+                _PlayerControllers.push(new PlayerController(playerElement));
+            }
+            var playerController = _PlayerControllers[playerElement.pId];
+            playerController.$InitPlayerController();
+        });
+    };
+}
\ No newline at end of file
diff --git a/doc/_static/jssor.slider.js b/doc/_static/jssor.slider.js
new file mode 100644
index 0000000..77c5acc
--- /dev/null
+++ b/doc/_static/jssor.slider.js
@@ -0,0 +1,4081 @@
+/// <reference path="Jssor.js" />
+
+/*
+* Jssor.Slider 18.0
+* http://www.jssor.com/
+* 
+* TERMS OF USE - Jssor.Slider
+* 
+* Copyright 2014 Jssor
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+* 
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+* 
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+var $JssorSlider$;
+var $JssorSlideshowFormations$ = window.$JssorSlideshowFormations$ = {};
+var $JssorSlideshowRunner$;
+
+new function () {
+    //Constants +++++++
+
+    var COLUMN_INCREASE = 0;
+    var COLUMN_DECREASE = 1;
+    var ROW_INCREASE = 2;
+    var ROW_DECREASE = 3;
+
+    var DIRECTION_HORIZONTAL = 0x0003;
+    var DIRECTION_VERTICAL = 0x000C;
+
+    var TO_LEFT = 0x0001;
+    var TO_RIGHT = 0x0002;
+    var TO_TOP = 0x0004;
+    var TO_BOTTOM = 0x0008;
+
+    var FROM_LEFT = 0x0100;
+    var FROM_TOP = 0x0200;
+    var FROM_RIGHT = 0x0400;
+    var FROM_BOTTOM = 0x0800;
+
+    var ASSEMBLY_BOTTOM_LEFT = FROM_BOTTOM + TO_LEFT;
+    var ASSEMBLY_BOTTOM_RIGHT = FROM_BOTTOM + TO_RIGHT;
+    var ASSEMBLY_TOP_LEFT = FROM_TOP + TO_LEFT;
+    var ASSEMBLY_TOP_RIGHT = FROM_TOP + TO_RIGHT;
+    var ASSEMBLY_LEFT_TOP = FROM_LEFT + TO_TOP;
+    var ASSEMBLY_LEFT_BOTTOM = FROM_LEFT + TO_BOTTOM;
+    var ASSEMBLY_RIGHT_TOP = FROM_RIGHT + TO_TOP;
+    var ASSEMBLY_RIGHT_BOTTOM = FROM_RIGHT + TO_BOTTOM;
+
+    //Constants -------
+
+    //Formation Definition +++++++
+    function isToLeft(roadValue) {
+        return (roadValue & TO_LEFT) == TO_LEFT;
+    }
+
+    function isToRight(roadValue) {
+        return (roadValue & TO_RIGHT) == TO_RIGHT;
+    }
+
+    function isToTop(roadValue) {
+        return (roadValue & TO_TOP) == TO_TOP;
+    }
+
+    function isToBottom(roadValue) {
+        return (roadValue & TO_BOTTOM) == TO_BOTTOM;
+    }
+
+    function PushFormationOrder(arr, order, formationItem) {
+        formationItem.push(order);
+        arr[order] = arr[order] || [];
+        arr[order].push(formationItem);
+    }
+
+    $JssorSlideshowFormations$.$FormationStraight = function (transition) {
+        var cols = transition.$Cols;
+        var rows = transition.$Rows;
+        var formationDirection = transition.$Assembly;
+        var count = transition.$Count;
+        var a = [];
+        var i = 0;
+        var col = 0;
+        var r = 0;
+        var cl = cols - 1;
+        var rl = rows - 1;
+        var il = count - 1;
+        var cr;
+        var order;
+        for (r = 0; r < rows; r++) {
+            for (col = 0; col < cols; col++) {
+                cr = r + ',' + col;
+                switch (formationDirection) {
+                    case ASSEMBLY_BOTTOM_LEFT:
+                        order = il - (col * rows + (rl - r));
+                        break;
+                    case ASSEMBLY_RIGHT_TOP:
+                        order = il - (r * cols + (cl - col));
+                        break;
+                    case ASSEMBLY_TOP_LEFT:
+                        order = il - (col * rows + r);
+                    case ASSEMBLY_LEFT_TOP:
+                        order = il - (r * cols + col);
+                        break;
+                    case ASSEMBLY_BOTTOM_RIGHT:
+                        order = col * rows + r;
+                        break;
+                    case ASSEMBLY_LEFT_BOTTOM:
+                        order = r * cols + (cl - col);
+                        break;
+                    case ASSEMBLY_TOP_RIGHT:
+                        order = col * rows + (rl - r);
+                        break;
+                    default:
+                        order = r * cols + col;
+                        break; //ASSEMBLY_RIGHT_BOTTOM
+                }
+                PushFormationOrder(a, order, [r, col]);
+            }
+        }
+
+        return a;
+    };
+
+    $JssorSlideshowFormations$.$FormationSwirl = function (transition) {
+        var cols = transition.$Cols;
+        var rows = transition.$Rows;
+        var formationDirection = transition.$Assembly;
+        var count = transition.$Count;
+        var a = [];
+        var hit = [];
+        var i = 0;
+        var col = 0;
+        var r = 0;
+        var cl = cols - 1;
+        var rl = rows - 1;
+        var il = count - 1;
+        var cr;
+        var courses;
+        var course = 0;
+        switch (formationDirection) {
+            case ASSEMBLY_BOTTOM_LEFT:
+                col = cl;
+                r = 0;
+                courses = [ROW_INCREASE, COLUMN_DECREASE, ROW_DECREASE, COLUMN_INCREASE];
+                break;
+            case ASSEMBLY_RIGHT_TOP:
+                col = 0;
+                r = rl;
+                courses = [COLUMN_INCREASE, ROW_DECREASE, COLUMN_DECREASE, ROW_INCREASE];
+                break;
+            case ASSEMBLY_TOP_LEFT:
+                col = cl;
+                r = rl;
+                courses = [ROW_DECREASE, COLUMN_DECREASE, ROW_INCREASE, COLUMN_INCREASE];
+                break;
+            case ASSEMBLY_LEFT_TOP:
+                col = cl;
+                r = rl;
+                courses = [COLUMN_DECREASE, ROW_DECREASE, COLUMN_INCREASE, ROW_INCREASE];
+                break;
+            case ASSEMBLY_BOTTOM_RIGHT:
+                col = 0;
+                r = 0;
+                courses = [ROW_INCREASE, COLUMN_INCREASE, ROW_DECREASE, COLUMN_DECREASE];
+                break;
+            case ASSEMBLY_LEFT_BOTTOM:
+                col = cl;
+                r = 0;
+                courses = [COLUMN_DECREASE, ROW_INCREASE, COLUMN_INCREASE, ROW_DECREASE];
+                break;
+            case ASSEMBLY_TOP_RIGHT:
+                col = 0;
+                r = rl;
+                courses = [ROW_DECREASE, COLUMN_INCREASE, ROW_INCREASE, COLUMN_DECREASE];
+                break;
+            default:
+                col = 0;
+                r = 0;
+                courses = [COLUMN_INCREASE, ROW_INCREASE, COLUMN_DECREASE, ROW_DECREASE];
+                break; //ASSEMBLY_RIGHT_BOTTOM
+        }
+        i = 0;
+        while (i < count) {
+            cr = r + ',' + col;
+            if (col >= 0 && col < cols && r >= 0 && r < rows && !hit[cr]) {
+                //a[cr] = i++;
+                hit[cr] = true;
+                PushFormationOrder(a, i++, [r, col]);
+            }
+            else {
+                switch (courses[course++ % courses.length]) {
+                    case COLUMN_INCREASE:
+                        col--;
+                        break;
+                    case ROW_INCREASE:
+                        r--;
+                        break;
+                    case COLUMN_DECREASE:
+                        col++;
+                        break;
+                    case ROW_DECREASE:
+                        r++;
+                        break;
+                }
+            }
+
+            switch (courses[course % courses.length]) {
+                case COLUMN_INCREASE:
+                    col++;
+                    break;
+                case ROW_INCREASE:
+                    r++;
+                    break;
+                case COLUMN_DECREASE:
+                    col--;
+                    break;
+                case ROW_DECREASE:
+                    r--;
+                    break;
+            }
+        }
+        return a;
+    };
+
+    $JssorSlideshowFormations$.$FormationZigZag = function (transition) {
+        var cols = transition.$Cols;
+        var rows = transition.$Rows;
+        var formationDirection = transition.$Assembly;
+        var count = transition.$Count;
+        var a = [];
+        var i = 0;
+        var col = 0;
+        var r = 0;
+        var cl = cols - 1;
+        var rl = rows - 1;
+        var il = count - 1;
+        var cr;
+        var courses;
+        var course = 0;
+        switch (formationDirection) {
+            case ASSEMBLY_BOTTOM_LEFT:
+                col = cl;
+                r = 0;
+                courses = [ROW_INCREASE, COLUMN_DECREASE, ROW_DECREASE, COLUMN_DECREASE];
+                break;
+            case ASSEMBLY_RIGHT_TOP:
+                col = 0;
+                r = rl;
+                courses = [COLUMN_INCREASE, ROW_DECREASE, COLUMN_DECREASE, ROW_DECREASE];
+                break;
+            case ASSEMBLY_TOP_LEFT:
+                col = cl;
+                r = rl;
+                courses = [ROW_DECREASE, COLUMN_DECREASE, ROW_INCREASE, COLUMN_DECREASE];
+                break;
+            case ASSEMBLY_LEFT_TOP:
+                col = cl;
+                r = rl;
+                courses = [COLUMN_DECREASE, ROW_DECREASE, COLUMN_INCREASE, ROW_DECREASE];
+                break;
+            case ASSEMBLY_BOTTOM_RIGHT:
+                col = 0;
+                r = 0;
+                courses = [ROW_INCREASE, COLUMN_INCREASE, ROW_DECREASE, COLUMN_INCREASE];
+                break;
+            case ASSEMBLY_LEFT_BOTTOM:
+                col = cl;
+                r = 0;
+                courses = [COLUMN_DECREASE, ROW_INCREASE, COLUMN_INCREASE, ROW_INCREASE];
+                break;
+            case ASSEMBLY_TOP_RIGHT:
+                col = 0;
+                r = rl;
+                courses = [ROW_DECREASE, COLUMN_INCREASE, ROW_INCREASE, COLUMN_INCREASE];
+                break;
+            default:
+                col = 0;
+                r = 0;
+                courses = [COLUMN_INCREASE, ROW_INCREASE, COLUMN_DECREASE, ROW_INCREASE];
+                break; //ASSEMBLY_RIGHT_BOTTOM
+        }
+        i = 0;
+        while (i < count) {
+            cr = r + ',' + col;
+            if (col >= 0 && col < cols && r >= 0 && r < rows && typeof (a[cr]) == 'undefined') {
+                PushFormationOrder(a, i++, [r, col]);
+                //a[cr] = i++;
+                switch (courses[course % courses.length]) {
+                    case COLUMN_INCREASE:
+                        col++;
+                        break;
+                    case ROW_INCREASE:
+                        r++;
+                        break;
+                    case COLUMN_DECREASE:
+                        col--;
+                        break;
+                    case ROW_DECREASE:
+                        r--;
+                        break;
+                }
+            }
+            else {
+                switch (courses[course++ % courses.length]) {
+                    case COLUMN_INCREASE:
+                        col--;
+                        break;
+                    case ROW_INCREASE:
+                        r--;
+                        break;
+                    case COLUMN_DECREASE:
+                        col++;
+                        break;
+                    case ROW_DECREASE:
+                        r++;
+                        break;
+                }
+                switch (courses[course++ % courses.length]) {
+                    case COLUMN_INCREASE:
+                        col++;
+                        break;
+                    case ROW_INCREASE:
+                        r++;
+                        break;
+                    case COLUMN_DECREASE:
+                        col--;
+                        break;
+                    case ROW_DECREASE:
+                        r--;
+                        break;
+                }
+            }
+        }
+        return a;
+    };
+
+    $JssorSlideshowFormations$.$FormationStraightStairs = function (transition) {
+        var cols = transition.$Cols;
+        var rows = transition.$Rows;
+        var formationDirection = transition.$Assembly;
+        var count = transition.$Count;
+        var a = [];
+        var i = 0;
+        var col = 0;
+        var r = 0;
+        var cl = cols - 1;
+        var rl = rows - 1;
+        var il = count - 1;
+        var cr;
+        switch (formationDirection) {
+            case ASSEMBLY_BOTTOM_LEFT:
+            case ASSEMBLY_TOP_RIGHT:
+            case ASSEMBLY_TOP_LEFT:
+            case ASSEMBLY_BOTTOM_RIGHT:
+                var C = 0;
+                var R = 0;
+                break;
+            case ASSEMBLY_LEFT_BOTTOM:
+            case ASSEMBLY_RIGHT_TOP:
+            case ASSEMBLY_LEFT_TOP:
+            case ASSEMBLY_RIGHT_BOTTOM:
+                var C = cl;
+                var R = 0;
+                break;
+            default:
+                formationDirection = ASSEMBLY_RIGHT_BOTTOM;
+                var C = cl;
+                var R = 0;
+                break;
+        }
+        col = C;
+        r = R;
+        while (i < count) {
+            cr = r + ',' + col;
+            if (isToTop(formationDirection) || isToRight(formationDirection)) {
+                PushFormationOrder(a, il - i++, [r, col]);
+                //a[cr] = il - i++;
+            }
+            else {
+                PushFormationOrder(a, i++, [r, col]);
+                //a[cr] = i++;
+            }
+            switch (formationDirection) {
+                case ASSEMBLY_BOTTOM_LEFT:
+                case ASSEMBLY_TOP_RIGHT:
+                    col--;
+                    r++;
+                    break;
+                case ASSEMBLY_TOP_LEFT:
+                case ASSEMBLY_BOTTOM_RIGHT:
+                    col++;
+                    r--;
+                    break;
+                case ASSEMBLY_LEFT_BOTTOM:
+                case ASSEMBLY_RIGHT_TOP:
+                    col--;
+                    r--;
+                    break;
+                case ASSEMBLY_RIGHT_BOTTOM:
+                case ASSEMBLY_LEFT_TOP:
+                default:
+                    col++;
+                    r++;
+                    break;
+            }
+            if (col < 0 || r < 0 || col > cl || r > rl) {
+                switch (formationDirection) {
+                    case ASSEMBLY_BOTTOM_LEFT:
+                    case ASSEMBLY_TOP_RIGHT:
+                        C++;
+                        break;
+                    case ASSEMBLY_LEFT_BOTTOM:
+                    case ASSEMBLY_RIGHT_TOP:
+                    case ASSEMBLY_TOP_LEFT:
+                    case ASSEMBLY_BOTTOM_RIGHT:
+                        R++;
+                        break;
+                    case ASSEMBLY_RIGHT_BOTTOM:
+                    case ASSEMBLY_LEFT_TOP:
+                    default:
+                        C--;
+                        break;
+                }
+                if (C < 0 || R < 0 || C > cl || R > rl) {
+                    switch (formationDirection) {
+                        case ASSEMBLY_BOTTOM_LEFT:
+                        case ASSEMBLY_TOP_RIGHT:
+                            C = cl;
+                            R++;
+                            break;
+                        case ASSEMBLY_TOP_LEFT:
+                        case ASSEMBLY_BOTTOM_RIGHT:
+                            R = rl;
+                            C++;
+                            break;
+                        case ASSEMBLY_LEFT_BOTTOM:
+                        case ASSEMBLY_RIGHT_TOP: R = rl; C--;
+                            break;
+                        case ASSEMBLY_RIGHT_BOTTOM:
+                        case ASSEMBLY_LEFT_TOP:
+                        default:
+                            C = 0;
+                            R++;
+                            break;
+                    }
+                    if (R > rl)
+                        R = rl;
+                    else if (R < 0)
+                        R = 0;
+                    else if (C > cl)
+                        C = cl;
+                    else if (C < 0)
+                        C = 0;
+                }
+                r = R;
+                col = C;
+            }
+        }
+        return a;
+    };
+
+    $JssorSlideshowFormations$.$FormationSquare = function (transition) {
+        var cols = transition.$Cols || 1;
+        var rows = transition.$Rows || 1;
+        var arr = [];
+        var i = 0;
+        var col;
+        var r;
+        var dc;
+        var dr;
+        var cr;
+        dc = cols < rows ? (rows - cols) / 2 : 0;
+        dr = cols > rows ? (cols - rows) / 2 : 0;
+        cr = Math.round(Math.max(cols / 2, rows / 2)) + 1;
+        for (col = 0; col < cols; col++) {
+            for (r = 0; r < rows; r++)
+                PushFormationOrder(arr, cr - Math.min(col + 1 + dc, r + 1 + dr, cols - col + dc, rows - r + dr), [r, col]);
+        }
+        return arr;
+    };
+
+    $JssorSlideshowFormations$.$FormationRectangle = function (transition) {
+        var cols = transition.$Cols || 1;
+        var rows = transition.$Rows || 1;
+        var arr = [];
+        var i = 0;
+        var col;
+        var r;
+        var cr;
+        cr = Math.round(Math.min(cols / 2, rows / 2)) + 1;
+        for (col = 0; col < cols; col++) {
+            for (r = 0; r < rows; r++)
+                PushFormationOrder(arr, cr - Math.min(col + 1, r + 1, cols - col, rows - r), [r, col]);
+        }
+        return arr;
+    };
+
+    $JssorSlideshowFormations$.$FormationRandom = function (transition) {
+        var a = [];
+        var r, col, i;
+        for (r = 0; r < transition.$Rows; r++) {
+            for (col = 0; col < transition.$Cols; col++)
+                PushFormationOrder(a, Math.ceil(100000 * Math.random()) % 13, [r, col]);
+        }
+
+        return a;
+    };
+
+    $JssorSlideshowFormations$.$FormationCircle = function (transition) {
+        var cols = transition.$Cols || 1;
+        var rows = transition.$Rows || 1;
+        var arr = [];
+        var i = 0;
+        var col;
+        var r;
+        var hc = cols / 2 - 0.5;
+        var hr = rows / 2 - 0.5;
+        for (col = 0; col < cols; col++) {
+            for (r = 0; r < rows; r++)
+                PushFormationOrder(arr, Math.round(Math.sqrt(Math.pow(col - hc, 2) + Math.pow(r - hr, 2))), [r, col]);
+        }
+        return arr;
+    };
+
+    $JssorSlideshowFormations$.$FormationCross = function (transition) {
+        var cols = transition.$Cols || 1;
+        var rows = transition.$Rows || 1;
+        var arr = [];
+        var i = 0;
+        var col;
+        var r;
+        var hc = cols / 2 - 0.5;
+        var hr = rows / 2 - 0.5;
+        for (col = 0; col < cols; col++) {
+            for (r = 0; r < rows; r++)
+                PushFormationOrder(arr, Math.round(Math.min(Math.abs(col - hc), Math.abs(r - hr))), [r, col]);
+        }
+        return arr;
+    };
+
+    $JssorSlideshowFormations$.$FormationRectangleCross = function (transition) {
+        var cols = transition.$Cols || 1;
+        var rows = transition.$Rows || 1;
+        var arr = [];
+        var i = 0;
+        var col;
+        var r;
+        var hc = cols / 2 - 0.5;
+        var hr = rows / 2 - 0.5;
+        var cr = Math.max(hc, hr) + 1;
+        for (col = 0; col < cols; col++) {
+            for (r = 0; r < rows; r++)
+                PushFormationOrder(arr, Math.round(cr - Math.max(hc - Math.abs(col - hc), hr - Math.abs(r - hr))) - 1, [r, col]);
+        }
+        return arr;
+    };
+
+    function GetFormation(transition) {
+
+        var formationInstance = transition.$Formation(transition);
+
+        return transition.$Reverse ? formationInstance.reverse() : formationInstance;
+
+    } //GetFormation
+
+    //var _PrototypeTransitions = [];
+    function EnsureTransitionInstance(options, slideshowInterval) {
+
+        var _SlideshowTransition = {
+            $Interval: slideshowInterval,  //Delay to play next frame
+            $Duration: 1, //Duration to finish the entire transition
+            $Delay: 0,  //Delay to assembly blocks
+            $Cols: 1,   //Number of columns
+            $Rows: 1,   //Number of rows
+            $Opacity: 0,   //Fade block or not
+            $Zoom: 0,   //Zoom block or not
+            $Clip: 0,   //Clip block or not
+            $Move: false,   //Move block or not
+            $SlideOut: false,   //Slide the previous slide out to display next slide instead
+            //$FlyDirection: 0,   //Specify fly transform with direction
+            $Reverse: false,    //Reverse the assembly or not
+            $Formation: $JssorSlideshowFormations$.$FormationRandom,    //Shape that assembly blocks as
+            $Assembly: ASSEMBLY_RIGHT_BOTTOM,   //The way to assembly blocks
+            $ChessMode: { $Column: 0, $Row: 0 },    //Chess move or fly direction
+            $Easing: $JssorEasing$.$EaseSwing,  //Specify variation of speed during transition
+            $Round: {},
+            $Blocks: [],
+            $During: {}
+        };
+
+        $Jssor$.$Extend(_SlideshowTransition, options);
+
+        _SlideshowTransition.$Count = _SlideshowTransition.$Cols * _SlideshowTransition.$Rows;
+        if ($Jssor$.$IsFunction(_SlideshowTransition.$Easing))
+            _SlideshowTransition.$Easing = { $Default: _SlideshowTransition.$Easing };
+
+        _SlideshowTransition.$FramesCount = Math.ceil(_SlideshowTransition.$Duration / _SlideshowTransition.$Interval);
+        _SlideshowTransition.$EasingInstance = GetEasing(_SlideshowTransition);
+
+        _SlideshowTransition.$GetBlocks = function (width, height) {
+            width /= _SlideshowTransition.$Cols;
+            height /= _SlideshowTransition.$Rows;
+            var wh = width + 'x' + height;
+            if (!_SlideshowTransition.$Blocks[wh]) {
+                _SlideshowTransition.$Blocks[wh] = { $Width: width, $Height: height };
+                for (var col = 0; col < _SlideshowTransition.$Cols; col++) {
+                    for (var r = 0; r < _SlideshowTransition.$Rows; r++)
+                        _SlideshowTransition.$Blocks[wh][r + ',' + col] = { $Top: r * height, $Right: col * width + width, $Bottom: r * height + height, $Left: col * width };
+                }
+            }
+
+            return _SlideshowTransition.$Blocks[wh];
+        };
+
+        if (_SlideshowTransition.$Brother) {
+            _SlideshowTransition.$Brother = EnsureTransitionInstance(_SlideshowTransition.$Brother, slideshowInterval);
+            _SlideshowTransition.$SlideOut = true;
+        }
+
+        return _SlideshowTransition;
+    }
+
+    function GetEasing(transition) {
+        var easing = transition.$Easing;
+        if (!easing.$Default)
+            easing.$Default = $JssorEasing$.$EaseSwing;
+
+        var duration = transition.$FramesCount;
+
+        var cache = easing.$Cache;
+        if (!cache) {
+            var enumerator = $Jssor$.$Extend({}, transition.$Easing, transition.$Round);
+            cache = easing.$Cache = {};
+
+            $Jssor$.$Each(enumerator, function (v, easingName) {
+                var easingFunction = easing[easingName] || easing.$Default;
+                var round = transition.$Round[easingName] || 1;
+
+                if (!$Jssor$.$IsArray(easingFunction.$Cache))
+                    easingFunction.$Cache = [];
+
+                var easingFunctionCache = easingFunction.$Cache[duration] = easingFunction.$Cache[duration] || [];
+
+                if (!easingFunctionCache[round]) {
+                    easingFunctionCache[round] = [0];
+                    for (var t = 1; t <= duration; t++) {
+                        var tRound = t / duration * round;
+                        var tRoundFloor = Math.floor(tRound);
+                        if (tRound != tRoundFloor)
+                            tRound -= tRoundFloor;
+                        easingFunctionCache[round][t] = easingFunction(tRound);
+                    }
+                }
+
+                cache[easingName] = easingFunctionCache;
+
+            });
+        }
+
+        return cache;
+    } //GetEasing
+
+    //Formation Definition -------
+
+    function JssorSlideshowPlayer(slideContainer, slideElement, slideTransition, beginTime, slideContainerWidth, slideContainerHeight) {
+        var _Self = this;
+
+        var _Block;
+        var _StartStylesArr = {};
+        var _AnimationStylesArrs = {};
+        var _AnimationBlockItems = [];
+        var _StyleStart;
+        var _StyleEnd;
+        var _StyleDif;
+        var _ChessModeColumn = slideTransition.$ChessMode.$Column || 0;
+        var _ChessModeRow = slideTransition.$ChessMode.$Row || 0;
+
+        var _Blocks = slideTransition.$GetBlocks(slideContainerWidth, slideContainerHeight);
+        var _FormationInstance = GetFormation(slideTransition);
+        var _MaxOrder = _FormationInstance.length - 1;
+        var _Period = slideTransition.$Duration + slideTransition.$Delay * _MaxOrder;
+        var _EndTime = beginTime + _Period;
+
+        var _SlideOut = slideTransition.$SlideOut;
+        var _IsIn;
+
+        _EndTime += $Jssor$.$IsBrowserChrome() ? 260 : 50;
+
+        _Self.$EndTime = _EndTime;
+
+        _Self.$ShowFrame = function (time) {
+            time -= beginTime;
+
+            var isIn = time < _Period;
+
+            if (isIn || _IsIn) {
+                _IsIn = isIn;
+
+                if (!_SlideOut)
+                    time = _Period - time;
+
+                var frameIndex = Math.ceil(time / slideTransition.$Interval);
+
+                $Jssor$.$Each(_AnimationStylesArrs, function (value, index) {
+
+                    var itemFrameIndex = Math.max(frameIndex, value.$Min);
+                    itemFrameIndex = Math.min(itemFrameIndex, value.length - 1);
+
+                    if (value.$LastFrameIndex != itemFrameIndex) {
+                        if (!value.$LastFrameIndex && !_SlideOut) {
+                            $Jssor$.$ShowElement(_AnimationBlockItems[index]);
+                        }
+                        else if (itemFrameIndex == value.$Max && _SlideOut) {
+                            $Jssor$.$HideElement(_AnimationBlockItems[index]);
+                        }
+                        value.$LastFrameIndex = itemFrameIndex;
+                        $Jssor$.$SetStylesEx(_AnimationBlockItems[index], value[itemFrameIndex]);
+                    }
+                });
+            }
+        };
+
+        function DisableHWA(elmt) {
+            $Jssor$.$DisableHWA(elmt);
+
+            var children = $Jssor$.$Children(elmt);
+
+            $Jssor$.$Each(children, function (child) {
+                DisableHWA(child);
+            });
+        }
+
+        //constructor
+        {
+            slideElement = $Jssor$.$CloneNode(slideElement, true);
+            DisableHWA(slideElement);
+            if ($Jssor$.$IsBrowserIe9Earlier()) {
+                var hasImage = !slideElement["no-image"];
+                var slideChildElements = $Jssor$.$FindChildrenByTag(slideElement, null, true);
+                $Jssor$.$Each(slideChildElements, function (slideChildElement) {
+                    if (hasImage || slideChildElement["jssor-slider"])
+                        $Jssor$.$CssOpacity(slideChildElement, $Jssor$.$CssOpacity(slideChildElement), true);
+                });
+            }
+
+            $Jssor$.$Each(_FormationInstance, function (formationItems, order) {
+                $Jssor$.$Each(formationItems, function (formationItem) {
+                    var row = formationItem[0];
+                    var col = formationItem[1];
+                    {
+                        var columnRow = row + ',' + col;
+
+                        var chessHorizontal = false;
+                        var chessVertical = false;
+                        var chessRotate = false;
+
+                        if (_ChessModeColumn && col % 2) {
+                            if ($JssorDirection$.$IsHorizontal(_ChessModeColumn)) {
+                                chessHorizontal = !chessHorizontal;
+                            }
+                            if ($JssorDirection$.$IsVertical(_ChessModeColumn)) {
+                                chessVertical = !chessVertical;
+                            }
+
+                            if (_ChessModeColumn & 16)
+                                chessRotate = !chessRotate;
+                        }
+
+                        if (_ChessModeRow && row % 2) {
+                            if ($JssorDirection$.$IsHorizontal(_ChessModeRow)) {
+                                chessHorizontal = !chessHorizontal;
+                            }
+                            if ($JssorDirection$.$IsVertical(_ChessModeRow)) {
+                                chessVertical = !chessVertical;
+                            }
+                            if (_ChessModeRow & 16)
+                                chessRotate = !chessRotate;
+                        }
+
+                        slideTransition.$Top = slideTransition.$Top || (slideTransition.$Clip & 4);
+                        slideTransition.$Bottom = slideTransition.$Bottom || (slideTransition.$Clip & 8);
+                        slideTransition.$Left = slideTransition.$Left || (slideTransition.$Clip & 1);
+                        slideTransition.$Right = slideTransition.$Right || (slideTransition.$Clip & 2);
+
+                        var topBenchmark = chessVertical ? slideTransition.$Bottom : slideTransition.$Top;
+                        var bottomBenchmark = chessVertical ? slideTransition.$Top : slideTransition.$Bottom;
+                        var leftBenchmark = chessHorizontal ? slideTransition.$Right : slideTransition.$Left;
+                        var rightBenchmark = chessHorizontal ? slideTransition.$Left : slideTransition.$Right;
+
+                        //$JssorDebug$.$Execute(function () {
+                        //    topBenchmark = bottomBenchmark = leftBenchmark = rightBenchmark = false;
+                        //});
+
+                        slideTransition.$Clip = topBenchmark || bottomBenchmark || leftBenchmark || rightBenchmark;
+
+                        _StyleDif = {};
+                        _StyleEnd = { $Top: 0, $Left: 0, $Opacity: 1, $Width: slideContainerWidth, $Height: slideContainerHeight };
+                        _StyleStart = $Jssor$.$Extend({}, _StyleEnd);
+                        _Block = $Jssor$.$Extend({}, _Blocks[columnRow]);
+
+                        if (slideTransition.$Opacity) {
+                            _StyleEnd.$Opacity = 2 - slideTransition.$Opacity;
+                        }
+
+                        if (slideTransition.$ZIndex) {
+                            _StyleEnd.$ZIndex = slideTransition.$ZIndex;
+                            _StyleStart.$ZIndex = 0;
+                        }
+
+                        var allowClip = slideTransition.$Cols * slideTransition.$Rows > 1 || slideTransition.$Clip;
+
+                        if (slideTransition.$Zoom || slideTransition.$Rotate) {
+                            var allowRotate = true;
+                            if ($Jssor$.$IsBrowserIE() && $Jssor$.$BrowserEngineVersion() < 9) {
+                                if (slideTransition.$Cols * slideTransition.$Rows > 1)
+                                    allowRotate = false;
+                                else
+                                    allowClip = false;
+                            }
+
+                            if (allowRotate) {
+                                _StyleEnd.$Zoom = slideTransition.$Zoom ? slideTransition.$Zoom - 1 : 1;
+                                _StyleStart.$Zoom = 1;
+
+                                if ($Jssor$.$IsBrowserIe9Earlier() || $Jssor$.$IsBrowserOpera())
+                                    _StyleEnd.$Zoom = Math.min(_StyleEnd.$Zoom, 2);
+
+                                var rotate = slideTransition.$Rotate;
+                                //if (rotate == true)
+                                //    rotate = 1;
+
+                                _StyleEnd.$Rotate = rotate * 360 * ((chessRotate) ? -1 : 1);
+                                _StyleStart.$Rotate = 0;
+                            }
+                        }
+
+                        if (allowClip) {
+                            if (slideTransition.$Clip) {
+                                var clipScale = slideTransition.$ScaleClip || 1;
+                                var blockOffset = _Block.$Offset = {};
+                                if (topBenchmark && bottomBenchmark) {
+                                    blockOffset.$Top = _Blocks.$Height / 2 * clipScale;
+                                    blockOffset.$Bottom = -blockOffset.$Top;
+                                }
+                                else if (topBenchmark) {
+                                    blockOffset.$Bottom = -_Blocks.$Height * clipScale;
+                                }
+                                else if (bottomBenchmark) {
+                                    blockOffset.$Top = _Blocks.$Height * clipScale;
+                                }
+
+                                if (leftBenchmark && rightBenchmark) {
+                                    blockOffset.$Left = _Blocks.$Width / 2 * clipScale;
+                                    blockOffset.$Right = -blockOffset.$Left;
+                                }
+                                else if (leftBenchmark) {
+                                    blockOffset.$Right = -_Blocks.$Width * clipScale;
+                                }
+                                else if (rightBenchmark) {
+                                    blockOffset.$Left = _Blocks.$Width * clipScale;
+                                }
+                            }
+
+                            _StyleDif.$Clip = _Block;
+                            _StyleStart.$Clip = _Blocks[columnRow];
+                        }
+
+                        //fly
+                        {
+                            var chessHor = chessHorizontal ? 1 : -1;
+                            var chessVer = chessVertical ? 1 : -1;
+
+                            if (slideTransition.x)
+                                _StyleEnd.$Left += slideContainerWidth * slideTransition.x * chessHor;
+
+                            if (slideTransition.y)
+                                _StyleEnd.$Top += slideContainerHeight * slideTransition.y * chessVer;
+                        }
+
+                        $Jssor$.$Each(_StyleEnd, function (propertyEnd, property) {
+                            if ($Jssor$.$IsNumeric(propertyEnd)) {
+                                if (propertyEnd != _StyleStart[property]) {
+                                    _StyleDif[property] = propertyEnd - _StyleStart[property];
+                                }
+                            }
+                        });
+
+                        _StartStylesArr[columnRow] = _SlideOut ? _StyleStart : _StyleEnd;
+
+                        var animationStylesArr = [];
+                        var virtualFrameCount = Math.round(order * slideTransition.$Delay / slideTransition.$Interval);
+                        _AnimationStylesArrs[columnRow] = new Array(virtualFrameCount);
+                        _AnimationStylesArrs[columnRow].$Min = virtualFrameCount;
+
+                        var framesCount = slideTransition.$FramesCount;
+                        for (var frameN = 0; frameN <= framesCount; frameN++) {
+                            var styleFrameN = {};
+
+                            $Jssor$.$Each(_StyleDif, function (propertyDiff, property) {
+                                var propertyEasings = slideTransition.$EasingInstance[property] || slideTransition.$EasingInstance.$Default;
+                                var propertyEasingArray = propertyEasings[slideTransition.$Round[property] || 1];
+
+                                var propertyDuring = slideTransition.$During[property] || [0, 1];
+                                var propertyFrameN = (frameN / framesCount - propertyDuring[0]) / propertyDuring[1] * framesCount;
+                                propertyFrameN = Math.round(Math.min(framesCount, Math.max(propertyFrameN, 0)));
+
+                                var propertyEasingValue = propertyEasingArray[propertyFrameN];
+
+                                if ($Jssor$.$IsNumeric(propertyDiff)) {
+                                    styleFrameN[property] = _StyleStart[property] + propertyDiff * propertyEasingValue;
+                                }
+                                else {
+                                    var value = styleFrameN[property] = $Jssor$.$Extend({}, _StyleStart[property]);
+                                    value.$Offset = [];
+                                    $Jssor$.$Each(propertyDiff.$Offset, function (rectX, n) {
+                                        var offsetValue = rectX * propertyEasingValue;
+                                        value.$Offset[n] = offsetValue;
+                                        value[n] += offsetValue;
+                                    });
+                                }
+                            });
+
+                            if (_StyleStart.$Zoom) {
+                                styleFrameN.$Transform = { $Rotate: styleFrameN.$Rotate || 0, $Scale: styleFrameN.$Zoom, $OriginalWidth: slideContainerWidth, $OriginalHeight: slideContainerHeight };
+                            }
+                            if (styleFrameN.$Clip && slideTransition.$Move) {
+                                var styleFrameNClipOffset = styleFrameN.$Clip.$Offset;
+                                var offsetY = (styleFrameNClipOffset.$Top || 0) + (styleFrameNClipOffset.$Bottom || 0);
+                                var offsetX = (styleFrameNClipOffset.$Left || 0) + (styleFrameNClipOffset.$Right || 0);
+
+                                styleFrameN.$Left = (styleFrameN.$Left || 0) + offsetX;
+                                styleFrameN.$Top = (styleFrameN.$Top || 0) + offsetY;
+                                styleFrameN.$Clip.$Left -= offsetX;
+                                styleFrameN.$Clip.$Right -= offsetX;
+                                styleFrameN.$Clip.$Top -= offsetY;
+                                styleFrameN.$Clip.$Bottom -= offsetY;
+                            }
+
+                            styleFrameN.$ZIndex = styleFrameN.$ZIndex || 1;
+
+                            _AnimationStylesArrs[columnRow].push(styleFrameN);
+                        }
+
+                    } //for
+                });
+            });
+
+            _FormationInstance.reverse();
+            $Jssor$.$Each(_FormationInstance, function (formationItems) {
+                $Jssor$.$Each(formationItems, function (formationItem) {
+                    var row = formationItem[0];
+                    var col = formationItem[1];
+
+                    var columnRow = row + ',' + col;
+
+                    var image = slideElement;
+                    if (col || row)
+                        image = $Jssor$.$CloneNode(slideElement, true);
+
+                    $Jssor$.$SetStyles(image, _StartStylesArr[columnRow]);
+                    $Jssor$.$CssOverflow(image, "hidden");
+
+                    $Jssor$.$CssPosition(image, "absolute");
+                    slideContainer.$AddClipElement(image);
+                    _AnimationBlockItems[columnRow] = image;
+                    $Jssor$.$ShowElement(image, _SlideOut);
+                });
+            });
+        }
+    }
+
+    //JssorSlideshowRunner++++++++
+    var _SlideshowRunnerCount = 1;
+    $JssorSlideshowRunner$ = window.$JssorSlideshowRunner$ = function (slideContainer, slideContainerWidth, slideContainerHeight, slideshowOptions, handleTouchEventOnly) {
+
+        var _SelfSlideshowRunner = this;
+
+        //var _State = 0; //-1 fullfill, 0 clean, 1 initializing, 2 stay, 3 playing
+        var _EndTime;
+
+        var _SliderFrameCount;
+
+        var _SlideshowPlayerBelow;
+        var _SlideshowPlayerAbove;
+
+        var _PrevItem;
+        var _SlideItem;
+
+        var _TransitionIndex = 0;
+        var _TransitionsOrder = slideshowOptions.$TransitionsOrder;
+
+        var _SlideshowTransition;
+
+        var _SlideshowPerformance = 16;
+
+        function SlideshowProcessor() {
+            var _SelfSlideshowProcessor = this;
+            var _CurrentTime = 0;
+
+            $JssorAnimator$.call(_SelfSlideshowProcessor, 0, _EndTime);
+
+            _SelfSlideshowProcessor.$OnPositionChange = function (oldPosition, newPosition) {
+                if ((newPosition - _CurrentTime) > _SlideshowPerformance) {
+                    _CurrentTime = newPosition;
+
+                    _SlideshowPlayerAbove && _SlideshowPlayerAbove.$ShowFrame(newPosition);
+                    _SlideshowPlayerBelow && _SlideshowPlayerBelow.$ShowFrame(newPosition);
+                }
+            };
+
+            _SelfSlideshowProcessor.$Transition = _SlideshowTransition;
+        }
+
+        //member functions
+        _SelfSlideshowRunner.$GetTransition = function (slideCount) {
+            var n = 0;
+
+            var transitions = slideshowOptions.$Transitions;
+
+            var transitionCount = transitions.length;
+
+            if (_TransitionsOrder) { /*Sequence*/
+                if (transitionCount > slideCount && ($Jssor$.$IsBrowserChrome() || $Jssor$.$IsBrowserSafari() || $Jssor$.$IsBrowserFireFox())) {
+                    transitionCount -= transitionCount % slideCount;
+                }
+                n = _TransitionIndex++ % transitionCount;
+            }
+            else { /*Random*/
+                n = Math.floor(Math.random() * transitionCount);
+            }
+
+            transitions[n] && (transitions[n].$Index = n);
+
+            return transitions[n];
+        };
+
+        _SelfSlideshowRunner.$Initialize = function (slideIndex, prevIndex, slideItem, prevItem, slideshowTransition) {
+            $JssorDebug$.$Execute(function () {
+                if (_SlideshowPlayerBelow) {
+                    $JssorDebug$.$Fail("slideshow runner has not been cleared.");
+                }
+            });
+
+            _SlideshowTransition = slideshowTransition;
+
+            slideshowTransition = EnsureTransitionInstance(slideshowTransition, _SlideshowPerformance);
+
+            _SlideItem = slideItem;
+            _PrevItem = prevItem;
+
+            var prevSlideElement = prevItem.$Item;
+            var currentSlideElement = slideItem.$Item;
+            prevSlideElement["no-image"] = !prevItem.$Image;
+            currentSlideElement["no-image"] = !slideItem.$Image;
+
+            var slideElementAbove = prevSlideElement;
+            var slideElementBelow = currentSlideElement;
+
+            var slideTransitionAbove = slideshowTransition;
+            var slideTransitionBelow = slideshowTransition.$Brother || EnsureTransitionInstance({}, _SlideshowPerformance);
+
+            if (!slideshowTransition.$SlideOut) {
+                slideElementAbove = currentSlideElement;
+                slideElementBelow = prevSlideElement;
+            }
+
+            var shift = slideTransitionBelow.$Shift || 0;
+
+            _SlideshowPlayerBelow = new JssorSlideshowPlayer(slideContainer, slideElementBelow, slideTransitionBelow, Math.max(shift - slideTransitionBelow.$Interval, 0), slideContainerWidth, slideContainerHeight);
+            _SlideshowPlayerAbove = new JssorSlideshowPlayer(slideContainer, slideElementAbove, slideTransitionAbove, Math.max(slideTransitionBelow.$Interval - shift, 0), slideContainerWidth, slideContainerHeight);
+
+            _SlideshowPlayerBelow.$ShowFrame(0);
+            _SlideshowPlayerAbove.$ShowFrame(0);
+
+            _EndTime = Math.max(_SlideshowPlayerBelow.$EndTime, _SlideshowPlayerAbove.$EndTime);
+
+            _SelfSlideshowRunner.$Index = slideIndex;
+        };
+
+        _SelfSlideshowRunner.$Clear = function () {
+            slideContainer.$Clear();
+            _SlideshowPlayerBelow = null;
+            _SlideshowPlayerAbove = null;
+        };
+
+        _SelfSlideshowRunner.$GetProcessor = function () {
+            var slideshowProcessor = null;
+
+            if (_SlideshowPlayerAbove)
+                slideshowProcessor = new SlideshowProcessor();
+
+            return slideshowProcessor;
+        };
+
+        //Constructor
+        {
+            if ($Jssor$.$IsBrowserIe9Earlier() || $Jssor$.$IsBrowserOpera() || (handleTouchEventOnly && $Jssor$.$WebKitVersion() < 537)) {
+                _SlideshowPerformance = 32;
+            }
+
+            $JssorObject$.call(_SelfSlideshowRunner);
+            $JssorAnimator$.call(_SelfSlideshowRunner, -10000000, 10000000);
+
+            $JssorDebug$.$LiveStamp(_SelfSlideshowRunner, "slideshow_runner_" + _SlideshowRunnerCount++);
+        }
+    };
+    //JssorSlideshowRunner--------
+
+    //JssorSlider
+    function JssorSlider(elmt, options) {
+        var _SelfSlider = this;
+
+        //private classes
+        function Conveyor() {
+            var _SelfConveyor = this;
+            $JssorAnimator$.call(_SelfConveyor, -100000000, 200000000);
+
+            _SelfConveyor.$GetCurrentSlideInfo = function () {
+                var positionDisplay = _SelfConveyor.$GetPosition_Display();
+                var virtualIndex = Math.floor(positionDisplay);
+                var slideIndex = GetRealIndex(virtualIndex);
+                var slidePosition = positionDisplay - Math.floor(positionDisplay);
+
+                return { $Index: slideIndex, $VirtualIndex: virtualIndex, $Position: slidePosition };
+            };
+
+            _SelfConveyor.$OnPositionChange = function (oldPosition, newPosition) {
+
+                var index = Math.floor(newPosition);
+                if (index != newPosition && newPosition > oldPosition)
+                    index++;
+
+                ResetNavigator(index, true);
+
+                _SelfSlider.$TriggerEvent(JssorSlider.$EVT_POSITION_CHANGE, GetRealIndex(newPosition), GetRealIndex(oldPosition), newPosition, oldPosition);
+            };
+        }
+
+        //Carousel
+        function Carousel() {
+            var _SelfCarousel = this;
+
+            $JssorAnimator$.call(_SelfCarousel, 0, 0, { $LoopLength: _SlideCount });
+
+            //Carousel Constructor
+            {
+                $Jssor$.$Each(_SlideItems, function (slideItem) {
+                    (_Loop & 1) && slideItem.$SetLoopLength(_SlideCount);
+                    _SelfCarousel.$Chain(slideItem);
+                    slideItem.$Shift(_ParkingPosition / _StepLength);
+                });
+            }
+        }
+        //Carousel
+
+        //Slideshow
+        function Slideshow() {
+            var _SelfSlideshow = this;
+            var _Wrapper = _SlideContainer.$Elmt;
+
+            $JssorAnimator$.call(_SelfSlideshow, -1, 2, { $Easing: $JssorEasing$.$EaseLinear, $Setter: { $Position: SetPosition }, $LoopLength: _SlideCount }, _Wrapper, { $Position: 1 }, { $Position: -1 });
+
+            _SelfSlideshow.$Wrapper = _Wrapper;
+
+            //Slideshow Constructor
+            {
+                $JssorDebug$.$Execute(function () {
+                    $Jssor$.$Attribute(_SlideContainer.$Elmt, "debug-id", "slide_container");
+                });
+            }
+        }
+        //Slideshow
+
+        //CarouselPlayer
+        function CarouselPlayer(carousel, slideshow) {
+            var _SelfCarouselPlayer = this;
+            var _FromPosition;
+            var _ToPosition;
+            var _Duration;
+            var _StandBy;
+            var _StandByPosition;
+
+            $JssorAnimator$.call(_SelfCarouselPlayer, -100000000, 200000000);
+
+            _SelfCarouselPlayer.$OnStart = function () {
+                _IsSliding = true;
+                _LoadingTicket = null;
+
+                //EVT_SWIPE_START
+                _SelfSlider.$TriggerEvent(JssorSlider.$EVT_SWIPE_START, GetRealIndex(_Conveyor.$GetPosition()), _Conveyor.$GetPosition());
+            };
+
+            _SelfCarouselPlayer.$OnStop = function () {
+
+                _IsSliding = false;
+                _StandBy = false;
+
+                var currentSlideInfo = _Conveyor.$GetCurrentSlideInfo();
+
+                //EVT_SWIPE_END
+                _SelfSlider.$TriggerEvent(JssorSlider.$EVT_SWIPE_END, GetRealIndex(_Conveyor.$GetPosition()), _Conveyor.$GetPosition());
+
+                if (!currentSlideInfo.$Position) {
+                    OnPark(currentSlideInfo.$VirtualIndex, _CurrentSlideIndex);
+                }
+            };
+
+            _SelfCarouselPlayer.$OnPositionChange = function (oldPosition, newPosition) {
+
+                var toPosition;
+
+                if (_StandBy)
+                    toPosition = _StandByPosition;
+                else {
+                    toPosition = _ToPosition;
+
+                    if (_Duration) {
+                        var interPosition = newPosition / _Duration;
+                        if ($Jssor$.$IsBrowserChrome() || $Jssor$.$IsBrowserFireFox()) {
+                            Math.round(interPosition * 16 / _Duration) / 16 * _Duration;
+                            interPosition = parseFloat(interPosition.toFixed(4));
+                        }
+                        toPosition = _Options.$SlideEasing(interPosition) * (_ToPosition - _FromPosition) + _FromPosition;
+                    }
+                }
+
+                _Conveyor.$GoToPosition(toPosition);
+            };
+
+            _SelfCarouselPlayer.$PlayCarousel = function (fromPosition, toPosition, duration, callback) {
+                $JssorDebug$.$Execute(function () {
+                    if (_SelfCarouselPlayer.$IsPlaying())
+                        $JssorDebug$.$Fail("The carousel is already playing.");
+                });
+
+                _FromPosition = fromPosition;
+                _ToPosition = toPosition;
+                _Duration = duration;
+
+                _Conveyor.$GoToPosition(fromPosition);
+                _SelfCarouselPlayer.$GoToPosition(0);
+
+                _SelfCarouselPlayer.$PlayToPosition(duration, callback);
+            };
+
+            _SelfCarouselPlayer.$StandBy = function (standByPosition) {
+                _StandBy = true;
+                _StandByPosition = standByPosition;
+                _SelfCarouselPlayer.$Play(standByPosition, null, true);
+            };
+
+            _SelfCarouselPlayer.$SetStandByPosition = function (standByPosition) {
+                _StandByPosition = standByPosition;
+            };
+
+            _SelfCarouselPlayer.$MoveCarouselTo = function (position) {
+                _Conveyor.$GoToPosition(position);
+            };
+
+            //CarouselPlayer Constructor
+            {
+                _Conveyor = new Conveyor();
+
+                _Conveyor.$Combine(carousel);
+                _Conveyor.$Combine(slideshow);
+            }
+        }
+        //CarouselPlayer
+
+        //SlideContainer
+        function SlideContainer() {
+            var _Self = this;
+            var elmt = CreatePanel();
+
+            $Jssor$.$CssZIndex(elmt, 0);
+
+            _Self.$Elmt = elmt;
+
+            _Self.$AddClipElement = function (clipElement) {
+                $Jssor$.$AppendChild(elmt, clipElement);
+                $Jssor$.$ShowElement(elmt);
+            };
+
+            _Self.$Clear = function () {
+                $Jssor$.$HideElement(elmt);
+                $Jssor$.$ClearInnerHtml(elmt);
+            };
+        }
+        //SlideContainer
+
+        //SlideItem
+        function SlideItem(slideElmt, slideIndex) {
+
+            var _SelfSlideItem = this;
+
+            var _CaptionSliderIn;
+            var _CaptionSliderOut;
+            var _CaptionSliderCurrent;
+            var _IsCaptionSliderPlayingWhenDragStart;
+
+            var _Wrapper;
+            var _BaseElement = slideElmt;
+
+            var _LoadingScreen;
+
+            var _ImageItem;
+            var _ImageElmts = [];
+            var _LinkItemOrigin;
+            var _LinkItem;
+            var _ImageLoading;
+            var _ImageLoaded;
+            var _ImageLazyLoading;
+            var _ContentRefreshed;
+
+            var _Processor;
+
+            var _PlayerInstanceElement;
+            var _PlayerInstance;
+
+            var _SequenceNumber;    //for debug only
+
+            $JssorAnimator$.call(_SelfSlideItem, -_DisplayPieces, _DisplayPieces + 1, { $SlideItemAnimator: true });
+
+            function ResetCaptionSlider(fresh) {
+                _CaptionSliderOut && _CaptionSliderOut.$Revert();
+                _CaptionSliderIn && _CaptionSliderIn.$Revert();
+
+                RefreshContent(slideElmt, fresh);
+                _ContentRefreshed = true;
+
+                _CaptionSliderIn = new _CaptionSliderOptions.$Class(slideElmt, _CaptionSliderOptions, 1);
+                $JssorDebug$.$LiveStamp(_CaptionSliderIn, "caption_slider_" + _CaptionSliderCount + "_in");
+                _CaptionSliderOut = new _CaptionSliderOptions.$Class(slideElmt, _CaptionSliderOptions);
+                $JssorDebug$.$LiveStamp(_CaptionSliderOut, "caption_slider_" + _CaptionSliderCount + "_out");
+
+                $JssorDebug$.$Execute(function () {
+                    _CaptionSliderCount++;
+                });
+
+                _CaptionSliderOut.$GoToBegin();
+                _CaptionSliderIn.$GoToBegin();
+            }
+
+            function EnsureCaptionSliderVersion() {
+                if (_CaptionSliderIn.$Version < _CaptionSliderOptions.$Version) {
+                    ResetCaptionSlider();
+                }
+            }
+
+            //event handling begin
+            function LoadImageCompleteEventHandler(completeCallback, loadingScreen, image) {
+                if (!_ImageLoaded) {
+                    _ImageLoaded = true;
+
+                    if (_ImageItem && image) {
+                        var imageWidth = image.width;
+                        var imageHeight = image.height;
+                        var fillWidth = imageWidth;
+                        var fillHeight = imageHeight;
+
+                        if (imageWidth && imageHeight && _Options.$FillMode) {
+
+                            //0 stretch, 1 contain (keep aspect ratio and put all inside slide), 2 cover (keep aspect ratio and cover whole slide), 4 actual size, 5 contain for large image, actual size for small image, default value is 0
+                            if (_Options.$FillMode & 3 && (!(_Options.$FillMode & 4) || imageWidth > _SlideWidth || imageHeight > _SlideHeight)) {
+                                var fitHeight = false;
+                                var ratio = _SlideWidth / _SlideHeight * imageHeight / imageWidth;
+
+                                if (_Options.$FillMode & 1) {
+                                    fitHeight = (ratio > 1);
+                                }
+                                else if (_Options.$FillMode & 2) {
+                                    fitHeight = (ratio < 1);
+                                }
+                                fillWidth = fitHeight ? imageWidth * _SlideHeight / imageHeight : _SlideWidth;
+                                fillHeight = fitHeight ? _SlideHeight : imageHeight * _SlideWidth / imageWidth;
+                            }
+
+                            $Jssor$.$CssWidth(_ImageItem, fillWidth);
+                            $Jssor$.$CssHeight(_ImageItem, fillHeight);
+                            $Jssor$.$CssTop(_ImageItem, (_SlideHeight - fillHeight) / 2);
+                            $Jssor$.$CssLeft(_ImageItem, (_SlideWidth - fillWidth) / 2);
+                        }
+
+                        $Jssor$.$CssPosition(_ImageItem, "absolute");
+
+                        _SelfSlider.$TriggerEvent(JssorSlider.$EVT_LOAD_END, slideItem);
+                    }
+                }
+
+                $Jssor$.$HideElement(loadingScreen);
+                completeCallback && completeCallback(_SelfSlideItem);
+            }
+
+            function LoadSlideshowImageCompleteEventHandler(nextIndex, nextItem, slideshowTransition, loadingTicket) {
+                if (loadingTicket == _LoadingTicket && _CurrentSlideIndex == slideIndex && _AutoPlay) {
+                    if (!_Frozen) {
+                        var nextRealIndex = GetRealIndex(nextIndex);
+                        _SlideshowRunner.$Initialize(nextRealIndex, slideIndex, nextItem, _SelfSlideItem, slideshowTransition);
+                        nextItem.$HideContentForSlideshow();
+                        _Slideshow.$Locate(nextRealIndex, 1);
+                        _Slideshow.$GoToPosition(nextRealIndex);
+                        _CarouselPlayer.$PlayCarousel(nextIndex, nextIndex, 0);
+                    }
+                }
+            }
+
+            function SlideReadyEventHandler(loadingTicket) {
+                if (loadingTicket == _LoadingTicket && _CurrentSlideIndex == slideIndex) {
+
+                    if (!_Processor) {
+                        var slideshowProcessor = null;
+                        if (_SlideshowRunner) {
+                            if (_SlideshowRunner.$Index == slideIndex)
+                                slideshowProcessor = _SlideshowRunner.$GetProcessor();
+                            else
+                                _SlideshowRunner.$Clear();
+                        }
+
+                        EnsureCaptionSliderVersion();
+
+                        _Processor = new Processor(slideIndex, slideshowProcessor, _SelfSlideItem.$GetCaptionSliderIn(), _SelfSlideItem.$GetCaptionSliderOut());
+                        _Processor.$SetPlayer(_PlayerInstance);
+                    }
+
+                    !_Processor.$IsPlaying() && _Processor.$Replay();
+                }
+            }
+
+            function ParkEventHandler(currentIndex, previousIndex) {
+                if (currentIndex == slideIndex) {
+
+                    if (currentIndex != previousIndex)
+                        _SlideItems[previousIndex] && _SlideItems[previousIndex].$ParkOut();
+                    else
+                        _Processor && _Processor.$AdjustIdleOnPark();
+
+                    _PlayerInstance && _PlayerInstance.$Enable();
+
+                    //park in
+                    var loadingTicket = _LoadingTicket = $Jssor$.$GetNow();
+                    _SelfSlideItem.$LoadImage($Jssor$.$CreateCallback(null, SlideReadyEventHandler, loadingTicket));
+                }
+                else {
+                    var distance = Math.abs(slideIndex - currentIndex);
+                    var loadRange = _DisplayPieces + _Options.$LazyLoading;
+                    if (!_ImageLazyLoading || distance <= loadRange || _SlideCount - distance <= loadRange) {
+                        _SelfSlideItem.$LoadImage();
+                    }
+                }
+            }
+
+            function SwipeStartEventHandler() {
+                if (_CurrentSlideIndex == slideIndex && _Processor) {
+                    _Processor.$Stop();
+                    _PlayerInstance && _PlayerInstance.$Quit();
+                    _PlayerInstance && _PlayerInstance.$Disable();
+                    _Processor.$OpenSlideshowPanel();
+                }
+            }
+
+            function FreezeEventHandler() {
+                if (_CurrentSlideIndex == slideIndex && _Processor) {
+                    _Processor.$Stop();
+                }
+            }
+
+            function LinkClickEventHandler(event) {
+                if (_LastDragSucceded) {
+                    $Jssor$.$CancelEvent(event);
+                }
+                else {
+                    _SelfSlider.$TriggerEvent(JssorSlider.$EVT_CLICK, slideIndex, event);
+                }
+            }
+
+            function PlayerAvailableEventHandler() {
+                _PlayerInstance = _PlayerInstanceElement.pInstance;
+                _Processor && _Processor.$SetPlayer(_PlayerInstance);
+            }
+
+            _SelfSlideItem.$LoadImage = function (completeCallback, loadingScreen) {
+                loadingScreen = loadingScreen || _LoadingScreen;
+
+                if (_ImageElmts.length && !_ImageLoaded) {
+
+                    $Jssor$.$ShowElement(loadingScreen);
+
+                    if (!_ImageLoading) {
+                        _ImageLoading = true;
+                        _SelfSlider.$TriggerEvent(JssorSlider.$EVT_LOAD_START);
+
+                        $Jssor$.$Each(_ImageElmts, function (imageElmt) {
+
+                            if (!imageElmt.src) {
+                                imageElmt.src = $Jssor$.$AttributeEx(imageElmt, "src2");
+                                $Jssor$.$CssDisplay(imageElmt, imageElmt["display-origin"]);
+                            }
+                        });
+                    }
+                    $Jssor$.$LoadImages(_ImageElmts, _ImageItem, $Jssor$.$CreateCallback(null, LoadImageCompleteEventHandler, completeCallback, loadingScreen));
+                }
+                else {
+                    LoadImageCompleteEventHandler(completeCallback, loadingScreen);
+                }
+            };
+
+            _SelfSlideItem.$GoForNextSlide = function () {
+                if (_SlideshowRunner) {
+                    var slideshowTransition = _SlideshowRunner.$GetTransition(_SlideCount);
+
+                    if (slideshowTransition) {
+                        var loadingTicket = _LoadingTicket = $Jssor$.$GetNow();
+
+                        var nextIndex = slideIndex + _PlayReverse;
+                        var nextItem = _SlideItems[GetRealIndex(nextIndex)];
+                        return nextItem.$LoadImage($Jssor$.$CreateCallback(null, LoadSlideshowImageCompleteEventHandler, nextIndex, nextItem, slideshowTransition, loadingTicket), _LoadingScreen);
+                    }
+                }
+
+                PlayTo(_CurrentSlideIndex + _Options.$AutoPlaySteps * _PlayReverse);
+            };
+
+            _SelfSlideItem.$TryActivate = function () {
+                ParkEventHandler(slideIndex, slideIndex);
+            };
+
+            _SelfSlideItem.$ParkOut = function () {
+                //park out
+                _PlayerInstance && _PlayerInstance.$Quit();
+                _PlayerInstance && _PlayerInstance.$Disable();
+                _SelfSlideItem.$UnhideContentForSlideshow();
+                _Processor && _Processor.$Abort();
+                _Processor = null;
+                ResetCaptionSlider();
+            };
+
+            //for debug only
+            _SelfSlideItem.$StampSlideItemElements = function (stamp) {
+                stamp = _SequenceNumber + "_" + stamp;
+
+                $JssorDebug$.$Execute(function () {
+                    if (_ImageItem)
+                        $Jssor$.$Attribute(_ImageItem, "debug-id", stamp + "_slide_item_image_id");
+
+                    $Jssor$.$Attribute(slideElmt, "debug-id", stamp + "_slide_item_item_id");
+                });
+
+                $JssorDebug$.$Execute(function () {
+                    $Jssor$.$Attribute(_Wrapper, "debug-id", stamp + "_slide_item_wrapper_id");
+                });
+
+                $JssorDebug$.$Execute(function () {
+                    $Jssor$.$Attribute(_LoadingScreen, "debug-id", stamp + "_loading_container_id");
+                });
+            };
+
+            _SelfSlideItem.$HideContentForSlideshow = function () {
+                $Jssor$.$HideElement(slideElmt);
+            };
+
+            _SelfSlideItem.$UnhideContentForSlideshow = function () {
+                $Jssor$.$ShowElement(slideElmt);
+            };
+
+            _SelfSlideItem.$EnablePlayer = function () {
+                _PlayerInstance && _PlayerInstance.$Enable();
+            };
+
+            function RefreshContent(elmt, fresh, level) {
+                if (elmt["jssor-slider"])
+                    return;
+
+                level = level || 0;
+
+                if (!_ContentRefreshed) {
+                    if (elmt.tagName == "IMG") {
+                        _ImageElmts.push(elmt);
+
+                        if (!elmt.src) {
+                            _ImageLazyLoading = true;
+                            elmt["display-origin"] = $Jssor$.$CssDisplay(elmt);
+                            $Jssor$.$HideElement(elmt);
+                        }
+                    }
+                    if ($Jssor$.$IsBrowserIe9Earlier()) {
+                        $Jssor$.$CssZIndex(elmt, ($Jssor$.$CssZIndex(elmt) || 0) + 1);
+                    }
+                    if (_Options.$HWA && $Jssor$.$WebKitVersion() > 0) {
+                        //if ((_HandleTouchEventOnly && ($Jssor$.$WebKitVersion() < 534 || !_SlideshowEnabled)) || (!_HandleTouchEventOnly && $Jssor$.$WebKitVersion() < 535)) {
+                        //    $Jssor$.$EnableHWA(elmt);
+                        //}
+                        if (!_HandleTouchEventOnly || ($Jssor$.$WebKitVersion() < 534 || !_SlideshowEnabled)) {
+                            $Jssor$.$EnableHWA(elmt);
+                        }
+                    }
+                }
+
+                var childElements = $Jssor$.$Children(elmt);
+
+                $Jssor$.$Each(childElements, function (childElement, i) {
+
+                    var uAttribute = $Jssor$.$AttributeEx(childElement, "u");
+                    if (uAttribute == "player" && !_PlayerInstanceElement) {
+                        _PlayerInstanceElement = childElement;
+                        if (_PlayerInstanceElement.pInstance) {
+                            PlayerAvailableEventHandler();
+                        }
+                        else {
+                            $Jssor$.$AddEvent(_PlayerInstanceElement, "dataavailable", PlayerAvailableEventHandler);
+                        }
+                    }
+
+                    if (uAttribute == "caption") {
+                        if (!$Jssor$.$IsBrowserIE() && !fresh) {
+                            var captionElement = $Jssor$.$CloneNode(childElement, true);
+                            $Jssor$.$InsertBefore(elmt, captionElement, childElement);
+                            $Jssor$.$RemoveChild(elmt, childElement);
+                            childElement = captionElement;
+
+                            fresh = true;
+                        }
+                    }
+                    else if (!_ContentRefreshed && !level && !_ImageItem && $Jssor$.$AttributeEx(childElement, "u") == "image") {
+                        _ImageItem = childElement;
+
+                        if (_ImageItem) {
+                            if (_ImageItem.tagName == "A") {
+                                _LinkItemOrigin = _ImageItem;
+                                $Jssor$.$SetStyles(_LinkItemOrigin, _StyleDef);
+
+                                _LinkItem = $Jssor$.$CloneNode(_ImageItem, false);
+                                //cancel click event on <A> element when a drag of slide succeeded
+                                $Jssor$.$AddEvent(_LinkItem, "click", LinkClickEventHandler);
+
+                                $Jssor$.$SetStyles(_LinkItem, _StyleDef);
+                                $Jssor$.$CssDisplay(_LinkItem, "block");
+                                $Jssor$.$CssOpacity(_LinkItem, 0);
+                                $Jssor$.$Css(_LinkItem, "backgroundColor", "#000");
+
+                                _ImageItem = $Jssor$.$FindFirstChildByTag(_ImageItem, "IMG");
+
+                                $JssorDebug$.$Execute(function () {
+                                    if (!_ImageItem) {
+                                        $JssorDebug$.$Error("slide html code definition error, no 'IMG' found in a 'image with link' slide.\r\n" + elmt.outerHTML);
+                                    }
+                                });
+                            }
+                            _ImageItem.border = 0;
+
+                            $Jssor$.$SetStyles(_ImageItem, _StyleDef);
+                        }
+                    }
+
+                    RefreshContent(childElement, fresh, level + 1);
+                });
+            }
+
+            _SelfSlideItem.$OnInnerOffsetChange = function (oldOffset, newOffset) {
+                var slidePosition = _DisplayPieces - newOffset;
+
+                SetPosition(_Wrapper, slidePosition);
+
+                //following lines are for future usage, not ready yet
+                //if (!_IsDragging || !_IsCaptionSliderPlayingWhenDragStart) {
+                //    var _DealWithParallax;
+                //    if (IsCurrentSlideIndex(slideIndex)) {
+                //        if (_CaptionSliderOptions.$PlayOutMode == 2)
+                //            _DealWithParallax = true;
+                //    }
+                //    else {
+                //        if (!_CaptionSliderOptions.$PlayInMode) {
+                //            //PlayInMode: 0 none
+                //            _CaptionSliderIn.$GoToEnd();
+                //        }
+                //        //else if (_CaptionSliderOptions.$PlayInMode == 1) {
+                //        //    //PlayInMode: 1 chain
+                //        //    _CaptionSliderIn.$GoToBegin();
+                //        //}
+                //        else if (_CaptionSliderOptions.$PlayInMode == 2) {
+                //            //PlayInMode: 2 parallel
+                //            _DealWithParallax = true;
+                //        }
+                //    }
+
+                //    if (_DealWithParallax) {
+                //        _CaptionSliderIn.$GoToPosition((_CaptionSliderIn.$GetPosition_OuterEnd() - _CaptionSliderIn.$GetPosition_OuterBegin()) * Math.abs(newOffset - 1) * .8 + _CaptionSliderIn.$GetPosition_OuterBegin());
+                //    }
+                //}
+            };
+
+            _SelfSlideItem.$GetCaptionSliderIn = function () {
+                return _CaptionSliderIn;
+            };
+
+            _SelfSlideItem.$GetCaptionSliderOut = function () {
+                return _CaptionSliderOut;
+            };
+
+            _SelfSlideItem.$Index = slideIndex;
+
+            $JssorObject$.call(_SelfSlideItem);
+
+            //SlideItem Constructor
+            {
+
+                var thumb = $Jssor$.$FindFirstChild(slideElmt, "thumb");
+                if (thumb) {
+                    _SelfSlideItem.$Thumb = $Jssor$.$CloneNode(thumb, true);
+                    $Jssor$.$RemoveAttribute(thumb, "id");
+                    $Jssor$.$HideElement(thumb);
+                }
+                $Jssor$.$ShowElement(slideElmt);
+
+                _LoadingScreen = $Jssor$.$CloneNode(_LoadingContainer, true);
+                $Jssor$.$CssZIndex(_LoadingScreen, 1000);
+
+                //cancel click event on <A> element when a drag of slide succeeded
+                $Jssor$.$AddEvent(slideElmt, "click", LinkClickEventHandler);
+
+                ResetCaptionSlider(true);
+
+                _SelfSlideItem.$Image = _ImageItem;
+                _SelfSlideItem.$Link = _LinkItem;
+
+                _SelfSlideItem.$Item = slideElmt;
+
+                _SelfSlideItem.$Wrapper = _Wrapper = slideElmt;
+                $Jssor$.$AppendChild(_Wrapper, _LoadingScreen);
+
+                _SelfSlider.$On(203, ParkEventHandler);
+                _SelfSlider.$On(28, FreezeEventHandler);
+                _SelfSlider.$On(24, SwipeStartEventHandler);
+
+                $JssorDebug$.$Execute(function () {
+                    _SequenceNumber = _SlideItemCreatedCount++;
+                });
+
+                $JssorDebug$.$Execute(function () {
+                    $Jssor$.$Attribute(_Wrapper, "debug-id", "slide-" + slideIndex);
+                });
+            }
+        }
+        //SlideItem
+
+        //Processor
+        function Processor(slideIndex, slideshowProcessor, captionSliderIn, captionSliderOut) {
+
+            var _SelfProcessor = this;
+
+            var _ProgressBegin = 0;
+            var _SlideshowBegin = 0;
+            var _SlideshowEnd;
+            var _CaptionInBegin;
+            var _IdleBegin;
+            var _IdleEnd;
+            var _ProgressEnd;
+
+            var _IsSlideshowRunning;
+            var _IsRollingBack;
+
+            var _PlayerInstance;
+            var _IsPlayerOnService;
+
+            var slideItem = _SlideItems[slideIndex];
+
+            $JssorAnimator$.call(_SelfProcessor, 0, 0);
+
+            function UpdateLink() {
+
+                $Jssor$.$ClearChildren(_LinkContainer);
+
+                if (_ShowLink && _IsSlideshowRunning && slideItem.$Link) {
+                    $Jssor$.$AppendChild(_LinkContainer, slideItem.$Link);
+                }
+
+                $Jssor$.$ShowElement(_LinkContainer, _IsSlideshowRunning || !slideItem.$Image);
+            }
+
+            function ProcessCompleteEventHandler() {
+
+                if (_IsRollingBack) {
+                    _IsRollingBack = false;
+                    _SelfSlider.$TriggerEvent(JssorSlider.$EVT_ROLLBACK_END, slideIndex, _IdleEnd, _ProgressBegin, _IdleBegin, _IdleEnd, _ProgressEnd);
+                    _SelfProcessor.$GoToPosition(_IdleBegin);
+                }
+
+                _SelfProcessor.$Replay();
+            }
+
+            function PlayerSwitchEventHandler(isOnService) {
+                _IsPlayerOnService = isOnService;
+
+                _SelfProcessor.$Stop();
+                _SelfProcessor.$Replay();
+            }
+
+            _SelfProcessor.$Replay = function () {
+
+                var currentPosition = _SelfProcessor.$GetPosition_Display();
+
+                if (!_IsDragging && !_IsSliding && !_IsPlayerOnService && _CurrentSlideIndex == slideIndex) {
+
+                    if (!currentPosition) {
+                        if (_SlideshowEnd && !_IsSlideshowRunning) {
+                            _IsSlideshowRunning = true;
+
+                            _SelfProcessor.$OpenSlideshowPanel(true);
+
+                            _SelfSlider.$TriggerEvent(JssorSlider.$EVT_SLIDESHOW_START, slideIndex, _ProgressBegin, _SlideshowBegin, _SlideshowEnd, _ProgressEnd);
+                        }
+
+                        UpdateLink();
+                    }
+
+                    var toPosition;
+                    var stateEvent = JssorSlider.$EVT_STATE_CHANGE;
+
+                    if (currentPosition != _ProgressEnd) {
+                        if (currentPosition == _IdleEnd) {
+                            toPosition = _ProgressEnd;
+                        }
+                        else if (currentPosition == _IdleBegin) {
+                            toPosition = _IdleEnd;
+                        }
+                        else if (!currentPosition) {
+                            toPosition = _IdleBegin;
+                        }
+                        else if (currentPosition > _IdleEnd) {
+                            _IsRollingBack = true;
+                            toPosition = _IdleEnd;
+                            stateEvent = JssorSlider.$EVT_ROLLBACK_START;
+                        }
+                        else {
+                            //continue from break (by drag or lock)
+                            toPosition = _SelfProcessor.$GetPlayToPosition();
+                        }
+                    }
+
+                    _SelfSlider.$TriggerEvent(stateEvent, slideIndex, currentPosition, _ProgressBegin, _IdleBegin, _IdleEnd, _ProgressEnd);
+
+                    var allowAutoPlay = _AutoPlay && (!_HoverToPause || _HoverStatus);
+
+                    if (currentPosition == _ProgressEnd) {
+                        allowAutoPlay && slideItem.$GoForNextSlide();
+                    }
+                    else if (allowAutoPlay || currentPosition != _IdleEnd) {
+                        _SelfProcessor.$PlayToPosition(toPosition, ProcessCompleteEventHandler);
+                    }
+                }
+            };
+
+            _SelfProcessor.$AdjustIdleOnPark = function () {
+                if (_IdleEnd == _ProgressEnd && _IdleEnd == _SelfProcessor.$GetPosition_Display())
+                    _SelfProcessor.$GoToPosition(_IdleBegin);
+            };
+
+            _SelfProcessor.$Abort = function () {
+                _SlideshowRunner && _SlideshowRunner.$Index == slideIndex && _SlideshowRunner.$Clear();
+
+                var currentPosition = _SelfProcessor.$GetPosition_Display();
+                if (currentPosition < _ProgressEnd) {
+                    _SelfSlider.$TriggerEvent(JssorSlider.$EVT_STATE_CHANGE, slideIndex, -currentPosition -1, _ProgressBegin, _IdleBegin, _IdleEnd, _ProgressEnd);
+                }
+            };
+
+            _SelfProcessor.$OpenSlideshowPanel = function (open) {
+                if (slideshowProcessor) {
+                    $Jssor$.$CssOverflow(_SlideshowPanel, open && slideshowProcessor.$Transition.$Outside ? "" : "hidden");
+                }
+            };
+
+            _SelfProcessor.$OnInnerOffsetChange = function (oldPosition, newPosition) {
+
+                if (_IsSlideshowRunning && newPosition >= _SlideshowEnd) {
+                    _IsSlideshowRunning = false;
+                    UpdateLink();
+                    slideItem.$UnhideContentForSlideshow();
+                    _SlideshowRunner.$Clear();
+
+                    _SelfSlider.$TriggerEvent(JssorSlider.$EVT_SLIDESHOW_END, slideIndex, _ProgressBegin, _SlideshowBegin, _SlideshowEnd, _ProgressEnd);
+                }
+
+                _SelfSlider.$TriggerEvent(JssorSlider.$EVT_PROGRESS_CHANGE, slideIndex, newPosition, _ProgressBegin, _IdleBegin, _IdleEnd, _ProgressEnd);
+            };
+
+            _SelfProcessor.$SetPlayer = function (playerInstance) {
+                if (playerInstance && !_PlayerInstance) {
+                    _PlayerInstance = playerInstance;
+
+                    playerInstance.$On($JssorPlayer$.$EVT_SWITCH, PlayerSwitchEventHandler);
+                }
+            };
+
+            //Processor Constructor
+            {
+                if (slideshowProcessor) {
+                    _SelfProcessor.$Chain(slideshowProcessor);
+                }
+
+                _SlideshowEnd = _SelfProcessor.$GetPosition_OuterEnd();
+                _CaptionInBegin = _SelfProcessor.$GetPosition_OuterEnd();
+                _SelfProcessor.$Chain(captionSliderIn);
+                _IdleBegin = captionSliderIn.$GetPosition_OuterEnd();
+                _IdleEnd = _IdleBegin + _Options.$AutoPlayInterval;
+
+                captionSliderOut.$Shift(_IdleEnd);
+                _SelfProcessor.$Combine(captionSliderOut);
+                _ProgressEnd = _SelfProcessor.$GetPosition_OuterEnd();
+            }
+        }
+        //Processor
+        //private classes
+
+        function SetPosition(elmt, position) {
+            var orientation = _DragOrientation > 0 ? _DragOrientation : _PlayOrientation;
+            var x = _StepLengthX * position * (orientation & 1);
+            var y = _StepLengthY * position * ((orientation >> 1) & 1);
+
+            if ($Jssor$.$IsBrowserChrome()) {
+                x = x.toFixed(3);
+                y = y.toFixed(3);
+            }
+            else {
+                x = Math.round(x);
+                y = Math.round(y);
+            }
+
+            if ($Jssor$.$IsBrowserIE() && $Jssor$.$BrowserVersion() >= 10 && $Jssor$.$BrowserVersion() < 11) {
+                elmt.style.msTransform = "translate(" + x + "px, " + y + "px)";
+            }
+            else if ($Jssor$.$IsBrowserChrome() && $Jssor$.$BrowserVersion() >= 30 && $Jssor$.$BrowserVersion() < 34) {
+                elmt.style.WebkitTransition = "transform 0s";
+                elmt.style.WebkitTransform = "translate3d(" + x + "px, " + y + "px, 0px) perspective(2000px)";
+            }
+            else {
+                $Jssor$.$CssLeft(elmt, x);
+                $Jssor$.$CssTop(elmt, y);
+            }
+        }
+
+        //Event handling begin
+
+        function OnMouseDown(event) {
+            var tagName = $Jssor$.$EventSrc(event).tagName;
+            if (!_DragOrientationRegistered && tagName != "INPUT" && tagName != "TEXTAREA" && tagName != "SELECT" && RegisterDrag()) {
+                OnDragStart(event);
+            }
+        }
+
+        function Freeze() {
+
+            _CarouselPlaying_OnFreeze = _IsSliding;
+            _PlayToPosition_OnFreeze = _CarouselPlayer.$GetPlayToPosition();
+            _Position_OnFreeze = _Conveyor.$GetPosition();
+
+            if (_IsDragging || !_HoverStatus && (_HoverToPause & 12)) {
+                _CarouselPlayer.$Stop();
+
+                _SelfSlider.$TriggerEvent(JssorSlider.$EVT_FREEZE);
+            }
+        }
+
+        function Unfreeze(byDrag) {
+
+            if (!_IsDragging && (_HoverStatus || !(_HoverToPause & 12)) && !_CarouselPlayer.$IsPlaying()) {
+
+                var currentPosition = _Conveyor.$GetPosition();
+                var toPosition = Math.ceil(_Position_OnFreeze);
+
+                if (byDrag && Math.abs(_DragOffsetTotal) >= _Options.$MinDragOffsetToSlide) {
+                    toPosition = Math.ceil(currentPosition);
+                    toPosition += _DragIndexAdjust;
+                }
+
+                if (!(_Loop & 1)) {
+                    toPosition = Math.min(_SlideCount - _DisplayPieces, Math.max(toPosition, 0));
+                }
+
+                var t = Math.abs(toPosition - currentPosition);
+                t = 1 - Math.pow(1 - t, 5);
+
+                if (!_LastDragSucceded && _CarouselPlaying_OnFreeze) {
+                    _CarouselPlayer.$Continue(_PlayToPosition_OnFreeze);
+                }
+                else if (currentPosition == toPosition) {
+                    _CurrentSlideItem.$EnablePlayer();
+                    _CurrentSlideItem.$TryActivate();
+                }
+                else {
+
+                    _CarouselPlayer.$PlayCarousel(currentPosition, toPosition, t * _SlideDuration);
+                }
+            }
+        }
+
+        function OnDragStart(event) {
+
+            _IsDragging = true;
+            _DragInvalid = false;
+            _LoadingTicket = null;
+
+            $Jssor$.$AddEvent(document, _MoveEvent, OnDragMove);
+
+            _LastTimeMoveByDrag = $Jssor$.$GetNow() - 50;
+
+            _LastDragSucceded = 0;
+            Freeze();
+
+            if (!_CarouselPlaying_OnFreeze)
+                _DragOrientation = 0;
+
+            if (_HandleTouchEventOnly) {
+                var touchPoint = event.touches[0];
+                _DragStartMouseX = touchPoint.clientX;
+                _DragStartMouseY = touchPoint.clientY;
+            }
+            else {
+                var mousePoint = $Jssor$.$MousePosition(event);
+
+                _DragStartMouseX = mousePoint.x;
+                _DragStartMouseY = mousePoint.y;
+
+                $Jssor$.$CancelEvent(event);
+            }
+
+            _DragOffsetTotal = 0;
+            _DragOffsetLastTime = 0;
+            _DragIndexAdjust = 0;
+
+            //Trigger EVT_DRAGSTART
+            _SelfSlider.$TriggerEvent(JssorSlider.$EVT_DRAG_START, GetRealIndex(_Position_OnFreeze), _Position_OnFreeze, event);
+        }
+
+        function OnDragMove(event) {
+            if (_IsDragging && (!$Jssor$.$IsBrowserIe9Earlier() || event.button)) {
+                var actionPoint;
+
+                if (_HandleTouchEventOnly) {
+                    var touches = event.touches;
+                    if (touches && touches.length > 0) {
+                        actionPoint = new $JssorPoint$(touches[0].clientX, touches[0].clientY);
+                    }
+                }
+                else {
+                    actionPoint = $Jssor$.$MousePosition(event);
+                }
+
+                if (actionPoint) {
+                    var distanceX = actionPoint.x - _DragStartMouseX;
+                    var distanceY = actionPoint.y - _DragStartMouseY;
+
+
+                    if (Math.floor(_Position_OnFreeze) != _Position_OnFreeze)
+                        _DragOrientation = _DragOrientation || (_PlayOrientation & _DragOrientationRegistered);
+
+                    if ((distanceX || distanceY) && !_DragOrientation) {
+                        if (_DragOrientationRegistered == 3) {
+                            if (Math.abs(distanceY) > Math.abs(distanceX)) {
+                                _DragOrientation = 2;
+                            }
+                            else
+                                _DragOrientation = 1;
+                        }
+                        else {
+                            _DragOrientation = _DragOrientationRegistered;
+                        }
+
+                        if (_HandleTouchEventOnly && _DragOrientation == 1 && Math.abs(distanceY) - Math.abs(distanceX) > 3) {
+                            _DragInvalid = true;
+                        }
+                    }
+
+                    if (_DragOrientation) {
+                        var distance = distanceY;
+                        var stepLength = _StepLengthY;
+
+                        if (_DragOrientation == 1) {
+                            distance = distanceX;
+                            stepLength = _StepLengthX;
+                        }
+
+                        if (!(_Loop & 1)) {
+                            if (distance > 0) {
+                                var normalDistance = stepLength * _CurrentSlideIndex;
+                                var sqrtDistance = distance - normalDistance;
+                                if (sqrtDistance > 0) {
+                                    distance = normalDistance + Math.sqrt(sqrtDistance) * 5;
+                                }
+                            }
+
+                            if (distance < 0) {
+                                var normalDistance = stepLength * (_SlideCount - _DisplayPieces - _CurrentSlideIndex);
+                                var sqrtDistance = -distance - normalDistance;
+
+                                if (sqrtDistance > 0) {
+                                    distance = -normalDistance - Math.sqrt(sqrtDistance) * 5;
+                                }
+                            }
+                        }
+
+                        if (_DragOffsetTotal - _DragOffsetLastTime < -2) {
+                            _DragIndexAdjust = 0;
+                        }
+                        else if (_DragOffsetTotal - _DragOffsetLastTime > 2) {
+                            _DragIndexAdjust = -1;
+                        }
+
+                        _DragOffsetLastTime = _DragOffsetTotal;
+                        _DragOffsetTotal = distance;
+                        _PositionToGoByDrag = _Position_OnFreeze - _DragOffsetTotal / stepLength / (_ScaleRatio || 1);
+
+                        if (_DragOffsetTotal && _DragOrientation && !_DragInvalid) {
+                            $Jssor$.$CancelEvent(event);
+                            if (!_IsSliding) {
+                                _CarouselPlayer.$StandBy(_PositionToGoByDrag);
+                            }
+                            else
+                                _CarouselPlayer.$SetStandByPosition(_PositionToGoByDrag);
+                        }
+                        else if ($Jssor$.$IsBrowserIe9Earlier()) {
+                            $Jssor$.$CancelEvent(event);
+                        }
+                    }
+                }
+            }
+            else {
+                OnDragEnd(event);
+            }
+        }
+
+        function OnDragEnd(event) {
+            UnregisterDrag();
+
+            if (_IsDragging) {
+
+                _IsDragging = false;
+
+                _LastTimeMoveByDrag = $Jssor$.$GetNow();
+
+                $Jssor$.$RemoveEvent(document, _MoveEvent, OnDragMove);
+
+                _LastDragSucceded = _DragOffsetTotal;
+
+                _LastDragSucceded && $Jssor$.$CancelEvent(event);
+
+                _CarouselPlayer.$Stop();
+
+                var currentPosition = _Conveyor.$GetPosition();
+
+                //Trigger EVT_DRAG_END
+                _SelfSlider.$TriggerEvent(JssorSlider.$EVT_DRAG_END, GetRealIndex(currentPosition), currentPosition, GetRealIndex(_Position_OnFreeze), _Position_OnFreeze, event);
+
+                Unfreeze(true);
+
+                Freeze();
+            }
+        }
+        //Event handling end
+
+        function SetCurrentSlideIndex(index) {
+            _PrevSlideItem = _SlideItems[_CurrentSlideIndex];
+            _PreviousSlideIndex = _CurrentSlideIndex;
+            _CurrentSlideIndex = GetRealIndex(index);
+            _CurrentSlideItem = _SlideItems[_CurrentSlideIndex];
+            ResetNavigator(index);
+            return _CurrentSlideIndex;
+        }
+
+        function OnPark(slideIndex, prevIndex) {
+            _DragOrientation = 0;
+
+            SetCurrentSlideIndex(slideIndex);
+
+            //Trigger EVT_PARK
+            _SelfSlider.$TriggerEvent(JssorSlider.$EVT_PARK, GetRealIndex(slideIndex), prevIndex);
+        }
+
+        function ResetNavigator(index, temp) {
+            _TempSlideIndex = index;
+            $Jssor$.$Each(_Navigators, function (navigator) {
+                navigator.$SetCurrentIndex(GetRealIndex(index), index, temp);
+            });
+        }
+
+        function RegisterDrag() {
+            var dragRegistry = JssorSlider.$DragRegistry || 0;
+            var dragOrientation = _DragEnabled;
+            if (_HandleTouchEventOnly)
+                (dragOrientation & 1) && (dragOrientation &= 1);
+            JssorSlider.$DragRegistry |= dragOrientation;
+
+            return (_DragOrientationRegistered = dragOrientation & ~dragRegistry);
+        }
+
+        function UnregisterDrag() {
+            if (_DragOrientationRegistered) {
+                JssorSlider.$DragRegistry &= ~_DragEnabled;
+                _DragOrientationRegistered = 0;
+            }
+        }
+
+        function CreatePanel() {
+            var div = $Jssor$.$CreateDiv();
+
+            $Jssor$.$SetStyles(div, _StyleDef);
+            $Jssor$.$CssPosition(div, "absolute");
+
+            return div;
+        }
+
+        function GetRealIndex(index) {
+            return (index % _SlideCount + _SlideCount) % _SlideCount;
+        }
+
+        function IsCurrentSlideIndex(index) {
+            return GetRealIndex(index) == _CurrentSlideIndex;
+        }
+
+        function IsPreviousSlideIndex(index) {
+            return GetRealIndex(index) == _PreviousSlideIndex;
+        }
+
+        //Navigation Request Handler
+        function NavigationClickHandler(index, relative) {
+            if (relative) {
+                if (!_Loop) {
+                    //Stop at threshold
+                    index = Math.min(Math.max(index + _TempSlideIndex, 0), _SlideCount - _DisplayPieces);
+                    relative = false;
+                }
+                else if (_Loop & 2) {
+                    //Rewind
+                    index = GetRealIndex(index + _TempSlideIndex);
+                    relative = false;
+                }
+            }
+            PlayTo(index, _Options.$SlideDuration, relative);
+        }
+
+        function ShowNavigators() {
+            $Jssor$.$Each(_Navigators, function (navigator) {
+                navigator.$Show(navigator.$Options.$ChanceToShow > _HoverStatus);
+            });
+        }
+
+        function MainContainerMouseLeaveEventHandler() {
+            if (!_HoverStatus) {
+
+                //$JssorDebug$.$Log("mouseleave");
+
+                _HoverStatus = 1;
+
+                ShowNavigators();
+
+                if (!_IsDragging) {
+                    (_HoverToPause & 12) && Unfreeze();
+                    (_HoverToPause & 3) && _SlideItems[_CurrentSlideIndex].$TryActivate();
+                }
+            }
+        }
+
+        function MainContainerMouseEnterEventHandler() {
+
+            if (_HoverStatus) {
+
+                //$JssorDebug$.$Log("mouseenter");
+
+                _HoverStatus = 0;
+
+                ShowNavigators();
+
+                _IsDragging || !(_HoverToPause & 12) || Freeze();
+            }
+        }
+
+        function AdjustSlidesContainerSize() {
+            _StyleDef = { $Width: _SlideWidth, $Height: _SlideHeight, $Top: 0, $Left: 0 };
+
+            $Jssor$.$Each(_SlideElmts, function (slideElmt, i) {
+
+                $Jssor$.$SetStyles(slideElmt, _StyleDef);
+                $Jssor$.$CssPosition(slideElmt, "absolute");
+                $Jssor$.$CssOverflow(slideElmt, "hidden");
+
+                $Jssor$.$HideElement(slideElmt);
+            });
+
+            $Jssor$.$SetStyles(_LoadingContainer, _StyleDef);
+        }
+
+        function PlayToOffset(offset, slideDuration) {
+            PlayTo(offset, slideDuration, true);
+        }
+
+        function PlayTo(slideIndex, slideDuration, relative) {
+            ///	<summary>
+            ///		PlayTo( slideIndex [, slideDuration] ); //Play slider to position 'slideIndex' within a period calculated base on 'slideDuration'.
+            ///	</summary>
+            ///	<param name="slideIndex" type="Number">
+            ///		slide slideIndex or position will be playing to
+            ///	</param>
+            ///	<param name="slideDuration" type="Number" optional="true">
+            ///		base slide duration in milliseconds to calculate the whole duration to complete this play request.
+            ///	    default value is '$SlideDuration' value which is specified when initialize the slider.
+            ///	</param>
+            /// http://msdn.microsoft.com/en-us/library/vstudio/bb385682.aspx
+            /// http://msdn.microsoft.com/en-us/library/vstudio/hh542720.aspx
+            if (_CarouselEnabled && (!_IsDragging || _Options.$NaviQuitDrag)) {
+                _IsSliding = true;
+                _IsDragging = false;
+                _CarouselPlayer.$Stop();
+
+                {
+                    //Slide Duration
+                    if (slideDuration == undefined)
+                        slideDuration = _SlideDuration;
+
+                    var positionDisplay = _Carousel.$GetPosition_Display();
+                    var positionTo = slideIndex;
+                    if (relative) {
+                        positionTo = positionDisplay + slideIndex;
+                        if (slideIndex > 0)
+                            positionTo = Math.ceil(positionTo);
+                        else
+                            positionTo = Math.floor(positionTo);
+                    }
+
+
+                    if (!(_Loop & 1)) {
+                        positionTo = GetRealIndex(positionTo);
+                        positionTo = Math.max(0, Math.min(positionTo, _SlideCount - _DisplayPieces));
+                    }
+
+                    var positionOffset = (positionTo - positionDisplay) % _SlideCount;
+                    positionTo = positionDisplay + positionOffset;
+
+                    var duration = positionDisplay == positionTo ? 0 : slideDuration * Math.abs(positionOffset);
+                    duration = Math.min(duration, slideDuration * _DisplayPieces * 1.5);
+
+                    _CarouselPlayer.$PlayCarousel(positionDisplay, positionTo, duration || 1);
+                }
+            }
+        }
+
+        //private functions
+
+        //member functions
+
+        _SelfSlider.$PlayTo = PlayTo;
+
+        _SelfSlider.$GoTo = function (slideIndex) {
+            ///	<summary>
+            ///		instance.$GoTo( slideIndex );   //Go to the specifed slide immediately with no play.
+            ///	</summary>
+            PlayTo(slideIndex, 1);
+        };
+
+        _SelfSlider.$Next = function () {
+            ///	<summary>
+            ///		instance.$Next();   //Play the slider to next slide.
+            ///	</summary>
+            PlayToOffset(1);
+        };
+
+        _SelfSlider.$Prev = function () {
+            ///	<summary>
+            ///		instance.$Prev();   //Play the slider to previous slide.
+            ///	</summary>
+            PlayToOffset(-1);
+        };
+
+        _SelfSlider.$Pause = function () {
+            ///	<summary>
+            ///		instance.$Pause();   //Pause the slider, prevent it from auto playing.
+            ///	</summary>
+            _AutoPlay = false;
+        };
+
+        _SelfSlider.$Play = function () {
+            ///	<summary>
+            ///		instance.$Play();   //Start auto play if the slider is currently paused.
+            ///	</summary>
+            if (!_AutoPlay) {
+                _AutoPlay = true;
+                _SlideItems[_CurrentSlideIndex] && _SlideItems[_CurrentSlideIndex].$TryActivate();
+            }
+        };
+
+        _SelfSlider.$SetSlideshowTransitions = function (transitions) {
+            ///	<summary>
+            ///		instance.$SetSlideshowTransitions( transitions );   //Reset slideshow transitions for the slider.
+            ///	</summary>
+            $JssorDebug$.$Execute(function () {
+                if (!transitions || !transitions.length) {
+                    $JssorDebug$.$Error("Can not set slideshow transitions, no transitions specified.");
+                }
+            });
+
+            $Jssor$.$TranslateTransitions(transitions);    //for old transition compatibility
+            _Options.$SlideshowOptions.$Transitions = transitions;
+        };
+
+        _SelfSlider.$SetCaptionTransitions = function (transitions) {
+            ///	<summary>
+            ///		instance.$SetCaptionTransitions( transitions );   //Reset caption transitions for the slider.
+            ///	</summary>
+            $JssorDebug$.$Execute(function () {
+                if (!transitions || !transitions.length) {
+                    $JssorDebug$.$Error("Can not set caption transitions, no transitions specified");
+                }
+            });
+
+            $Jssor$.$TranslateTransitions(transitions);    //for old transition compatibility
+            _CaptionSliderOptions.$CaptionTransitions = transitions;
+            _CaptionSliderOptions.$Version = $Jssor$.$GetNow();
+        };
+
+        _SelfSlider.$SlidesCount = function () {
+            ///	<summary>
+            ///		instance.$SlidesCount();   //Retrieve slides count of the slider.
+            ///	</summary>
+            return _SlideElmts.length;
+        };
+
+        _SelfSlider.$CurrentIndex = function () {
+            ///	<summary>
+            ///		instance.$CurrentIndex();   //Retrieve current slide index of the slider.
+            ///	</summary>
+            return _CurrentSlideIndex;
+        };
+
+        _SelfSlider.$IsAutoPlaying = function () {
+            ///	<summary>
+            ///		instance.$IsAutoPlaying();   //Retrieve auto play status of the slider.
+            ///	</summary>
+            return _AutoPlay;
+        };
+
+        _SelfSlider.$IsDragging = function () {
+            ///	<summary>
+            ///		instance.$IsDragging();   //Retrieve drag status of the slider.
+            ///	</summary>
+            return _IsDragging;
+        };
+
+        _SelfSlider.$IsSliding = function () {
+            ///	<summary>
+            ///		instance.$IsSliding();   //Retrieve right<-->left sliding status of the slider.
+            ///	</summary>
+            return _IsSliding;
+        };
+
+        _SelfSlider.$IsMouseOver = function () {
+            ///	<summary>
+            ///		instance.$IsMouseOver();   //Retrieve mouse over status of the slider.
+            ///	</summary>
+            return !_HoverStatus;
+        };
+
+        _SelfSlider.$LastDragSucceded = function () {
+            ///	<summary>
+            ///		instance.$IsLastDragSucceded();   //Retrieve last drag succeded status, returns 0 if failed, returns drag offset if succeded
+            ///	</summary>
+            return _LastDragSucceded;
+        };
+
+        function OriginalWidth() {
+            ///	<summary>
+            ///		instance.$OriginalWidth();   //Retrieve original width of the slider.
+            ///	</summary>
+            return $Jssor$.$CssWidth(_ScaleWrapper || elmt);
+        }
+
+        function OriginalHeight() {
+            ///	<summary>
+            ///		instance.$OriginalHeight();   //Retrieve original height of the slider.
+            ///	</summary>
+            return $Jssor$.$CssHeight(_ScaleWrapper || elmt);
+        }
+
+        _SelfSlider.$OriginalWidth = _SelfSlider.$GetOriginalWidth = OriginalWidth;
+
+        _SelfSlider.$OriginalHeight = _SelfSlider.$GetOriginalHeight = OriginalHeight;
+
+        function Scale(dimension, isHeight) {
+            ///	<summary>
+            ///		instance.$ScaleWidth();   //Retrieve scaled dimension the slider currently displays.
+            ///		instance.$ScaleWidth( dimension );   //Scale the slider to new width and keep aspect ratio.
+            ///	</summary>
+
+            if (dimension == undefined)
+                return $Jssor$.$CssWidth(elmt);
+
+            $JssorDebug$.$Execute(function () {
+                if (!dimension || dimension < 0) {
+                    $JssorDebug$.$Fail("'$ScaleWidth' error, 'dimension' should be positive value.");
+                }
+            });
+
+            if (!_ScaleWrapper) {
+                $JssorDebug$.$Execute(function () {
+                    var originalWidthStr = $Jssor$.$Css(elmt, "width");
+                    var originalHeightStr = $Jssor$.$Css(elmt, "height");
+                    var originalWidth = $Jssor$.$CssP(elmt, "width");
+                    var originalHeight = $Jssor$.$CssP(elmt, "height");
+
+                    if (!originalWidthStr) {
+                        $JssorDebug$.$Fail("Cannot scale jssor slider, 'dimension' of 'outer container' not specified. Please specify 'dimension' in pixel. e.g. 'dimension: 600px;'");
+                    }
+
+                    if (!originalHeightStr) {
+                        $JssorDebug$.$Fail("Cannot scale jssor slider, 'height' of 'outer container' not specified. Please specify 'height' in pixel. e.g. 'height: 300px;'");
+                    }
+
+                    if (originalWidthStr.indexOf('%') != -1) {
+                        $JssorDebug$.$Fail("Cannot scale jssor slider, 'dimension' of 'outer container' not valid. Please specify 'dimension' in pixel. e.g. 'dimension: 600px;'");
+                    }
+
+                    if (originalHeightStr.indexOf('%') != -1) {
+                        $JssorDebug$.$Fail("Cannot scale jssor slider, 'height' of 'outer container' not valid. Please specify 'height' in pixel. e.g. 'height: 300px;'");
+                    }
+
+                    if (!originalWidth) {
+                        $JssorDebug$.$Fail("Cannot scale jssor slider, 'dimension' of 'outer container' not valid. 'dimension' of 'outer container' should be positive number. e.g. 'dimension: 600px;'");
+                    }
+
+                    if (!originalHeight) {
+                        $JssorDebug$.$Fail("Cannot scale jssor slider, 'height' of 'outer container' not valid. 'height' of 'outer container' should be positive number. e.g. 'height: 300px;'");
+                    }
+                });
+
+                //var innerWrapper = $Jssor$.$CloneNode(elmt, false);
+                //$Jssor$.$RemoveAttribute(innerWrapper, "id");
+                var innerWrapper = $Jssor$.$CreateDiv(document);
+                $Jssor$.$CssCssText(innerWrapper, $Jssor$.$CssCssText(elmt));
+                $Jssor$.$ClassName(innerWrapper, $Jssor$.$ClassName(elmt));
+
+                $Jssor$.$CssPosition(innerWrapper, "relative");
+                $Jssor$.$CssTop(innerWrapper, 0);
+                $Jssor$.$CssLeft(innerWrapper, 0);
+                $Jssor$.$CssOverflow(innerWrapper, "visible");
+
+                //_ScaleWrapper = $Jssor$.$CloneNode(elmt, false);
+                //$Jssor$.$RemoveAttribute(_ScaleWrapper, "id");
+                //$Jssor$.$CssCssText(_ScaleWrapper, "");
+                _ScaleWrapper = $Jssor$.$CreateDiv(document);
+
+                $Jssor$.$CssPosition(_ScaleWrapper, "absolute");
+                $Jssor$.$CssTop(_ScaleWrapper, 0);
+                $Jssor$.$CssLeft(_ScaleWrapper, 0);
+                $Jssor$.$CssWidth(_ScaleWrapper, $Jssor$.$CssWidth(elmt));
+                $Jssor$.$CssHeight(_ScaleWrapper, $Jssor$.$CssHeight(elmt));
+                $Jssor$.$SetStyleTransformOrigin(_ScaleWrapper, "0 0");
+
+                $Jssor$.$AppendChild(_ScaleWrapper, innerWrapper);
+
+                var children = $Jssor$.$Children(elmt);
+                $Jssor$.$AppendChild(elmt, _ScaleWrapper);
+
+                $Jssor$.$Css(elmt, "backgroundImage", "");
+
+                var noMoveElmts = {
+                    "navigator": _BulletNavigatorOptions && _BulletNavigatorOptions.$Scale == false,
+                    "arrowleft": _ArrowNavigatorOptions && _ArrowNavigatorOptions.$Scale == false,
+                    "arrowright": _ArrowNavigatorOptions && _ArrowNavigatorOptions.$Scale == false,
+                    "thumbnavigator": _ThumbnailNavigatorOptions && _ThumbnailNavigatorOptions.$Scale == false,
+                    "thumbwrapper": _ThumbnailNavigatorOptions && _ThumbnailNavigatorOptions.$Scale == false
+                };
+
+                $Jssor$.$Each(children, function (child) {
+                    $Jssor$.$AppendChild(noMoveElmts[$Jssor$.$AttributeEx(child, "u")] ? elmt : innerWrapper, child);
+                });
+
+                $Jssor$.$ShowElement(innerWrapper);
+                $Jssor$.$ShowElement(_ScaleWrapper);
+            }
+
+            $JssorDebug$.$Execute(function () {
+                if (!_InitialScrollWidth) {
+                    _InitialScrollWidth = _SelfSlider.$Elmt.scrollWidth;
+                }
+            });
+
+            _ScaleRatio = dimension /  (isHeight? $Jssor$.$CssHeight : $Jssor$.$CssWidth)(_ScaleWrapper);
+            $Jssor$.$CssScale(_ScaleWrapper, _ScaleRatio);
+
+            $Jssor$.$CssWidth(elmt, isHeight ? (_ScaleRatio * OriginalWidth()) : dimension);
+            $Jssor$.$CssHeight(elmt, isHeight ? dimension : (_ScaleRatio * OriginalHeight()));
+
+            $Jssor$.$Each(_Navigators, function (navigator) {
+                navigator.$Relocate();
+            });
+        }
+
+        _SelfSlider.$ScaleHeight = _SelfSlider.$GetScaleHeight = function (height) {
+            ///	<summary>
+            ///		instance.$ScaleHeight();   //Retrieve scaled height the slider currently displays.
+            ///		instance.$ScaleHeight( dimension );   //Scale the slider to new height and keep aspect ratio.
+            ///	</summary>
+
+            if (height == undefined)
+                return $Jssor$.$CssHeight(elmt);
+
+            Scale(height, true);
+        };
+
+        _SelfSlider.$ScaleWidth = _SelfSlider.$SetScaleWidth = _SelfSlider.$GetScaleWidth = Scale;
+
+        _SelfSlider.$GetVirtualIndex = function (index) {
+            var parkingIndex = Math.ceil(GetRealIndex(_ParkingPosition / _StepLength));
+            var displayIndex = GetRealIndex(index - _CurrentSlideIndex + parkingIndex);
+
+            if (displayIndex > _DisplayPieces) {
+                if (index - _CurrentSlideIndex > _SlideCount / 2)
+                    index -= _SlideCount;
+                else if (index - _CurrentSlideIndex <= -_SlideCount / 2)
+                    index += _SlideCount;
+            }
+            else {
+                index = _CurrentSlideIndex + displayIndex - parkingIndex;
+            }
+
+            return index;
+        };
+
+        //member functions
+
+        $JssorObject$.call(this);
+
+        //initialize member variables
+        _SelfSlider.$Elmt = elmt = $Jssor$.$GetElement(elmt);
+        //initialize member variables
+
+        var _InitialScrollWidth;    //for debug only
+        var _CaptionSliderCount = 1;    //for debug only
+
+        $JssorDebug$.$Execute(function () {
+            var outerContainerElmt = $Jssor$.$GetElement(elmt);
+            if (!outerContainerElmt)
+                $JssorDebug$.$Fail("Outer container '" + elmt + "' not found.");
+        });
+
+        var _Options = $Jssor$.$Extend({
+            $FillMode: 0,                   //[Optional] The way to fill image in slide, 0 stretch, 1 contain (keep aspect ratio and put all inside slide), 2 cover (keep aspect ratio and cover whole slide), 4 actual size, 5 contain for large image, actual size for small image, default value is 0
+            $LazyLoading: 1,                //[Optional] For image with  lazy loading format (<IMG src2="url" .../>), by default it will be loaded only when the slide comes.
+            //But an integer value (maybe 0, 1, 2 or 3) indicates that how far of nearby slides should be loaded immediately as well, default value is 1.
+            $StartIndex: 0,                 //[Optional] Index of slide to display when initialize, default value is 0
+            $AutoPlay: false,               //[Optional] Whether to auto play, default value is false
+            $Loop: 1,                       //[Optional] Enable loop(circular) of carousel or not, 0: stop, 1: loop, 2 rewind, default value is 1
+            $HWA: true,                     //[Optional] Enable hardware acceleration or not, default value is true
+            $NaviQuitDrag: true,
+            $AutoPlaySteps: 1,              //[Optional] Steps to go of every play (this options applys only when slideshow disabled), default value is 1
+            $AutoPlayInterval: 3000,        //[Optional] Interval to play next slide since the previous stopped if a slideshow is auto playing, default value is 3000
+            $PauseOnHover: 1,               //[Optional] Whether to pause when mouse over if a slider is auto playing, 0 no pause, 1 pause for desktop, 2 pause for touch device, 3 pause for desktop and touch device, 4 freeze for desktop, 8 freeze for touch device, 12 freeze for desktop and touch device, default value is 1
+
+            $SlideDuration: 500,            //[Optional] Specifies default duration (swipe) for slide in milliseconds, default value is 400
+            $SlideEasing: $JssorEasing$.$EaseOutQuad,   //[Optional] Specifies easing for right to left animation, default value is $JssorEasing$.$EaseOutQuad
+            $MinDragOffsetToSlide: 20,      //[Optional] Minimum drag offset that trigger slide, default value is 20
+            $SlideSpacing: 0, 				//[Optional] Space between each slide in pixels, default value is 0
+            $DisplayPieces: 1,              //[Optional] Number of pieces to display (the slideshow would be disabled if the value is set to greater than 1), default value is 1
+            $ParkingPosition: 0,            //[Optional] The offset position to park slide (this options applys only when slideshow disabled), default value is 0.
+            $UISearchMode: 1,               //[Optional] The way (0 parellel, 1 recursive, default value is recursive) to search UI components (slides container, loading screen, navigator container, arrow navigator container, thumbnail navigator container etc.
+            $PlayOrientation: 1,            //[Optional] Orientation to play slide (for auto play, navigation), 1 horizental, 2 vertical, 5 horizental reverse, 6 vertical reverse, default value is 1
+            $DragOrientation: 1             //[Optional] Orientation to drag slide, 0 no drag, 1 horizental, 2 vertical, 3 both, default value is 1 (Note that the $DragOrientation should be the same as $PlayOrientation when $DisplayPieces is greater than 1, or parking position is not 0)
+
+        }, options);
+
+        //Sodo statement for development time intellisence only
+        $JssorDebug$.$Execute(function () {
+            _Options = $Jssor$.$Extend({
+                $ArrowKeyNavigation: undefined,
+                $SlideWidth: undefined,
+                $SlideHeight: undefined,
+                $SlideshowOptions: undefined,
+                $CaptionSliderOptions: undefined,
+                $BulletNavigatorOptions: undefined,
+                $ArrowNavigatorOptions: undefined,
+                $ThumbnailNavigatorOptions: undefined
+            },
+            _Options);
+        });
+
+        var _PlayOrientation = _Options.$PlayOrientation & 3;
+        var _PlayReverse = (_Options.$PlayOrientation & 4) / -4 || 1;
+
+        var _SlideshowOptions = _Options.$SlideshowOptions;
+        var _CaptionSliderOptions = $Jssor$.$Extend({ $Class: $JssorCaptionSliderBase$, $PlayInMode: 1, $PlayOutMode: 1 }, _Options.$CaptionSliderOptions);
+        $Jssor$.$TranslateTransitions(_CaptionSliderOptions.$CaptionTransitions); //for old transition compatibility
+        var _BulletNavigatorOptions = _Options.$BulletNavigatorOptions;
+        var _ArrowNavigatorOptions = _Options.$ArrowNavigatorOptions;
+        var _ThumbnailNavigatorOptions = _Options.$ThumbnailNavigatorOptions;
+
+        $JssorDebug$.$Execute(function () {
+            if (_SlideshowOptions && !_SlideshowOptions.$Class) {
+                $JssorDebug$.$Fail("Option $SlideshowOptions error, class not specified.");
+            }
+        });
+
+        $JssorDebug$.$Execute(function () {
+            if (_Options.$CaptionSliderOptions && !_Options.$CaptionSliderOptions.$Class) {
+                $JssorDebug$.$Fail("Option $CaptionSliderOptions error, class not specified.");
+            }
+        });
+
+        $JssorDebug$.$Execute(function () {
+            if (_BulletNavigatorOptions && !_BulletNavigatorOptions.$Class) {
+                $JssorDebug$.$Fail("Option $BulletNavigatorOptions error, class not specified.");
+            }
+        });
+
+        $JssorDebug$.$Execute(function () {
+            if (_ArrowNavigatorOptions && !_ArrowNavigatorOptions.$Class) {
+                $JssorDebug$.$Fail("Option $ArrowNavigatorOptions error, class not specified.");
+            }
+        });
+
+        $JssorDebug$.$Execute(function () {
+            if (_ThumbnailNavigatorOptions && !_ThumbnailNavigatorOptions.$Class) {
+                $JssorDebug$.$Fail("Option $ThumbnailNavigatorOptions error, class not specified.");
+            }
+        });
+
+        var _UISearchMode = _Options.$UISearchMode;
+        var _ScaleWrapper;
+        var _SlidesContainer = $Jssor$.$FindFirstChild(elmt, "slides", null, _UISearchMode);
+        var _LoadingContainer = $Jssor$.$FindFirstChild(elmt, "loading", null, _UISearchMode) || $Jssor$.$CreateDiv(document);
+
+        var _BulletNavigatorContainer = $Jssor$.$FindFirstChild(elmt, "navigator", null, _UISearchMode);
+
+        var _ArrowLeft = $Jssor$.$FindFirstChild(elmt, "arrowleft", null, _UISearchMode);
+        var _ArrowRight = $Jssor$.$FindFirstChild(elmt, "arrowright", null, _UISearchMode);
+
+        var _ThumbnailNavigatorContainer = $Jssor$.$FindFirstChild(elmt, "thumbnavigator", null, _UISearchMode);
+
+        $JssorDebug$.$Execute(function () {
+            //if (_BulletNavigatorOptions && !_BulletNavigatorContainer) {
+            //    throw new Error("$BulletNavigatorOptions specified but bullet navigator container (<div u=\"navigator\" ...) not defined.");
+            //}
+            if (_BulletNavigatorContainer && !_BulletNavigatorOptions) {
+                throw new Error("Bullet navigator container defined but $BulletNavigatorOptions not specified.");
+            }
+
+            //if (_ArrowNavigatorOptions) {
+            //    if (!_ArrowLeft) {
+            //        throw new Error("$ArrowNavigatorOptions specified, but arrowleft (<span u=\"arrowleft\" ...) not defined.");
+            //    }
+
+            //    if (!_ArrowRight) {
+            //        throw new Error("$ArrowNavigatorOptions specified, but arrowright (<span u=\"arrowright\" ...) not defined.");
+            //    }
+            //}
+
+            if ((_ArrowLeft || _ArrowRight) && !_ArrowNavigatorOptions) {
+                throw new Error("arrowleft or arrowright defined, but $ArrowNavigatorOptions not specified.");
+            }
+
+            //if (_ThumbnailNavigatorOptions && !_ThumbnailNavigatorContainer) {
+            //    throw new Error("$ThumbnailNavigatorOptions specified, but thumbnail navigator container (<div u=\"thumbnavigator\" ...) not defined.");
+            //}
+
+            if (_ThumbnailNavigatorContainer && !_ThumbnailNavigatorOptions) {
+                throw new Error("Thumbnail navigator container defined, but $ThumbnailNavigatorOptions not specified.");
+            }
+        });
+
+        var _SlidesContainerWidth = $Jssor$.$CssWidth(_SlidesContainer);
+        var _SlidesContainerHeight = $Jssor$.$CssHeight(_SlidesContainer);
+
+        $JssorDebug$.$Execute(function () {
+            if (isNaN(_SlidesContainerWidth))
+                $JssorDebug$.$Fail("Width of slides container wrong specification, it should be specified by inline style in pixels (like style='width: 600px;').");
+
+            if (_SlidesContainerWidth == undefined)
+                $JssorDebug$.$Fail("Width of slides container not specified, it should be specified by inline style in pixels (like style='width: 600px;').");
+
+            if (isNaN(_SlidesContainerHeight))
+                $JssorDebug$.$Fail("Height of slides container wrong specification, it should be specified by inline style in pixels (like style='height: 300px;').");
+
+            if (_SlidesContainerHeight == undefined)
+                $JssorDebug$.$Fail("Height of slides container not specified, it should be specified by inline style in pixels (like style='height: 300px;').");
+
+            var slidesContainerOverflow = $Jssor$.$CssOverflow(_SlidesContainer);
+            var slidesContainerOverflowX = $Jssor$.$Css(_SlidesContainer, "overflowX");
+            var slidesContainerOverflowY = $Jssor$.$Css(_SlidesContainer, "overflowY");
+            if (slidesContainerOverflow != "hidden" && (slidesContainerOverflowX != "hidden" || slidesContainerOverflowY != "hidden"))
+                $JssorDebug$.$Fail("Overflow of slides container wrong specification, it should be specified as 'hidden' (style='overflow:hidden;').");
+
+            //var slidesContainerTop = $Jssor$.$CssTop(_SlidesContainer);
+            //var slidesContainerLeft = $Jssor$.$CssLeft(_SlidesContainer);
+
+            //if (isNaN(slidesContainerTop))
+            //    $JssorDebug$.$Fail("Top of slides container wrong specification, it should be specified by inline style in pixels (like style='top: 0px;').");
+
+            //if (slidesContainerTop == undefined)
+            //    $JssorDebug$.$Fail("Top of slides container not specified, it should be specified by inline style in pixels (like style='top: 0px;').");
+
+            //if (isNaN(slidesContainerLeft))
+            //    $JssorDebug$.$Fail("Left of slides container wrong specification, it should be specified by inline style in pixels (like style='left: 0px;').");
+
+            //if (slidesContainerLeft == undefined)
+            //    $JssorDebug$.$Fail("Left of slides container not specified, it should be specified by inline style in pixels (like style='left: 0px;').");
+        });
+
+        $JssorDebug$.$Execute(function () {
+            if (!$Jssor$.$IsNumeric(_Options.$DisplayPieces))
+                $JssorDebug$.$Fail("Option $DisplayPieces error, it should be a numeric value and greater than or equal to 1.");
+
+            if (_Options.$DisplayPieces < 1)
+                $JssorDebug$.$Fail("Option $DisplayPieces error, it should be greater than or equal to 1.");
+
+            if (_Options.$DisplayPieces > 1 && _Options.$DragOrientation && _Options.$DragOrientation != _PlayOrientation)
+                $JssorDebug$.$Fail("Option $DragOrientation error, it should be 0 or the same of $PlayOrientation when $DisplayPieces is greater than 1.");
+
+            if (!$Jssor$.$IsNumeric(_Options.$ParkingPosition))
+                $JssorDebug$.$Fail("Option $ParkingPosition error, it should be a numeric value.");
+
+            if (_Options.$ParkingPosition && _Options.$DragOrientation && _Options.$DragOrientation != _PlayOrientation)
+                $JssorDebug$.$Fail("Option $DragOrientation error, it should be 0 or the same of $PlayOrientation when $ParkingPosition is not equal to 0.");
+        });
+
+        var _StyleDef;
+
+        var _SlideElmts = [];
+
+        {
+            var slideElmts = $Jssor$.$Children(_SlidesContainer);
+            $Jssor$.$Each(slideElmts, function (slideElmt) {
+                if (slideElmt.tagName == "DIV" && !$Jssor$.$AttributeEx(slideElmt, "u")) {
+                    _SlideElmts.push(slideElmt);
+                }
+            });
+        }
+
+        $JssorDebug$.$Execute(function () {
+            if (_SlideElmts.length < 1) {
+                $JssorDebug$.$Error("Slides html code definition error, there must be at least 1 slide to initialize a slider.");
+            }
+        });
+
+        var _SlideItemCreatedCount = 0; //for debug only
+        var _SlideItemReleasedCount = 0;    //for debug only
+
+        var _PreviousSlideIndex;
+        var _CurrentSlideIndex = -1;
+        var _TempSlideIndex;
+        var _PrevSlideItem;
+        var _CurrentSlideItem;
+        var _SlideCount = _SlideElmts.length;
+
+        var _SlideWidth = _Options.$SlideWidth || _SlidesContainerWidth;
+        var _SlideHeight = _Options.$SlideHeight || _SlidesContainerHeight;
+
+        var _SlideSpacing = _Options.$SlideSpacing;
+        var _StepLengthX = _SlideWidth + _SlideSpacing;
+        var _StepLengthY = _SlideHeight + _SlideSpacing;
+        var _StepLength = (_PlayOrientation & 1) ? _StepLengthX : _StepLengthY;
+        var _DisplayPieces = Math.min(_Options.$DisplayPieces, _SlideCount);
+
+        var _SlideshowPanel;
+        var _CurrentBoardIndex = 0;
+        var _DragOrientation;
+        var _DragOrientationRegistered;
+        var _DragInvalid;
+
+        var _HandleTouchEventOnly;
+
+        var _Navigators = [];
+        var _BulletNavigator;
+        var _ArrowNavigator;
+        var _ThumbnailNavigator;
+
+        var _ShowLink;
+
+        var _Frozen;
+        var _AutoPlay;
+        var _AutoPlaySteps = _Options.$AutoPlaySteps;
+        var _HoverToPause = _Options.$PauseOnHover;
+        var _AutoPlayInterval = _Options.$AutoPlayInterval;
+        var _SlideDuration = _Options.$SlideDuration;
+
+        var _SlideshowRunnerClass;
+        var _TransitionsOrder;
+
+        var _SlideshowEnabled;
+        var _ParkingPosition;
+        var _CarouselEnabled = _DisplayPieces < _SlideCount;
+        var _Loop = _CarouselEnabled ? _Options.$Loop : 0;
+
+        var _DragEnabled;
+        var _LastDragSucceded;
+
+        var _HoverStatus = 1;   //0 Hovering, 1 Not hovering
+
+        //Variable Definition
+        var _IsSliding;
+        var _IsDragging;
+        var _LoadingTicket;
+
+        //The X position of mouse/touch when a drag start
+        var _DragStartMouseX = 0;
+        //The Y position of mouse/touch when a drag start
+        var _DragStartMouseY = 0;
+        var _DragOffsetTotal;
+        var _DragOffsetLastTime;
+        var _DragIndexAdjust;
+
+        var _Carousel;
+        var _Conveyor;
+        var _Slideshow;
+        var _CarouselPlayer;
+        var _SlideContainer = new SlideContainer();
+        var _ScaleRatio;
+
+        //$JssorSlider$ Constructor
+        {
+            _AutoPlay = _Options.$AutoPlay;
+            _SelfSlider.$Options = options;
+
+            AdjustSlidesContainerSize();
+
+            elmt["jssor-slider"] = true;
+
+            //_SlideshowPanel = CreatePanel();
+            //$Jssor$.$CssZIndex(elmt, $Jssor$.$CssZIndex(elmt));
+            //$Jssor$.$CssLeft(_SlideshowPanel, $Jssor$.$CssLeft(_SlidesContainer));
+            //$Jssor$.$CssZIndex(_SlidesContainer, $Jssor$.$CssZIndex(_SlidesContainer));
+            //$Jssor$.$CssTop(_SlideshowPanel, $Jssor$.$CssTop(_SlidesContainer));
+            $Jssor$.$CssZIndex(_SlidesContainer, $Jssor$.$CssZIndex(_SlidesContainer) || 0);
+            $Jssor$.$CssPosition(_SlidesContainer, "absolute");
+            _SlideshowPanel = $Jssor$.$CloneNode(_SlidesContainer);
+            $Jssor$.$InsertBefore($Jssor$.$ParentNode(_SlidesContainer), _SlideshowPanel, _SlidesContainer);
+
+            if (_SlideshowOptions) {
+                _ShowLink = _SlideshowOptions.$ShowLink;
+                _SlideshowRunnerClass = _SlideshowOptions.$Class;
+
+                $JssorDebug$.$Execute(function () {
+                    if (!_SlideshowOptions.$Transitions || !_SlideshowOptions.$Transitions.length) {
+                        $JssorDebug$.$Error("Invalid '$SlideshowOptions', no '$Transitions' specified.");
+                    }
+                });
+
+                $Jssor$.$TranslateTransitions(_SlideshowOptions.$Transitions); //for old transition compatibility
+
+                _SlideshowEnabled = _DisplayPieces == 1 && _SlideCount > 1 && _SlideshowRunnerClass && (!$Jssor$.$IsBrowserIE() || $Jssor$.$BrowserVersion() >= 8);
+            }
+
+            _ParkingPosition = (_SlideshowEnabled || _DisplayPieces >= _SlideCount || !(_Loop & 1)) ? 0 : _Options.$ParkingPosition;
+
+            _DragEnabled = ((_DisplayPieces > 1 || _ParkingPosition) ? _PlayOrientation : -1) & _Options.$DragOrientation;
+
+            //SlideBoard
+            var _SlideboardElmt = _SlidesContainer;
+            var _SlideItems = [];
+
+            var _SlideshowRunner;
+            var _LinkContainer;
+
+            var _DownEvent = "mousedown";
+            var _MoveEvent = "mousemove";
+            var _UpEvent = "mouseup";
+            var _CancelEvent;
+
+            var _LastTimeMoveByDrag;
+            var _Position_OnFreeze;
+            var _CarouselPlaying_OnFreeze;
+            var _PlayToPosition_OnFreeze;
+            var _PositionToGoByDrag;
+
+            //SlideBoard Constructor
+            {
+                var msPrefix;
+                if (window.navigator.pointerEnabled || (msPrefix = window.navigator.msPointerEnabled)) {
+
+                    _DownEvent = msPrefix ? "MSPointerDown" : "pointerdown";
+                    _MoveEvent = msPrefix ? "MSPointerMove" : "pointermove";
+                    _UpEvent = msPrefix ? "MSPointerUp" : "pointerup";
+                    _CancelEvent = msPrefix ? "MSPointerCancel" : "pointercancel";
+
+                    if (_DragEnabled) {
+                        var touchAction = "none";
+                        if (_DragEnabled == 1) {
+                            touchAction = "pan-y";
+                        }
+                        else if (_DragEnabled == 2) {
+                            touchAction = "pan-x";
+                        }
+
+                        $Jssor$.$Css(_SlideboardElmt, msPrefix ? "msTouchAction" : "touchAction", touchAction);
+                    }
+                }
+                else if ("ontouchstart" in window || "createTouch" in document) {
+                    _HandleTouchEventOnly = true;
+
+                    _DownEvent = "touchstart";
+                    _MoveEvent = "touchmove";
+                    _UpEvent = "touchend";
+                    _CancelEvent = "touchcancel";
+                }
+
+                _Slideshow = new Slideshow();
+
+                if (_SlideshowEnabled)
+                    _SlideshowRunner = new _SlideshowRunnerClass(_SlideContainer, _SlideWidth, _SlideHeight, _SlideshowOptions, _HandleTouchEventOnly);
+
+                $Jssor$.$AppendChild(_SlideshowPanel, _Slideshow.$Wrapper);
+                $Jssor$.$CssOverflow(_SlidesContainer, "hidden");
+
+                //link container
+                {
+                    _LinkContainer = CreatePanel();
+                    $Jssor$.$Css(_LinkContainer, "backgroundColor", "#000");
+                    $Jssor$.$CssOpacity(_LinkContainer, 0);
+                    $Jssor$.$InsertBefore(_SlideboardElmt, _LinkContainer, _SlideboardElmt.firstChild);
+                }
+
+                for (var i = 0; i < _SlideElmts.length; i++) {
+                    var slideElmt = _SlideElmts[i];
+                    var slideItem = new SlideItem(slideElmt, i);
+                    _SlideItems.push(slideItem);
+                }
+
+                $Jssor$.$HideElement(_LoadingContainer);
+
+                $JssorDebug$.$Execute(function () {
+                    $Jssor$.$Attribute(_LoadingContainer, "debug-id", "loading-container");
+                });
+
+                _Carousel = new Carousel()
+                _CarouselPlayer = new CarouselPlayer(_Carousel, _Slideshow);
+
+                $JssorDebug$.$Execute(function () {
+                    $Jssor$.$Attribute(_SlideboardElmt, "debug-id", "slide-board");
+                });
+
+                if (_DragEnabled) {
+                    $Jssor$.$AddEvent(_SlidesContainer, _DownEvent, OnMouseDown);
+                    $Jssor$.$AddEvent(document, _UpEvent, OnDragEnd);
+                    _CancelEvent && $Jssor$.$AddEvent(document, _CancelEvent, OnDragEnd);
+                }
+            }
+            //SlideBoard
+
+            _HoverToPause &= (_HandleTouchEventOnly ? 10 : 5);
+
+            //Bullet Navigator
+            if (_BulletNavigatorContainer && _BulletNavigatorOptions) {
+                _BulletNavigator = new _BulletNavigatorOptions.$Class(_BulletNavigatorContainer, _BulletNavigatorOptions, OriginalWidth(), OriginalHeight());
+                _Navigators.push(_BulletNavigator);
+            }
+
+            //Arrow Navigator
+            if (_ArrowNavigatorOptions && _ArrowLeft && _ArrowRight) {
+                _ArrowNavigator = new _ArrowNavigatorOptions.$Class(_ArrowLeft, _ArrowRight, _ArrowNavigatorOptions, OriginalWidth(), OriginalHeight());
+                _Navigators.push(_ArrowNavigator);
+            }
+
+            //Thumbnail Navigator
+            if (_ThumbnailNavigatorContainer && _ThumbnailNavigatorOptions) {
+                _ThumbnailNavigatorOptions.$StartIndex = _Options.$StartIndex;
+                _ThumbnailNavigator = new _ThumbnailNavigatorOptions.$Class(_ThumbnailNavigatorContainer, _ThumbnailNavigatorOptions);
+                _Navigators.push(_ThumbnailNavigator);
+            }
+
+            $Jssor$.$Each(_Navigators, function (navigator) {
+                navigator.$Reset(_SlideCount, _SlideItems, _LoadingContainer);
+                navigator.$On($JssorNavigatorEvents$.$NAVIGATIONREQUEST, NavigationClickHandler);
+            });
+
+            Scale(OriginalWidth());
+
+            $Jssor$.$AddEvent(elmt, "mouseout", $Jssor$.$MouseOverOutFilter(MainContainerMouseLeaveEventHandler, elmt));
+            $Jssor$.$AddEvent(elmt, "mouseover", $Jssor$.$MouseOverOutFilter(MainContainerMouseEnterEventHandler, elmt));
+
+            ShowNavigators();
+
+            //Keyboard Navigation
+            if (_Options.$ArrowKeyNavigation) {
+                $Jssor$.$AddEvent(document, "keydown", function (e) {
+                    if (e.keyCode == $JssorKeyCode$.$LEFT) {
+                        //Arrow Left
+                        PlayToOffset(-1);
+                    }
+                    else if (e.keyCode == $JssorKeyCode$.$RIGHT) {
+                        //Arrow Right
+                        PlayToOffset(1);
+                    }
+                });
+            }
+
+            var startPosition = _Options.$StartIndex;
+            if (!(_Loop & 1)) {
+                startPosition = Math.max(0, Math.min(startPosition, _SlideCount - _DisplayPieces));
+            }
+            _CarouselPlayer.$PlayCarousel(startPosition, startPosition, 0);
+        }
+    }
+    //Jssor Slider
+
+    //JssorSlider.$ASSEMBLY_BOTTOM_LEFT = ASSEMBLY_BOTTOM_LEFT;
+    //JssorSlider.$ASSEMBLY_BOTTOM_RIGHT = ASSEMBLY_BOTTOM_RIGHT;
+    //JssorSlider.$ASSEMBLY_TOP_LEFT = ASSEMBLY_TOP_LEFT;
+    //JssorSlider.$ASSEMBLY_TOP_RIGHT = ASSEMBLY_TOP_RIGHT;
+    //JssorSlider.$ASSEMBLY_LEFT_TOP = ASSEMBLY_LEFT_TOP;
+    //JssorSlider.$ASSEMBLY_LEFT_BOTTOM = ASSEMBLY_LEFT_BOTTOM;
+    //JssorSlider.$ASSEMBLY_RIGHT_TOP = ASSEMBLY_RIGHT_TOP;
+    //JssorSlider.$ASSEMBLY_RIGHT_BOTTOM = ASSEMBLY_RIGHT_BOTTOM;
+
+    JssorSlider.$EVT_CLICK = 21;
+    JssorSlider.$EVT_DRAG_START = 22;
+    JssorSlider.$EVT_DRAG_END = 23;
+    JssorSlider.$EVT_SWIPE_START = 24;
+    JssorSlider.$EVT_SWIPE_END = 25;
+
+    JssorSlider.$EVT_LOAD_START = 26;
+    JssorSlider.$EVT_LOAD_END = 27;
+    JssorSlider.$EVT_FREEZE = 28;
+
+    JssorSlider.$EVT_POSITION_CHANGE = 202;
+    JssorSlider.$EVT_PARK = 203;
+
+    JssorSlider.$EVT_SLIDESHOW_START = 206;
+    JssorSlider.$EVT_SLIDESHOW_END = 207;
+
+    JssorSlider.$EVT_PROGRESS_CHANGE = 208;
+    JssorSlider.$EVT_STATE_CHANGE = 209;
+    JssorSlider.$EVT_ROLLBACK_START = 210;
+    JssorSlider.$EVT_ROLLBACK_END = 211;
+
+    window.$JssorSlider$ = $JssorSlider$ = JssorSlider;
+
+    //(function ($) {
+    //    jQuery.fn.jssorSlider = function (options) {
+    //        return this.each(function () {
+    //            return $(this).data('jssorSlider') || $(this).data('jssorSlider', new JssorSlider(this, options));
+    //        });
+    //    };
+    //})(jQuery);
+
+    //window.jQuery && (jQuery.fn.jssorSlider = function (options) {
+    //    return this.each(function () {
+    //        return jQuery(this).data('jssorSlider') || jQuery(this).data('jssorSlider', new JssorSlider(this, options));
+    //    });
+    //});
+};
+
+//$JssorBulletNavigator$
+var $JssorNavigatorEvents$ = {
+    $NAVIGATIONREQUEST: 1,
+    $INDEXCHANGE: 2,
+    $RESET: 3
+};
+
+var $JssorBulletNavigator$ = window.$JssorBulletNavigator$ = function (elmt, options, containerWidth, containerHeight) {
+    var self = this;
+    $JssorObject$.call(self);
+
+    elmt = $Jssor$.$GetElement(elmt);
+
+    var _Count;
+    var _Length;
+    var _Width;
+    var _Height;
+    var _CurrentIndex;
+    var _CurrentInnerIndex = 0;
+    var _Options;
+    var _Steps;
+    var _Lanes;
+    var _SpacingX;
+    var _SpacingY;
+    var _Orientation;
+    var _ItemPrototype;
+    var _PrototypeWidth;
+    var _PrototypeHeight;
+
+    var _ButtonElements = [];
+    var _Buttons = [];
+
+    function Highlight(index) {
+        if (index != -1)
+            _Buttons[index].$Activate(index == _CurrentInnerIndex);
+    }
+
+    function OnNavigationRequest(index) {
+        self.$TriggerEvent($JssorNavigatorEvents$.$NAVIGATIONREQUEST, index * _Steps);
+    }
+
+    self.$Elmt = elmt;
+    self.$GetCurrentIndex = function () {
+        return _CurrentIndex;
+    };
+
+    self.$SetCurrentIndex = function (index) {
+        if (index != _CurrentIndex) {
+            var lastInnerIndex = _CurrentInnerIndex;
+            var innerIndex = Math.floor(index / _Steps);
+            _CurrentInnerIndex = innerIndex;
+            _CurrentIndex = index;
+
+            Highlight(lastInnerIndex);
+            Highlight(innerIndex);
+
+            //self.$TriggerEvent($JssorNavigatorEvents$.$INDEXCHANGE, index);
+        }
+    };
+
+    self.$Show = function (show) {
+        $Jssor$.$ShowElement(elmt, show);
+    };
+
+    var _Located;
+    self.$Relocate = function (force) {
+        if (!_Located || _Options.$Scale == false) {
+            if (_Options.$AutoCenter & 1) {
+                $Jssor$.$CssLeft(elmt, (containerWidth - _Width) / 2);
+            }
+            if (_Options.$AutoCenter & 2) {
+                $Jssor$.$CssTop(elmt, (containerHeight - _Height) / 2);
+            }
+
+            _Located = true;
+        }
+    };
+
+    var _Initialized;
+    self.$Reset = function (length) {
+        if (!_Initialized) {
+            _Length = length;
+            _Count = Math.ceil(length / _Steps);
+            _CurrentInnerIndex = 0;
+
+            var itemOffsetX = _PrototypeWidth + _SpacingX;
+            var itemOffsetY = _PrototypeHeight + _SpacingY;
+
+            var maxIndex = Math.ceil(_Count / _Lanes) - 1;
+
+            _Width = _PrototypeWidth + itemOffsetX * (!_Orientation ? maxIndex : _Lanes - 1);
+            _Height = _PrototypeHeight + itemOffsetY * (_Orientation ? maxIndex : _Lanes - 1);
+
+            $Jssor$.$CssWidth(elmt, _Width);
+            $Jssor$.$CssHeight(elmt, _Height);
+
+            //self.$Relocate(true);
+
+            for (var buttonIndex = 0; buttonIndex < _Count; buttonIndex++) {
+
+                var numberDiv = $Jssor$.$CreateSpan();
+                $Jssor$.$InnerText(numberDiv, buttonIndex + 1);
+
+                var div = $Jssor$.$BuildElement(_ItemPrototype, "NumberTemplate", numberDiv, true);
+                $Jssor$.$CssPosition(div, "absolute");
+
+                var columnIndex = buttonIndex % (maxIndex + 1);
+                $Jssor$.$CssLeft(div, !_Orientation ? itemOffsetX * columnIndex : buttonIndex % _Lanes * itemOffsetX);
+                $Jssor$.$CssTop(div, _Orientation ? itemOffsetY * columnIndex : Math.floor(buttonIndex / (maxIndex + 1)) * itemOffsetY);
+
+                $Jssor$.$AppendChild(elmt, div);
+                _ButtonElements[buttonIndex] = div;
+
+                if (_Options.$ActionMode & 1)
+                    $Jssor$.$AddEvent(div, "click", $Jssor$.$CreateCallback(null, OnNavigationRequest, buttonIndex));
+
+                if (_Options.$ActionMode & 2)
+                    $Jssor$.$AddEvent(div, "mouseover", $Jssor$.$MouseOverOutFilter($Jssor$.$CreateCallback(null, OnNavigationRequest, buttonIndex), div));
+
+                _Buttons[buttonIndex] = $Jssor$.$Buttonize(div);
+            }
+
+            //self.$TriggerEvent($JssorNavigatorEvents$.$RESET);
+            _Initialized = true;
+        }
+    };
+
+    //JssorBulletNavigator Constructor
+    {
+        self.$Options = _Options = $Jssor$.$Extend({
+            $SpacingX: 0,
+            $SpacingY: 0,
+            $Orientation: 1,
+            $ActionMode: 1
+        }, options);
+
+        //Sodo statement for development time intellisence only
+        $JssorDebug$.$Execute(function () {
+            _Options = $Jssor$.$Extend({
+                $Steps: undefined,
+                $Lanes: undefined
+            }, _Options);
+        });
+
+        _ItemPrototype = $Jssor$.$FindFirstChild(elmt, "prototype");
+
+        $JssorDebug$.$Execute(function () {
+            if (!_ItemPrototype)
+                $JssorDebug$.$Fail("Navigator item prototype not defined.");
+
+            if (isNaN($Jssor$.$CssWidth(_ItemPrototype))) {
+                $JssorDebug$.$Fail("Width of 'navigator item prototype' not specified.");
+            }
+
+            if (isNaN($Jssor$.$CssHeight(_ItemPrototype))) {
+                $JssorDebug$.$Fail("Height of 'navigator item prototype' not specified.");
+            }
+        });
+
+        _PrototypeWidth = $Jssor$.$CssWidth(_ItemPrototype);
+        _PrototypeHeight = $Jssor$.$CssHeight(_ItemPrototype);
+
+        $Jssor$.$RemoveChild(elmt, _ItemPrototype);
+
+        _Steps = _Options.$Steps || 1;
+        _Lanes = _Options.$Lanes || 1;
+        _SpacingX = _Options.$SpacingX;
+        _SpacingY = _Options.$SpacingY;
+        _Orientation = _Options.$Orientation - 1;
+    }
+};
+
+var $JssorArrowNavigator$ = window.$JssorArrowNavigator$ = function (arrowLeft, arrowRight, options, containerWidth, containerHeight) {
+    var self = this;
+    $JssorObject$.call(self);
+
+    $JssorDebug$.$Execute(function () {
+        //var arrowLeft = $Jssor$.$FindFirstChild(elmt, "arrowleft", null, uiSearchMode);
+        //var arrowRight = $Jssor$.$FindFirstChild(elmt, "arrowright", null, uiSearchMode);
+
+        if (!arrowLeft)
+            $JssorDebug$.$Fail("Option '$ArrowNavigatorOptions' spepcified, but UI 'arrowleft' not defined. Define 'arrowleft' to enable direct navigation, or remove option '$ArrowNavigatorOptions' to disable direct navigation.");
+
+        if (!arrowRight)
+            $JssorDebug$.$Fail("Option '$ArrowNavigatorOptions' spepcified, but UI 'arrowright' not defined. Define 'arrowright' to enable direct navigation, or remove option '$ArrowNavigatorOptions' to disable direct navigation.");
+
+        if (isNaN($Jssor$.$CssWidth(arrowLeft))) {
+            $JssorDebug$.$Fail("Width of 'arrow left' not specified.");
+        }
+
+        if (isNaN($Jssor$.$CssWidth(arrowRight))) {
+            $JssorDebug$.$Fail("Width of 'arrow right' not specified.");
+        }
+
+        if (isNaN($Jssor$.$CssHeight(arrowLeft))) {
+            $JssorDebug$.$Fail("Height of 'arrow left' not specified.");
+        }
+
+        if (isNaN($Jssor$.$CssHeight(arrowRight))) {
+            $JssorDebug$.$Fail("Height of 'arrow right' not specified.");
+        }
+    });
+
+    //var arrowLeft = $Jssor$.$FindFirstChild(elmt, "arrowleft", null, uiSearchMode);
+    //var arrowRight = $Jssor$.$FindFirstChild(elmt, "arrowright", null, uiSearchMode);
+    var _Length;
+    var _CurrentIndex;
+    var _Options;
+    var _Steps;
+    var _ArrowWidth = $Jssor$.$CssWidth(arrowLeft);
+    var _ArrowHeight = $Jssor$.$CssHeight(arrowLeft);
+
+    function OnNavigationRequest(steps) {
+        self.$TriggerEvent($JssorNavigatorEvents$.$NAVIGATIONREQUEST, steps, true);
+    }
+
+    self.$GetCurrentIndex = function () {
+        return _CurrentIndex;
+    };
+
+    self.$SetCurrentIndex = function (index, virtualIndex, temp) {
+        if (temp) {
+            _CurrentIndex = virtualIndex;
+        }
+        else {
+            _CurrentIndex = index;
+        }
+        //self.$TriggerEvent($JssorNavigatorEvents$.$INDEXCHANGE, index);
+    };
+
+    self.$Show = function (show) {
+        $Jssor$.$ShowElement(arrowLeft, show);
+        $Jssor$.$ShowElement(arrowRight, show);
+    };
+
+    var _Located;
+    self.$Relocate = function (force) {
+        if (!_Located || _Options.$Scale == false) {
+
+            if (_Options.$AutoCenter & 1) {
+                $Jssor$.$CssLeft(arrowLeft, (containerWidth - _ArrowWidth) / 2);
+                $Jssor$.$CssLeft(arrowRight, (containerWidth - _ArrowWidth) / 2);
+            }
+
+            if (_Options.$AutoCenter & 2) {
+                $Jssor$.$CssTop(arrowLeft, (containerHeight - _ArrowHeight) / 2);
+                $Jssor$.$CssTop(arrowRight, (containerHeight - _ArrowHeight) / 2);
+            }
+
+            _Located = true;
+        }
+    };
+
+    var _Initialized;
+    self.$Reset = function (length) {
+        _Length = length;
+        _CurrentIndex = 0;
+
+        if (!_Initialized) {
+
+            //self.$Relocate(true);
+
+            $Jssor$.$AddEvent(arrowLeft, "click", $Jssor$.$CreateCallback(null, OnNavigationRequest, -_Steps));
+            $Jssor$.$AddEvent(arrowRight, "click", $Jssor$.$CreateCallback(null, OnNavigationRequest, _Steps));
+
+            $Jssor$.$Buttonize(arrowLeft);
+            $Jssor$.$Buttonize(arrowRight);
+
+            _Initialized = true;
+        }
+
+        //self.$TriggerEvent($JssorNavigatorEvents$.$RESET);
+    };
+
+    //JssorArrowNavigator Constructor
+    {
+        self.$Options = _Options = $Jssor$.$Extend({
+            $Steps: 1
+        }, options);
+
+        _Steps = _Options.$Steps;
+    }
+};
+
+//$JssorThumbnailNavigator$
+var $JssorThumbnailNavigator$ = window.$JssorThumbnailNavigator$ = function (elmt, options) {
+    var _Self = this;
+    var _Length;
+    var _Count;
+    var _CurrentIndex;
+    var _Options;
+    var _NavigationItems = [];
+
+    var _Width;
+    var _Height;
+    var _Lanes;
+    var _SpacingX;
+    var _SpacingY;
+    var _PrototypeWidth;
+    var _PrototypeHeight;
+    var _DisplayPieces;
+
+    var _Slider;
+    var _CurrentMouseOverIndex = -1;
+
+    var _SlidesContainer;
+    var _ThumbnailPrototype;
+
+    $JssorObject$.call(_Self);
+    elmt = $Jssor$.$GetElement(elmt);
+
+    function NavigationItem(item, index) {
+        var self = this;
+        var _Wrapper;
+        var _Button;
+        var _Thumbnail;
+
+        function Highlight(mouseStatus) {
+            _Button.$Activate(_CurrentIndex == index);
+        }
+
+        function OnNavigationRequest(event) {
+            if (!_Slider.$LastDragSucceded()) {
+                var tail = _Lanes - index % _Lanes;
+                var slideVirtualIndex = _Slider.$GetVirtualIndex((index + tail) / _Lanes - 1);
+                var itemVirtualIndex = slideVirtualIndex * _Lanes + _Lanes - tail;
+                _Self.$TriggerEvent($JssorNavigatorEvents$.$NAVIGATIONREQUEST, itemVirtualIndex);
+            }
+
+            //$JssorDebug$.$Log("navigation request");
+        }
+
+        $JssorDebug$.$Execute(function () {
+            self.$Wrapper = undefined;
+        });
+
+        self.$Index = index;
+
+        self.$Highlight = Highlight;
+
+        //NavigationItem Constructor
+        {
+            _Thumbnail = item.$Thumb || item.$Image || $Jssor$.$CreateDiv();
+            self.$Wrapper = _Wrapper = $Jssor$.$BuildElement(_ThumbnailPrototype, "ThumbnailTemplate", _Thumbnail, true);
+
+            _Button = $Jssor$.$Buttonize(_Wrapper);
+            if (_Options.$ActionMode & 1)
+                $Jssor$.$AddEvent(_Wrapper, "click", OnNavigationRequest);
+            if (_Options.$ActionMode & 2)
+                $Jssor$.$AddEvent(_Wrapper, "mouseover", $Jssor$.$MouseOverOutFilter(OnNavigationRequest, _Wrapper));
+        }
+    }
+
+    _Self.$GetCurrentIndex = function () {
+        return _CurrentIndex;
+    };
+
+    _Self.$SetCurrentIndex = function (index, virtualIndex, temp) {
+        var oldIndex = _CurrentIndex;
+        _CurrentIndex = index;
+        if (oldIndex != -1)
+            _NavigationItems[oldIndex].$Highlight();
+        _NavigationItems[index].$Highlight();
+
+        if (!temp) {
+            _Slider.$PlayTo(_Slider.$GetVirtualIndex(Math.floor(virtualIndex / _Lanes)));
+        }
+    };
+
+    _Self.$Show = function (show) {
+        $Jssor$.$ShowElement(elmt, show);
+    };
+
+    _Self.$Relocate = $Jssor$.$EmptyFunction;
+
+    var _Initialized;
+    _Self.$Reset = function (length, items, loadingContainer) {
+        if (!_Initialized) {
+            _Length = length;
+            _Count = Math.ceil(_Length / _Lanes);
+            _CurrentIndex = -1;
+            _DisplayPieces = Math.min(_DisplayPieces, items.length);
+
+            var horizontal = _Options.$Orientation & 1;
+
+            var slideWidth = _PrototypeWidth + (_PrototypeWidth + _SpacingX) * (_Lanes - 1) * (1 - horizontal);
+            var slideHeight = _PrototypeHeight + (_PrototypeHeight + _SpacingY) * (_Lanes - 1) * horizontal;
+
+            var slidesContainerWidth = slideWidth + (slideWidth + _SpacingX) * (_DisplayPieces - 1) * horizontal;
+            var slidesContainerHeight = slideHeight + (slideHeight + _SpacingY) * (_DisplayPieces - 1) * (1 - horizontal);
+
+            $Jssor$.$CssPosition(_SlidesContainer, "absolute");
+            $Jssor$.$CssOverflow(_SlidesContainer, "hidden");
+            if (_Options.$AutoCenter & 1) {
+                $Jssor$.$CssLeft(_SlidesContainer, (_Width - slidesContainerWidth) / 2);
+            }
+            if (_Options.$AutoCenter & 2) {
+                $Jssor$.$CssTop(_SlidesContainer, (_Height - slidesContainerHeight) / 2);
+            }
+            //$JssorDebug$.$Execute(function () {
+            //    if (!_Options.$AutoCenter) {
+            //        var slidesContainerTop = $Jssor$.$CssTop(_SlidesContainer);
+            //        var slidesContainerLeft = $Jssor$.$CssLeft(_SlidesContainer);
+
+            //        if (isNaN(slidesContainerTop)) {
+            //            $JssorDebug$.$Fail("Position 'top' wrong specification of thumbnail navigator slides container (<div u=\"thumbnavigator\">...<div u=\"slides\">), \r\nwhen option $ThumbnailNavigatorOptions.$AutoCenter set to 0, it should be specified by inline style in pixels (like <div u=\"slides\" style=\"top: 0px;\">)");
+            //        }
+
+            //        if (isNaN(slidesContainerLeft)) {
+            //            $JssorDebug$.$Fail("Position 'left' wrong specification of thumbnail navigator slides container (<div u=\"thumbnavigator\">...<div u=\"slides\">), \r\nwhen option $ThumbnailNavigatorOptions.$AutoCenter set to 0, it should be specified by inline style in pixels (like <div u=\"slides\" style=\"left: 0px;\">)");
+            //        }
+            //    }
+            //});
+            $Jssor$.$CssWidth(_SlidesContainer, slidesContainerWidth);
+            $Jssor$.$CssHeight(_SlidesContainer, slidesContainerHeight);
+
+            var slideItemElmts = [];
+            $Jssor$.$Each(items, function (item, index) {
+                var navigationItem = new NavigationItem(item, index);
+                var navigationItemWrapper = navigationItem.$Wrapper;
+
+                var columnIndex = Math.floor(index / _Lanes);
+                var laneIndex = index % _Lanes;
+
+                $Jssor$.$CssLeft(navigationItemWrapper, (_PrototypeWidth + _SpacingX) * laneIndex * (1 - horizontal));
+                $Jssor$.$CssTop(navigationItemWrapper, (_PrototypeHeight + _SpacingY) * laneIndex * horizontal);
+
+                if (!slideItemElmts[columnIndex]) {
+                    slideItemElmts[columnIndex] = $Jssor$.$CreateDiv();
+                    $Jssor$.$AppendChild(_SlidesContainer, slideItemElmts[columnIndex]);
+                }
+
+                $Jssor$.$AppendChild(slideItemElmts[columnIndex], navigationItemWrapper);
+
+                _NavigationItems.push(navigationItem);
+            });
+
+            var thumbnailSliderOptions = $Jssor$.$Extend({
+                $AutoPlay: false,
+                $NaviQuitDrag: false,
+                $SlideWidth: slideWidth,
+                $SlideHeight: slideHeight,
+                $SlideSpacing: _SpacingX * horizontal + _SpacingY * (1 - horizontal),
+                $MinDragOffsetToSlide: 12,
+                $SlideDuration: 200,
+                $PauseOnHover: 1,
+                $PlayOrientation: _Options.$Orientation,
+                $DragOrientation: _Options.$DisableDrag ? 0 : _Options.$Orientation
+            }, _Options);
+
+            _Slider = new $JssorSlider$(elmt, thumbnailSliderOptions);
+
+            _Initialized = true;
+        }
+
+        //_Self.$TriggerEvent($JssorNavigatorEvents$.$RESET);
+    };
+
+    //JssorThumbnailNavigator Constructor
+    {
+        _Self.$Options = _Options = $Jssor$.$Extend({
+            $SpacingX: 3,
+            $SpacingY: 3,
+            $DisplayPieces: 1,
+            $Orientation: 1,
+            $AutoCenter: 3,
+            $ActionMode: 1
+        }, options);
+
+        //Sodo statement for development time intellisence only
+        $JssorDebug$.$Execute(function () {
+            _Options = $Jssor$.$Extend({
+                $Lanes: undefined,
+                $Width: undefined,
+                $Height: undefined
+            }, _Options);
+        });
+
+        _Width = $Jssor$.$CssWidth(elmt);
+        _Height = $Jssor$.$CssHeight(elmt);
+
+        $JssorDebug$.$Execute(function () {
+            if (!_Width)
+                $JssorDebug$.$Fail("width of 'thumbnavigator' container not specified.");
+            if (!_Height)
+                $JssorDebug$.$Fail("height of 'thumbnavigator' container not specified.");
+        });
+
+        _SlidesContainer = $Jssor$.$FindFirstChild(elmt, "slides");
+        _ThumbnailPrototype = $Jssor$.$FindFirstChild(_SlidesContainer, "prototype");
+
+        $JssorDebug$.$Execute(function () {
+            if (!_ThumbnailPrototype)
+                $JssorDebug$.$Fail("prototype of 'thumbnavigator' not defined.");
+        });
+
+        _PrototypeWidth = $Jssor$.$CssWidth(_ThumbnailPrototype);
+        _PrototypeHeight = $Jssor$.$CssHeight(_ThumbnailPrototype);
+
+        $Jssor$.$RemoveChild(_SlidesContainer, _ThumbnailPrototype);
+
+        _Lanes = _Options.$Lanes || 1;
+        _SpacingX = _Options.$SpacingX;
+        _SpacingY = _Options.$SpacingY;
+        _DisplayPieces = _Options.$DisplayPieces;
+    }
+};
+
+//$JssorCaptionSlider$
+function $JssorCaptionSliderBase$() {
+    $JssorAnimator$.call(this, 0, 0);
+    this.$Revert = $Jssor$.$EmptyFunction;
+}
+
+var $JssorCaptionSlider$ = window.$JssorCaptionSlider$ = function (container, captionSlideOptions, playIn) {
+    $JssorDebug$.$Execute(function () {
+        if (!captionSlideOptions.$CaptionTransitions) {
+            $JssorDebug$.$Error("'$CaptionSliderOptions' option error, '$CaptionSliderOptions.$CaptionTransitions' not specified.");
+        }
+        //else if (!$Jssor$.$IsArray(captionSlideOptions.$CaptionTransitions)) {
+        //    $JssorDebug$.$Error("'$CaptionSliderOptions' option error, '$CaptionSliderOptions.$CaptionTransitions' is not an array.");
+        //}
+    });
+
+    var _Self = this;
+    var _ImmediateOutCaptionHanger;
+    var _PlayMode = playIn ? captionSlideOptions.$PlayInMode : captionSlideOptions.$PlayOutMode;
+
+    var _CaptionTransitions = captionSlideOptions.$CaptionTransitions;
+    var _CaptionTuningFetcher = { $Transition: "t", $Delay: "d", $Duration: "du", x: "x", y: "y", $Rotate: "r", $Zoom: "z", $Opacity: "f", $BeginTime: "b" };
+    var _CaptionTuningTransfer = {
+        $Default: function (value, tuningValue) {
+            if (!isNaN(tuningValue.$Value))
+                value = tuningValue.$Value;
+            else
+                value *= tuningValue.$Percent;
+
+            return value;
+        },
+        $Opacity: function (value, tuningValue) {
+            return this.$Default(value - 1, tuningValue);
+        }
+    };
+    _CaptionTuningTransfer.$Zoom = _CaptionTuningTransfer.$Opacity;
+
+    $JssorAnimator$.call(_Self, 0, 0);
+
+    function GetCaptionItems(element, level) {
+
+        var itemsToPlay = [];
+        var lastTransitionName;
+        var namedTransitions = [];
+        var namedTransitionOrders = [];
+
+        //$JssorDebug$.$Execute(function () {
+
+        //    var debugInfoElement = $Jssor$.$GetElement("debugInfo");
+
+        //    if (debugInfoElement && playIn) {
+
+        //        var text = $Jssor.$InnerHtml(debugInfoElement) + "<br>";
+
+        //        $Jssor$.$InnerHtml(debugInfoElement, text);
+        //    }
+        //});
+
+        function FetchRawTransition(captionElmt, index) {
+            var rawTransition = {};
+
+            $Jssor$.$Each(_CaptionTuningFetcher, function (fetchAttribute, fetchProperty) {
+                var attributeValue = $Jssor$.$AttributeEx(captionElmt, fetchAttribute + (index || ""));
+                if (attributeValue) {
+                    var propertyValue = {};
+
+                    if (fetchAttribute == "t") {
+                        //if (($Jssor$.$IsBrowserChrome() || $Jssor$.$IsBrowserSafari() || $Jssor$.$IsBrowserFireFox()) && attributeValue == "*") {
+                        //    attributeValue = Math.floor(Math.random() * captionSlideOptions.$CaptionTransitions.length);
+                        //    $Jssor$.$Attribute(captionElmt, fetchAttribute + (index || ""), attributeValue);
+                        //}
+
+                        propertyValue.$Value = attributeValue;
+                    }
+                    else if (attributeValue.indexOf("%") + 1)
+                        propertyValue.$Percent = $Jssor$.$ParseFloat(attributeValue) / 100;
+                    else
+                        propertyValue.$Value = $Jssor$.$ParseFloat(attributeValue);
+
+                    rawTransition[fetchProperty] = propertyValue;
+                }
+            });
+
+            return rawTransition;
+        }
+
+        function GetRandomTransition() {
+            //return _CaptionTransitions.length && _CaptionTransitions[Math.floor(Math.random() * 42737 / (i + 1)) % _CaptionTransitions.length];
+            return _CaptionTransitions[Math.floor(Math.random() * _CaptionTransitions.length)];
+        }
+
+        function EvaluateCaptionTransition(transitionName) {
+
+            var transition;
+
+            if (transitionName == "*") {
+                transition = GetRandomTransition();
+            }
+            else if (transitionName) {
+
+                //indexed transition allowed, just the same as named transition
+                var tempTransition = _CaptionTransitions[$Jssor$.$ParseInt(transitionName)] || _CaptionTransitions[transitionName];
+
+                if ($Jssor$.$IsArray(tempTransition)) {
+                    if (transitionName != lastTransitionName) {
+                        lastTransitionName = transitionName;
+                        namedTransitionOrders[transitionName] = 0;
+
+                        namedTransitions[transitionName] = tempTransition[Math.floor(Math.random() * tempTransition.length)];
+                    }
+                    else {
+                        namedTransitionOrders[transitionName]++;
+                    }
+
+                    tempTransition = namedTransitions[transitionName];
+
+                    if ($Jssor$.$IsArray(tempTransition)) {
+                        tempTransition = tempTransition.length && tempTransition[namedTransitionOrders[transitionName] % tempTransition.length];
+
+                        if ($Jssor$.$IsArray(tempTransition)) {
+                            //got transition from array level 3, random for all captions
+                            tempTransition = tempTransition[Math.floor(Math.random() * tempTransition.length)];
+                        }
+                        //else {
+                        //    //got transition from array level 2, in sequence for all adjacent captions with same name specified
+                        //    transition = tempTransition;
+                        //}
+                    }
+                    //else {
+                    //    //got transition from array level 1, random but same for all adjacent captions with same name specified
+                    //    transition = tempTransition;
+                    //}
+                }
+                //else {
+                //    //got transition directly from a simple transition object
+                //    transition = tempTransition;
+                //}
+
+                transition = tempTransition;
+
+                if ($Jssor$.$IsString(transition))
+                    transition = EvaluateCaptionTransition(transition);
+            }
+
+            return transition;
+        }
+
+        var captionElmts = $Jssor$.$Children(element);
+        $Jssor$.$Each(captionElmts, function (captionElmt, i) {
+
+            var transitionsWithTuning = [];
+            transitionsWithTuning.$Elmt = captionElmt;
+            var isCaption = $Jssor$.$AttributeEx(captionElmt, "u") == "caption";
+
+            $Jssor$.$Each(playIn ? [0, 3] : [2], function (j, k) {
+
+                if (isCaption) {
+                    var transition;
+                    var rawTransition;
+
+                    if (j != 2 || !$Jssor$.$AttributeEx(captionElmt, "t3")) {
+                        rawTransition = FetchRawTransition(captionElmt, j);
+
+                        if (j == 2 && !rawTransition.$Transition) {
+                            rawTransition.$Delay = rawTransition.$Delay || { $Value: 0 };
+                            rawTransition = $Jssor$.$Extend(FetchRawTransition(captionElmt, 0), rawTransition);
+                        }
+                    }
+
+                    if (rawTransition && rawTransition.$Transition) {
+
+                        transition = EvaluateCaptionTransition(rawTransition.$Transition.$Value);
+
+                        if (transition) {
+
+                            //var transitionWithTuning = $Jssor$.$Extend({ $Delay: 0, $ScaleHorizontal: 1, $ScaleVertical: 1 }, transition);
+                            var transitionWithTuning = $Jssor$.$Extend({ $Delay: 0 }, transition);
+
+                            $Jssor$.$Each(rawTransition, function (rawPropertyValue, propertyName) {
+                                var tuningPropertyValue = (_CaptionTuningTransfer[propertyName] || _CaptionTuningTransfer.$Default).apply(_CaptionTuningTransfer, [transitionWithTuning[propertyName], rawTransition[propertyName]]);
+                                if (!isNaN(tuningPropertyValue))
+                                    transitionWithTuning[propertyName] = tuningPropertyValue;
+                            });
+
+                            if (!k) {
+                                if (rawTransition.$BeginTime)
+                                    transitionWithTuning.$BeginTime = rawTransition.$BeginTime.$Value || 0;
+                                else if ((_PlayMode) & 2)
+                                    transitionWithTuning.$BeginTime = 0;
+                            }
+                        }
+                    }
+
+                    transitionsWithTuning.push(transitionWithTuning);
+                }
+
+                if ((level % 2) && !k) {
+                    //transitionsWithTuning.$Children = GetCaptionItems(captionElmt, lastTransitionName, [].concat(namedTransitions), [].concat(namedTransitionOrders), level + 1);
+                    transitionsWithTuning.$Children = GetCaptionItems(captionElmt, level + 1);
+                }
+            });
+
+            itemsToPlay.push(transitionsWithTuning);
+        });
+
+        return itemsToPlay;
+    }
+
+    function CreateAnimator(item, transition, immediateOut) {
+
+        var animatorOptions = {
+            $Easing: transition.$Easing,
+            $Round: transition.$Round,
+            $During: transition.$During,
+            $Reverse: playIn && !immediateOut,
+            $Optimize: true
+        };
+
+        $JssorDebug$.$Execute(function () {
+            animatorOptions.$CaptionAnimator = true;
+        });
+
+        var captionItem = item;
+        var captionParent = $Jssor$.$ParentNode(item);
+
+        var captionItemWidth = $Jssor$.$CssWidth(captionItem);
+        var captionItemHeight = $Jssor$.$CssHeight(captionItem);
+        var captionParentWidth = $Jssor$.$CssWidth(captionParent);
+        var captionParentHeight = $Jssor$.$CssHeight(captionParent);
+
+        var toStyles = {};
+        var fromStyles = {};
+        var scaleClip = transition.$ScaleClip || 1;
+
+        //Opacity
+        if (transition.$Opacity) {
+            toStyles.$Opacity = 2 - transition.$Opacity;
+        }
+
+        animatorOptions.$OriginalWidth = captionItemWidth;
+        animatorOptions.$OriginalHeight = captionItemHeight;
+
+        //Transform
+        if (transition.$Zoom || transition.$Rotate) {
+            toStyles.$Zoom = transition.$Zoom ? transition.$Zoom - 1 : 1;
+
+            if ($Jssor$.$IsBrowserIe9Earlier() || $Jssor$.$IsBrowserOpera())
+                toStyles.$Zoom = Math.min(toStyles.$Zoom, 2);
+
+            fromStyles.$Zoom = 1;
+
+            var rotate = transition.$Rotate || 0;
+
+            toStyles.$Rotate = rotate * 360;
+            fromStyles.$Rotate = 0;
+        }
+            //Clip
+        else if (transition.$Clip) {
+            var fromStyleClip = { $Top: 0, $Right: captionItemWidth, $Bottom: captionItemHeight, $Left: 0 };
+            var toStyleClip = $Jssor$.$Extend({}, fromStyleClip);
+
+            var blockOffset = toStyleClip.$Offset = {};
+
+            var topBenchmark = transition.$Clip & 4;
+            var bottomBenchmark = transition.$Clip & 8;
+            var leftBenchmark = transition.$Clip & 1;
+            var rightBenchmark = transition.$Clip & 2;
+
+            if (topBenchmark && bottomBenchmark) {
+                blockOffset.$Top = captionItemHeight / 2 * scaleClip;
+                blockOffset.$Bottom = -blockOffset.$Top;
+            }
+            else if (topBenchmark)
+                blockOffset.$Bottom = -captionItemHeight * scaleClip;
+            else if (bottomBenchmark)
+                blockOffset.$Top = captionItemHeight * scaleClip;
+
+            if (leftBenchmark && rightBenchmark) {
+                blockOffset.$Left = captionItemWidth / 2 * scaleClip;
+                blockOffset.$Right = -blockOffset.$Left;
+            }
+            else if (leftBenchmark)
+                blockOffset.$Right = -captionItemWidth * scaleClip;
+            else if (rightBenchmark)
+                blockOffset.$Left = captionItemWidth * scaleClip;
+
+            animatorOptions.$Move = transition.$Move;
+            toStyles.$Clip = toStyleClip;
+            fromStyles.$Clip = fromStyleClip;
+        }
+
+        //Fly
+        {
+            var toLeft = 0;
+            var toTop = 0;
+
+            if (transition.x)
+                toLeft -= captionParentWidth * transition.x;
+
+            if (transition.y)
+                toTop -= captionParentHeight * transition.y;
+
+            if (toLeft || toTop || animatorOptions.$Move) {
+                toStyles.$Left = toLeft + $Jssor$.$CssLeft(captionItem);
+                toStyles.$Top = toTop + $Jssor$.$CssTop(captionItem);
+            }
+        }
+
+        //duration
+        var duration = transition.$Duration;
+
+        fromStyles = $Jssor$.$Extend(fromStyles, $Jssor$.$GetStyles(captionItem, toStyles));
+
+        animatorOptions.$Setter = $Jssor$.$StyleSetterEx();
+
+        return new $JssorAnimator$(transition.$Delay, duration, animatorOptions, captionItem, fromStyles, toStyles);
+    }
+
+    function CreateAnimators(streamLineLength, captionItems) {
+
+        $Jssor$.$Each(captionItems, function (captionItem, i) {
+
+            $JssorDebug$.$Execute(function () {
+                if (captionItem.length) {
+                    var top = $Jssor$.$CssTop(captionItem.$Elmt);
+                    var left = $Jssor$.$CssLeft(captionItem.$Elmt);
+                    var width = $Jssor$.$CssWidth(captionItem.$Elmt);
+                    var height = $Jssor$.$CssHeight(captionItem.$Elmt);
+
+                    var error = null;
+
+                    if (isNaN(top))
+                        error = "Style 'top' for caption not specified. Please always specify caption like 'position: absolute; top: ...px; left: ...px; width: ...px; height: ...px;'.";
+                    else if (isNaN(left))
+                        error = "Style 'left' not specified. Please always specify caption like 'position: absolute; top: ...px; left: ...px; width: ...px; height: ...px;'.";
+                    else if (isNaN(width))
+                        error = "Style 'width' not specified. Please always specify caption like 'position: absolute; top: ...px; left: ...px; width: ...px; height: ...px;'.";
+                    else if (isNaN(height))
+                        error = "Style 'height' not specified. Please always specify caption like 'position: absolute; top: ...px; left: ...px; width: ...px; height: ...px;'.";
+
+                    if (error)
+                        $JssorDebug$.$Error("Caption " + (i + 1) + " definition error, \r\n" + error + "\r\n" + captionItem.$Elmt.outerHTML);
+                }
+            });
+
+            var animator;
+            var captionElmt = captionItem.$Elmt;
+            var transition = captionItem[0];
+            var transition3 = captionItem[1];
+
+            if (transition) {
+
+                animator = CreateAnimator(captionElmt, transition);
+                streamLineLength = animator.$Locate(transition.$BeginTime == undefined ? streamLineLength : transition.$BeginTime, 1);
+            }
+
+            streamLineLength = CreateAnimators(streamLineLength, captionItem.$Children);
+
+            if (transition3) {
+                var animator3 = CreateAnimator(captionElmt, transition3, 1);
+                animator3.$Locate(streamLineLength, 1);
+                _Self.$Combine(animator3);
+                _ImmediateOutCaptionHanger.$Combine(animator3);
+            }
+
+            if (animator)
+                _Self.$Combine(animator);
+        });
+
+        return streamLineLength;
+    }
+
+    _Self.$Revert = function () {
+        _Self.$GoToPosition(_Self.$GetPosition_OuterEnd() * (playIn || 0));
+        _ImmediateOutCaptionHanger.$GoToBegin();
+    };
+
+    //Constructor
+    {
+        _ImmediateOutCaptionHanger = new $JssorAnimator$(0, 0);
+
+        //var streamLineLength = 0;
+        //var captionItems = GetCaptionItems(container, null, [], [], 1);
+
+        CreateAnimators(0, _PlayMode ? GetCaptionItems(container, 1) : []);
+    }
+};
+
+//Event Table
+
+//$EVT_CLICK = 21;			    function(slideIndex[, event])
+//$EVT_DRAG_START = 22;		    function(position[, virtualPosition, event])
+//$EVT_DRAG_END = 23;		    function(position, startPosition[, virtualPosition, virtualStartPosition, event])
+//$EVT_SWIPE_START = 24;		function(position[, virtualPosition])
+//$EVT_SWIPE_END = 25;		    function(position[, virtualPosition])
+
+//$EVT_LOAD_START = 26;			function(slideIndex)
+//$EVT_LOAD_END = 27;			function(slideIndex)
+
+//$EVT_POSITION_CHANGE = 202;	function(position, fromPosition[, virtualPosition, virtualFromPosition])
+//$EVT_PARK = 203;			    function(slideIndex, fromIndex)
+
+//$EVT_PROGRESS_CHANGE = 208;	function(slideIndex, progress[, progressBegin, idleBegin, idleEnd, progressEnd])
+//$EVT_STATE_CHANGE = 209;	    function(slideIndex, progress[, progressBegin, idleBegin, idleEnd, progressEnd])
+
+//$EVT_ROLLBACK_START = 210;	function(slideIndex, progress[, progressBegin, idleBegin, idleEnd, progressEnd])
+//$EVT_ROLLBACK_END = 211;	    function(slideIndex, progress[, progressBegin, idleBegin, idleEnd, progressEnd])
+
+//$EVT_SLIDESHOW_START = 206;   function(slideIndex[, progressBegin, slideshowBegin, slideshowEnd, progressEnd])
+//$EVT_SLIDESHOW_END = 207;     function(slideIndex[, progressBegin, slideshowBegin, slideshowEnd, progressEnd])
+
+//http://www.jssor.com/development/reference-api.html
diff --git a/doc/_static/motor.png b/doc/_static/motor.png
new file mode 100644
index 0000000..abc5f20
Binary files /dev/null and b/doc/_static/motor.png differ
diff --git a/doc/_static/serial.png b/doc/_static/serial.png
new file mode 100644
index 0000000..a7eef8d
Binary files /dev/null and b/doc/_static/serial.png differ
diff --git a/doc/_static/starter.png b/doc/_static/starter.png
new file mode 100644
index 0000000..2095e90
Binary files /dev/null and b/doc/_static/starter.png differ
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index 8d53c05..4b81976 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -1,10 +1,54 @@
 {% extends "layout.html" %}
 {% set title = 'PyTango documentation' %}
 
-{% set script_files = script_files + ["_static/slideshow.js"] %}
+{% set script_files = script_files + ["http://code.jquery.com/jquery-1.9.1.min.js",
+                                      "_static/jssor.js",
+                                      "_static/jssor.slider.js"] %}
 
 {% block body %}
 
+<script>
+    jQuery(document).ready(function ($) {
+
+        var _SlideshowTransitions = [
+            //Fade
+            { $Duration: 1200, $Opacity: 2 }
+        ];
+
+        var options = {
+            $AutoPlay: true,
+            $AutoPlaySteps: 1,
+            $AutoPlayInterval: 3000, 
+            $PauseOnHover: 1,
+            $ArrowKeyNavigation: true,
+            $SlideDuration: 500,
+            $MinDragOffsetToSlide: 20,
+            $SlideSpacing: 0,
+            $DisplayPieces: 1,
+            $ParkingPosition: 0,
+            $UISearchMode: 1,
+            $PlayOrientation: 1, 
+            $DragOrientation: 3,
+
+            $SlideshowOptions: {
+                $Class: $JssorSlideshowRunner$,
+                $Transitions: _SlideshowTransitions,
+                $TransitionsOrder: 1,
+                $ShowLink: false
+            },     
+
+            $ArrowNavigatorOptions: {
+                $Class: $JssorArrowNavigator$,
+                $ChanceToShow: 1,
+                $AutoCenter: 0,
+                $Steps: 1
+            }
+        };
+
+        var jssor_gallery = new $JssorSlider$("gallery", options);
+    });
+</script>
+
 <h1>Welcome to PyTango documentation!</h1>
 
 <p>
@@ -15,20 +59,24 @@
   of this in pure python.
 </p>
 
-<div id="gallery" style="width:780px; height:525px; margin:auto;">
+<div id="gallery" style="position:relative; top:0px; left:0px; width:610px; height:415px; margin:auto;">
 
-  <!-- 1st snap -->
-  <img src="_static/banner1.png" />
-  <!-- 2nd snap -->
-  <img src="_static/banner2.png" />
-  <!-- 3rd snap -->
-  <img src="_static/banner3.png" />
+  <div u="slides" style="cursor: move; position:absolute; top:0px; left:0px; width: 610px; height: 415px; overflow: hidden;">
 
-  <!-- 4th snap -->
-  <table style="width:100%;"><tr>
-  <td>
+    <div style="background:white;"><img src="_static/banner1.png"/></div>
+
+    <div style="background:white;"><iframe src="_static/ipython_tango.html" style="width:600px; height:411px;border: 0;"></iframe></div>
+
+    <div style="background:white;"><iframe src="_static/ipython_db.html" style="width:600px; height:411px;border:0;"></iframe></div>
+
+    <div style="background:white;"><iframe src="_static/ipython_motor.html" style="width:600px; height:411px;border:0;"></iframe></div>
+
+    <div style="background:white;"><iframe src="_static/ipython_serial.html" style="width:600px; height:411px;border:0;"></iframe></div>
 
-<div class="highlight-python"><div class="highlight"><pre>
+    <!-- 6th item -->
+    <div style="background: white;">
+      <table style="width:100%;"><tr><td>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
 <span class="c"># ----------------- server ------------------</span>
 
 <span class="kn">import</span> <span class="nn">time</span>
@@ -52,13 +100,14 @@
 
 <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
     <span class="n">server_run</span><span class="p">((</span><span class="n">Clock</span><span class="p">,))</span>
+
 </pre></div>
 </div>
 
   </td>
   <td>
 
-<div class="highlight-python"><div class="highlight"><pre>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
 <span class="c">$ # ---------------- client -----------------</span>
 
 <span class="n">$ python</span>
@@ -73,27 +122,29 @@
 <span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">read_attribute</span><span class="p">(</span><span class="s">"time"</span><span class="p">)</span><span class="o">.</span><span class="n">value</span>
 <span class="go">1384447252.037578</span>
 
+<span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">"%H:%M:%S"</span><span class="p">)</span>
+<span class="go">'17:41:50'</span>
+
 <span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">command_inout</span><span class="p">(</span><span class="s">"strftime"</span><span class="p">,</span>
 <span class="gp">... </span>                    <span class="s">"%H:%M:%S"</span><span class="p">)</span>
-<span class="go">'17:41:50'</span>
+<span class="go">'17:43:12'</span>
 
 <span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">status</span><span class="p">()</span>
-<span class="go">The device is in UNKNOWN state.</span>
-
-
+<span class="go">'The device is in UNKNOWN state.'</span>
 
-</pre></div>
-</div>
+</pre></div></div>
+    </td></tr></table>
+  </div>
 
-  </td>
-  </tr></table>
+  <!-- 7th item -->
+  <div>
 
-  <!-- 5th snap -->
-  <table style="width:100%;"><tr>
+  <table style="width:100%;  background: white;"><tr>
   <td>
 
-<div class="highlight-python"><div class="highlight"><pre>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
 <span class="gp">>>> </span><span class="c"># ---------- gevent client -----------</span>
+
 <span class="gp">>>> </span><span class="kn">from</span> <span class="nn">PyTango.gevent</span> <span class="kn">import</span> <span class="n">DeviceProxy</span>
 
 <span class="gp">>>> </span><span class="n">dev</span> <span class="o">=</span> <span class="n">DeviceProxy</span><span class="p">(</span><span class="s">"sys/tg_test/1"</span><span class="p">)</span>
@@ -128,8 +179,9 @@
   </td>
   <td>
 
-<div class="highlight-python"><div class="highlight"><pre>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
 <span class="gp">>>> </span><span class="c"># ----- concurrent.futures client -----</span>
+
 <span class="gp">>>> </span><span class="kn">from</span> <span class="nn">PyTango.futures</span> <span class="kn">import</span> <span class="n">DeviceProxy</span>
 
 <span class="gp">>>> </span><span class="n">dev</span> <span class="o">=</span> <span class="n">DeviceProxy</span><span class="p">(</span><span class="s">"sys/tg_test/1"</span><span class="p">)</span>
@@ -164,6 +216,44 @@
   </td>
   </tr></table>
 
+  </div>
+
+  </div> <!-- end of slide items -->
+
+  <!-- Arrow Navigator Skin Begin -->
+  <style>
+        /* jssor slider arrow navigator skin 03 css */
+        /*
+        .jssora03l              (normal)
+        .jssora03r              (normal)
+        .jssora03l:hover        (normal mouseover)
+        .jssora03r:hover        (normal mouseover)
+        .jssora03ldn            (mousedown)
+        .jssora03rdn            (mousedown)
+        */
+        .jssora03l, .jssora03r, .jssora03ldn, .jssora03rdn
+        {
+        	position: absolute;
+        	cursor: pointer;
+        	display: block;
+            background: url(_static/arrow03.png) no-repeat;
+            overflow:hidden;
+        }
+        .jssora03l { background-position: -3px -33px; }
+        .jssora03r { background-position: -63px -33px; }
+        .jssora03l:hover { background-position: -123px -33px; }
+        .jssora03r:hover { background-position: -183px -33px; }
+        .jssora03ldn { background-position: -243px -33px; }
+        .jssora03rdn { background-position: -303px -33px; }
+
+  </style>
+  <!-- Arrow Left -->
+  <span u="arrowleft" class="jssora03l" style="width: 55px; height: 55px; top: 123px; left: 8px;">
+  </span>
+  <!-- Arrow Right -->
+  <span u="arrowright" class="jssora03r" style="width: 55px; height: 55px; top: 123px; right: 8px">
+  </span>
+
 </div>
 
 <p>
@@ -175,18 +265,4 @@
     homepage where you will find plenty of documentation, FAQ and tutorials.
 </p>
 
-<p>
-    A PDF version of this documentation can be downloaded from <a href="PyTango.pdf">here</a>.
-</p>
-<p>
-    For convenience here are the links to other versions:<br/>
-    <a href="http://www.tango-controls.org/static/PyTango/development/doc/html">Development</a>
-     
-    <a href="http://www.tango-controls.org/static/PyTango/latest/doc/html">Latest stable</a>
-     
-    <a href="http://www.tango-controls.org/static/PyTango/v803/doc/html">8.0.3</a>
-     
-    <a href="http://www.tango-controls.org/static/PyTango/v723/doc/html">7.2.3</a>
-</p>
-
 {% endblock %}
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
index f91d749..7f4ef73 100644
--- a/doc/_templates/indexsidebar.html
+++ b/doc/_templates/indexsidebar.html
@@ -9,6 +9,14 @@ or
 <h3>PDF</h3>
 <p>A PDF version <a href="PyTango.pdf">here</a>.
 
-<h3>Development</h3>
-<p>Latest <a href="http://www.tango-controls.org/static/PyTango/development/doc/html">development version docs</a>
-are also available.</p>
+
+<h3>Other versions</h3>
+<a href="http://www.tango-controls.org/static/PyTango/development/doc/html">Development</a>
+<br/>
+<a href="http://www.tango-controls.org/static/PyTango/latest/doc/html">Latest stable</a>
+<br/>
+<a href="http://www.tango-controls.org/static/PyTango/v812/doc/html">8.1.2</a>
+<br/>
+<a href="http://www.tango-controls.org/static/PyTango/v811/doc/html">8.1.1</a>
+<br/>
+<a href="http://www.tango-controls.org/static/PyTango/v723/doc/html">7.2.3</a>
diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html
index 0e1e248..dece097 100644
--- a/doc/_templates/layout.html
+++ b/doc/_templates/layout.html
@@ -7,5 +7,7 @@
     <li><a href="{{ pathto('index') }}">home</a>| </li>
     <li><a href="{{ pathto('start') }}">getting started</a>| </li>
     <li><a href="{{ pathto('quicktour') }}">quick tour</a>| </li>
+    <li><a href="{{ pathto('howto') }}">how to</a>| </li>
+    <li><a href="{{ pathto('faq') }}">FAQ</a>| </li>
     <li><a href="{{ pathto('contents') }}">documentation </a> »</li>
 {% endblock %}
diff --git a/doc/api.rst b/doc/api.rst
index 745ab10..122da04 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -1,7 +1,7 @@
 
 .. currentmodule:: PyTango
 
-.. _api:
+.. _pytango-api:
 
 ===========
 PyTango API
@@ -10,12 +10,12 @@ PyTango API
 .. automodule:: PyTango
 
 .. toctree::
-    :maxdepth: 2
+    :maxdepth: 1
 
     data_types
-    client/index
+    client_api/index
+    server_api/index
     database
-    server/index
     encoded
     utilities
     exception
diff --git a/doc/client/attribute_proxy.rst b/doc/client_api/attribute_proxy.rst
similarity index 100%
rename from doc/client/attribute_proxy.rst
rename to doc/client_api/attribute_proxy.rst
diff --git a/doc/client/device_proxy.rst b/doc/client_api/device_proxy.rst
similarity index 100%
rename from doc/client/device_proxy.rst
rename to doc/client_api/device_proxy.rst
diff --git a/doc/client_api/green.rst b/doc/client_api/green.rst
new file mode 100644
index 0000000..9048861
--- /dev/null
+++ b/doc/client_api/green.rst
@@ -0,0 +1,21 @@
+
+.. pytango-client-green-api:
+
+=========
+Green API
+=========
+
+Summary:
+    * :func:`PyTango.get_green_mode` 
+    * :func:`PyTango.set_green_mode` 
+    * :func:`PyTango.futures.DeviceProxy` 
+    * :func:`PyTango.gevent.DeviceProxy` 
+
+.. autofunction:: PyTango.get_green_mode
+
+.. autofunction:: PyTango.set_green_mode
+
+.. autofunction:: PyTango.futures.DeviceProxy
+
+.. autofunction:: PyTango.gevent.DeviceProxy
+
diff --git a/doc/client/group.rst b/doc/client_api/group.rst
similarity index 100%
rename from doc/client/group.rst
rename to doc/client_api/group.rst
diff --git a/doc/client/index.rst b/doc/client_api/index.rst
similarity index 92%
rename from doc/client/index.rst
rename to doc/client_api/index.rst
index 317614a..b5ee594 100644
--- a/doc/client/index.rst
+++ b/doc/client_api/index.rst
@@ -7,5 +7,6 @@ Client API
     device_proxy
     attribute_proxy
     group
+    green
     miscellaneous
     other
diff --git a/doc/client_api/miscellaneous.rst b/doc/client_api/miscellaneous.rst
new file mode 100644
index 0000000..27905d5
--- /dev/null
+++ b/doc/client_api/miscellaneous.rst
@@ -0,0 +1,144 @@
+.. currentmodule:: PyTango
+
+API util
+--------
+
+.. autoclass:: ApiUtil
+    :members:
+
+Information classes
+-------------------
+
+See also `Event configuration information`_
+
+Attribute
+~~~~~~~~~
+
+.. autoclass:: AttributeAlarmInfo
+    :members:
+
+.. autoclass:: AttributeDimension
+    :members:
+
+.. autoclass:: AttributeInfo
+    :members:
+
+.. autoclass:: AttributeInfoEx
+    :members:
+    
+see also :class:`AttributeInfo`
+
+.. autoclass:: DeviceAttributeConfig
+    :members:
+
+Command
+~~~~~~~
+
+.. autoclass:: DevCommandInfo
+   :members:
+
+.. autoclass:: CommandInfo
+   :members:
+
+Other
+~~~~~
+
+.. autoclass:: DeviceInfo
+    :members:
+
+.. autoclass:: LockerInfo
+    :members:
+
+.. autoclass:: PollDevice
+    :members:
+
+
+Storage classes
+---------------
+
+Attribute: DeviceAttribute
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: DeviceAttribute
+    :members:
+
+
+Command: DeviceData
+~~~~~~~~~~~~~~~~~~~
+
+Device data is the type used internally by Tango to deal with command parameters
+and return values. You don't usually need to deal with it, as command_inout
+will automatically convert the parameters from any other type and the result
+value to another type.
+
+You can still use them, using command_inout_raw to get the result in a DeviceData.
+
+You also may deal with it when reading command history.
+
+.. autoclass:: DeviceData
+    :members:
+
+
+Callback related classes
+------------------------
+
+If you subscribe a callback in a DeviceProxy, it will be run with a parameter.
+This parameter depends will be of one of the following classes depending on
+the callback type.
+
+.. autoclass:: AttrReadEvent
+    :members:
+
+.. autoclass:: AttrWrittenEvent
+    :members:
+
+.. autoclass:: CmdDoneEvent
+    :members:
+
+
+Event related classes
+---------------------
+
+Event configuration information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: AttributeEventInfo
+    :members:
+
+.. autoclass:: ArchiveEventInfo
+    :members:
+
+.. autoclass:: ChangeEventInfo
+    :members:
+
+.. autoclass:: PeriodicEventInfo
+    :members:
+
+Event arrived structures
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: EventData
+    :members:
+
+.. autoclass:: AttrConfEventData
+    :members:
+
+.. autoclass:: DataReadyEventData
+    :members:
+
+
+History classes
+---------------
+
+.. autoclass:: DeviceAttributeHistory
+    :show-inheritance:
+    :members:
+
+See :class:`DeviceAttribute`.
+
+.. autoclass:: DeviceDataHistory
+    :show-inheritance:
+    :members:
+
+See :class:`DeviceData`.
+
diff --git a/doc/client/other.rst b/doc/client_api/other.rst
similarity index 100%
rename from doc/client/other.rst
rename to doc/client_api/other.rst
diff --git a/doc/conf.py b/doc/conf.py
index 302e1b5..fcd04e0 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -29,6 +29,7 @@ extensions = ['sphinx.ext.pngmath',
               'sphinx.ext.autodoc',
               'sphinx.ext.doctest',
               'sphinx.ext.intersphinx',
+              'sphinx.ext.todo',
               'ipython_console_highlighting',
               'tango_console_highlighting']
 
@@ -253,6 +254,9 @@ intersphinx_mapping = {
     'http://docs.sqlalchemy.org/en/rel_0_7/' : None,
 }
 
+todo_include_todos = True
+
+
 def copy_spaces(origin):
     r = ''
     for x in xrange(len(origin)):
diff --git a/doc/contents.rst b/doc/contents.rst
index dacf3b2..f17733c 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -9,18 +9,17 @@ Contents
 
 .. toctree::
     :maxdepth: 2
+    :titlesonly:
 
     start
     quicktour
-    itango/index
+    ITango <itango>
+    green
     API <api>
+    How to <howto>
+    FAQ <faq>
     TEP <tep>
-    faq
     History of changes <revision>
     
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
 **Last update:** |today|
 
diff --git a/doc/data_types.rst b/doc/data_types.rst
index 01d17cc..423c3f1 100644
--- a/doc/data_types.rst
+++ b/doc/data_types.rst
@@ -1,4 +1,4 @@
-.. currentmodule:: PyTang
+.. currentmodule:: PyTango
 
 .. _pytango-data-types:
 
diff --git a/doc/exception.rst b/doc/exception.rst
index e31e74d..9371d9b 100644
--- a/doc/exception.rst
+++ b/doc/exception.rst
@@ -3,8 +3,10 @@
 .. highlight:: python
    :linenothreshold: 4
 
-Exception Handling
-==================
+.. _pytango-exception-api:
+
+Exception API
+=============
 
 Exception definition
 --------------------
@@ -42,40 +44,34 @@ all of which containing the following kind of key-value pairs:
 
 ::
 
-    #  Protect the script from Exceptions raised by the Tango or python itself
+    import PyTango
+
+    #  How to protect the script from exceptions raised by the Tango
     try:
-        # Get proxy on the tangotest1 device
-        print "Getting DeviceProxy "
-        tangotest = DeviceProxy("tango/tangotest/1")
-
-    #Catch Tango and Systems  Exceptions
-    except DevFailed:
-        exctype , value = sys.exc_info()[:2]
-        print "Failed with exception ! " , exctype
-        for err in value:
-            print " reason" , err.reason
-            print " description" , err.desc
-            print " origin" , err.origin
-            print " severity" , err.severity
+        # Get proxy on a non existing device should throw an exception
+        device = DeviceProxy("non/existing/device")
+    except DevFailed as df:
+        print("Failed to create proxy to non/existing/device:\n%s" % df)
+
 
 Throwing exception in a device server
 -------------------------------------
 
-The C++ Tango::Except class with its most important methods have been wrapped to Python.
-Therefore, in a Python device server, you have the following methods to throw, re-throw or
-print a Tango::DevFailed exception :
+The C++ :class:`~PyTango::Except` class with its most important methods have 
+been wrapped to Python. Therefore, in a Python device server, you have the 
+following methods to throw, re-throw or print a Tango::DevFailed exception :
 
-- *throw_exception()* which is a static method
-- *re_throw_exception()* which is also a static method
-- *print_exception()* which is also a static method
+- :meth:`~PyTango.Except.throw_exception` which is a static method
+- :meth:`~PyTango.Except.re_throw_exception` which is also a static method
+- :meth:`~PyTango.Except.print_exception` which is also a static method
 
-The following code is an example of a command method requesting a command on a sub-device and re-throwing
-the exception in case of::
+The following code is an example of a command method requesting a command on a
+sub-device and re-throwing the exception in case of::
 
     try:
         dev.command_inout("SubDevCommand")
-    except PyTango.DevFailed, e:
-        PyTango.Except.re_throw_exception(e,
+    except PyTango.DevFailed as df:
+        PyTango.Except.re_throw_exception(df,
             "MyClass_CommandFailed",
             "Sub device command SubdevCommand failed",
             "Command()")
diff --git a/doc/faq.rst b/doc/faq.rst
index d3b4908..c559ec7 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -1,5 +1,7 @@
 .. currentmodule:: PyTango
 
+.. _pytango-faq:
+
 FAQ
 ===
 
@@ -7,8 +9,7 @@ Answers to general Tango questions can be found at http://www.tango-controls.org
 
 Please also check http://www.tango-controls.org/howtos for a list of Tango howtos
 
-Where are the usual bjam files?
--------------------------------
+**Where are the usual bjam files?**
 
 Starting from PyTango 7.0.0 the prefered way to build PyTango is using the standard
 python distutils package. This means that:
@@ -19,8 +20,7 @@ python distutils package. This means that:
 
 Please check the compilation chapter for details on how to build PyTango.
 
-I got a libbost_python error when I try to import PyTango module
-----------------------------------------------------------------
+**I got a libbost_python error when I try to import PyTango module**
 
 doing:
     >>> import PyTango
@@ -48,8 +48,7 @@ To see which boost python file PyTango needs type::
     /lib64/ld-linux-x86-64.so.2 (0x00007f3940a4c000)
 
 
-My python code uses PyTango 3.0.4 API. How do I change to 7.0.0 API?
---------------------------------------------------------------------
+**My python code uses PyTango 3.0.4 API. How do I change to 7.0.0 API?**
 
 To ease migration effort, PyTango 7 provides an alternative module called
 PyTango3.
@@ -69,8 +68,7 @@ since the PyTango team cannot assure the maintainability of the PyTango3 module.
 
 Please find below a basic set of rules to migrate from PyTango 3.0.x to 7:
 
-General rule of thumb for data types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*General rule of thumb for data types*
 
 The first important thing to be aware of when migrating from PyTango <= 3.0.4 to
 PyTango >= 7 is that the data type mapping from tango to python and vice versa is
@@ -162,8 +160,8 @@ reference, the proper code would be::
     if not da.data_format is PyTango.AttrDataFormat.SCALAR:
         print "array_attr is NOT a scalar attribute"
     
-Server
-~~~~~~
+*Server*
+
 
 #. replace `PyTango.PyUtil` with :class:`Util`
 
@@ -176,8 +174,7 @@ Server
     in PyTango 7 the methods have been renamed to **dev_state()** and **dev_status()** in
     order to match the C++ API.
 
-General
-~~~~~~~
+*General*
 
 #. AttributeValue does **NOT** exist anymore.
     - the result of a read_attribute call on a :class:`DeviceProxy` / :class:`Group`
@@ -224,118 +221,15 @@ General
             print e.args[0].reason
 
 
-Optional
-~~~~~~~~
+*Optional*
 
 The following is a list of API improvements. Some where added for performance 
 reasons, others to allow for a more pythonic interface, others still to reflect 
 more adequately the C++ interface. They are not mandatory since the original 
 interface will still be available.
 
-Server side V3 to V4 upgrade
-############################
-
-If you want your server to support the V4 interface provided by Tango 7
-instead of the V3 provided by Tango 6:
-
-- replace the inheritance of your device class from :class:`Device_3Impl` to :class:`Device_4Impl`
-- in the `init_device` method replace the call::
-     
-     Device_3Impl.init_device(self)
-
-  with::
-  
-     Device_4Impl.init_device(self)
-
-  or better yet, if your device class only inherits from :class:`Device_4Impl`::
-  
-     super(<your class>, self).init_device()
-
-Improved server side image attribute read API
-#############################################
-
-In PyTango <= 3.0.4, to set the value of an image attribute you needed it
-as a flat list. Consider you want to set as value the following image::
-
-    # Image:
-    #  | 1  2 |
-    #  | 3  4 |
-    
-In order to tell tango the dimensions of the image you had to specify them as::
-
-    image = [ 1, 2, 3, 4]
-    dim_x = 2
-    dim_y = 2
-    attr.set_value(image, dim_x, dim_y)
-
-In PyTango 8 it is still supported, but the preferred way is to use a
-sequence of sequences (instead of a flat sequence), so the dimensions
-are inherent and not needed anymore::
-
-    image = [ [1, 2], [3, 4]]
-    attr.set_value(image)
-
-If you use a numpy array as the sequence of sequences you can get better
-performance::
-
-    image = numpy.array([ [1, 2], [3, 4]], dtype=numpy.int32)
-    attr.set_value(image)
-
-Likewise, calls to::
-
-    PyTango.set_attribute_value_date_quality(attr, value, date, quality, dim_x, dim_y)
-
-can be replaced with::
+**Why is there a "-Wstrict-prototypes" warning when I compile PyTango?**
 
-    attr.set_value_date_quality(value, date, quality)
-
-Improved server side attribute write API
-########################################
-
-Imagine the following value is written to our IMAGE attribute::
-
-    # Image:
-    #  | 1  2 |
-    #  | 3  4 |
-
-This is what you would do with PyTango <= 3.0.4::
-
-    flatList = []
-    attr.get_write_value(flatList)
-    print "flatList =", flatList
-    # flatList = [ 1, 2, 3, 4 ]
-
-You can still do it with PyTango 8. However I recommend::
-
-    image = attr.get_write_value()
-    print "image =", image
-    # image = numpy.array([[1, 2], [3, 4]])
-
-If PyTango 8 is compiled without numpy support, you will get a sequence
-of sequences, which makes more sense than a flat list.
-
-If PyTango 8 is compiled with numpy support it does not only makes more sense
-but it is also considerably **faster and memory friendlier**.
-
-If PyTango is compiled with numpy support but you prefer a list of lists for
-some attribute, you can do::
-
-    image = attr.get_write_value(PyTango.ExtractAs.List)
-    print "image =", image
-    # image = [[1, 2], [3, 4]]
-
-Also the SCALAR attribute case is much **cleaner** now. Instead of::
-
-    data = []
-    attr.get_write_value(data)
-    actualData = data[0]
-
-You can just write::
-
-    actualData = attr.get_write_value()
-
-Why is there a "-Wstrict-prototypes" warning when I compile PyTango?
---------------------------------------------------------------------
 
 The PyTango prefered build system (distutils) uses the same flags used to compile
 Python to compile PyTango. It happens that Python is compiled as a pure C library
@@ -349,8 +243,8 @@ For reference here is the complete error message you may have:
 Do not worry about this warning since the compiler is ignoring the presence of this flag
 in the compilation.
 
-Why are there so many warnings when generating the documentation?
------------------------------------------------------------------
+**Why are there so many warnings when generating the documentation?**
+
 PyTango uses boost python for the binding between C++ and Python and sphinx for
 document generation.
 When sphinx generates the PyTango API documentation it uses introspection to search
diff --git a/doc/client/miscellaneous.rst b/doc/green.rst
similarity index 75%
rename from doc/client/miscellaneous.rst
rename to doc/green.rst
index 0eaba34..00cbeff 100644
--- a/doc/client/miscellaneous.rst
+++ b/doc/green.rst
@@ -1,7 +1,7 @@
 .. currentmodule:: PyTango
 
-Green objects
--------------
+Green mode
+----------
 
 PyTango supports cooperative green Tango objects. Since version 8.1 two *green*
 modes have been added: :obj:`~PyTango.GreenMode.Futures` and
@@ -13,8 +13,8 @@ The :obj:`~PyTango.GreenMode.Gevent` mode uses the well known gevent_ library.
 
 Currently, in version 8.1, only :class:`DeviceProxy` has been modified to work
 in a green cooperative way. If the work is found to be useful, the same can
-be implemented in the future for :class:`AttributeProxy` and even 
-to :class:`Database`.
+be implemented in the future for :class:`AttributeProxy`, :class:`Database`, 
+:class:`Group` or even in the server side.
 
 You can set the PyTango green mode at a global level. Set the environment
 variable :envvar:`PYTANGO_GREEN_MODE` to either *futures* or *gevent*
@@ -264,171 +264,3 @@ Here is another example using :meth:`~DeviceProxy.read_attribute`::
    This is, in fact, one of the major bonus of working with :mod:`gevent` when
    compared with :mod:`concurrent.futures`
 
-
-Green API
-~~~~~~~~~
-
-Summary:
-    * :func:`PyTango.get_green_mode` 
-    * :func:`PyTango.set_green_mode` 
-    * :func:`PyTango.futures.DeviceProxy` 
-    * :func:`PyTango.gevent.DeviceProxy` 
-
-.. autofunction:: PyTango.get_green_mode
-
-.. autofunction:: PyTango.set_green_mode
-
-.. autofunction:: PyTango.futures.DeviceProxy
-
-.. autofunction:: PyTango.gevent.DeviceProxy
-
-
-Low level API
-#############
-
-.. autofunction:: get_device_proxy
-
-.. autofunction:: get_attribute_proxy
-
-API util
---------
-
-.. autoclass:: PyTango.ApiUtil
-    :members:
-
-Information classes
--------------------
-
-See also `Event configuration information`_
-
-Attribute
-~~~~~~~~~
-
-.. autoclass:: PyTango.AttributeAlarmInfo
-    :members:
-
-.. autoclass:: PyTango.AttributeDimension
-    :members:
-
-.. autoclass:: PyTango.AttributeInfo
-    :members:
-
-.. autoclass:: PyTango.AttributeInfoEx
-    :members:
-    
-see also :class:`PyTango.AttributeInfo`
-
-.. autoclass:: PyTango.DeviceAttributeConfig
-    :members:
-
-Command
-~~~~~~~
-
-.. autoclass:: PyTango.DevCommandInfo
-   :members:
-
-.. autoclass:: PyTango.CommandInfo
-   :members:
-
-Other
-~~~~~
-
-.. autoclass:: PyTango.DeviceInfo
-    :members:
-
-.. autoclass:: PyTango.LockerInfo
-    :members:
-
-.. autoclass:: PyTango.PollDevice
-    :members:
-
-
-Storage classes
----------------
-
-Attribute: DeviceAttribute
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: DeviceAttribute
-    :members:
-
-
-Command: DeviceData
-~~~~~~~~~~~~~~~~~~~
-
-Device data is the type used internally by Tango to deal with command parameters
-and return values. You don't usually need to deal with it, as command_inout
-will automatically convert the parameters from any other type and the result
-value to another type.
-
-You can still use them, using command_inout_raw to get the result in a DeviceData.
-
-You also may deal with it when reading command history.
-
-.. autoclass:: DeviceData
-    :members:
-
-
-Callback related classes
-------------------------
-
-If you subscribe a callback in a DeviceProxy, it will be run with a parameter.
-This parameter depends will be of one of the following classes depending on
-the callback type.
-
-.. autoclass:: PyTango.AttrReadEvent
-    :members:
-
-.. autoclass:: PyTango.AttrWrittenEvent
-    :members:
-
-.. autoclass:: PyTango.CmdDoneEvent
-    :members:
-
-
-Event related classes
----------------------
-
-Event configuration information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: PyTango.AttributeEventInfo
-    :members:
-
-.. autoclass:: PyTango.ArchiveEventInfo
-    :members:
-
-.. autoclass:: PyTango.ChangeEventInfo
-    :members:
-
-.. autoclass:: PyTango.PeriodicEventInfo
-    :members:
-
-Event arrived structures
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: PyTango.EventData
-    :members:
-
-.. autoclass:: PyTango.AttrConfEventData
-    :members:
-
-.. autoclass:: PyTango.DataReadyEventData
-    :members:
-
-
-History classes
----------------
-
-.. autoclass:: PyTango.DeviceAttributeHistory
-    :show-inheritance:
-    :members:
-
-See :class:`DeviceAttribute`.
-
-.. autoclass:: PyTango.DeviceDataHistory
-    :show-inheritance:
-    :members:
-
-See :class:`DeviceData`.
-
diff --git a/doc/server/index.rst b/doc/howto.rst
similarity index 61%
rename from doc/server/index.rst
rename to doc/howto.rst
index d437c04..dbda286 100644
--- a/doc/server/index.rst
+++ b/doc/howto.rst
@@ -2,37 +2,789 @@
 
 .. highlight:: python
    :linenothreshold: 3
-   
-.. _server:
-   
-Server API
-==========
 
+.. _pytango-howto:
+
+======
+How to
+======
+
+This is a small list of how-tos specific to PyTango. A more general Tango how-to
+list can be found `here <http://www.tango-controls.org/howtos>`_.
+
+
+Check the default TANGO host
+----------------------------
+
+The default TANGO host can be defined using the environment variable 
+:envvar:`TANGO_HOST` or in a `tangorc` file
+(see `Tango environment variables <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node11.html#SECTION0011123000000000000000>`_
+for complete information)
+
+To check what is the current value that TANGO uses for the default configuration
+simple do::
+ 
+    >>> import PyTango
+    >>> PyTango.ApiUtil.get_env_var("TANGO_HOST")
+    'homer.simpson.com:10000'
+
+Check TANGO version
+-------------------
+
+There are two library versions you might be interested in checking:
+The PyTango version::
+
+    >>> import PyTango
+    >>> PyTango.__version__
+    '8.1.1'
+
+    >>> PyTango.__version_info__
+    (8, 1, 1, 'final', 0)
+
+... and the Tango C++ library version that PyTango was compiled with::
+
+    >>> import PyTango
+    >>> PyTango.constants.TgLibVers
+    '8.1.2'
+
+
+Report a bug
+------------
+
+Bugs can be reported as tickets in `TANGO Source forge <https://sourceforge.net/p/tango-cs/bugs/>`_.
+
+When making a bug report don't forget to select *PyTango* in **Category**.
+
+It is also helpfull if you can put in the ticket description the PyTango information.
+It can be a dump of::
+
+   $ python -c "from PyTango.utils import info; print(info())"
+
+Test the connection to the Device and get it's current state
+------------------------------------------------------------
+
+One of the most basic examples is to get a reference to a device and
+determine if it is running or not::
+
+    from PyTango import DeviceProxy
+
+    # Get proxy on the tango_test1 device
+    print("Creating proxy to TangoTest device...")
+    tango_test = DeviceProxy("sys/tg_test/1")
+
+    # ping it
+    print(tango_test.ping())
+            
+    # get the state
+    print(tango_test.state())
+
+Read and write attributes
+-------------------------
+
+Basic read/write attribute operations::
+
+    from PyTango import DeviceProxy
+
+    # Get proxy on the tango_test1 device
+    print("Creating proxy to TangoTest device...")
+    tango_test = DeviceProxy("sys/tg_test/1")
+
+    # Read a scalar attribute. This will return a PyTango.DeviceAttribute
+    # Member 'value' contains the attribute value
+    scalar = tango_test.read_attribute("long_scalar")
+    print("Long_scalar value = {0}".format(scalar.value))
+
+    # PyTango provides a shorter way: 
+    scalar = tango_test.long_scalar.value
+    print("Long_scalar value = {0}".format(scalar))
+
+    # Read a spectrum attribute
+    spectrum = tango_test.read_attribute("double_spectrum")
+    # ... or, the shorter version:
+    spectrum = tango_test.double_spectrum
+
+    # Write a scalar attribute
+    scalar_value = 18
+    tango_test.write_attribute("long_scalar", scalar_value)
+
+    #  PyTango provides a shorter way: 
+    tango_test.long_scalar = scalar_value
+
+    # Write a spectrum attribute
+    spectrum_value = [1.2, 3.2, 12.3]
+    tango_test.write_attribute("double_spectrum", spectrum_value)
+    # ... or, the shorter version:
+    tango_test.double_spectrum = spectrum_value
+
+    # Write an image attribute
+    image_value = [ [1, 2], [3, 4] ]
+    tango_test.write_attribute("long_image", image_value)
+    # ... or, the shorter version:
+    tango_test.long_image = image_value
+
+Note that if PyTango is compiled with numpy support the values got when reading
+a spectrum or an image will be numpy arrays. This results in a faster and
+more memory efficient PyTango. You can also use numpy to specify the values when
+writing attributes, especially if you know the exact attribute type::
+
+    import numpy
+    from PyTango import DeviceProxy
+
+    # Get proxy on the tango_test1 device
+    print("Creating proxy to TangoTest device...")
+    tango_test = DeviceProxy("sys/tg_test/1")
+
+    data_1d_long = numpy.arange(0, 100, dtype=numpy.int32)
+
+    tango_test.long_spectrum = data_1d_long
+
+    data_2d_float = numpy.zeros((10,20), dtype=numpy.float64)
+
+    tango_test.double_image = data_2d_float
+
+
+Execute commands
+----------------
+
+As you can see in the following example, when scalar types are used, the Tango
+binding automagically manages the data types, and writing scripts is quite easy::            
+
+    from PyTango import DeviceProxy
+
+    # Get proxy on the tango_test1 device
+    print("Creating proxy to TangoTest device...")
+    tango_test = DeviceProxy("sys/tg_test/1")
+
+    # First use the classical command_inout way to execute the DevString command
+    # (DevString in this case is a command of the Tango_Test device)
+
+    result = tango_test.command_inout("DevString", "First hello to device")
+    print("Result of execution of DevString command = {0}".format(result))
+
+    # the same can be achieved with a helper method
+    result = tango_test.DevString("Second Hello to device")
+    print("Result of execution of DevString command = {0}".format(result))
+
+    # Please note that argin argument type is automatically managed by python
+    result = tango_test.DevULong(12456)
+    print("Result of execution of DevULong command = {0}".format(result))
+
+
+Execute commands with more complex types
+----------------------------------------
+
+In this case you have to use put your arguments data in the correct python
+structures::
+
+    from PyTango import DeviceProxy
+
+    # Get proxy on the tango_test1 device
+    print("Creating proxy to TangoTest device...")
+    tango_test = DeviceProxy("sys/tg_test/1")
+
+    # The input argument is a DevVarLongStringArray so create the argin 
+    # variable containing an array of longs and an array of strings
+    argin = ([1,2,3], ["Hello", "TangoTest device"])
+
+    result = tango_test.DevVarLongArray(argin)
+    print("Result of execution of DevVarLongArray command = {0}".format(result))
+
+Work with Groups
+----------------
+
+.. todo:: 
+   write this how to
+
+Handle errors
+-------------
+
+.. todo:: 
+   write this how to
+
+.. _pytango-howto-server:
+
+For now check :ref:`pytango-exception-api`.
+
+Registering devices
+-------------------
+
+Here is how to define devices in the Tango DataBase::
+
+    from PyTango import Database, DbDevInfo
+
+    #  A reference on the DataBase
+    db = Database()
+
+    # The 3 devices name we want to create
+    # Note: these 3 devices will be served by the same DServer
+    new_device_name1 = "px1/tdl/mouse1"
+    new_device_name2 = "px1/tdl/mouse2"
+    new_device_name3 = "px1/tdl/mouse3"
+
+    # Define the Tango Class served by this  DServer
+    new_device_info_mouse = DbDevInfo()
+    new_device_info_mouse._class = "Mouse"
+    new_device_info_mouse.server = "ds_Mouse/server_mouse"
+
+    # add the first device
+    print("Creating device: %s" % new_device_name1)
+    new_device_info_mouse.name = new_device_name1
+    db.add_device(new_device_info_mouse)
+
+    # add the next device
+    print("Creating device: %s" % new_device_name2)
+    new_device_info_mouse.name = new_device_name2
+    db.add_device(new_device_info_mouse)
+
+    # add the third device
+    print("Creating device: %s" % new_device_name3)
+    new_device_info_mouse.name = new_device_name3
+    db.add_device(new_device_info_mouse)
+
+
+Setting up device properties
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A more complex example using python subtilities.
+The following python script example (containing some functions and instructions
+manipulating a Galil motor axis device server) gives an idea of how the Tango
+API should be accessed from Python::
+
+    from PyTango import DeviceProxy
+
+    # connecting to the motor axis device
+    axis1 = DeviceProxy("microxas/motorisation/galilbox")
+
+    # Getting Device Properties
+    property_names = ["AxisBoxAttachement",
+                      "AxisEncoderType",
+                      "AxisNumber",
+                      "CurrentAcceleration",
+                      "CurrentAccuracy",
+                      "CurrentBacklash",
+                      "CurrentDeceleration",
+                      "CurrentDirection",
+                      "CurrentMotionAccuracy",
+                      "CurrentOvershoot",
+                      "CurrentRetry",
+                      "CurrentScale",
+                      "CurrentSpeed",
+                      "CurrentVelocity",
+                      "EncoderMotorRatio",
+                      "logging_level",
+                      "logging_target",
+                      "UserEncoderRatio",
+                      "UserOffset"]
+
+    axis_properties = axis1.get_property(property_names)
+    for prop in axis_properties.keys():
+        print("%s: %s" % (prop, axis_properties[prop][0]))
+
+    # Changing Properties
+    axis_properties["AxisBoxAttachement"] = ["microxas/motorisation/galilbox"]
+    axis_properties["AxisEncoderType"] = ["1"]
+    axis_properties["AxisNumber"] = ["6"]
+    axis1.put_property(axis_properties)
+
+Write a server
+--------------
+
+Before reading this chapter you should be aware of the TANGO basic concepts.
 This chapter does not explain what a Tango device or a device server is.
-This is explained in details in "The Tango control system manual" available at
-http://www.tango-controls.org/TangoKernel.
-The device server described in the following example is a Tango device server
-with one Tango class called *PyDsExp*. This class has two commands called
-*IOLong* and *IOStringArray* and two attributes called *Long_attr* and
-*Short_attr_rw*.
+This is explained in details in the
+`Tango control system manual <http://www.tango-controls.org/TangoKernel>`_
+
+Since version 8.1, PyTango provides a helper module which simplifies the 
+development of a Tango device server. This helper is provided through the
+:mod:`PyTango.server` module.
+
+Here is a simple example on how to write a *Clock* device server using the
+high level API
+
+.. code-block:: python
+   :linenos:
+    
+    import time
+    from PyTango.server import run
+    from PyTango.server import Device, DeviceMeta
+    from PyTango.server import attribute, command   
+
+
+    class Clock(Device):
+        __metaclass__ = DeviceMeta
+
+        @attribute
+        def time(self):
+            return time.time()
+
+        @command(dtype_in=str, dtype_out=str)
+        def strftime(self, format):
+            return time.strftime(format)
+
+
+    if __name__ == "__main__":
+        run([Clock])
+
+
+**line 2-4**
+    import the necessary symbols
+
+**line 7**
+    tango device class definition. A Tango device must inherit from 
+    :class:`PyTango.server.Device`
+
+**line 8**
+    mandatory *magic* line. A Tango device must define the metaclass as
+    :class:`PyTango.server.DeviceClass`. This has to be done due to a limitation
+    on boost-python
+
+**line 10-12**
+    definition of the *time* attribute. By default, attributes are double, scalar, 
+    read-only. Check the :class:`~PyTango.server.attribute` for the complete
+    list of attribute options.
+
+**line 14-16**
+    the method *strftime* is exported as a Tango command. In receives a string
+    as argument and it returns a string. If a method is to be exported as a
+    Tango command, it must be decorated as such with the
+    :func:`~PyTango.server.command` decorator
+
+**line 20**
+    start the Tango run loop. The mandatory argument is a list of python classes
+    that are to be exported as Tango classes. Check :func:`~PyTango.server.run`
+    for the complete list of options
+
+Here is a more complete example on how to write a *PowerSupply* device server
+using the high level API. The example contains:
+
+#. a read-only double scalar attribute called *voltage*
+#. a read/write double scalar expert attribute *current*
+#. a read-only double image attribute called *noise*
+#. a *ramp* command
+#. a *host* device property
+#. a *port* class property
+
+.. code-block:: python
+    :linenos:
+
+    from time import time
+    from numpy.random import random_sample
+
+    from PyTango import AttrQuality, AttrWriteType, DispLevel, run
+    from PyTango.server import Device, DeviceMeta, attribute, command
+    from PyTango.server import class_property, device_property
+
+
+    class PowerSupply(Device):
+        __metaclass__ = DeviceMeta
+
+        current = attribute(label="Current", dtype=float,
+                            display_level=DispLevel.EXPERT,
+                            access=AttrWriteType.READ_WRITE,
+                            unit="A", format="8.4f",
+                            min_value=0.0, max_value=8.5,
+                            min_alarm=0.1, max_alarm=8.4,
+                            min_warning=0.5, max_warning=8.0,
+                            fget="get_current", fset="set_current",
+                            doc="the power supply current")
+    
+        noise = attribute(label="Noise", dtype=((float,),),
+                          max_dim_x=1024, max_dim_y=1024,
+                          fget="get_noise")
+ 
+        host = device_property(dtype=str)
+        port = class_property(dtype=int, default_value=9788)
+
+	@attribute
+        def voltage(self):
+            self.info_stream("get voltage(%s, %d)" % (self.host, self.port))
+            return 10.0
+
+        def get_current(self):
+            return 2.3456, time(), AttrQuality.ATTR_WARNING
+    
+        def set_current(self, current):
+            print("Current set to %f" % current)
+    
+        def get_noise(self):
+            return random_sample((1024, 1024))
+
+        @command(dtype_in=float)
+        def ramp(self, value):
+            print("Ramping up...")
+
+
+    if __name__ == "__main__":
+        run([PowerSupply])
+
+
+.. note::
+    the ``__metaclass__`` statement is mandatory due to a limitation in the
+    *boost-python* library used by PyTango.
+    
+    If you are using python 3 you can write instead::
+        
+        class PowerSupply(Device, metaclass=DeviceMeta)
+            pass
+
+.. _logging:
+
+Server logging
+--------------
+
+This chapter instructs you on how to use the tango logging API (log4tango) to
+create tango log messages on your device server.
+
+The logging system explained here is the Tango Logging Service (TLS). For
+detailed information on how this logging system works please check:
+
+    * `3.5 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node4.html#sec:The-Tango-Logging>`_
+    * `9.3 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node9.html#SECTION00930000000000000000>`_
+
+The easiest way to start seeing log messages on your device server console is
+by starting it with the verbose option. Example::
+
+    python PyDsExp.py PyDs1 -v4
+
+This activates the console tango logging target and filters messages with 
+importance level DEBUG or more.
+The links above provided detailed information on how to configure log levels 
+and log targets. In this document we will focus on how to write log messages on
+your device server.
+
+Basic logging
+~~~~~~~~~~~~~
+
+The most basic way to write a log message on your device is to use the
+:class:`~PyTango.server.Device` logging related methods:
+
+    * :meth:`~PyTango.server.Device.debug_stream`
+    * :meth:`~PyTango.server.Device.info_stream`
+    * :meth:`~PyTango.server.Device.warn_stream`
+    * :meth:`~PyTango.server.Device.error_stream`
+    * :meth:`~PyTango.server.Device.fatal_stream`
+
+Example::
+
+    def read_voltage(self):
+        self.info_stream("read voltage attribute")
+	# ... 
+	return voltage_value
+
+This will print a message like::
+
+    1282206864 [-1215867200] INFO test/power_supply/1 read voltage attribute
+
+every time a client asks to read the *voltage* attribute value.
+
+The logging methods support argument list feature (since PyTango 8.1). Example::
+
+    def read_voltage(self):
+        self.info_stream("read_voltage(%s, %d)", self.host, self.port)
+	# ...
+	return voltage_value
+
+
+Logging with print statement
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+*This feature is only possible since PyTango 7.1.3*
+
+It is possible to use the print statement to log messages into the tango logging
+system. This is achieved by using the python's print extend form sometimes
+refered to as *print chevron*.
+
+Same example as above, but now using *print chevron*::
+
+    def read_voltage(self, the_att):
+        print >>self.log_info, "read voltage attribute"
+	# ...
+	return voltage_value
+
+Or using the python 3k print function::
+
+    def read_Long_attr(self, the_att):
+        print("read voltage attribute", file=self.log_info)
+	# ...
+	return voltage_value
+
+
+Logging with decorators
+~~~~~~~~~~~~~~~~~~~~~~~
+
+*This feature is only possible since PyTango 7.1.3*
+
+PyTango provides a set of decorators that place automatic log messages when
+you enter and when you leave a python method. For example::
+
+    @PyTango.DebugIt()
+    def read_Long_attr(self, the_att):
+        the_att.set_value(self.attr_long)
+
+will generate a pair of log messages each time a client asks for the 'Long_attr'
+value. Your output would look something like::
+
+    1282208997 [-1215965504] DEBUG test/pydsexp/1 -> read_Long_attr()
+    1282208997 [-1215965504] DEBUG test/pydsexp/1 <- read_Long_attr()
+
+Decorators exist for all tango log levels:
+    * :class:`PyTango.DebugIt`
+    * :class:`PyTango.InfoIt`
+    * :class:`PyTango.WarnIt`
+    * :class:`PyTango.ErrorIt`
+    * :class:`PyTango.FatalIt`
+
+The decorators receive three optional arguments:
+    * show_args - shows method arguments in log message (defaults to False)
+    * show_kwargs shows keyword method arguments in log message (defaults to False)
+    * show_ret - shows return value in log message (defaults to False)
+
+Example::
+    
+    @PyTango.DebugIt(show_args=True, show_ret=True)
+    def IOLong(self, in_data):
+        return in_data * 2
+
+will output something like::
+
+    1282221947 [-1261438096] DEBUG test/pydsexp/1 -> IOLong(23)
+    1282221947 [-1261438096] DEBUG test/pydsexp/1 46 <- IOLong()
+
+
+Multiple device classes (Python and C++) in a server
+----------------------------------------------------
+
+Within the same python interpreter, it is possible to mix several Tango classes.
+Let's say two of your colleagues programmed two separate Tango classes in two
+separated python files: A :class:`PLC` class in a :file:`PLC.py`::
+
+    # PLC.py
+
+    from PyTango.server import Device, DeviceMeta, run
+    
+    class PLC(Device):
+        __metaclass__ = DeviceMeta
+
+        # bla, bla my PLC code
+
+    if __name__ == "__main__":
+        run([PLC])
+
+... and a :class:`IRMirror` in a :file:`IRMirror.py`::
+
+    # IRMirror.py
+
+    from PyTango.server import Device, DeviceMeta, run
+    
+    class IRMirror(Device):
+        __metaclass__ = DeviceMeta
+
+        # bla, bla my IRMirror code
+
+    if __name__ == "__main__":
+        run([IRMirror])
+
+You want to create a Tango server called `PLCMirror` that is able to contain
+devices from both PLC and IRMirror classes. All you have to do is write
+a :file:`PLCMirror.py` containing the code::
+
+    # PLCMirror.py
+
+    from PyTango.server import run
+    from PLC import PLC
+    from IRMirror import IRMirror
+
+    run([PLC, IRMirror])
+
+It is also possible to add C++ Tango class in a Python device server as soon as:
+    1. The Tango class is in a shared library
+    2. It exist a C function to create the Tango class
+
+For a Tango class called MyTgClass, the shared library has to be called 
+MyTgClass.so and has to be in a directory listed in the LD_LIBRARY_PATH 
+environment variable. The C function creating the Tango class has to be called 
+_create_MyTgClass_class() and has to take one parameter of type "char \*" which 
+is the Tango class name. Here is an example of the main function of the same 
+device server than before but with one C++ Tango class called SerialLine::
+
+    import PyTango
+    import sys
+    
+    if __name__ == '__main__':
+        py = PyTango.Util(sys.argv)
+        util.add_class('SerialLine', 'SerialLine', language="c++")
+        util.add_class(PLCClass, PLC, 'PLC')
+        util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
+        
+        U = PyTango.Util.instance()
+        U.server_init()
+        U.server_run()
+
+:Line 6: The C++ class is registered in the device server
+:Line 7 and 8: The two Python classes are registered in the device server
+
+Create attributes dynamically
+-----------------------------
+
+It is also possible to create dynamic attributes within a Python device server. 
+There are several ways to create dynamic attributes. One of the way, is to 
+create all the devices within a loop, then to create the dynamic attributes and
+finally to make all the devices available for the external world. In C++ device
+server, this is typically done within the <Device>Class::device_factory() method.
+In Python device server, this method is generic and the user does not have one.
+Nevertheless, this generic device_factory method calls a method named dyn_attr()
+allowing the user to create his dynamic attributes. It is simply necessary to
+re-define this method within your <Device>Class and to create the dynamic 
+attribute within this method:
+
+    ``dyn_attr(self, dev_list)``
+    
+    where dev_list is a list containing all the devices created by the 
+    generic device_factory() method.
+
+There is another point to be noted regarding dynamic attribute within Python 
+device server. The Tango Python device server core checks that for each 
+attribute it exists methods named <attribute_name>_read and/or
+<attribute_name>_write and/or is_<attribute_name>_allowed. Using dynamic
+attribute, it is not possible to define these methods because attributes name
+and number are known only at run-time.
+To address this issue, the Device_3Impl::add_attribute() method has a diferent
+signature for Python device server which is:
+
+    ``add_attribute(self, attr, r_meth = None, w_meth = None, is_allo_meth = None)``
+    
+attr is an instance of the Attr class, r_meth is the method which has to be 
+executed with the attribute is read, w_meth is the method to be executed 
+when the attribute is written and is_allo_meth is the method to be executed
+to implement the attribute state machine. The method passed here as argument
+as to be class method and not object method. Which argument you have to use 
+depends on the type of the attribute (A WRITE attribute does not need a 
+read method). Note, that depending on the number of argument you pass to this
+method, you may have to use Python keyword argument. The necessary methods 
+required by the Tango Python device server core will be created automatically
+as a forward to the methods given as arguments.
+
+Here is an example of a device which has a TANGO command called 
+*createFloatAttribute*. When called, this command creates a new scalar floating
+point attribute with the specified name::
+
+ 
+    from PyTango import Util, Attr
+    from PyTango.server import DeviceMeta, Device, command
+
+    class MyDevice(Device):
+    	__metaclass__ = DeviceMeta
+
+	@command(dtype_in=str)
+        def CreateFloatAttribute(self, attr_name):
+	    attr = Attr(attr_name, PyTango.DevDouble) 
+	    self.add_attribute(attr, self.read_General, self.write_General)
+
+	def read_General(self, attr):
+	    self.info_stream("Reading attribute %s", attr.get_name())
+	    attr.set_value(99.99)
+
+	def write_General(self, attr):
+	    self.info_stream("Writting attribute %s", attr.get_name())
+
+
+Create/Delete devices dynamically
+---------------------------------
+
+*This feature is only possible since PyTango 7.1.2*
+
+Starting from PyTango 7.1.2 it is possible to create devices in a device server
+"en caliente". This means that you can create a command in your "management device"
+of a device server that creates devices of (possibly) several other tango classes.
+There are two ways to create a new device which are described below.
+
+Tango imposes a limitation: the tango class(es) of the device(s) that is(are)
+to be created must have been registered before the server starts.
+If you use the high level API, the tango class(es) must be listed in the call
+to :func:`~PyTango.server.run`. If you use the lower level server API, it must
+be done using individual calls to :meth:`~PyTango.Util.add_class`.
+
+
+Dynamic device from a known tango class name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you know the tango class name but you don't have access to the :class:`PyTango.DeviceClass`
+(or you are too lazy to search how to get it ;-) the way to do it is call 
+:meth:`~PyTango.Util.create_device` / :meth:`~PyTango.Util.delete_device`.
+Here is an example of implementing a tango command on one of your devices that 
+creates a device of some arbitrary class (the example assumes the tango commands
+'CreateDevice' and 'DeleteDevice' receive a parameter of type DevVarStringArray
+with two strings. No error processing was done on the code for simplicity sake)::
+
+    from PyTango import Util
+    from PyTango.server import DeviceMeta, Device, command
+
+    class MyDevice(Device):
+    	__metaclass__ = DeviceMeta
+
+	@command(dtype_in=[str])
+        def CreateDevice(self, pars):
+            klass_name, dev_name = pars
+            util = Util.instance()
+            util.create_device(klass_name, dev_name, alias=None, cb=None)
+        
+	@command(dtype_in=[str])
+        def DeleteDevice(self, pars):
+            klass_name, dev_name = pars
+            util = Util.instance()
+            util.delete_device(klass_name, dev_name)
+
+An optional callback can be registered that will be executed after the device is
+registed in the tango database but before the actual device object is created
+and its init_device method is called. It can be used, for example, to initialize
+some device properties.
+
+Dynamic device from a known tango class
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Importing python modules
-------------------------
+If you already have access to the :class:`~PyTango.DeviceClass` object that
+corresponds to the tango class of the device to be created you can call directly
+the :meth:`~PyTango.DeviceClass.create_device` / :meth:`~PyTango.DeviceClass.delete_device`.
+For example, if you wish to create a clone of your device, you can create a 
+tango command called *Clone*::
 
-To write a Python script which is a Tango device server, you need to import 
-two modules which are:
+    class MyDevice(PyTango.Device_4Impl):
+        
+        def fill_new_device_properties(self, dev_name):
+            prop_names = db.get_device_property_list(self.get_name(), "*")
+            prop_values = db.get_device_property(self.get_name(), prop_names.value_string)
+            db.put_device_property(dev_name, prop_values)
+            
+            # do the same for attributes...
+            ... 
+        
+        def Clone(self, dev_name):
+            klass = self.get_device_class()
+            klass.create_device(dev_name, alias=None, cb=self.fill_new_device_properties)
+            
+        def DeleteSibling(self, dev_name):
+            klass = self.get_device_class()
+            klass.delete_device(dev_name)
+            
+Note that the cb parameter is optional. In the example it is given for
+demonstration purposes only.
 
-1. The :mod:`PyTango` module which is the Python to C++ interface
-2. The Python classical :mod:`sys` module
+.. _server:
 
-This could be done with code like (supposing the PYTHONPATH environment variable
-is correctly set)::
+Write a server (original API)
+-----------------------------
 
-    import PyTango
-    import sys
+This chapter describes how to develop a PyTango device server using the
+original PyTango server API. This API mimics the C++ API and is considered
+low level.
+You should write a server using this API if you are using code generated by 
+`Pogo tool <http://www.esrf.eu/computing/cs/tango/tango_doc/tools_doc/pogo_doc/index.html>`_
+or if for some reason the high level API helper doesn't provide a feature
+you need (in that case think of writing a mail to tango mailing list explaining
+what you cannot do).
 
 The main part of a Python device server
----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The rule of this part of a Tango device server is to:
 
@@ -68,7 +820,7 @@ The following is a typical code for this main function::
     Run the device server loop
 
 The PyDsExpClass class in Python
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The rule of this class is to :
 
@@ -122,7 +874,7 @@ constructor. An example of such a contructor is ::
 The device type is set at line 3.
 
 Defining commands
------------------
+~~~~~~~~~~~~~~~~~
 
 As shown in the previous example, commands have to be defined in a :class:`dict`
 called *cmd_list* as a data member of the xxxClass class of the Tango class.
@@ -152,7 +904,7 @@ this :class:`dict` are summarized in the following array:
     +-------------------+----------------------+------------------------------------------+
 
 Defining attributes
--------------------
+~~~~~~~~~~~~~~~~~~~
 
 As shown in the previous example, attributes have to be defined in a :class:`dict`
 called **attr_list** as a data
@@ -217,7 +969,7 @@ array:
     +-------------------+-----------------------------------+------------------------------------------+
 
 The PyDsExp class in Python
----------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The rule of this class is to implement methods executed by commands and attributes.
 In our example, the code of this class looks like::
@@ -480,284 +1232,4 @@ write the Short_attr_rw attribute::
     store the value written in the device object. Our attribute is a scalar 
     short attribute so the return value is an int
 
-.. _logging:
-
-Logging
-#######
-
-This chapter instructs you on how to use the tango logging API (log4tango) to
-create tango log messages on your device server.
-
-The logging system explained here is the Tango Logging Service (TLS). For
-detailed information on how this logging system works please check:
-
-    * `3.5 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node4.html#sec:The-Tango-Logging>`_
-    * `9.3 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node9.html#SECTION00930000000000000000>`_
-
-The easiest way to start seeing log messages on your device server console is
-by starting it with the verbose option. Example::
-
-    python PyDsExp.py PyDs1 -v4
-
-This activates the console tango logging target and filters messages with 
-importance level DEBUG or more.
-The links above provided detailed information on how to configure log levels 
-and log targets. In this document we will focus on how to write log messages on
-your device server.
-
-Basic logging
-~~~~~~~~~~~~~
-
-The most basic way to write a log message on your device is to use the
-:class:`PyTango.DeviceImpl` logging related methods:
-
-    * :meth:`PyTango.DeviceImpl.debug_stream`
-    * :meth:`PyTango.DeviceImpl.info_stream`
-    * :meth:`PyTango.DeviceImpl.warn_stream`
-    * :meth:`PyTango.DeviceImpl.error_stream`
-    * :meth:`PyTango.DeviceImpl.fatal_stream`
-
-Example::
-
-    def read_Long_attr(self, the_att):
-        self.info_stream("read attribute name Long_attr")
-        the_att.set_value(self.attr_long)
-
-This will print a message like::
-
-    1282206864 [-1215867200] INFO test/pydsexp/1 read attribute name Long_attr
-
-every time a client asks to read the 'Long_attr' attribute value.
-
-Logging with print statement
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-*This feature is only possible since PyTango 7.1.3*
-
-It is possible to use the print statement to log messages into the tango logging
-system. This is achieved by using the python's print extend form sometimes
-refered to as *print chevron*.
-
-Same example as above, but now using *print chevron*::
-
-    def read_Long_attr(self, the_att):
-        print >>self.log_info, "read attribute name Long_attr"
-        the_att.set_value(self.attr_long)
-
-Or using the python 3k print function::
-
-    def read_Long_attr(self, the_att):
-        print("read attribute name Long_attr", file=self.log_info)
-        the_att.set_value(self.attr_long)
-
-Logging with decorators
-~~~~~~~~~~~~~~~~~~~~~~~
-
-*This feature is only possible since PyTango 7.1.3*
-
-PyTango provides a set of decorators that place automatic log messages when
-you enter and when you leave a python method. For example::
-
-    @PyTango.DebugIt()
-    def read_Long_attr(self, the_att):
-        the_att.set_value(self.attr_long)
-
-will generate a pair of log messages each time a client asks for the 'Long_attr'
-value. Your output would look something like::
-
-    1282208997 [-1215965504] DEBUG test/pydsexp/1 -> read_Long_attr()
-    1282208997 [-1215965504] DEBUG test/pydsexp/1 <- read_Long_attr()
-
-Decorators exist for all tango log levels:
-    * :class:`PyTango.DebugIt`
-    * :class:`PyTango.InfoIt`
-    * :class:`PyTango.WarnIt`
-    * :class:`PyTango.ErrorIt`
-    * :class:`PyTango.FatalIt`
-
-The decorators receive three optional arguments:
-    * show_args - shows method arguments in log message (defaults to False)
-    * show_kwargs shows keyword method arguments in log message (defaults to False)
-    * show_ret - shows return value in log message (defaults to False)
-
-Example::
-    
-    @PyTango.DebugIt(show_args=True, show_ret=True)
-    def IOLong(self, in_data):
-        return in_data * 2
-
-will output something like::
-
-    1282221947 [-1261438096] DEBUG test/pydsexp/1 -> IOLong(23)
-    1282221947 [-1261438096] DEBUG test/pydsexp/1 46 <- IOLong()
-
-Dynamic devices
-###############
-
-*This feature is only possible since PyTango 7.1.2*
-
-Starting from PyTango 7.1.2 it is possible to create devices in a device server
-"en caliente". This means that you can create a command in your "management device"
-of a device server that creates devices of (possibly) several other tango classes.
-There are two ways to create a new device which are described below.
-
-Dynamic device from a known tango class name
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you know the tango class name but you don't have access to the :class:`PyTango.DeviceClass`
-(or you are too lazy to search how to get it ;-) the way to do it is call 
-:meth:`PyTango.Util.create_device` / :meth:`PyTango.Util.delete_device`.
-Here is an example of implementing a tango command on one of your devices that 
-creates a device of some arbitrary class (the example assumes the tango commands
-'CreateDevice' and 'DeleteDevice' receive a parameter of type DevVarStringArray
-with two strings. No error processing was done on the code for simplicity sake)::
-
-    class MyDevice(PyTango.Device_4Impl):
-        
-        def CreateDevice(self, pars):
-            klass_name, dev_name = pars
-            util = PyTango.Util.instance()
-            util.create_device(klass_name, dev_name, alias=None, cb=None)
-        
-        def DeleteDevice(self, pars):
-            klass_name, dev_name = pars
-            util = PyTango.Util.instance()
-            util.delete_device(klass_name, dev_name)
-
-An optional callback can be registered that will be executed after the device is
-registed in the tango database but before the actual device object is created and its
-init_device method is called. You can, for example, initialize some device properties
-here.
-
-Dynamic device from a known tango class
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you already have access to the :class:`PyTango.DeviceClass` object that
-corresponds to the tango class of the device to be created you can call directly
-the :meth:`PyTango.DeviceClass.create_device` / :meth:`PyTango.DeviceClass.delete_device`.
-For example, if you wish to create a clone of your device, you can create a 
-tango command called Clone::
-
-    class MyDevice(PyTango.Device_4Impl):
-        
-        def fill_new_device_properties(self, dev_name):
-            prop_names = db.get_device_property_list(self.get_name(), "*")
-            prop_values = db.get_device_property(self.get_name(), prop_names.value_string)
-            db.put_device_property(dev_name, prop_values)
-            
-            # do the same for attributes...
-            ... 
-        
-        def Clone(self, dev_name):
-            klass = self.get_device_class()
-            klass.create_device(dev_name, alias=None, cb=self.fill_new_device_properties)
-            
-        def DeleteSibling(self, dev_name):
-            klass = self.get_device_class()
-            klass.delete_device(dev_name)
-            
-Note that the cb parameter is optional. In the example it is given for
-demonstration purposes only.
-
-Dynamic attributes
-##################
-
-It is also possible to create dynamic attributes within a Python device server. 
-There are several ways to create dynamic attributes. One of the way, is to 
-create all the devices within a loop, then to create the dynamic attributes and
-finally to make all the devices available for the external world. In C++ device
-server, this is typically done within the <Device>Class::device_factory() method.
-In Python device server, this method is generic and the user does not have one.
-Nevertheless, this generic device_factory method calls a method named dyn_attr()
-allowing the user to create his dynamic attributes. It is simply necessary to
-re-define this method within your <Device>Class and to create the dynamic 
-attribute within this method:
-
-    ``dyn_attr(self, dev_list)``
-    
-    where dev_list is a list containing all the devices created by the 
-    generic device_factory() method.
-
-There is another point to be noted regarding dynamic attribute within Python 
-device server. The Tango Python device server core checks that for each 
-attribute it exists methods named <attribute_name>_read and/or
-<attribute_name>_write and/or is_<attribute_name>_allowed. Using dynamic
-attribute, it is not possible to define these methods because attributes name
-and number are known only at run-time.
-To address this issue, the Device_3Impl::add_attribute() method has a diferent
-signature for Python device server which is:
-
-    ``add_attribute(self, attr, r_meth = None, w_meth = None, is_allo_meth = None)``
-    
-    attr is an instance of the Attr class, r_meth is the method which has to be 
-    executed with the attribute is read, w_meth is the method to be executed 
-    when the attribute is written and is_allo_meth is the method to be executed
-    to implement the attribute state machine. The method passed here as argument
-    as to be class method and not object method. Which argument you have to use 
-    depends on the type of the attribute (A WRITE attribute does not need a 
-    read method). Note, that depending on the number of argument you pass to this
-    method, you may have to use Python keyword argument. The necessary methods 
-    required by the Tango Python device server core will be created automatically
-    as a forward to the methods given as arguments.
-
-Mixing Tango classes (Python and C++) in a Python Tango device server
----------------------------------------------------------------------
-
-Within the same python interpreter, it is possible to mix several Tango classes. 
-Here is an example of the main function of a device server with two Tango classes
-called IRMiror and PLC::
-
-    import PyTango
-    import sys
-
-    if __name__ == '__main__':
-        util = PyTango.Util(sys.argv)
-        util.add_class(PLCClass, PLC, 'PLC')
-        util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
-        
-        U = PyTango.Util.instance()
-        U.server_init()
-        U.server_run()
-
-:Line 6: The Tango class PLC is registered in the device server
-:Line 7: The Tango class IRMirror is registered in the device server
-
-It is also possible to add C++ Tango class in a Python device server as soon as:
-    1. The Tango class is in a shared library
-    2. It exist a C function to create the Tango class
-
-For a Tango class called MyTgClass, the shared library has to be called 
-MyTgClass.so and has to be in a directory listed in the LD_LIBRARY_PATH 
-environment variable. The C function creating the Tango class has to be called 
-_create_MyTgClass_class() and has to take one parameter of type "char \*" which 
-is the Tango class name. Here is an example of the main function of the same 
-device server than before but with one C++ Tango class called SerialLine::
-
-    import PyTango
-    import sys
-    
-    if __name__ == '__main__':
-        py = PyTango.Util(sys.argv)
-        util.add_class('SerialLine', 'SerialLine', language="c++")
-        util.add_class(PLCClass, PLC, 'PLC')
-        util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
-        
-        U = PyTango.Util.instance()
-        U.server_init()
-        U.server_run()
-
-:Line 6: The C++ class is registered in the device server
-:Line 7 and 8: The two Python classes are registered in the device server
 
-Server API
-----------
-
-.. toctree::
-    :maxdepth: 2
-    
-    server
-    device
-    device_class
-    logging
-    attribute
-    util
diff --git a/doc/itango/highlights.rst b/doc/itango.rst
similarity index 91%
rename from doc/itango/highlights.rst
rename to doc/itango.rst
index 02575e8..97931c1 100644
--- a/doc/itango/highlights.rst
+++ b/doc/itango.rst
@@ -1,13 +1,81 @@
+.. highlight:: python
+   :linenothreshold: 4
+
+.. _itango:
+
+======
+ITango
+======
+
+ITango is a PyTango CLI based on IPython_. It is designed to be used as an
+IPython profile.
+
+ITango is available since PyTango 7.1.2
+
+You can start ITango by typing on the command line::
+
+    $ itango
+
+or the equivalent::
+
+    $ ipython --profile=tango
+
+and you should get something like this:
+
+.. image:: _static/itango00.png
+    :align: center
+    :width: 75%
+
+
+.. _itango-features:
+
+Features
+--------
+
+ITango works like a normal python console, but it gives you in addition a nice
+set of features from IPython_ like:
+
+    - proper (bash-like) command completion
+    - automatic expansion of python variables, functions, types
+    - command history (with up/down arrow keys, %hist command)
+    - help system ( object? syntax, help(object))
+    - persistently store your favorite variables
+    - color modes
+ 
+(for a complete list checkout the `IPython web page <http://ipython.org/>`_)
+
+Plus an additional set o Tango_ specific features:
+
+    - automatic import of Tango objects to the console namespace (:mod:`PyTango`
+      module, :class:`~PyTango.DeviceProxy` (=Device),
+      :class:`~PyTango.Database`, :class:`~PyTango.Group`
+      and :class:`~PyTango.AttributeProxy` (=Attribute))
+    - device name completion
+    - attribute name completion
+    - automatic tango object member completion
+    - list tango devices, classes, servers
+    - customized tango error message
+    - tango error introspection
+    - switch database
+    - refresh database
+    - list tango devices, classes
+    - store favorite tango objects
+    - store favorite tango devices
+    - tango color modes
+
+Check the :ref:`itango-highlights` to see how to put these feature to good use
+:-)
+
 
 .. currentmodule:: PyTango
 
 .. _itango-highlights:
 
 Highlights
-==========
+----------
 
 Tab completion
---------------
+~~~~~~~~~~~~~~
 
 ITango exports many tango specific objects to the console namespace.
 These include:
@@ -40,7 +108,7 @@ These include:
             ITango [4]: Att<tab>
             Attribute       AttributeError  AttributeProxy
             
-    - The tango :class:`PyTango.Database` object to which the itango session is 
+    - The Tango :class:`Database` object to which the itango session is 
       currently connected
       
       .. sourcecode:: itango
@@ -49,7 +117,7 @@ These include:
             Result [1]: Database(homer, 10000)
     
 Device name completion
-----------------------
+~~~~~~~~~~~~~~~~~~~~~~
 
 ITango knows the complete list of device names (including alias) for the current
 tango database. This means that when you try to create a new Device, by pressing
@@ -66,7 +134,7 @@ tango database. This means that when you try to create a new Device, by pressing
     ITango [2]: test = Device("sys/tg_test/1")
 
 Attribute name completion
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
 
 ITango can inspect the list of attributes in case the device server for the device
 where the attribute resides is running.
@@ -113,7 +181,7 @@ where the attribute resides is running.
     w_value = 0]
 
 Automatic tango object member completion
-----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 When you create a new tango object, (ex.: a device), itango is able to find out
 dynamically which are the members of this device (including tango commands 
@@ -153,7 +221,7 @@ and attributes if the device is currently running)
     Result [3]: 56.433
 
 Tango classes as :class:`DeviceProxy`
----------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 ITango exports all known tango classes as python alias to :class:`DeviceProxy`. 
 This way, if you want to create a device of class which you already know 
@@ -187,7 +255,7 @@ class 'Libera' will show up as possible completions.
     ITango [1]: bpm1 = Libera("BO01/DI/BPM-01")
 
 Customized device representation
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
 
 When you use ipython >= 0.11 with a Qt console frontend::
 
@@ -197,7 +265,7 @@ typing a variable containing a tango device object followend by :kbd:`Enter`
 will present you with a customized representation of the object instead of the
 usual :func:`repr` :
 
-    .. image:: itango06.png
+    .. image:: _static/itango06.png
 
 You can customize the icon that itango displays for a specific device.
 The first thing to do is to copy the image file into
@@ -225,7 +293,7 @@ the class property value, if defined)::
 
 
 List tango devices, classes, servers
---------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
 
 ITango provides a set of magic functions (ipython lingo) that allow you to check
 for the list tango devices, classes and servers which are registered in the 
@@ -272,7 +340,7 @@ current database.
     MacroServer/tcoutinho          Simulator/BL99
 
 Customized tango error message and introspection
-----------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 ITango intercepts tango exceptions that occur when you do tango operations 
 (ex.: write an attribute with a value outside the allowed limits) and tries to
@@ -303,7 +371,7 @@ the magic command 'tango_error'.
     severity = PyTango._PyTango.ErrSeverity.ERR]]
 
 Switching database
----------------------
+~~~~~~~~~~~~~~~~~~
 
 You can switch database simply by executing the 'switchdb <host> [<port>]' magic
 command.
@@ -339,7 +407,7 @@ command.
     Database(marge, 10005)
 
 Refreshing the database
---------------------------
+~~~~~~~~~~~~~~~~~~~~~~~
 
 When itango starts up or when the database is switched, a query is made to the
 tango Database device server which provides all necessary data. This
@@ -355,7 +423,7 @@ all tango information from the database.
     ITango [1]: refreshdb
     
 Storing your favorite tango objects for later usage
--------------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 .. note::
     This feature is not available if you have installed IPython 0.11!
@@ -384,7 +452,7 @@ then store these for the next time you startup IPython_ with itango profile.
     DeviceProxy(motor/bl99/1)
 
 Adding itango to your own ipython profile
---------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Adding itango to the ipython default profile
 ##################################################
@@ -581,10 +649,10 @@ Then start your CLI with::
 
 and you will have something like this
 
-.. image:: itango02.png
+.. image:: _static/itango02.png
 
 Advanced event monitoring
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
 
 .. note::
     This chapter has a pending update. The contents only apply to
diff --git a/doc/itango/features.rst b/doc/itango/features.rst
deleted file mode 100644
index 8a5b66f..0000000
--- a/doc/itango/features.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-
-.. _itango-features:
-
-Features
-========
-
-ITango works like a normal python console, but it gives you in addition a nice
-set of features from IPython_ like:
-
-    - proper (bash-like) command completion
-    - automatic expansion of python variables, functions, types
-    - command history (with up/down arrow keys, %hist command)
-    - help system ( object? syntax, help(object))
-    - persistently store your favorite variables
-    - color modes
- 
-(for a complete list checkout the `IPython web page <http://ipython.org/>`_)
-
-Plus an additional set o Tango_ specific features:
-
-    - automatic import of Tango objects to the console namespace (:mod:`PyTango`
-      module, :class:`~PyTango.DeviceProxy` (=Device),
-      :class:`~PyTango.Database`, :class:`~PyTango.Group`
-      and :class:`~PyTango.AttributeProxy` (=Attribute))
-    - device name completion
-    - attribute name completion
-    - automatic tango object member completion
-    - list tango devices, classes, servers
-    - customized tango error message
-    - tango error introspection
-    - switch database
-    - refresh database
-    - list tango devices, classes
-    - store favorite tango objects
-    - store favorite tango devices
-    - tango color modes
-
-Check the :ref:`itango-highlights` to see how to put these feature to good use
-:-)
-
diff --git a/doc/itango/index.rst b/doc/itango/index.rst
deleted file mode 100644
index 25f123b..0000000
--- a/doc/itango/index.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-.. highlight:: python
-   :linenothreshold: 4
-
-.. _itango:
-
-ITango
-======
-
-ITango is a PyTango CLI based on IPython_. It is designed to be used as an
-IPython profile.
-
-ITango is available since PyTango 7.1.2
-
-You can start ITango by typing on the command line::
-
-    $ itango
-
-or the equivalent::
-
-    $ ipython --profile=tango
-
-and you should get something like this:
-
-.. image:: itango00.png
-    :align: center
-    :width: 75%
-
-.. toctree::
-    :maxdepth: 1
-
-    features
-    highlights
diff --git a/doc/quicktour.rst b/doc/quicktour.rst
index 1ed0cbc..cd346a2 100644
--- a/doc/quicktour.rst
+++ b/doc/quicktour.rst
@@ -1,502 +1,251 @@
-.. _quick-tour:
+.. _pytango-quick-tour:
 
 Quick tour
-============
+==========
 
 This quick tour will guide you through the first steps on using PyTango.
-This is the new quick tour guide based on the :ref:`itango` console.
-You can still find the old version of this tour based on a simple python
-console :ref:`here <quick-tour-old>`.
 
-Quick tour on the client side
------------------------------
+Fundamental TANGO concepts
+--------------------------
 
-Check PyTango version
-~~~~~~~~~~~~~~~~~~~~~
+Before you begin there are some fundamental TANGO concepts you should be aware of.
 
-Start an IPython_ tango console with::
+Tango consists basically of a set of **devices** running somewhere on the network.
 
-    $ itango
+A device is identified by a unique case insensitive name in the format 
+*<domain>/<family>/<member>*. Examples: `LAB-01/PowerSupply/01`, 
+`ID21/OpticsHutch/energy`. 
 
-and type:
+Each device has a series of *attributes*, *properties* and *commands*. 
 
-    .. sourcecode:: itango
+An attribute is identified by a name in a device. It has a value that can 
+be read. Some attributes can also be changed (read-write attributes).
 
-        ITango [1]: PyTango.__version__
-        Result [1]: '8.0.0'
+A property is identified by a name in a device. Usually, devices properties are
+used to provide a way to configure a device. 
 
-        ITango [2]: PyTango.__version_long__
-        Result [2]: '8.0.0dev0'
+A command is also identified by a name. A command may or not receive a parameter
+and may or not return a value when it is executed.
 
-        ITango [3]: PyTango.__version_number__
-        Result [3]: 800
+Any device has **at least** a *State* and *Status* attributes and *State*,
+*Status* and *Init* commands. Reading the *State* or *Status* attributes has 
+the same effect as executing the *State* or *Status* commands.
 
-        ITango [4]: PyTango.__version_description__
-        Result [4]: 'This version implements the C++ Tango 8.0 API.'
+Each device as an associated *TANGO Class*. Most of the times the TANGO class 
+has the same name as the object oriented programming class which implements it
+but that is not mandatory. 
 
-or alternatively:
+TANGO devices *live* inside a operating system process called *TANGO Device Server*.
+This server acts as a container of devices. A device server can host multiple
+devices of multiple TANGO classes. Devices are, therefore, only accessible when
+the corresponding TANGO Device Server is running.
 
-    .. sourcecode:: itango
+A special TANGO device server called the *TANGO Database Server* will act as
+a naming service between TANGO servers and clients. This server has a known 
+address where it can be reached. The machines that run TANGO Device Servers 
+and/or TANGO clients, should export an environment variable called
+:envvar:`TANGO_HOST` that points to the TANGO Database server address. Example:
+``TANGO_HOST=homer.lab.eu:10000``
 
-        ITango [1]: PyTango.Release.version
-        Result [1]: '8.0.0'
+Minimum setup
+-------------
 
-        ITango [2]: PyTango.Release.version_long
-        Result [2]: '8.0.0dev0'
+This chapter assumes you have already installed PyTango.
 
-        ITango [3]: PyTango.Release.version_number
-        Result [3]: 800
+To explore PyTango you should have a running Tango system. If you are working in
+a facility/institute that uses Tango, this has probably already been prepared
+for you. You need to ask your facility/institute tango contact for the
+:envvar:`TANGO_HOST` variable where Tango system is running. 
 
-        ITango [4]: PyTango.Release.version_description
-        Result [4]: 'This version implements the C++ Tango 8.0 API.'
+If you are working in an isolate machine you first need to make sure the Tango
+system is installed and running (`Tango howtos <http://www.tango-controls.org/howtos>`_).
 
-.. tip::
+Most examples here connect to a device called *sys/tg_test/1* that runs in a 
+TANGO server called *TangoTest* with the instance name *test*.
+This server comes with the TANGO installation. The TANGO installation
+also registers the *test* instance. All you have to do is start the TangoTest 
+server on a console::
 
-    When typing, try pressing <tab>. Since ITango has autocomplete embedded you
-    should get a list of possible completions. Example::
-    
-        PyTango.Release.<tab>
-        
-    Should get a list of all members of :class:`PyTango.Release` class.
+    $ TangoTest test
+    Ready to accept request
 
-Check Tango C++ version
-~~~~~~~~~~~~~~~~~~~~~~~
+.. note::
+   if you receive a message saying that the server is already running, 
+   it just means that somebody has already started the test server so you don't
+   need to do anything.
+
+Client
+------
+
+Finally you can get your hands dirty. The first thing to do is start a python
+console and import the :mod:`PyTango` module. The following example shows
+how to create a proxy to an existing TANGO device, how to read and write
+attributes and execute commands from a python console::
+    
+    >>> import PyTango
+    
+    >>> # create a device object 
+    >>> test_device = PyTango.DeviceProxy("sys/tg_test/1")
+
+    >>> # every device has a state and status which can be checked with:
+    >>> print(test_device.state())
+    RUNNING
+
+    >>> print(test_device.status())
+    The device is in RUNNING state.
+
+    >>> # this device has an attribute called "long_scalar". Let's see which value it has...
+    >>> data = test_device.read_attribute("long_scalar")
+
+    >>> # ...PyTango provides a shortcut to do the same:
+    >>> data = test_device["long_scalar"]
+
+    >>> # the result of reading an attribute is a DeviceAttribute python object. 
+    >>> # It has a member called "value" which contains the value of the attribute
+    >>> data.value
+    136
 
-From a client (This is only possible since PyTango 7.0.0)
+    >>> # Check the complete DeviceAttribute members:
+    >>> print(data)
+    DeviceAttribute[
+    data_format = SCALAR
+          dim_x = 1
+          dim_y = 0
+     has_failed = False
+       is_empty = False
+           name = 'long_scalar'
+        nb_read = 1
+     nb_written = 1
+        quality = ATTR_VALID
+    r_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
+           time = TimeVal(tv_nsec = 0, tv_sec = 1399450183, tv_usec = 323990)
+           type = DevLong
+          value = 136
+        w_dim_x = 1
+        w_dim_y = 0
+    w_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
+        w_value = 0]
 
-    .. sourcecode:: itango
+    >>> # PyTango provides a handy pythonic shortcut to read the attribute value:
+    >>> test_device.long_scalar
+    136 
 
-        ITango [1]: import PyTango.constants
+    >>> # Setting an attribute value is equally easy:
+    >>> test_device.write_attribute("long_scalar", 8776)
 
-        ITango [2]: PyTango.constants.TgLibVers
-        Result [2]: '8.0.0'
+    >>> # ... and a handy shortcut to do the same exists as well:
+    >>> test_device.long_scalar = 8776
 
-From a server you can alternatively do::
-    
-    u = PyTango.Util.instance()
-    tg_cpp_lib_ver = u.get_tango_lib_release()
-    
+    >>> # TangoTest has a command called "DevDouble" which receives a number 
+    >>> # as parameter and returns the same number as a result. Let's
+    >>> # execute this command:
+    >>> test_device.command_inout("DevDouble", 45.67)
+    45.67
 
-Test the connection to the Device and get it's current state
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    >>> # PyTango provides a handy shortcut: it exports commands as device methods:
+    >>> test_device.DevDouble(45.67)
+    45.67
 
-One of the most basic examples is to get a reference to a device and
-determine if it is running or not.
+    >>> # Introspection: check the list of attributes:
+    >>> test_device.get_attribute_list()
+    ['ampli', 'boolean_scalar', 'double_scalar', '...', 'State', 'Status']
+    
+    >>> 
 
-    .. sourcecode:: itango
-        
-        ITango [1]: # What is a DeviceProxy, really?
-        ITango [1]: DeviceProxy?
-        DeviceProxy is the high level Tango object which provides the client with
-        an easy-to-use interface to TANGO devices. DeviceProxy provides interfaces
-        to all TANGO Device interfaces.The DeviceProxy manages timeouts, stateless
-        connections and reconnection if the device server is restarted. To create
-        a DeviceProxy, a Tango Device name must be set in the object constructor.
+This is just the tip of the iceberg. Check the :class:`~PyTango.DeviceProxy` for
+the complete API. 
 
-        Example :
-           dev = PyTango.DeviceProxy("sys/tg_test/1")
-           
-        ITango [2]: tangotest = DeviceProxy("sys/tg_test/1")
+PyTango comes with an integrated IPython_ based console called :ref:`itango`.
+It provides helpers to simplify console usage. You can use this console instead
+of the traditional python console. Be aware, though, that many of the *tricks*
+you can do in an :ref:`itango` console cannot be done in a python program.
 
-        ITango [3]: # ping it
-        ITango [4]: tangotest.ping()
-        Result [4]: 110
+Server
+------
 
-        ITango [5]: # Lets test the state
-        ITango [6]: tangotest.state()
-        Result [6]: PyTango._PyTango.DevState.RUNNING
+Since PyTango 8.1 it has become much easier to program a Tango device server.
+PyTango provides some helpers that allow developers to simplify the programming
+of a Tango device server.
 
-        ITango [7]: # And now the status
-        ITango [8]: tangotest.status()
-        Result [8]: 'The device is in RUNNING state.'
+Before creating a server you need to decide:
 
-.. note::
+1. The name of the device server (example: `PowerSupplyDS`). This will be
+   the mandatory name of your python file.
+2. The Tango Class name of your device (example: `PowerSupply`). In our 
+   example we will use the same name as the python class name.
+3. the list of attributes of the device, their data type, access (read-only vs
+   read-write), data_format (scalar, 1D, 2D)
+4. the list of commands, their parameters and their result
 
-    Did you notice that you didn't write `PyTango.DeviceProxy` but instead just
-    `DeviceProxy` ? This is because :ref:`itango` automatically exports the
-    :class:`~PyTango.DeviceProxy`, :class:`~PyTango.AttributeProxy`,
-    :class:`~PyTango.Database` and :class:`~PyTango.Group` classes to the
-    namespace. If you are writting code outside :ref:`itango` you **MUST**
-    use the `PyTango` module prefix.
+In our example we will write a fake power supply device server. The server
+will be called `PowerSupplyDS`. There will be a class called `PowerSupply`
+which will have attributes:
 
-.. tip::
+* *voltage* (scalar, read-only, numeric)
+* *current* (scalar, read_write, numeric, expert mode) 
+* *noise* (2D, read-only, numeric)
 
-    When typing the device name in the :class:`~PyTango.DeviceProxy` creation
-    line, try pressing the <tab> key. You should get a list of devices::
-    
-        tangotest = DeviceProxy("sys<tab>
-        
-    Better yet (and since the Tango Class of 'sys/tg_test/1' is 'TangoTest'),
-    try doing::
-    
-        tangotest = TangoTest("<tab>
+commands:
 
-    Now the list of devices should be reduced to the ones that belong to the 
-    'TangoTest' class. Note that TangoTest only works in ITango. If you are 
-    writting code outside :ref:`itango` you **MUST** use 
-    :class:`PyTango.DeviceProxy` instead.
-    
-Execute commands with scalar arguments on a Device
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As you can see in the following example, when scalar types are used, PyTango
-automatically manages the data types, and writing scripts is quite easy.
+* *TurnOn* (argument: None, result: None)
+* *TurnOff* (argument: None, result: None)
+* *Ramp* (param: scalar, numeric; result: bool)
 
-    .. sourcecode:: itango
-    
-        ITango [1]: tangotest = TangoTest("sys/tg_test/1")
+properties:
 
-        ITango [2]: # classical way
-        ITango [2]: r = tangotest.command_inout("DevString", "Hello, world!")
+* *host* (string representing the host name of the actual power supply)
+* *port* (port number in the host with default value = 9788)
 
-        ITango [3]: print "Result of execution of DevString command =", r
-        Result of execution of DevString command = Hello, world!
+Here is the code for the :file:`PowerSupplyDS.py`
 
-        ITango [4]: # 'pythonic' way
-        ITango [5]: tangotest.DevString("Hello, world!")
-        Result [5]: 'Hello, world!'
-        
-        ITango [6]: # type is automatically managed by PyTango
-        ITango [7]: tangotest.DevULong(12456)
-        Result [7]: 12456
+.. literalinclude:: _static/PowerSupplyDS.py
+    :linenos:
 
-Execute commands with more complex types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Check the :ref:`high level server API <pytango-hlapi>` for the complete
+reference API. The :ref:`write a server how to <pytango-howto-server>` can help
+as well.
 
-In this case you have to use put your arguments data in the correct python
-structures.
+Before running this brand new server we need to register it in the Tango system.
+You can do it with Jive (`Jive->Edit->Create server`):
 
-    .. sourcecode:: itango
-    
-        ITango [1]: tangotest = TangoTest("sys/tg_test/1")
-
-        ITango [2]: argin = [1, 2, 3], ["Hello", "World"]
-
-        ITango [3]: tango_test.DevVarLongArray(argin)
-        Result [3]: [array([1, 2, 3]), ['Hello', 'World']]
-        
-.. note::
-    notice that the command returns a list of two elements. The first element is
-    a :class:`numpy.ndarray` (assuming PyTango is compiled with numpy_ support).
-    This is because PyTango does a best effort to convert all numeric array types
-    to numpy_ arrays.
-    
-Reading and writing attributes
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. image:: _static/jive_powersupply.png
 
-Basic read/write attribute operations.
+... or in a python script::
 
-    .. sourcecode:: itango
-    
-        ITango [1]: # Read a scalar attribute
-        ITango [2]: print tangotest.read_attribute("long_scalar")
-        DeviceAttribute[
-        data_format = PyTango._PyTango.AttrDataFormat.SCALAR
-              dim_x = 1
-              dim_y = 0
-         has_failed = False
-           is_empty = False
-               name = 'long_scalar'
-            nb_read = 1
-         nb_written = 1
-            quality = PyTango._PyTango.AttrQuality.ATTR_VALID
-        r_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
-               time = TimeVal(tv_nsec = 0, tv_sec = 1281084943, tv_usec = 461730)
-               type = PyTango._PyTango.CmdArgType.DevLong
-              value = 239
-            w_dim_x = 1
-            w_dim_y = 0
-        w_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
-            w_value = 0]
-            
-        ITango [3]: # Read a spectrum attribute
-        ITango [4]: print tangotest.read_attribute("double_spectrum")
-        DeviceAttribute[
-        data_format = PyTango._PyTango.AttrDataFormat.SPECTRUM
-              dim_x = 20
-              dim_y = 0
-         has_failed = False
-           is_empty = False
-               name = 'double_spectrum'
-            nb_read = 20
-         nb_written = 20
-            quality = PyTango._PyTango.AttrQuality.ATTR_VALID
-        r_dimension = AttributeDimension(dim_x = 20, dim_y = 0)
-               time = TimeVal(tv_nsec = 0, tv_sec = 1281085195, tv_usec = 244760)
-               type = PyTango._PyTango.CmdArgType.DevDouble
-              value = array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
-                11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.])
-            w_dim_x = 20
-            w_dim_y = 0
-        w_dimension = AttributeDimension(dim_x = 20, dim_y = 0)
-            w_value = array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
-                11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.])]
-
-        ITango [5]: # Write a scalar attribute
-        ITango [6]: scalar_value = 18
-        ITango [7]: tangotest.write_attribute("long_scalar", scalar_value)
-
-        ITango [8]: # Write a spectrum attribute
-        ITango [9]: spectrum_value = numpy.random.rand(100)*10
-        ITango [10]: tangotest.write_attribute("double_spectrum", spectrum_value)
-        
-        
-        ITango [11]: # Write an image attribute
-        ITango [12]: image_value = numpy.random.randint(0,10,size=(10,10))
-        ITango [13]: tangotest.write_attribute("long_image", image_value)
-
-.. tip::
-    
-    If you are only interested in the attribute's read value you can do insted:
-    
-    .. sourcecode:: itango
-        
-            ITango [1]: tangotest.long_scalar
-            Result [1]: 239
+    >>> import PyTango
     
-    The same is valid for writting a new value to an attribute:
+    >>> dev_info = PyTango.DbDevInfo()
+    >>> dev_info.server = "PowerSupplyDS/test"
+    >>> dev_info._class = "PowerSupply"
+    >>> dev_info.name = "test/power_supply/1"
     
-    .. sourcecode:: itango
-        
-            ITango [1]: tangotest.long_scalar = 18
-    
-.. note::
+    >>> db = PyTango.Database()
+    >>> db.add_device(dev_info)
 
-    If PyTango is compiled with numpy support the values got when reading
-    a spectrum or an image will be numpy arrays. This results in a faster and
-    more memory efficient PyTango. You can also use numpy to specify the values when
-    writing attributes, especially if you know the exact attribute type.::
+After, you can run the server on a console with::
 
-        # Creating an unitialized double spectrum of 1000 elements
-        spectrum_value = PyTango.numpy_spectrum(PyTango.DevDouble, 1000)
+    $ python PowerSupplyDS.py test
+    Ready to accept request
 
-        # Creating an spectrum with a range
-        # Note that I do NOT use PyTango.DevLong here, BUT PyTango.NumpyType.DevLong
-        # numpy functions do not understand normal python types, so there's a
-        # translation available in PyTango.NumpyType
-        spectrum_value = numpy.arange(5, 1000, 2, PyTango.NumpyType.DevLong)
+Now you can access it from a python console::
 
-        # Creating a 2x2 long image from an existing one
-        image_value = PyTango.numpy_image(PyTango.DevLong, [[1,2],[3,4]])
+    >>> import PyTango
 
-Registering devices
-~~~~~~~~~~~~~~~~~~~
+    >>> power_supply = PyTango.DeviceProxy("test/power/supply/1")
+    >>> power_supply.state()
+    STANDBY
 
-Defining devices in the Tango DataBase:
-
-    .. sourcecode:: itango
-    
-        ITango [1]: # The 3 devices name we want to create
-        ITango [2]: # Note: these 3 devices will be served by the same DServer
-        ITango [3]: new_device_name1="px1/tdl/mouse1"
-        ITango [4]: new_device_name2="px1/tdl/mouse2"
-        ITango [5]: new_device_name3="px1/tdl/mouse3"
-
-        ITango [6]: # Define the Tango Class served by this DServer
-        ITango [7]: new_device_info_mouse = PyTango.DbDevInfo()
-        ITango [8]: new_device_info_mouse._class = "Mouse"
-        ITango [9]: new_device_info_mouse.server = "ds_Mouse/server_mouse"
-
-        ITango [10]: # add the first device
-        ITango [11]: new_device_info_mouse.name = new_device_name1
-        ITango [12]: db.add_device(new_device_info_mouse)
-
-        ITango [13]: # add the next device
-        ITango [14]: new_device_info_mouse.name = new_device_name2
-        ITango [15]: db.add_device(new_device_info_mouse)
-
-        ITango [16]: # add the third device
-        ITango [17]: new_device_info_mouse.name = new_device_name3
-        ITango [18]: db.add_device(new_device_info_mouse)
-
-Setting up Device properties
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A more complex example using python subtilities.
-The following python script example (containing some functions and instructions
-manipulating a Galil motor axis device server) gives an idea of how the Tango
-API should be accessed from Python.
-
-    .. sourcecode:: itango
+    >>> power_supply.current = 2.3
     
-        ITango [1]: # connecting to the motor axis device
-        ITango [2]: axis1 = DeviceProxy ("microxas/motorisation/galilbox")
-
-        ITango [3]: # Getting Device Properties
-        ITango [4]: property_names = ["AxisBoxAttachement",
-              ....:                   "AxisEncoderType",
-              ....:                   "AxisNumber",
-              ....:                   "CurrentAcceleration",
-              ....:                   "CurrentAccuracy",
-              ....:                   "CurrentBacklash",
-              ....:                   "CurrentDeceleration",
-              ....:                   "CurrentDirection",
-              ....:                   "CurrentMotionAccuracy",
-              ....:                   "CurrentOvershoot",
-              ....:                   "CurrentRetry",
-              ....:                   "CurrentScale",
-              ....:                   "CurrentSpeed",
-              ....:                   "CurrentVelocity",
-              ....:                   "EncoderMotorRatio",
-              ....:                   "logging_level",
-              ....:                   "logging_target",
-              ....:                   "UserEncoderRatio",
-              ....:                   "UserOffset"]
-       
-        ITango [5]: axis_properties = axis1.get_property(property_names)
-        ITango [6]: for prop in axis_properties.keys():
-              ....:     print "%s: %s" % (prop, axis_properties[prop][0])
-
-        ITango [7]: # Changing Properties
-        ITango [8]: axis_properties["AxisBoxAttachement"] = ["microxas/motorisation/galilbox"]
-        ITango [9]: axis_properties["AxisEncoderType"] = ["1"]
-        ITango [10]: axis_properties["AxisNumber"] = ["6"]
-        ITango [11]: axis1.put_property(axis_properties)
-
-        ITango [12]: # Reading attributes
-        ITango [13]: att_list = axis.get_attribute_list()
-        ITango [14]: for att in att_list:
-               ....:     att_val = axis.read_attribute(att)
-               ....:     print "%s: %s" % (att.name, att_val.value)
-
-        ITango [15]: # Changing some attribute values
-        ITango [16]: axis1.write_attribute("AxisBackslash", 0.5)
-        ITango [17]: axis1.write_attribute("AxisDirection", 1.0)
-        ITango [18]: axis1.write_attribute("AxisVelocity", 1000.0)
-        ITango [19]: axis1.write_attribute("AxisOvershoot", 500.0)
-
-        ITango [20]: # Testing some device commands
-        ITango [21]: pos1=axis1.read_attribute("AxisCurrentPosition")
-        ITango [22]: axis1.command_inout("AxisBackward")
-        ITango [23]: while pos1.value > 1000.0:
-               ....:     pos1 = axis1.read_attribute("AxisCurrentPosition")
-               ....:     print "position axis 1 = ", pos1.value
-                            
-        ITango [24]: axis1.command_inout("AxisStop")
-
-Quick tour on the server side
------------------------------
-
-To write a tango device server in python, you must first import the
-:mod:`PyTango` module in your code.
-
-Below is the python code for a Tango device server with two commands and two
-attributes. The commands are:
-
-1. IOLOng which receives a Tango Long and return it multiply by 2. This command
-   is allowed only if the device is in the ON state.
-
-2. IOStringArray which receives an array of Tango strings and which returns it
-   but in the reverse order. This command is only allowed if the device is in
-   the ON state.
-
-The attributes are:
-
-1. Long_attr wich is a Tango long attribute, Scalar and Read only with a
-   minimum alarm set to 1000 and a maximum alarm set to 1500
-
-2. Short_attr_rw which is a Tango short attribute, Scalar and Read/Write
-
-The following code is the complete device server code::
-
-    import PyTango
-
-    class PyDsExp(PyTango.Device_4Impl):
-
-        def __init__(self,cl,name):
-            PyTango.Device_4Impl.__init__(self,cl,name)
-            self.debug_stream('In PyDsExp __init__')
-            PyDsExp.init_device(self)
-
-        def init_device(self):
-            self.debug_stream('In Python init_device method')
-            self.set_state(PyTango.DevState.ON)
-            self.attr_short_rw = 66
-            self.attr_long = 1246
-
-        def delete_device(self):
-            self.debug_stream('[delete_device] for device %s ' % self.get_name())
-
-        #------------------------------------------------------------------
-        # COMMANDS
-        #------------------------------------------------------------------
-
-        def is_IOLong_allowed(self):
-            return self.get_state() == PyTango.DevState.ON
-
-        def IOLong(self, in_data):
-            self.debug_stream('[IOLong::execute] received number %s' % str(in_data))
-            in_data = in_data * 2;
-            self.debug_stream('[IOLong::execute] return number %s' % str(in_data))
-            return in_data;
-
-        def is_IOStringArray_allowed(self):
-            return self.get_state() == PyTango.DevState.ON
-
-        def IOStringArray(self, in_data):
-            l = range(len(in_data)-1, -1, -1);
-            out_index=0
-            out_data=[]
-            for i in l:
-                self.debug_stream('[IOStringArray::execute] received String' % in_data[out_index])
-                out_data.append(in_data[i])
-                self.debug_stream('[IOStringArray::execute] return String %s' %out_data[out_index])
-                out_index += 1
-            self.y = out_data
-            return out_data
-
-        #------------------------------------------------------------------
-        # ATTRIBUTES
-        #------------------------------------------------------------------
-
-        def read_attr_hardware(self, data):
-            self.debug_stream('In read_attr_hardware')
+    >>> power_supply.current
+    2.3
 
-        def read_Long_attr(self, the_att):
-            self.debug_stream('[PyDsExp::read_attr] attribute name Long_attr')
-            the_att.set_value(self.attr_long)
-
-        def read_Short_attr_rw(self, the_att):
-            self.debug_stream('[PyDsExp::read_attr] attribute name Short_attr_rw')
-            the_att.set_value(self.attr_short_rw)
-
-        def write_Short_attr_rw(self, the_att):
-            self.debug_stream('In write_Short_attr_rw for attribute %s' % the_att.get_name())
-            data = the_att.get_write_value()
-            self.attr_short_rw = data[0]
-
-    
-    class PyDsExpClass(PyTango.DeviceClass):
-
-        def __init__(self, name):
-            PyTango.DeviceClass.__init__(self, name)
-            self.set_type("PyDsExp")
-
-        cmd_list = { 'IOLong' : [ [ PyTango.ArgType.DevLong, "Number" ],
-                                  [ PyTango.ArgType.DevLong, "Number * 2" ] ],
-                     'IOStringArray' : [ [ PyTango.ArgType.DevVarStringArray, "Array of string" ],
-                                         [ PyTango.ArgType.DevVarStringArray, "This reversed array"] ],
-        }
-
-        attr_list = { 'Long_attr' : [ [ PyTango.ArgType.DevLong ,
-                                        PyTango.AttrDataFormat.SCALAR ,
-                                        PyTango.AttrWriteType.READ],
-                                      { 'min alarm' : 1000, 'max alarm' : 1500 } ],
-
-                     'Short_attr_rw' : [ [ PyTango.ArgType.DevShort,
-                                           PyTango.AttrDataFormat.SCALAR,
-                                           PyTango.AttrWriteType.READ_WRITE ] ]
-        }
+    >>> power_supply.PowerOn()
+    >>> power_supply.Ramp(2.1)
+    True
     
-    
-    def main():
-        PyTango.server_run({"PyDsExp" : (PyDsExpClass, PyDsExp)})
-
-    if __name__ == '__main__':
-        main()
-
-.. toctree::
-    :hidden:
+    >>> power_supply.state()
+    ON
 
-    Quick tour (original) <quicktour_old>
+Next steps: Check out the :ref:`pytango-api`.
     
diff --git a/doc/quicktour_old.rst b/doc/quicktour_old.rst
deleted file mode 100644
index 5efcceb..0000000
--- a/doc/quicktour_old.rst
+++ /dev/null
@@ -1,439 +0,0 @@
-.. _quick-tour-old:
-
-A quick tour (original)
------------------------
-
-This quick tour will guide you through the first steps on using PyTango.
-This is the original quick tour guide that uses a simple Python console.
-There is a new version of this document which uses :ref:`itango` console in its
-examples. You can find this new version :ref:`here <quick-tour>`.
-
-Check PyTango version
-~~~~~~~~~~~~~~~~~~~~~
-
-Start a python console and type:
-
-    >>> import PyTango
-    >>> PyTango.__version__
-    '7.1.2'
-    >>> PyTango.__version_long__
-    '7.1.2dev0'
-    >>> PyTango.__version_number__
-    712
-    >>> PyTango.__version_description__
-    'This version implements the C++ Tango 7.1 API.'
-
-or alternatively:
-
-    >>> import PyTango
-    >>> PyTango.Release.version
-    '7.1.2'
-    >>> PyTango.Release.version_long
-    '7.1.2dev0'
-    >>> PyTango.Release.version_number
-    712
-    >>> PyTango.Release.version_description
-    'This version implements the C++ Tango 7.1 API.'
-
-Check Tango C++ version
-~~~~~~~~~~~~~~~~~~~~~~~
-
-From a client (This is only possible since PyTango 7.0.0)
-
-    >>> import PyTango.constants
-    >>> PyTango.constants.TgLibVers
-    '7.1.1'
-    
-From a server you can alternatively do::
-    
-    u = PyTango.Util.instance()
-    tg_cpp_lib_ver = u.get_tango_lib_release()
-    
-
-Test the connection to the Device and get it's current state
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-One of the most basic examples is to get a reference to a device and
-determine if it is running or not::
-
-    from PyTango import *
-    import sys, os, time
-
-    # Protect the script from Exceptions
-    try:
-            # Get proxy on the tangotest1 device
-            print "Getting DeviceProxy "
-            tangotest = DeviceProxy("tango/tangotest/1")
-
-            # ping it
-            print tangotest.ping()
-            
-            # get the state
-            print tangotest.state()
-            
-            # First use the classical command_inout way to execute the DevString command
-            # (DevString in this case is a command of the TangoTest device)
-
-            result= tangotest.command_inout("DevString", "First hello to device")
-            print "Result of execution of DevString command=", result
-
-            # the same with a Device specific command
-            result= tangotest.DevString("Second Hello to device")
-            print "Result of execution of DevString command=", result
-
-            # Please note that argin argument type is automagically managed by python
-            result= tangotest.DevULong(12456)
-            print "Result of execution of DevULong command=", result
-
-    # Catch Tango and Systems  Exceptions
-    except:
-            print "Failed with exception !"
-            print sys.exc_info()[0]
-
-Execute commands with scalar arguments on a Device
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As you can see in the following example, when scalar types are used, the Tango
-binding automagically manages the data types, and writing scripts is quite easy::
-
-    from PyTango import *
-    import sys, os, time
-
-    tangotest = DeviceProxy("tango/tangotest/1")
-
-    # First use the classical command_inout way to execute the DevString command
-    # (DevString in this case is a command of the TangoTest device)
-
-    result= tangotest.command_inout("DevString", "First hello to device")
-    print "Result of execution of DevString command=", result
-
-    # the same with a Device specific command
-    result= tangotest.DevString("Second Hello to device")
-    print "Result of execution of DevString command=", result
-
-    # Please note that argin argument type is automagically managed by python
-    result= tangotest.DevULong(12456)
-    print "Result of execution of DevULong command=", result
-
-Execute commands with more complex types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In this case you have to use put your arguments data in the correct python
-structures::
-
-    from PyTango import *
-    import sys, os, time
-
-    print "Getting DeviceProxy "
-    tango_test = DeviceProxy("tango/tangotest/1")
-    # The input argument is a DevVarLongStringArray
-    # so create the argin variable containing
-    # an array of longs and an array of strings
-    argin = ([1,2,3], ["Hello", "TangoTest device"])
-
-    result= tango_test.DevVarLongArray(argin)
-    print "Result of execution of DevVarLongArray command=", result
-
-Reading and writing attributes
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Basic read/write attribute operations::
-
-    #Read a scalar attribute
-    scalar=tangotest.read_attribute("long_scalar")
-
-    #Read a spectrum attribute
-    spectrum=tangotest.read_attribute("double_spectrum")
-
-    # Write a scalar attribute
-    scalar_value = 18
-    tangotest.write_attribute("long_scalar", scalar_value)
-
-    # Write a spectrum attribute
-    spectrum_value = [1.2, 3.2, 12.3]
-    tangotest.write_attribute("double_spectrum", spectrum_value)
-
-    # Write an image attribute
-    image_value = [ [1, 2], [3, 4] ]
-    tangotest.write_attribute("long_image", image_value)
-
-
-Note that if PyTango is compiled with numpy support the values got when reading
-a spectrum or an image will be numpy arrays. This results in a faster and
-more memory efficient PyTango. You can also use numpy to specify the values when
-writing attributes, especially if you know the exact attribute type.::
-
-    import PyTango, numpy
-
-    # Creating an unitialized double spectrum of 1000 elements
-    spectrum_value = PyTango.numpy_spectrum(PyTango.DevDouble, 1000)
-
-    # Creating an spectrum with a range
-    # Note that I do NOT use PyTango.DevLong here, BUT PyTango.NumpyType.DevLong
-    # numpy functions do not understand normal python types, so there's a
-    # translation available in PyTango.NumpyType
-    spectrum_value = numpy.arange(5, 1000, 2, PyTango.NumpyType.DevLong)
-
-    # Creating a 2x2 long image from an existing one
-    image_value = PyTango.numpy_image(PyTango.DevLong, [[1,2],[3,4]])
-
-
-Registering devices
-~~~~~~~~~~~~~~~~~~~
-
-Defining devices in the Tango DataBase::
-
-    from PyTango import *
-    import sys, os, time
-
-    #  A reference on the DataBase
-    db = Database()
-
-    # The 3 devices name we want to create
-    # Note: these 3 devices will be served by the same DServer
-    new_device_name1="px1/tdl/mouse1"
-    new_device_name2="px1/tdl/mouse2"
-    new_device_name3="px1/tdl/mouse3"
-
-    # Define the Tango Class served by this  DServer
-    new_device_info_mouse = DbDevInfo()
-    new_device_info_mouse._class = "Mouse"
-    new_device_info_mouse.server = "ds_Mouse/server_mouse"
-
-    # add the first device
-    print "Creation Device:" , new_device_name1
-    new_device_info_mouse.name = new_device_name1
-    db.add_device(new_device_info_mouse)
-
-    # add the next device
-    print "Creation Device:" , new_device_name2
-    new_device_info_mouse.name = new_device_name2
-    db.add_device(new_device_info_mouse)
-    # add the third device
-    print "Creation Device:" , new_device_name3
-    new_device_info_mouse.name = new_device_name3
-    db.add_device(new_device_info_mouse)
-
-
-Setting up Device properties
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A more complex example using python subtilities.
-The following python script example (containing some functions and instructions
-manipulating a Galil motor axis device server) gives an idea of how the Tango
-API should be accessed from Python::
-
-    from PyTango import *
-    import sys, os, time
-
-    # connecting to the motor axis device
-    axis1 = DeviceProxy ("microxas/motorisation/galilbox")
-
-    # Getting Device Properties
-    property_names = ["AxisBoxAttachement",
-                      "AxisEncoderType",
-                      "AxisNumber",
-                      "CurrentAcceleration",
-                      "CurrentAccuracy",
-                      "CurrentBacklash",
-                      "CurrentDeceleration",
-                      "CurrentDirection",
-                      "CurrentMotionAccuracy",
-                      "CurrentOvershoot",
-                      "CurrentRetry",
-                      "CurrentScale",
-                      "CurrentSpeed",
-                      "CurrentVelocity",
-                      "EncoderMotorRatio",
-                      "logging_level",
-                      "logging_target",
-                      "UserEncoderRatio",
-                      "UserOffset"]
-    axis_properties = axis1.get_property(property_names)
-    for prop in axis_properties.keys():
-        print "%s: %s" % (prop, axis_properties[prop][0])
-
-    # Changing Properties
-    axis_properties["AxisBoxAttachement"] = ["microxas/motorisation/galilbox"]
-    axis_properties["AxisEncoderType"] = ["1"]
-    axis_properties["AxisNumber"] = ["6"]
-    axis1.put_property(axis_properties)
-
-    # Reading attributes
-    att_list = axis.get_attribute_list()
-    for att in att_list:
-        att_val = axis.read_attribute(att)
-        print "%s: %s" % (att, att_val.value)
-
-    # Changing some attribute values
-    axis1.write_attribute("AxisBackslash", 0.5)
-    axis1.write_attribute("AxisDirection", 1.0)
-    axis1.write_attribute("AxisVelocity", 1000.0)
-    axis1.write_attribute("AxisOvershoot", 500.0)
-
-    # Testing some device commands
-    pos1=axis1.read_attribute("AxisCurrentPosition")
-    axis1.command_inout("AxisBackward")
-    while pos1.value > 1000.0:
-        pos1=axis1.read_attribute("AxisCurrentPosition")
-        print "position axis 1 = ",pos1.value
-    axis1.command_inout("AxisStop")
-
-A quick tour of Tango device server binding through an example
---------------------------------------------------------------
-
-To write a tango device server in python, you need to import two modules in your script which are:
-
-1. The PyTango module
-
-2. The python sys module provided in the classical python distribution
-
-The following in the python script for a Tango device server with two commands and two attributes. The commands are:
-
-1. IOLOng which receives a Tango Long and return it multiply by 2. This command is allowed only if the device is in the ON state.
-
-2. IOStringArray which receives an array of Tango strings and which returns it but in the reverse order. This command is only allowed if the device is in the ON state.
-
-The attributes are:
-
-1. Long_attr wich is a Tango long attribute, Scalar and Read only with a minimum alarm set to 1000 and a maximum alarm set to 1500
-
-2. Short_attr_rw which is a Tango short attribute, Scalar and Read/Write
-
-The following code is the complete device server code::
-
-    import PyTango
-    import sys
-
-    class PyDsExp(PyTango.Device_3Impl):
-
-        def __init__(self,cl,name):
-            PyTango.Device_3Impl.__init__(self,cl,name)
-            self.debug_stream('In PyDsExp __init__')
-            PyDsExp.init_device(self)
-
-        def init_device(self):
-            self.debug_stream('In Python init_device method')
-            self.set_state(PyTango.DevState.ON)
-            self.attr_short_rw = 66
-            self.attr_long = 1246
-
-    #------------------------------------------------------------------
-
-        def delete_device(self):
-            self.debug_stream('[delete_device] for device %s ' % self.get_name())
-
-    #------------------------------------------------------------------
-    # COMMANDS
-    #------------------------------------------------------------------
-
-        def is_IOLong_allowed(self):
-            return self.get_state() == PyTango.DevState.ON
-
-        def IOLong(self, in_data):
-            self.debug_stream('[IOLong::execute] received number %s' % str(in_data))
-            in_data = in_data * 2;
-            self.debug_stream('[IOLong::execute] return number %s' % str(in_data))
-            return in_data;
-
-    #------------------------------------------------------------------
-
-        def is_IOStringArray_allowed(self):
-            return self.get_state() == PyTango.DevState.ON
-
-        def IOStringArray(self, in_data):
-            l = range(len(in_data)-1, -1, -1);
-            out_index=0
-            out_data=[]
-            for i in l:
-                self.debug_stream('[IOStringArray::execute] received String' % in_data[out_index])
-                out_data.append(in_data[i])
-                self.debug_stream('[IOStringArray::execute] return String %s' %out_data[out_index])
-                out_index += 1
-            self.y = out_data
-            return out_data
-
-    #------------------------------------------------------------------
-    # ATTRIBUTES
-    #------------------------------------------------------------------
-
-        def read_attr_hardware(self, data):
-            self.debug_stream('In read_attr_hardware')
-
-    #------------------------------------------------------------------
-
-        def read_Long_attr(self, the_att):
-            self.debug_stream('[PyDsExp::read_attr] attribute name Long_attr')
-
-            # Before PyTango 7.0.0
-            #PyTango.set_attribute_value(the_att, self.attr_long)
-
-            # Now:
-            the_att.set_value(self.attr_long)
-
-    #------------------------------------------------------------------
-
-        def read_Short_attr_rw(self, the_att):
-            self.debug_stream('[PyDsExp::read_attr] attribute name Short_attr_rw')
-
-            # Before PyTango 7.0.0
-            #PyTango.set_attribute_value(the_att, self.attr_short_rw)
-            
-            # Now:
-            the_att.set_value(self.attr_short_rw)
-
-    #------------------------------------------------------------------
-
-        def write_Short_attr_rw(self, the_att):
-            self.debug_stream('In write_Short_attr_rw for attribute %s' % the_att.get_name())
-
-            # Before PyTango 7.0.0
-            #data = []
-            #PyTango.get_write_value(the_att, data)
-
-            # Now:
-            data = the_att.get_write_value()
-            self.attr_short_rw = data[0]
-
-    #------------------------------------------------------------------
-    # CLASS
-    #------------------------------------------------------------------
-
-    class PyDsExpClass(PyTango.DeviceClass):
-
-        def __init__(self, name):
-            PyTango.DeviceClass.__init__(self, name)
-            self.set_type("TestDevice")
-            print 'In PyDsExpClass __init__'
-
-        cmd_list = { 'IOLong' : [ [ PyTango.ArgType.DevLong, "Number" ],
-                                  [ PyTango.ArgType.DevLong, "Number * 2" ] ],
-                     'IOStringArray' : [ [ PyTango.ArgType.DevVarStringArray, "Array of string" ],
-                                         [ PyTango.ArgType.DevVarStringArray, "This reversed array"] ],
-        }
-
-        attr_list = { 'Long_attr' : [ [ PyTango.ArgType.DevLong ,
-                                        PyTango.AttrDataFormat.SCALAR ,
-                                        PyTango.AttrWriteType.READ],
-                                      { 'min alarm' : 1000, 'max alarm' : 1500 } ],
-
-                     'Short_attr_rw' : [ [ PyTango.ArgType.DevShort,
-                                           PyTango.AttrDataFormat.SCALAR,
-                                           PyTango.AttrWriteType.READ_WRITE ] ]
-        }
-
-    if __name__ == '__main__':
-        try:
-            util = PyTango.Util(sys.argv)
-            
-            # 
-            # Deprecated: util.add_TgClass(PyDsExpClass, PyDsExp, 'PyDsExp')
-            util.add_class(PyDsExpClass, PyDsExp, 'PyDsExp')
-            
-            U = PyTango.Util.instance()
-            U.server_init()
-            U.server_run()
-        except PyTango.DevFailed,e:
-            print '-------> Received a DevFailed exception:',e
-        except Exception,e:
-            print '-------> An unforeseen exception occured....',e
diff --git a/doc/revision.rst b/doc/revision.rst
index 0a2d7ae..6a81cca 100644
--- a/doc/revision.rst
+++ b/doc/revision.rst
@@ -1,15 +1,17 @@
-.. _revision:
+.. _pytango-history-changes:
 
-Revision
---------
+==================
+History of changes
+==================
 
 :Contributers: T\. Coutinho
 
 :Last Update: |today|
 
-.. _history-modifications:
+.. _pytango-revisions:
 
-History of modifications:
+Document revisions
+-------------------
 
 +----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
 | Date     | Revision                                                                         | Description                                         | Author                |
@@ -73,241 +75,294 @@ History of modifications:
 +----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
 | 28/08/13 | `8.13 <http://www.tango-controls.org/static/PyTango/v723/doc/html/index.html>`_  | Update to PyTango 7.2.4                             | T\. Coutinho          |
 +----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
-| 27/11/13 | `8.18 <http://www.tango-controls.org/static/PyTango/v810/doc/html/index.html>`_  | Update to PyTango 8.1.1                             | T\. Coutinho          |
+| 27/11/13 | `8.18 <http://www.tango-controls.org/static/PyTango/v811/doc/html/index.html>`_  | Update to PyTango 8.1.1                             | T\. Coutinho          |
++----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
+| 16/05/14 | `8.19 <http://www.tango-controls.org/static/PyTango/v812/doc/html/index.html>`_  | Update to PyTango 8.1.2                             | T\. Coutinho          |
++----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
+| 10/09/14 | `8.20 <http://www.tango-controls.org/static/PyTango/v813/doc/html/index.html>`_  | Update to PyTango 8.1.4                             | T\. Coutinho          |
 +----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
 
-.. _version-history:
+.. _pytango-version-history:
 
 Version history
 ---------------
 
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| version  | Changes                                                                                                                                                           |
-+==========+===================================================================================================================================================================+
-| 8.1.1    | Features:                                                                                                                                                         |
-|          | - Implemented tango C++ 8.1 API                                                                                                                                   |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - `527: set_value() for ULong64 <https://sourceforge.net/p/tango-cs/bugs/527/>`_                                                                                  |
-|          | - `573: [pytango] python3 error with unregistered device <https://sourceforge.net/p/tango-cs/bugs/573/>`_                                                         |
-|          | - `611: URGENT fail to write attribute with PyTango 8.0.3 <https://sourceforge.net/p/tango-cs/bugs/611/>`_                                                        |
-|          | - `612: [pytango][8.0.3] failed to build from source on s390 <https://sourceforge.net/p/tango-cs/bugs/612/>`_                                                     |
-|          | - `615: Threading problem when setting a DevULong64 attribute <https://sourceforge.net/p/tango-cs/bugs/615/>`_                                                    |
-|          | - `622: PyTango broken when running on Ubuntu 13 <https://sourceforge.net/p/tango-cs/bugs/622/>`_                                                                 |
-|          | - `626: attribute_history extraction can raised an exception <https://sourceforge.net/p/tango-cs/bugs/626/>`_                                                     |
-|          | - `628: Problem in installing PyTango 8.0.3 on Scientific Linux 6 <https://sourceforge.net/p/tango-cs/bugs/628/>`_                                                |
-|          | - `635: Reading of ULong64 attributes does not work <https://sourceforge.net/p/tango-cs/bugs/635/>`_                                                              |
-|          | - `636: PyTango log messages are not filtered by level <https://sourceforge.net/p/tango-cs/bugs/636/>`_                                                           |
-|          | - `637: [pytango] segfault doing write_attribute on Group <https://sourceforge.net/p/tango-cs/bugs/637/>`_                                                        |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 8.1.0    | *SKIPPED*                                                                                                                                                         |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 8.0.3    | Features:                                                                                                                                                         |
-|          | - `88: Implement Util::server_set_event_loop method in python <https://sourceforge.net/p/tango-cs/feature-requests/88>`_                                          |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - `3576353: [pytango] segfault on 'RestartServer' <https://sourceforge.net/tracker/?func=detail&aid=3576353&group_id=57612&atid=484769>`_                         |
-|          | - `3579062: [pytango] Attribute missing methods <https://sourceforge.net/tracker/?func=detail&aid=3579062&group_id=57612&atid=484769>`_                           |
-|          | - `3586337: [pytango] Some DeviceClass methods are not python safe <https://sourceforge.net/tracker/?func=detail&aid=3586337&group_id=57612&atid=484769>`_        |
-|          | - `3598514: DeviceProxy.__setattr__ break python's descriptors <https://sourceforge.net/tracker/?func=detail&aid=3598514&group_id=57612&atid=484769>`_            |
-|          | - `3607779: [pytango] IPython 0.10 error <https://sourceforge.net/tracker/?func=detail&aid=3607779&group_id=57612&atid=484769>`_                                  |
-|          | - `598: Import DLL by PyTango failed on windows <https://sourceforge.net/p/tango-cs/bugs/598/>`_                                                                  |
-|          | - `605: [pytango] use distutils.version module <https://sourceforge.net/p/tango-cs/bugs/605/>`_                                                                   |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 8.0.2    | Bug fixes:                                                                                                                                                        |
-|          | - `3570970: [pytango] problem during the python3 building <https://sourceforge.net/tracker/?func=detail&aid=3570970&group_id=57612&atid=484769>`_                 |
-|          | - `3570971: [pytango] itango does not work without qtconsole <https://sourceforge.net/tracker/?func=detail&aid=3570971&group_id=57612&atid=484769>`_              |
-|          | - `3570972: [pytango] warning/error when building 8.0.0 <https://sourceforge.net/tracker/?func=detail&aid=3570972&group_id=57612&atid=484769>`_                   |
-|          | - `3570975: [pytango] problem during use of python3 version <https://sourceforge.net/tracker/?func=detail&aid=3570975&group_id=57612&atid=484769>`_               |
-|          | - `3574099: [pytango] compile error with gcc < 4.5 <https://sourceforge.net/tracker/?func=detail&aid=3574099&group_id=57612&atid=484769>`_                        |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 8.0.1    | *SKIPPED*                                                                                                                                                         |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 8.0.0    | Features:                                                                                                                                                         |
-|          | - Implemented tango C++ 8.0 API                                                                                                                                   |
-|          | - Python 3k compatible                                                                                                                                            |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - `3023857: DevEncoded write attribute not supported <https://sourceforge.net/tracker/?func=detail&aid=3023857&group_id=57612&atid=484769>`_                      |
-|          | - `3521545: [pytango] problem with tango profile <https://sourceforge.net/tracker/?func=detail&aid=3521545&group_id=57612&atid=484769>`_                          |
-|          | - `3530535: PyTango group writting fails <https://sourceforge.net/tracker/?func=detail&aid=3530535&group_id=57612&atid=484769>`_                                  |
-|          | - `3564959: EncodedAttribute.encode_xxx() methods don't accept bytearray  <https://sourceforge.net/tracker/?func=detail&aid=3564959&group_id=57612&atid=484769>`_ |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.2.4    | Bug fixes:                                                                                                                                                        |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `551: [pytango] Some DeviceClass methods are not python safe <https://sourceforge.net/p/tango-cs/bugs/551/>`_                                                   |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.2.3    | Features:                                                                                                                                                         |
-|          | - `3495607: DeviceClass.device_name_factory is missing <https://sourceforge.net/tracker/?func=detail&aid=3495607&group_id=57612&atid=484772>`_                    |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - `3103588: documentation of PyTango.Attribute.Group <https://sourceforge.net/tracker/?func=detail&aid=3103588&group_id=57612&atid=484769>`_                      |
-|          | - `3458336: Problem with pytango 7.2.2 <https://sourceforge.net/tracker/?func=detail&aid=3458336&group_id=57612&atid=484769>`_                                    |
-|          | - `3463377: PyTango memory leak in read encoded attribute <https://sourceforge.net/tracker/?func=detail&aid=3463377&group_id=57612&atid=484769>`_                 |
-|          | - `3487930: [pytango] wrong python dependency <https://sourceforge.net/tracker/?func=detail&aid=3487930&group_id=57612&atid=484769>`_                             |
-|          | - `3511509: Attribute.set_value_date_quality for encoded does not work <https://sourceforge.net/tracker/?func=detail&aid=3511509&group_id=57612&atid=484769>`_    |
-|          | - `3514457: [pytango]  TANGO_HOST multi-host support <https://sourceforge.net/tracker/?func=detail&aid=3514457&group_id=57612&atid=484769>`_                      |
-|          | - `3520739: command_history(...) in  PyTango <https://sourceforge.net/tracker/?func=detail&aid=3520739&group_id=57612&atid=484769>`_                              |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.2.2    | Features:                                                                                                                                                         |
-|          | - `3305251: DS dynamic attributes discards some Attr properties <https://sourceforge.net/tracker/?func=detail&aid=3305251&group_id=57612&atid=484769>`_           |
-|          | - `3365792: DeviceProxy.<cmd_name> could be documented <https://sourceforge.net/tracker/?func=detail&aid=3365792&group_id=57612&atid=484772>`_                    |
-|          | - `3386079: add support for ipython 0.11 <https://sourceforge.net/tracker/?func=detail&aid=3386079&group_id=57612&atid=484772>`_                                  |
-|          | - `3437654: throw python exception as tango exception <https://sourceforge.net/tracker/?func=detail&aid=3437654&group_id=57612&atid=484772>`_                     |
-|          | - `3447477: spock profile installation <https://sourceforge.net/tracker/?func=detail&aid=3447477&group_id=57612&atid=484772>`_                                    |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - `3372371: write attribute of DevEncoded doesn't work <https://sourceforge.net/tracker/?func=detail&aid=3372371&group_id=57612&atid=484769>`_                    |
-|          | - `3374026: [pytango] pyflakes warning <https://sourceforge.net/tracker/?func=detail&aid=3374026&group_id=57612&atid=484769>`_                                    |
-|          | - `3404771: PyTango.MultiAttribute.get_attribute_list missing <https://sourceforge.net/tracker/?func=detail&aid=3404771&group_id=57612&atid=484769>`_             |
-|          | - `3405580: PyTango.MultiClassAttribute missing <https://sourceforge.net/tracker/?func=detail&aid=3405580&group_id=57612&atid=484769>`_                           |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.2.1    | *SKIPPED*                                                                                                                                                         |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.2.0    | Features:                                                                                                                                                         |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `3286678: Add missing EncodedAttribute JPEG methods <https://sourceforge.net/tracker/?func=detail&aid=3286678&group_id=57612&atid=484772>`_                     |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.6    | Bug fixes:                                                                                                                                                        |
-|          | - 7.1.5 distribution is missing some files                                                                                                                        |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.5    | Bug fixes:                                                                                                                                                        |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `3284174: 7.1.4 does not build with gcc 4.5 and tango 7.2.6 <https://sourceforge.net/tracker/?func=detail&aid=3284174&group_id=57612&atid=484769>`_             |
-|          | - `3284265: [pytango][7.1.4] a few files without licence and copyright <https://sourceforge.net/tracker/?func=detail&aid=3284265&group_id=57612&atid=484769>`_    |
-|          | - `3284318: copyleft vs copyright <https://sourceforge.net/tracker/?func=detail&aid=3284318&group_id=57612&atid=484769>`_                                         |
-|          | - `3284434: [pytango][doc] few ERROR during the doc generation <https://sourceforge.net/tracker/?func=detail&aid=3284434&group_id=57612&atid=484769>`_            |
-|          | - `3284435: [pytango][doc] few warning during the doc generation <https://sourceforge.net/tracker/?func=detail&aid=3284435&group_id=57612&atid=484769>`_          |
-|          | - `3284440: [pytango][spock] the profile can't be installed <https://sourceforge.net/tracker/?func=detail&aid=3284440&group_id=57612&atid=484769>`_               |
-|          | - `3285185: PyTango Device Server does not load Class Properties values <https://sourceforge.net/tracker/?func=detail&aid=3285185&group_id=57612&atid=484769>`_   |
-|          | - `3286055: PyTango 7.1.x DS using Tango C++ 7.2.x seg faults on exit <https://sourceforge.net/tracker/?func=detail&aid=3286055&group_id=57612&atid=484769>`_     |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.4    | Features:                                                                                                                                                         |
-|          | - `3274309: Generic Callback for events <https://sourceforge.net/tracker/?func=detail&aid=3274309&group_id=57612&atid=484772>`_                                   |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - `3011775: Seg Faults due to removed dynamic attributes <https://sourceforge.net/tracker/?func=detail&aid=3011775&group_id=57612&atid=484769>`_                  |
-|          | - `3105169: PyTango 7.1.3 does not compile with Tango 7.2.X <https://sourceforge.net/tracker/?func=detail&aid=3105169&group_id=57612&atid=484769>`_               |
-|          | - `3107243: spock profile does not work with python 2.5 <https://sourceforge.net/tracker/?func=detail&aid=3107243&group_id=57612&atid=484769>`_                   |
-|          | - `3124427: PyTango.WAttribute.set_max_value() changes min value <https://sourceforge.net/tracker/?func=detail&aid=3124427&group_id=57612&atid=484769>`_          |
-|          | - `3170399: Missing documentation about is_<attr>_allowed method <https://sourceforge.net/tracker/?func=detail&aid=3170399&group_id=57612&atid=484769>`_          |
-|          | - `3189082: Missing get_properties() for Attribute class <https://sourceforge.net/tracker/?func=detail&aid=3189082&group_id=57612&atid=484769>`_                  |
-|          | - `3196068: delete_device() not called after server_admin.Kill() <https://sourceforge.net/tracker/?func=detail&aid=3196068&group_id=57612&atid=484769>`_          |
-|          | - `3257286: Binding crashes when reading a WRITE string attribute <https://sourceforge.net/tracker/?func=detail&aid=3257286&group_id=57612&atid=484769>`_         |
-|          | - `3267628: DP.read_attribute(, extract=List/tuple) write value is wrong <https://sourceforge.net/tracker/?func=detail&aid=3267628&group_id=57612&atid=484769>`_  |
-|          | - `3274262: Database.is_multi_tango_host missing <https://sourceforge.net/tracker/?func=detail&aid=3274262&group_id=57612&atid=484769>`_                          |
-|          | - `3274319: EncodedAttribute is missing in PyTango (<= 7.1.3) <https://sourceforge.net/tracker/?func=detail&aid=3274319&group_id=57612&atid=484769>`_             |
-|          | - `3277269: read_attribute(DevEncoded) is not numpy as expected <https://sourceforge.net/tracker/?func=detail&aid=3277269&group_id=57612&atid=484769>`_           |
-|          | - `3278946: DeviceAttribute copy constructor is not working <https://sourceforge.net/tracker/?func=detail&aid=3278946&group_id=57612&atid=484769>`_               |
-|          |                                                                                                                                                                   |
-|          | Documentation:                                                                                                                                                    |
-|          | - Added :ref:`utilities` chapter                                                                                                                                  |
-|          | - Added :ref:`encoded` chapter                                                                                                                                    |
-|          | - Improved :ref:`server` chapter                                                                                                                                  |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.3    | Features:                                                                                                                                                         |
-|          | - tango logging with print statement                                                                                                                              |
-|          | - tango logging with decorators                                                                                                                                   |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `3060380: ApiUtil should be exported to PyTango  <https://sourceforge.net/tracker/?func=detail&aid=3060380&group_id=57612&atid=484772>`_                        |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - added licence header to all source code files                                                                                                                   |
-|          | - spock didn't work without TANGO_HOST env. variable (it didn't recognize                                                                                         |
-|          | tangorc)                                                                                                                                                          |
-|          | - spock should give a proper message if it tries to be initialized outside                                                                                        |
-|          | ipython                                                                                                                                                           |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `3048798: licence issue GPL != LGPL <https://sourceforge.net/tracker/?func=detail&aid=3048798&group_id=57612&atid=484769>`_                                     |
-|          | - `3073378: DeviceImpl.signal_handler raising exception crashes DS <https://sourceforge.net/tracker/?func=detail&aid=3073378&group_id=57612&atid=484769>`_        |
-|          | - `3088031: Python DS unable to read DevVarBooleanArray property <https://sourceforge.net/tracker/?func=detail&aid=3088031&group_id=57612&atid=484769>`_          |
-|          | - `3102776: PyTango 7.1.2 does not work with python 2.4 & boost 1.33.0 <https://sourceforge.net/tracker/?func=detail&aid=3102776&group_id=57612&atid=484769>`_    |
-|          | - `3102778: Fix compilation warnings in linux <https://sourceforge.net/tracker/?func=detail&aid=3102778&group_id=57612&atid=484769>`_                             |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.2    | Features:                                                                                                                                                         |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `2995964: Dynamic device creation <https://sourceforge.net/tracker/?func=detail&aid=2995964&group_id=57612&atid=484772>`_                                       |
-|          | - `3010399: The DeviceClass.get_device_list that exists in C++ is missing <https://sourceforge.net/tracker/?func=detail&aid=3010399&group_id=57612&atid=484772>`_ |
-|          | - `3023686: Missing DeviceProxy.<attribute name> <https://sourceforge.net/tracker/?func=detail&aid=3023686&group_id=57612&atid=484772>`_                          |
-|          | - `3025396: DeviceImpl is missing some CORBA methods <https://sourceforge.net/tracker/?func=detail&aid=3025396&group_id=57612&atid=484772>`_                      |
-|          | - `3032005: IPython extension for PyTango <https://sourceforge.net/tracker/?func=detail&aid=3032005&group_id=57612&atid=484772>`_                                 |
-|          | - `3033476: Make client objects pickable <https://sourceforge.net/tracker/?func=detail&aid=3033476&group_id=57612&atid=484772>`_                                  |
-|          | - `3039902: PyTango.Util.add_class would be useful <https://sourceforge.net/tracker/?func=detail&aid=3039902&group_id=57612&atid=484772>`_                        |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `2975940: DS command with DevVarCharArray return type fails <https://sourceforge.net/tracker/?func=detail&aid=2975940&group_id=57612&atid=484769>`_             |
-|          | - `3000467: DeviceProxy.unlock is LOCKING instead of unlocking! <https://sourceforge.net/tracker/?func=detail&aid=3000467&group_id=57612&atid=484769>`_           |
-|          | - `3010395: Util.get_device_* methods don't work <https://sourceforge.net/tracker/?func=detail&aid=3010395&group_id=57612&atid=484769>`_                          |
-|          | - `3010425: Database.dev_name does not work <https://sourceforge.net/tracker/?func=detail&aid=3010425&group_id=57612&atid=484769>`_                               |
-|          | - `3016949: command_inout_asynch callback does not work <https://sourceforge.net/tracker/?func=detail&aid=3016949&group_id=57612&atid=484769>`_                   |
-|          | - `3020300: PyTango does not compile with gcc 4.1.x <https://sourceforge.net/tracker/?func=detail&aid=3020300&group_id=57612&atid=484769>`_                       |
-|          | - `3030399: Database put(delete)_attribute_alias generates segfault <https://sourceforge.net/tracker/?func=detail&aid=3030399&group_id=57612&atid=484769>`_       |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.1    | Features:                                                                                                                                                         |
-|          | - Improved setup script                                                                                                                                           |
-|          | - Interfaced with PyPI                                                                                                                                            |
-|          | - Cleaned build script warnings due to unclean python C++ macro definitions                                                                                       |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `2985993: PyTango numpy command support <https://sourceforge.net/tracker/?func=detail&aid=2985993&group_id=57612&atid=484772>`_                                 |
-|          | - `2971217: PyTango.GroupAttrReplyList slicing <https://sourceforge.net/tracker/?func=detail&aid=2971217&group_id=57612&atid=484772>`_                            |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `2983299: Database.put_property() deletes the property <https://sourceforge.net/tracker/?func=detail&aid=2983299&group_id=57612&atid=484769>`_                  |
-|          | - `2953689: can not write_attribute scalar/spectrum/image <https://sourceforge.net/tracker/?func=detail&aid=2953689&group_id=57612&atid=484769>`_                 |
-|          | - `2953030: PyTango doc installation <https://sourceforge.net/tracker/?func=detail&aid=2953030&group_id=57612&atid=484769>`_                                      |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.0    | Features:                                                                                                                                                         |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `2908176: read_*, write_* and is_*_allowed() methods can now be defined <https://sourceforge.net/tracker/?func=detail&aid=2908176&group_id=57612&atid=484772>`_ |
-|          | - `2941036: TimeVal conversion to time and datetime <https://sourceforge.net/tracker/?func=detail&aid=2941036&group_id=57612&atid=484772>`_                       |
-|          | - added str representation on Attr, Attribute, DeviceImpl and DeviceClass                                                                                         |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - from sourceforge:                                                                                                                                               |
-|          | - `2903755: get_device_properties() bug reading DevString properties <https://sourceforge.net/tracker/?func=detail&aid=2903755group_id=57612&atid=484769>`_       |
-|          | - `2909927: PyTango.Group.read_attribute() return values <https://sourceforge.net/tracker/?func=detail&aid=2909927&group_id=57612&atid=484769>`_                  |
-|          | - `2914194: DevEncoded does not work <https://sourceforge.net/tracker/?func=detail&aid=2914194&group_id=57612&atid=484769>`_                                      |
-|          | - `2916397: PyTango.DeviceAttribute copy constructor does not work <https://sourceforge.net/tracker/?func=detail&aid=2916397&group_id=57612&atid=484769>`_        |
-|          | - `2936173: PyTango.Group.read_attributes() fails <https://sourceforge.net/tracker/?func=detail&aid=2936173&group_id=57612&atid=484769>`_                         |
-|          | - `2949099: Missing PyTango.Except.print_error_stack <https://sourceforge.net/tracker/?func=detail&aid=2949099&group_id=57612&atid=484769>`_                      |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| 7.1.0rc1 | Features:                                                                                                                                                         |
-|          | - v = image_attribute.get_write_value() returns square sequences (arrays of                                                                                       |
-|          | arrays, or numpy objects) now instead of flat lists. Also for spectrum                                                                                            |
-|          | attributes a numpy is returned by default now instead.                                                                                                            |
-|          | - image_attribute.set_value(v) accepts numpy arrays now or square sequences                                                                                       |
-|          | instead of just flat lists. So, dim_x and dim_y are useless now. Also the                                                                                         |
-|          | numpy path is faster.                                                                                                                                             |
-|          | - new enum AttrSerialModel                                                                                                                                        |
-|          | - Attribute new methods: set(get)_attr_serial_model, set_change_event,                                                                                            |
-|          | set_archive_event, is_change_event, is_check_change_event,                                                                                                        |
-|          | is_archive_criteria, is_check_archive_criteria, remove_configuration                                                                                              |
-|          | - added support for numpy scalars in tango operations like write_attribute                                                                                        |
-|          | (ex: now a DEV_LONG attribute can receive a numpy.int32 argument in a                                                                                             |
-|          | write_attribute method call)                                                                                                                                      |
-|          |                                                                                                                                                                   |
-|          | Bug fixes:                                                                                                                                                        |
-|          | - DeviceImpl.set_value for scalar attributes                                                                                                                      |
-|          | - DeviceImpl.push_***_event                                                                                                                                       |
-|          | - server commands with DevVar***StringArray as parameter or as return type                                                                                        |
-|          | - in windows,a bug in PyTango.Util prevented servers from starting up                                                                                             |
-|          | - DeviceImpl.get_device_properties for string properties assigns only first                                                                                       |
-|          | character of string to object member instead of entire string                                                                                                     |
-|          | - added missing methods to Util                                                                                                                                   |
-|          | - exported SubDevDiag class                                                                                                                                       |
-|          | - error in read/events of attributes of type DevBoolean READ_WRITE                                                                                                |
-|          | - error in automatic unsubscribe events of DeviceProxy when the object                                                                                            |
-|          | disapears (happens only on some compilers with some optimization flags)                                                                                           |
-|          | - fix possible bug when comparing attribute names in DeviceProxy                                                                                                  |
-|          | - pretty print of DevFailed -> fix deprecation warning in python 2.6                                                                                              |
-|          | - device class properties where not properly fetched when there is no                                                                                             |
-|          | property value defined                                                                                                                                            |
-|          | - memory leak when converting DevFailed exceptions from C++ to python                                                                                             |
-|          | - python device server file without extension does not start                                                                                                      |
-|          |                                                                                                                                                                   |
-|          | Documentation:                                                                                                                                                    |
-|          | - Improved FAQ                                                                                                                                                    |
-|          | - Improved compilation chapter                                                                                                                                    |
-|          | - Improved migration information                                                                                                                                  |
-+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| version  | Changes                                                                                                                                                               |
++==========+=======================================================================================================================================================================+
+| 8.1.4    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `107: Nice to check Tango/PyTango version at runtime <https://sourceforge.net/p/tango-cs/feature-requests/107>`_                                                |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `659: segmentation fault when unsubscribing from events <https://sourceforge.net/p/tango-cs/bugs/659/>`_                                                        |
+|          |     - `664: problem while installing PyTango 8.1.1 with pip (using pip 1.4.1) <https://sourceforge.net/p/tango-cs/bugs/664/>`_                                        |
+|          |     - `678: [pytango] 8.1.2 unexpected files in the source package  <https://sourceforge.net/p/tango-cs/bugs/678/>`_                                                  |
+|          |     - `679: PyTango.server tries to import missing __builtin__ module on Python 3 <https://sourceforge.net/p/tango-cs/bugs/679/>`_                                    |
+|          |     - `680: Cannot import PyTango.server.run <https://sourceforge.net/p/tango-cs/bugs/680/>`_                                                                         |
+|          |     - `686: Device property substitution for a multi-device server <https://sourceforge.net/p/tango-cs/bugs/686/>`_                                                   |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.1.3    | *SKIPPED*                                                                                                                                                             |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.1.2    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `98: PyTango.server.server_run needs additional post_init_callback parameter <https://sourceforge.net/p/tango-cs/feature-requests/98>`_                         |
+|          |     - `102: DevEncoded attribute should support a python buffer object <https://sourceforge.net/p/tango-cs/feature-requests/102>`_                                    |
+|          |     - `103: Make creation of *EventData objects possible in PyTango <https://sourceforge.net/p/tango-cs/feature-requests/103>`_                                       |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `641: python3 error handling issue <https://sourceforge.net/p/tango-cs/bugs/641/>`_                                                                             |
+|          |     - `648: PyTango unicode method parameters fail <https://sourceforge.net/p/tango-cs/bugs/648/>`_                                                                   |
+|          |     - `649: write_attribute of spectrum/image fails in PyTango without numpy <https://sourceforge.net/p/tango-cs/bugs/649/>`_                                         |
+|          |     - `650: [pytango] 8.1.1 not compatible with ipyton 1.2.0-rc1 <https://sourceforge.net/p/tango-cs/bugs/650/>`_                                                     |
+|          |     - `651: PyTango segmentation fault when run a DS that use attr_data.py <https://sourceforge.net/p/tango-cs/bugs/651/>`_                                           |
+|          |     - `660: command_inout_asynch (polling mode) fails <https://sourceforge.net/p/tango-cs/bugs/660/>`_                                                                |
+|          |     - `666: PyTango shutdown sometimes blocks. <https://sourceforge.net/p/tango-cs/bugs/666/>`_                                                                       |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.1.1    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - Implemented tango C++ 8.1 API                                                                                                                                   |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `527: set_value() for ULong64 <https://sourceforge.net/p/tango-cs/bugs/527/>`_                                                                                  |
+|          |     - `573: [pytango] python3 error with unregistered device <https://sourceforge.net/p/tango-cs/bugs/573/>`_                                                         |
+|          |     - `611: URGENT fail to write attribute with PyTango 8.0.3 <https://sourceforge.net/p/tango-cs/bugs/611/>`_                                                        |
+|          |     - `612: [pytango][8.0.3] failed to build from source on s390 <https://sourceforge.net/p/tango-cs/bugs/612/>`_                                                     |
+|          |     - `615: Threading problem when setting a DevULong64 attribute <https://sourceforge.net/p/tango-cs/bugs/615/>`_                                                    |
+|          |     - `622: PyTango broken when running on Ubuntu 13 <https://sourceforge.net/p/tango-cs/bugs/622/>`_                                                                 |
+|          |     - `626: attribute_history extraction can raised an exception <https://sourceforge.net/p/tango-cs/bugs/626/>`_                                                     |
+|          |     - `628: Problem in installing PyTango 8.0.3 on Scientific Linux 6 <https://sourceforge.net/p/tango-cs/bugs/628/>`_                                                |
+|          |     - `635: Reading of ULong64 attributes does not work <https://sourceforge.net/p/tango-cs/bugs/635/>`_                                                              |
+|          |     - `636: PyTango log messages are not filtered by level <https://sourceforge.net/p/tango-cs/bugs/636/>`_                                                           |
+|          |     - `637: [pytango] segfault doing write_attribute on Group <https://sourceforge.net/p/tango-cs/bugs/637/>`_                                                        |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.1.0    | *SKIPPED*                                                                                                                                                             |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.0.3    | Features:                                                                                                                                                             |
+|          |     - `88: Implement Util::server_set_event_loop method in python <https://sourceforge.net/p/tango-cs/feature-requests/88>`_                                          |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3576353: [pytango] segfault on 'RestartServer' <https://sourceforge.net/tracker/?func=detail&aid=3576353&group_id=57612&atid=484769>`_                         |
+|          |     - `3579062: [pytango] Attribute missing methods <https://sourceforge.net/tracker/?func=detail&aid=3579062&group_id=57612&atid=484769>`_                           |
+|          |     - `3586337: [pytango] Some DeviceClass methods are not python safe <https://sourceforge.net/tracker/?func=detail&aid=3586337&group_id=57612&atid=484769>`_        |
+|          |     - `3598514: DeviceProxy.__setattr__ break python's descriptors <https://sourceforge.net/tracker/?func=detail&aid=3598514&group_id=57612&atid=484769>`_            |
+|          |     - `3607779: [pytango] IPython 0.10 error <https://sourceforge.net/tracker/?func=detail&aid=3607779&group_id=57612&atid=484769>`_                                  |
+|          |     - `598: Import DLL by PyTango failed on windows <https://sourceforge.net/p/tango-cs/bugs/598/>`_                                                                  |
+|          |     - `605: [pytango] use distutils.version module <https://sourceforge.net/p/tango-cs/bugs/605/>`_                                                                   |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.0.2    | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3570970: [pytango] problem during the python3 building <https://sourceforge.net/tracker/?func=detail&aid=3570970&group_id=57612&atid=484769>`_                 |
+|          |     - `3570971: [pytango] itango does not work without qtconsole <https://sourceforge.net/tracker/?func=detail&aid=3570971&group_id=57612&atid=484769>`_              |
+|          |     - `3570972: [pytango] warning/error when building 8.0.0 <https://sourceforge.net/tracker/?func=detail&aid=3570972&group_id=57612&atid=484769>`_                   |
+|          |     - `3570975: [pytango] problem during use of python3 version <https://sourceforge.net/tracker/?func=detail&aid=3570975&group_id=57612&atid=484769>`_               |
+|          |     - `3574099: [pytango] compile error with gcc < 4.5 <https://sourceforge.net/tracker/?func=detail&aid=3574099&group_id=57612&atid=484769>`_                        |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.0.1    | *SKIPPED*                                                                                                                                                             |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 8.0.0    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - Implemented tango C++ 8.0 API                                                                                                                                   |
+|          |     - Python 3k compatible                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3023857: DevEncoded write attribute not supported <https://sourceforge.net/tracker/?func=detail&aid=3023857&group_id=57612&atid=484769>`_                      |
+|          |     - `3521545: [pytango] problem with tango profile <https://sourceforge.net/tracker/?func=detail&aid=3521545&group_id=57612&atid=484769>`_                          |
+|          |     - `3530535: PyTango group writting fails <https://sourceforge.net/tracker/?func=detail&aid=3530535&group_id=57612&atid=484769>`_                                  |
+|          |     - `3564959: EncodedAttribute.encode_xxx() methods don't accept bytearray  <https://sourceforge.net/tracker/?func=detail&aid=3564959&group_id=57612&atid=484769>`_ |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.2.4    | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `551: [pytango] Some DeviceClass methods are not python safe <https://sourceforge.net/p/tango-cs/bugs/551/>`_                                                   |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.2.3    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `3495607: DeviceClass.device_name_factory is missing <https://sourceforge.net/tracker/?func=detail&aid=3495607&group_id=57612&atid=484772>`_                    |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3103588: documentation of PyTango.Attribute.Group <https://sourceforge.net/tracker/?func=detail&aid=3103588&group_id=57612&atid=484769>`_                      |
+|          |     - `3458336: Problem with pytango 7.2.2 <https://sourceforge.net/tracker/?func=detail&aid=3458336&group_id=57612&atid=484769>`_                                    |
+|          |     - `3463377: PyTango memory leak in read encoded attribute <https://sourceforge.net/tracker/?func=detail&aid=3463377&group_id=57612&atid=484769>`_                 |
+|          |     - `3487930: [pytango] wrong python dependency <https://sourceforge.net/tracker/?func=detail&aid=3487930&group_id=57612&atid=484769>`_                             |
+|          |     - `3511509: Attribute.set_value_date_quality for encoded does not work <https://sourceforge.net/tracker/?func=detail&aid=3511509&group_id=57612&atid=484769>`_    |
+|          |     - `3514457: [pytango]  TANGO_HOST multi-host support <https://sourceforge.net/tracker/?func=detail&aid=3514457&group_id=57612&atid=484769>`_                      |
+|          |     - `3520739: command_history(...) in  PyTango <https://sourceforge.net/tracker/?func=detail&aid=3520739&group_id=57612&atid=484769>`_                              |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.2.2    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `3305251: DS dynamic attributes discards some Attr properties <https://sourceforge.net/tracker/?func=detail&aid=3305251&group_id=57612&atid=484769>`_           |
+|          |     - `3365792: DeviceProxy.<cmd_name> could be documented <https://sourceforge.net/tracker/?func=detail&aid=3365792&group_id=57612&atid=484772>`_                    |
+|          |     - `3386079: add support for ipython 0.11 <https://sourceforge.net/tracker/?func=detail&aid=3386079&group_id=57612&atid=484772>`_                                  |
+|          |     - `3437654: throw python exception as tango exception <https://sourceforge.net/tracker/?func=detail&aid=3437654&group_id=57612&atid=484772>`_                     |
+|          |     - `3447477: spock profile installation <https://sourceforge.net/tracker/?func=detail&aid=3447477&group_id=57612&atid=484772>`_                                    |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3372371: write attribute of DevEncoded doesn't work <https://sourceforge.net/tracker/?func=detail&aid=3372371&group_id=57612&atid=484769>`_                    |
+|          |     - `3374026: [pytango] pyflakes warning <https://sourceforge.net/tracker/?func=detail&aid=3374026&group_id=57612&atid=484769>`_                                    |
+|          |     - `3404771: PyTango.MultiAttribute.get_attribute_list missing <https://sourceforge.net/tracker/?func=detail&aid=3404771&group_id=57612&atid=484769>`_             |
+|          |     - `3405580: PyTango.MultiClassAttribute missing <https://sourceforge.net/tracker/?func=detail&aid=3405580&group_id=57612&atid=484769>`_                           |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.2.1    | *SKIPPED*                                                                                                                                                             |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.2.0    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `3286678: Add missing EncodedAttribute JPEG methods <https://sourceforge.net/tracker/?func=detail&aid=3286678&group_id=57612&atid=484772>`_                     |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.6    | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - 7.1.5 distribution is missing some files                                                                                                                        |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.5    | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3284174: 7.1.4 does not build with gcc 4.5 and tango 7.2.6 <https://sourceforge.net/tracker/?func=detail&aid=3284174&group_id=57612&atid=484769>`_             |
+|          |     - `3284265: [pytango][7.1.4] a few files without licence and copyright <https://sourceforge.net/tracker/?func=detail&aid=3284265&group_id=57612&atid=484769>`_    |
+|          |     - `3284318: copyleft vs copyright <https://sourceforge.net/tracker/?func=detail&aid=3284318&group_id=57612&atid=484769>`_                                         |
+|          |     - `3284434: [pytango][doc] few ERROR during the doc generation <https://sourceforge.net/tracker/?func=detail&aid=3284434&group_id=57612&atid=484769>`_            |
+|          |     - `3284435: [pytango][doc] few warning during the doc generation <https://sourceforge.net/tracker/?func=detail&aid=3284435&group_id=57612&atid=484769>`_          |
+|          |     - `3284440: [pytango][spock] the profile can't be installed <https://sourceforge.net/tracker/?func=detail&aid=3284440&group_id=57612&atid=484769>`_               |
+|          |     - `3285185: PyTango Device Server does not load Class Properties values <https://sourceforge.net/tracker/?func=detail&aid=3285185&group_id=57612&atid=484769>`_   |
+|          |     - `3286055: PyTango 7.1.x DS using Tango C++ 7.2.x seg faults on exit <https://sourceforge.net/tracker/?func=detail&aid=3286055&group_id=57612&atid=484769>`_     |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.4    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `3274309: Generic Callback for events <https://sourceforge.net/tracker/?func=detail&aid=3274309&group_id=57612&atid=484772>`_                                   |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `3011775: Seg Faults due to removed dynamic attributes <https://sourceforge.net/tracker/?func=detail&aid=3011775&group_id=57612&atid=484769>`_                  |
+|          |     - `3105169: PyTango 7.1.3 does not compile with Tango 7.2.X <https://sourceforge.net/tracker/?func=detail&aid=3105169&group_id=57612&atid=484769>`_               |
+|          |     - `3107243: spock profile does not work with python 2.5 <https://sourceforge.net/tracker/?func=detail&aid=3107243&group_id=57612&atid=484769>`_                   |
+|          |     - `3124427: PyTango.WAttribute.set_max_value() changes min value <https://sourceforge.net/tracker/?func=detail&aid=3124427&group_id=57612&atid=484769>`_          |
+|          |     - `3170399: Missing documentation about is_<attr>_allowed method <https://sourceforge.net/tracker/?func=detail&aid=3170399&group_id=57612&atid=484769>`_          |
+|          |     - `3189082: Missing get_properties() for Attribute class <https://sourceforge.net/tracker/?func=detail&aid=3189082&group_id=57612&atid=484769>`_                  |
+|          |     - `3196068: delete_device() not called after server_admin.Kill() <https://sourceforge.net/tracker/?func=detail&aid=3196068&group_id=57612&atid=484769>`_          |
+|          |     - `3257286: Binding crashes when reading a WRITE string attribute <https://sourceforge.net/tracker/?func=detail&aid=3257286&group_id=57612&atid=484769>`_         |
+|          |     - `3267628: DP.read_attribute(, extract=List/tuple) write value is wrong <https://sourceforge.net/tracker/?func=detail&aid=3267628&group_id=57612&atid=484769>`_  |
+|          |     - `3274262: Database.is_multi_tango_host missing <https://sourceforge.net/tracker/?func=detail&aid=3274262&group_id=57612&atid=484769>`_                          |
+|          |     - `3274319: EncodedAttribute is missing in PyTango (<= 7.1.3) <https://sourceforge.net/tracker/?func=detail&aid=3274319&group_id=57612&atid=484769>`_             |
+|          |     - `3277269: read_attribute(DevEncoded) is not numpy as expected <https://sourceforge.net/tracker/?func=detail&aid=3277269&group_id=57612&atid=484769>`_           |
+|          |     - `3278946: DeviceAttribute copy constructor is not working <https://sourceforge.net/tracker/?func=detail&aid=3278946&group_id=57612&atid=484769>`_               |
+|          |                                                                                                                                                                       |
+|          | Documentation:                                                                                                                                                        |
+|          |                                                                                                                                                                       |
+|          |     - Added :ref:`utilities` chapter                                                                                                                                  |
+|          |     - Added :ref:`encoded` chapter                                                                                                                                    |
+|          |     - Improved :ref:`server` chapter                                                                                                                                  |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.3    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - tango logging with print statement                                                                                                                              |
+|          |     - tango logging with decorators                                                                                                                                   |
+|          |     - from sourceforge:                                                                                                                                               |
+|          |     - `3060380: ApiUtil should be exported to PyTango  <https://sourceforge.net/tracker/?func=detail&aid=3060380&group_id=57612&atid=484772>`_                        |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - added licence header to all source code files                                                                                                                   |
+|          |     - spock didn't work without TANGO_HOST env. variable (it didn't recognize tangorc)                                                                                |
+|          |     - spock should give a proper message if it tries to be initialized outside ipython                                                                                |
+|          |                                                                                                                                                                       |
+|          |     - `3048798: licence issue GPL != LGPL <https://sourceforge.net/tracker/?func=detail&aid=3048798&group_id=57612&atid=484769>`_                                     |
+|          |     - `3073378: DeviceImpl.signal_handler raising exception crashes DS <https://sourceforge.net/tracker/?func=detail&aid=3073378&group_id=57612&atid=484769>`_        |
+|          |     - `3088031: Python DS unable to read DevVarBooleanArray property <https://sourceforge.net/tracker/?func=detail&aid=3088031&group_id=57612&atid=484769>`_          |
+|          |     - `3102776: PyTango 7.1.2 does not work with python 2.4 & boost 1.33.0 <https://sourceforge.net/tracker/?func=detail&aid=3102776&group_id=57612&atid=484769>`_    |
+|          |     - `3102778: Fix compilation warnings in linux <https://sourceforge.net/tracker/?func=detail&aid=3102778&group_id=57612&atid=484769>`_                             |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.2    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `2995964: Dynamic device creation <https://sourceforge.net/tracker/?func=detail&aid=2995964&group_id=57612&atid=484772>`_                                       |
+|          |     - `3010399: The DeviceClass.get_device_list that exists in C++ is missing <https://sourceforge.net/tracker/?func=detail&aid=3010399&group_id=57612&atid=484772>`_ |
+|          |     - `3023686: Missing DeviceProxy.<attribute name> <https://sourceforge.net/tracker/?func=detail&aid=3023686&group_id=57612&atid=484772>`_                          |
+|          |     - `3025396: DeviceImpl is missing some CORBA methods <https://sourceforge.net/tracker/?func=detail&aid=3025396&group_id=57612&atid=484772>`_                      |
+|          |     - `3032005: IPython extension for PyTango <https://sourceforge.net/tracker/?func=detail&aid=3032005&group_id=57612&atid=484772>`_                                 |
+|          |     - `3033476: Make client objects pickable <https://sourceforge.net/tracker/?func=detail&aid=3033476&group_id=57612&atid=484772>`_                                  |
+|          |     - `3039902: PyTango.Util.add_class would be useful <https://sourceforge.net/tracker/?func=detail&aid=3039902&group_id=57612&atid=484772>`_                        |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `2975940: DS command with DevVarCharArray return type fails <https://sourceforge.net/tracker/?func=detail&aid=2975940&group_id=57612&atid=484769>`_             |
+|          |     - `3000467: DeviceProxy.unlock is LOCKING instead of unlocking! <https://sourceforge.net/tracker/?func=detail&aid=3000467&group_id=57612&atid=484769>`_           |
+|          |     - `3010395: Util.get_device_* methods don't work <https://sourceforge.net/tracker/?func=detail&aid=3010395&group_id=57612&atid=484769>`_                          |
+|          |     - `3010425: Database.dev_name does not work <https://sourceforge.net/tracker/?func=detail&aid=3010425&group_id=57612&atid=484769>`_                               |
+|          |     - `3016949: command_inout_asynch callback does not work <https://sourceforge.net/tracker/?func=detail&aid=3016949&group_id=57612&atid=484769>`_                   |
+|          |     - `3020300: PyTango does not compile with gcc 4.1.x <https://sourceforge.net/tracker/?func=detail&aid=3020300&group_id=57612&atid=484769>`_                       |
+|          |     - `3030399: Database put(delete)_attribute_alias generates segfault <https://sourceforge.net/tracker/?func=detail&aid=3030399&group_id=57612&atid=484769>`_       |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.1    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - Improved setup script                                                                                                                                           |
+|          |     - Interfaced with PyPI                                                                                                                                            |
+|          |     - Cleaned build script warnings due to unclean python C++ macro definitions                                                                                       |
+|          |     - `2985993: PyTango numpy command support <https://sourceforge.net/tracker/?func=detail&aid=2985993&group_id=57612&atid=484772>`_                                 |
+|          |     - `2971217: PyTango.GroupAttrReplyList slicing <https://sourceforge.net/tracker/?func=detail&aid=2971217&group_id=57612&atid=484772>`_                            |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `2983299: Database.put_property() deletes the property <https://sourceforge.net/tracker/?func=detail&aid=2983299&group_id=57612&atid=484769>`_                  |
+|          |     - `2953689: can not write_attribute scalar/spectrum/image <https://sourceforge.net/tracker/?func=detail&aid=2953689&group_id=57612&atid=484769>`_                 |
+|          |     - `2953030: PyTango doc installation <https://sourceforge.net/tracker/?func=detail&aid=2953030&group_id=57612&atid=484769>`_                                      |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.0    | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - `2908176: read_*, write_* and is_*_allowed() methods can now be defined <https://sourceforge.net/tracker/?func=detail&aid=2908176&group_id=57612&atid=484772>`_ |
+|          |     - `2941036: TimeVal conversion to time and datetime <https://sourceforge.net/tracker/?func=detail&aid=2941036&group_id=57612&atid=484772>`_                       |
+|          |     - added str representation on Attr, Attribute, DeviceImpl and DeviceClass                                                                                         |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - `2903755: get_device_properties() bug reading DevString properties <https://sourceforge.net/tracker/?func=detail&aid=2903755group_id=57612&atid=484769>`_       |
+|          |     - `2909927: PyTango.Group.read_attribute() return values <https://sourceforge.net/tracker/?func=detail&aid=2909927&group_id=57612&atid=484769>`_                  |
+|          |     - `2914194: DevEncoded does not work <https://sourceforge.net/tracker/?func=detail&aid=2914194&group_id=57612&atid=484769>`_                                      |
+|          |     - `2916397: PyTango.DeviceAttribute copy constructor does not work <https://sourceforge.net/tracker/?func=detail&aid=2916397&group_id=57612&atid=484769>`_        |
+|          |     - `2936173: PyTango.Group.read_attributes() fails <https://sourceforge.net/tracker/?func=detail&aid=2936173&group_id=57612&atid=484769>`_                         |
+|          |     - `2949099: Missing PyTango.Except.print_error_stack <https://sourceforge.net/tracker/?func=detail&aid=2949099&group_id=57612&atid=484769>`_                      |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| 7.1.0rc1 | Features:                                                                                                                                                             |
+|          |                                                                                                                                                                       |
+|          |     - v = image_attribute.get_write_value() returns square sequences (arrays of                                                                                       |
+|          |       arrays, or numpy objects) now instead of flat lists. Also for spectrum                                                                                          |
+|          |       attributes a numpy is returned by default now instead.                                                                                                          |
+|          |     - image_attribute.set_value(v) accepts numpy arrays now or square sequences                                                                                       |
+|          |       instead of just flat lists. So, dim_x and dim_y are useless now. Also the                                                                                       |
+|          |       numpy path is faster.                                                                                                                                           |
+|          |     - new enum AttrSerialModel                                                                                                                                        |
+|          |     - Attribute new methods: set(get)_attr_serial_model, set_change_event,                                                                                            |
+|          |       set_archive_event, is_change_event, is_check_change_event,                                                                                                      |
+|          |       is_archive_criteria, is_check_archive_criteria, remove_configuration                                                                                            |
+|          |     - added support for numpy scalars in tango operations like write_attribute                                                                                        |
+|          |       (ex: now a DEV_LONG attribute can receive a numpy.int32 argument in a                                                                                           |
+|          |       write_attribute method call)                                                                                                                                    |
+|          |                                                                                                                                                                       |
+|          | Bug fixes:                                                                                                                                                            |
+|          |                                                                                                                                                                       |
+|          |     - DeviceImpl.set_value for scalar attributes                                                                                                                      |
+|          |     - DeviceImpl.push_***_event                                                                                                                                       |
+|          |     - server commands with DevVar***StringArray as parameter or as return type                                                                                        |
+|          |     - in windows,a bug in PyTango.Util prevented servers from starting up                                                                                             |
+|          |     - DeviceImpl.get_device_properties for string properties assigns only first                                                                                       |
+|          |       character of string to object member instead of entire string                                                                                                   |
+|          |     - added missing methods to Util                                                                                                                                   |
+|          |     - exported SubDevDiag class                                                                                                                                       |
+|          |     - error in read/events of attributes of type DevBoolean READ_WRITE                                                                                                |
+|          |     - error in automatic unsubscribe events of DeviceProxy when the object                                                                                            |
+|          |       disapears (happens only on some compilers with some optimization flags)                                                                                         |
+|          |     - fix possible bug when comparing attribute names in DeviceProxy                                                                                                  |
+|          |     - pretty print of DevFailed -> fix deprecation warning in python 2.6                                                                                              |
+|          |     - device class properties where not properly fetched when there is no                                                                                             |
+|          |       property value defined                                                                                                                                          |
+|          |     - memory leak when converting DevFailed exceptions from C++ to python                                                                                             |
+|          |     - python device server file without extension does not start                                                                                                      |
+|          |                                                                                                                                                                       |
+|          | Documentation:                                                                                                                                                        |
+|          |                                                                                                                                                                       |
+|          |     - Improved FAQ                                                                                                                                                    |
+|          |     - Improved compilation chapter                                                                                                                                    |
+|          |     - Improved migration information                                                                                                                                  |
++----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
diff --git a/doc/server/server.rst b/doc/server/server.rst
deleted file mode 100644
index 2eb660b..0000000
--- a/doc/server/server.rst
+++ /dev/null
@@ -1,109 +0,0 @@
-
-.. currentmodule:: PyTango.server
-
-.. _pytango-hlapi:
-
-High Level API
-==============
-
-This module provides a high level device server API. It implements
-:ref:`TEP1 <pytango-TEP1>`. It exposes an easier API for developing a Tango
-device server.
-
-Here is a simple example on how to write a *Clock* device server using the
-high level API::
-    
-    import time
-    from PyTango.server import server_run
-    from PyTango.server import Device, DeviceMeta
-    from PyTango.server import attribute, command   
-
-    class Clock(Device):
-        __metaclass__ = DeviceMeta
-
-	time = attribute()
-
-	def read_time(self):
-	    return time.time()
-
-	@command(din_type=str, dout_type=str)
-	def strftime(self, format):
-	    return time.strftime(format)
-
-    if __name__ == "__main__":
-        server_run((Clock,))
-
-Here is a more complete  example on how to write a *PowerSupply* device server
-using the high level API. The example contains:
-
-#. a read-only double scalar attribute called *voltage*
-#. a read/write double scalar expert attribute *current*
-#. a read-only double image attribute called *noise*
-#. a *ramp* command
-#. a *host* device property
-#. a *port* class property
-
-.. code-block:: python
-    :linenos:
-
-    from time import time
-
-    from PyTango import AttrQuality, AttrWriteType, DispLevel, server_run
-    from PyTango.server import Device, DeviceMeta, attribute, command
-    from PyTango.server import class_property, device_property
-
-    class PowerSupply(Device):
-        __metaclass__ = DeviceMeta
-
-        voltage = attribute()
-
-        current = attribute(label="Current",
-                            dtype=float,
-                            display_level=DispLevel.EXPERT,
-                            access=AttrWriteType.READ_WRITE,
-                            unit="A",
-                            format="8.4f",
-			    min_value=0.0, max_value=8.5,
-			    min_alarm=0.1, max_alarm=8.4,
-			    min_warning=0.5, max_warning=8.0,
-                            fget="get_current",
-                            fset="set_current",
-                            doc="the power supply current")
-	
-	noise = attribute(label="Noise",
-			  dtype=((float,),),
-			  max_dim_x=1024, max_dim_y=1024,
-			  fget="get_noise")
- 
-        host = device_property(dtype=str)
-        port = class_property(dtype=int, default_value=9788)
-
-        def read_voltage(self):
-            self.info_stream("get voltage(%s, %d)" %(self.host, self.port))
-            return 10.0
-
-        def get_current(self):
-            return 2.3456, time(), AttrQuality.ATTR_WARNING
-     
-
-*Pretty cool, uh?*
-
-API
----
-
-.. automodule:: PyTango.server
-
-   .. autoclass:: Device
-      :show-inheritance:
-      :inherited-members:
-      :members:
-
-   .. autoclass:: attribute
-
-   .. autofunction:: command
-
-   .. autoclass:: device_property
-
-   .. autoclass:: class_property
-
-   .. autofunction:: server_run
diff --git a/doc/server/attribute.rst b/doc/server_api/attribute.rst
similarity index 100%
rename from doc/server/attribute.rst
rename to doc/server_api/attribute.rst
diff --git a/doc/server/device.rst b/doc/server_api/device.rst
similarity index 94%
rename from doc/server/device.rst
rename to doc/server_api/device.rst
index 0815b24..ce1c421 100644
--- a/doc/server/device.rst
+++ b/doc/server_api/device.rst
@@ -1,5 +1,5 @@
-Device classes
-==============
+Device
+======
 
 .. currentmodule:: PyTango
 
diff --git a/doc/server/device_class.rst b/doc/server_api/device_class.rst
similarity index 100%
rename from doc/server/device_class.rst
rename to doc/server_api/device_class.rst
diff --git a/doc/server_api/index.rst b/doc/server_api/index.rst
new file mode 100644
index 0000000..f2a28ef
--- /dev/null
+++ b/doc/server_api/index.rst
@@ -0,0 +1,14 @@
+.. currentmodule:: PyTango
+
+Server API
+----------
+
+.. toctree::
+    :maxdepth: 2
+    
+    server
+    device
+    device_class
+    logging
+    attribute
+    util
diff --git a/doc/server/logging.rst b/doc/server_api/logging.rst
similarity index 100%
rename from doc/server/logging.rst
rename to doc/server_api/logging.rst
diff --git a/doc/server_api/server.rst b/doc/server_api/server.rst
new file mode 100644
index 0000000..9a3a65b
--- /dev/null
+++ b/doc/server_api/server.rst
@@ -0,0 +1,303 @@
+
+.. currentmodule:: PyTango.server
+
+.. _pytango-hlapi:
+
+High level server API
+=====================
+
+.. automodule:: PyTango.server
+
+.. hlist::
+
+   * :class:`~PyTango.server.Device`
+   * :class:`~PyTango.server.attribute`
+   * :class:`~PyTango.server.command`
+   * :class:`~PyTango.server.device_property`
+   * :class:`~PyTango.server.class_property`
+   * :func:`~PyTango.server.run`
+   * :func:`~PyTango.server.server_run`
+
+This module provides a high level device server API. It implements
+:ref:`TEP1 <pytango-TEP1>`. It exposes an easier API for developing a Tango
+device server.
+
+Here is a simple example on how to write a *Clock* device server using the
+high level API::
+    
+    import time
+    from PyTango.server import run
+    from PyTango.server import Device, DeviceMeta
+    from PyTango.server import attribute, command   
+
+
+    class Clock(Device):
+        __metaclass__ = DeviceMeta
+
+        time = attribute()
+
+        def read_time(self):
+            return time.time()
+
+        @command(din_type=str, dout_type=str)
+        def strftime(self, format):
+            return time.strftime(format)
+
+
+    if __name__ == "__main__":
+        run((Clock,))
+
+
+Here is a more complete  example on how to write a *PowerSupply* device server
+using the high level API. The example contains:
+
+#. a read-only double scalar attribute called *voltage*
+#. a read/write double scalar expert attribute *current*
+#. a read-only double image attribute called *noise*
+#. a *ramp* command
+#. a *host* device property
+#. a *port* class property
+
+.. code-block:: python
+    :linenos:
+
+    from time import time
+    from numpy.random import random_sample
+
+    from PyTango import AttrQuality, AttrWriteType, DispLevel, server_run
+    from PyTango.server import Device, DeviceMeta, attribute, command
+    from PyTango.server import class_property, device_property
+
+    class PowerSupply(Device):
+        __metaclass__ = DeviceMeta
+
+        voltage = attribute()
+
+        current = attribute(label="Current", dtype=float,
+                            display_level=DispLevel.EXPERT,
+                            access=AttrWriteType.READ_WRITE,
+                            unit="A", format="8.4f",
+                            min_value=0.0, max_value=8.5,
+                            min_alarm=0.1, max_alarm=8.4,
+                            min_warning=0.5, max_warning=8.0,
+                            fget="get_current", fset="set_current",
+                            doc="the power supply current")
+    
+        noise = attribute(label="Noise", dtype=((float,),),
+                          max_dim_x=1024, max_dim_y=1024,
+                          fget="get_noise")
+ 
+        host = device_property(dtype=str)
+        port = class_property(dtype=int, default_value=9788)
+
+        def read_voltage(self):
+            self.info_stream("get voltage(%s, %d)" % (self.host, self.port))
+            return 10.0
+
+        def get_current(self):
+            return 2.3456, time(), AttrQuality.ATTR_WARNING
+    
+        def set_current(self, current):
+            print("Current set to %f" % current)
+    
+        def get_noise(self):
+            return random_sample((1024, 1024))
+
+        @command(dtype_in=float)
+        def ramp(self, value):
+            print("Ramping up...")
+
+    if __name__ == "__main__":
+        server_run((PowerSupply,))
+
+*Pretty cool, uh?*
+
+.. note::
+    the ``__metaclass__`` statement is mandatory due to a limitation in the
+    *boost-python* library used by PyTango.
+    
+    If you are using python 3 you can write instead::
+        
+        class PowerSupply(Device, metaclass=DeviceMeta)
+            pass
+
+.. _pytango-hlapi-datatypes:
+
+.. rubric:: Data types
+
+When declaring attributes, properties or commands, one of the most important
+information is the data type. It is given by the keyword argument *dtype*.
+In order to provide a more *pythonic* interface, this argument is not restricted
+to the :obj:`~PyTango.CmdArgType` options.
+
+For example, to define a *SCALAR* :obj:`~PyTango.CmdArgType.DevLong`
+attribute you have several possibilities:
+
+#. :obj:`int`
+#. 'int'
+#. 'int32'
+#. 'integer' 
+#. :obj:`PyTango.CmdArgType.DevLong`
+#. 'DevLong' 
+#. :obj:`numpy.int32`
+
+To define a *SPECTRUM* attribute simply wrap the scalar data type in any
+python sequence:
+
+* using a *tuple*: ``(:obj:`int`,)`` or
+* using a *list*: ``[:obj:`int`]`` or
+* any other sequence type
+
+To define an *IMAGE* attribute simply wrap the scalar data type in any
+python sequence of sequences:
+
+* using a *tuple*: ``((:obj:`int`,),)`` or
+* using a *list*: ``[[:obj:`int`]]`` or
+* any other sequence type
+
+Below is the complete table of equivalences.
+
+========================================  ========================================
+dtype argument                            converts to tango type                             
+========================================  ========================================
+ ``None``                                  ``DevVoid``
+ ``'None'``                                ``DevVoid``
+ ``DevVoid``                               ``DevVoid``
+ ``'DevVoid'``                             ``DevVoid``
+
+ ``DevState``                              ``DevState``                           
+ ``'DevState'``                            ``DevState``                           
+
+ :py:obj:`bool`                            ``DevBoolean``
+ ``'bool'``                                ``DevBoolean``
+ ``'boolean'``                             ``DevBoolean``
+ ``DevBoolean``                            ``DevBoolean``
+ ``'DevBoolean'``                          ``DevBoolean``
+ :py:obj:`numpy.bool_`                     ``DevBoolean``
+
+ ``'char'``                                ``DevUChar``
+ ``'chr'``                                 ``DevUChar``
+ ``'byte'``                                ``DevUChar``
+ ``chr``                                   ``DevUChar``
+ ``DevUChar``                              ``DevUChar``
+ ``'DevUChar'``                            ``DevUChar``
+ :py:obj:`numpy.uint8`                     ``DevUChar``
+
+ ``'int16'``                               ``DevShort``
+ ``DevShort``                              ``DevShort``
+ ``'DevShort'``                            ``DevShort``
+ :py:obj:`numpy.int16`                     ``DevShort``
+
+ ``'uint16'``                              ``DevUShort``
+ ``DevUShort``                             ``DevUShort``
+ ``'DevUShort'``                           ``DevUShort``
+ :py:obj:`numpy.uint16`                    ``DevUShort``
+
+ :py:obj:`int`                             ``DevLong``
+ ``'int'``                                 ``DevLong``
+ ``'int32'``                               ``DevLong``
+ ``DevLong``                               ``DevLong``
+ ``'DevLong'``                             ``DevLong``
+ :py:obj:`numpy.int32`                     ``DevLong``
+
+ ``'uint'``                                ``DevULong``
+ ``'uint32'``                              ``DevULong``
+ ``DevULong``                              ``DevULong``
+ ``'DevULong'``                            ``DevULong``
+ :py:obj:`numpy.uint32`                    ``DevULong``
+
+ ``'int64'``                               ``DevLong64``
+ ``DevLong64``                             ``DevLong64``
+ ``'DevLong64'``                           ``DevLong64``
+ :py:obj:`numpy.int64`                     ``DevLong64``
+ 
+ ``'uint64'``                              ``DevULong64``
+ ``DevULong64``                            ``DevULong64``
+ ``'DevULong64'``                          ``DevULong64``
+ :py:obj:`numpy.uint64`                    ``DevULong64``
+
+ ``DevInt``                                ``DevInt``                             
+ ``'DevInt'``                              ``DevInt``                             
+ 
+ ``'float32'``                             ``DevFloat``
+ ``DevFloat``                              ``DevFloat``
+ ``'DevFloat'``                            ``DevFloat``
+ :py:obj:`numpy.float32`                   ``DevFloat``
+ 
+ :py:obj:`float`                           ``DevDouble``
+ ``'double'``                              ``DevDouble``
+ ``'float'``                               ``DevDouble``
+ ``'float64'``                             ``DevDouble``
+ ``DevDouble``                             ``DevDouble``
+ ``'DevDouble'``                           ``DevDouble``
+ :py:obj:`numpy.float64`                   ``DevDouble``
+ 
+ :py:obj:`str`                             ``DevString``
+ ``'str'``                                 ``DevString``
+ ``'string'``                              ``DevString``
+ ``'text'``                                ``DevString``
+ ``DevString``                             ``DevString``
+ ``'DevString'``                           ``DevString``
+ 
+ :py:obj:`bytearray`                       ``DevEncoded``
+ ``'bytearray'``                           ``DevEncoded``
+ ``'bytes'``                               ``DevEncoded``
+ ``DevEncoded``                            ``DevEncoded``
+ ``'DevEncoded'``                          ``DevEncoded``
+
+ ``DevVarBooleanArray``                    ``DevVarBooleanArray``
+ ``'DevVarBooleanArray'``                  ``DevVarBooleanArray``
+ 
+ ``DevVarCharArray``                       ``DevVarCharArray``
+ ``'DevVarCharArray'``                     ``DevVarCharArray``
+ 
+ ``DevVarShortArray``                      ``DevVarShortArray``
+ ``'DevVarShortArray'``                    ``DevVarShortArray``
+ 
+ ``DevVarLongArray``                       ``DevVarLongArray``
+ ``'DevVarLongArray'``                     ``DevVarLongArray``
+ 
+ ``DevVarLong64Array``                     ``DevVarLong64Array``
+ ``'DevVarLong64Array'``                   ``DevVarLong64Array``
+ 
+ ``DevVarULong64Array``                    ``DevVarULong64Array``
+ ``'DevVarULong64Array'``                  ``DevVarULong64Array``
+ 
+ ``DevVarFloatArray``                      ``DevVarFloatArray``
+ ``'DevVarFloatArray'``                    ``DevVarFloatArray``
+ 
+ ``DevVarDoubleArray``                     ``DevVarDoubleArray``
+ ``'DevVarDoubleArray'``                   ``DevVarDoubleArray``
+ 
+ ``DevVarUShortArray``                     ``DevVarUShortArray``
+ ``'DevVarUShortArray'``                   ``DevVarUShortArray``
+ 
+ ``DevVarULongArray``                      ``DevVarULongArray``
+ ``'DevVarULongArray'``                    ``DevVarULongArray``
+ 
+ ``DevVarStringArray``                     ``DevVarStringArray``
+ ``'DevVarStringArray'``                   ``DevVarStringArray``
+ 
+ ``DevVarLongStringArray``                 ``DevVarLongStringArray``
+ ``'DevVarLongStringArray'``               ``DevVarLongStringArray``
+ 
+ ``DevVarDoubleStringArray``               ``DevVarDoubleStringArray``
+ ``'DevVarDoubleStringArray'``             ``DevVarDoubleStringArray``
+========================================  ========================================
+
+.. autoclass:: Device
+   :show-inheritance:
+   :inherited-members:
+   :members:
+
+.. autoclass:: attribute
+
+.. autofunction:: command
+
+.. autoclass:: device_property
+
+.. autoclass:: class_property
+
+.. autofunction:: run
+
+.. autofunction:: server_run
diff --git a/doc/server/util.rst b/doc/server_api/util.rst
similarity index 100%
rename from doc/server/util.rst
rename to doc/server_api/util.rst
diff --git a/doc/start.rst b/doc/start.rst
index 4408937..48d6df9 100644
--- a/doc/start.rst
+++ b/doc/start.rst
@@ -6,8 +6,11 @@
 Getting started
 ===============
 
-Quick installation: Linux
--------------------------
+Installing
+----------
+
+Linux
+~~~~~
 
 PyTango is available on linux as an official debian/ubuntu package::
 
@@ -23,8 +26,8 @@ RPM packages are also available for RHEL & CentOS:
    * `RHEL 6/CentOS 6 32bits <ftp://ftp.maxlab.lu.se/pub/maxlab/packages/el6/i386/repoview/index.html>`_
    * `RHEL 6/CentOS 6 64bits <ftp://ftp.maxlab.lu.se/pub/maxlab/packages/el6/x86_64/repoview/index.html>`_
 
-From PyPi
-~~~~~~~~~
+PyPi
+~~~~
 
 You can also install the latest version from `PyPi`_.
 
@@ -43,8 +46,8 @@ or easy_install::
 
     $ easy_install -U PyTango
 
-Quick installation: Windows
----------------------------
+Windows
+~~~~~~~
 
 First, make sure `Python`_ and `numpy`_ are installed.
 
@@ -55,9 +58,8 @@ Windows XP/Vista/7/8. The complete list of binaries can be downloaded from
 Select the proper windows package, download it and finally execute the 
 installion wizard.
 
-
-Compiling & installing
-----------------------
+Compiling
+---------
 
 Linux
 ~~~~~
@@ -95,8 +97,8 @@ Since it is rarely needed and the instructions are so complicated, I have
 choosen to place the how-to in a separate text file. You can find it in the
 source package under :file:`doc/windows_notes.txt`.
 
-Testing your installation
--------------------------
+Testing
+-------
 
 If you have IPython_ installed, the best way to test your PyTango installation
 is by starting the new PyTango CLI called :ref:`itango` by typing on the command
@@ -121,3 +123,4 @@ python console and type:
     >>> PyTango.Release.version
     '8.0.2'
 
+Next steps: Check out the :ref:`pytango-quick-tour`.
diff --git a/doc/tep/tep-0001.rst b/doc/tep/tep-0001.rst
index 27ef5cf..3348ff4 100644
--- a/doc/tep/tep-0001.rst
+++ b/doc/tep/tep-0001.rst
@@ -1,5 +1,5 @@
 
-.. currentmodule:: PyTango.hlapi
+.. currentmodule:: PyTango.server
 
 .. _pytango-TEP1:
 
@@ -11,8 +11,8 @@ TEP 1 - Device Server High Level API
  TEP:               1   
 ================== ====================================================
  Title:             Device Server High Level API
- Version:           2.1.0
- Last-Modified:     13-Nov-2013
+ Version:           2.2.0
+ Last-Modified:     10-Sep-2014
  Author:            Tiago Coutinho <tcoutinho at cells.es>
  Status:            Active
  Type:              Standards Track
@@ -125,10 +125,10 @@ In order to keep the code up to date with tango, every time a new Tango IDL
 is released, the code of **every** device server needs to be manually
 updated to ihnerit from the newest tango version.
 
-By inheriting from a new high-level :class:`~PyTango.hlapi.Device` (which
+By inheriting from a new high-level :class:`~PyTango.server.Device` (which
 itself automatically *decides* from which DeviceImpl version it should
 inherit), the device servers are always up to date with the latest tango
-release without need for manual intervention (see :mod:`PyTango.hlapi`).
+release without need for manual intervention (see :mod:`PyTango.server`).
 
 Low-level way::
 
@@ -137,17 +137,17 @@ Low-level way::
     
 High-level way::
 
-    class Motor(PyTango.hlapi.Device):
+    class Motor(PyTango.server.Device):
         pass
     
-Default implementation of :class:`~PyTango.hlapi.Device` constructor
+Default implementation of :class:`~PyTango.server.Device` constructor
 --------------------------------------------------------------------------------
 
 99% of the different device classes which inherit from low level
 :class:`~PyTango.DeviceImpl` only implement `__init__` to call their
-`init_device` (see :mod:`PyTango.hlapi`).
+`init_device` (see :mod:`PyTango.server`).
 
-:class:`~PyTango.hlapi.Device` already calls init_device.
+:class:`~PyTango.server.Device` already calls init_device.
 
 Low-level way::
 
@@ -159,21 +159,21 @@ Low-level way::
     
 High-level way::
 
-    class Motor(PyTango.hlapi.Device):
+    class Motor(PyTango.server.Device):
 
         # Nothing to be done!
         
         pass
 
-Default implementation of :meth:`~PyTango.hlapi.Device.init_device`
+Default implementation of :meth:`~PyTango.server.Device.init_device`
 --------------------------------------------------------------------------------
 
 99% of different device classes which inherit from low level
 :class:`~PyTango.DeviceImpl` have an implementation of `init_device` which
 *at least* calls :meth:`~PyTango.DeviceImpl.get_device_properties`
-(see :mod:`PyTango.hlapi`).
+(see :mod:`PyTango.server`).
 
-:meth:`~PyTango.hlapi.Device.init_device` already calls :meth:`~PyTango.hlapi.Device.get_device_properties`.
+:meth:`~PyTango.server.Device.init_device` already calls :meth:`~PyTango.server.Device.get_device_properties`.
 
 Low-level way::
 
@@ -184,7 +184,7 @@ Low-level way::
     
 High-level way::
 
-    class Motor(PyTango.hlapi.Device):
+    class Motor(PyTango.server.Device):
         # Nothing to be done!
         pass
 
@@ -199,8 +199,8 @@ device and class properties by using the corresponding
 
 With the high-level API we completely remove the need to code the
 :class:`~PyTango.DeviceClass` by registering attribute, commands,
-device and class properties in the :class:`~PyTango.hlapi.Device` with a more
-pythonic API (see :mod:`PyTango.hlapi`)
+device and class properties in the :class:`~PyTango.server.Device` with a more
+pythonic API (see :mod:`PyTango.server`)
 
 
 #. Hide `<Device>Class` class completely
@@ -232,9 +232,9 @@ Low-level way::
 
 High-level way::
 
-    class Motor(PyTango.hlapi.Device):
+    class Motor(PyTango.server.Device):
         
-        position = PyTango.hlapi.attribute(dtype=float, )
+        position = PyTango.server.attribute(dtype=float, )
 
         def read_position(self):
             pass
@@ -574,7 +574,7 @@ And the equivalent HLAPI version of the code would be::
     #!/usr/bin/env python
 
     from PyTango import DebugIt, server_run
-    from PyTango.hlapi import Device, DeviceMeta, attribute
+    from PyTango.server import Device, DeviceMeta, attribute
 
 
     class Motor(Device):
@@ -595,11 +595,16 @@ And the equivalent HLAPI version of the code would be::
 References
 ==========
 
-:mod:`PyTango.hlapi`
+:mod:`PyTango.server`
 
 Changes
 =======
 
+from 2.1.0 to 2.2.0
+-------------------
+
+Changed module name from *hlapi* to *server*
+
 from 2.0.0 to 2.1.0
 -------------------
 
diff --git a/doc/utilities.rst b/doc/utilities.rst
index 26152c9..95975e0 100644
--- a/doc/utilities.rst
+++ b/doc/utilities.rst
@@ -9,6 +9,18 @@ The Utilities API
     :members:
     :undoc-members:
 
+.. autofunction:: PyTango.utils.is_pure_str
+
+.. autofunction:: PyTango.utils.is_seq
+
+.. autofunction:: PyTango.utils.is_non_str_seq
+
+.. autofunction:: PyTango.utils.is_integer
+
+.. autofunction:: PyTango.utils.is_number
+
+.. autofunction:: PyTango.utils.is_bool
+
 .. autofunction:: PyTango.utils.is_scalar_type
     
 .. autofunction:: PyTango.utils.is_array_type
@@ -19,9 +31,20 @@ The Utilities API
 
 .. autofunction:: PyTango.utils.is_float_type
 
+.. autofunction:: PyTango.utils.is_bool_type
+
+.. autofunction:: PyTango.utils.is_bin_type
+
+.. autofunction:: PyTango.utils.is_str_type
+
 .. autofunction:: PyTango.utils.obj_2_str
 
 .. autofunction:: PyTango.utils.seqStr_2_obj
 
+.. autofunction:: PyTango.utils.scalar_to_array_type
+
 .. autofunction:: PyTango.utils.get_home
 
+.. autofunction:: PyTango.utils.requires_pytango
+
+.. autofunction:: PyTango.utils.requires_tango
diff --git a/setup.py b/setup.py
index d77114d..5ceef5a 100644
--- a/setup.py
+++ b/setup.py
@@ -47,17 +47,20 @@ except:
 
 try:
     import numpy
-except:
+except ImportError:
     numpy = None
 
 is64 = 8 * struct.calcsize("P") == 64
 
 
 def pkg_config(*packages, **config):
-    config_map = {"-I": "include_dirs",
-                "-L": "library_dirs",
-                "-l": "libraries"}
-    cmd = ["pkg-config", "--libs", "--cflags-only-I", " ".join(packages)]
+    config_map = {
+        "-I": "include_dirs",
+        "-L": "library_dirs",
+        "-l": "libraries",
+    }
+    cmd = ["pkg-config", "--cflags-only-I",
+           "--libs-only-L", " ".join(packages)]
     proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
     result = proc.wait()
     result = str(proc.communicate()[0].decode("utf-8"))
@@ -67,8 +70,8 @@ def pkg_config(*packages, **config):
         if value not in config_values:
             config_values.append(value)
     return config
-        
-        
+
+
 def abspath(*path):
     """A method to determine absolute path for a given relative path to the
     directory where this setup.py script is located"""
@@ -105,7 +108,7 @@ def get_c_numpy():
         if os.path.isdir(inc):
             return inc
 
-        
+
 def has_c_numpy():
     return get_c_numpy() is not None
 
@@ -119,9 +122,12 @@ def has_numpy(with_src=True):
 
 def get_script_files():
 
-    FILTER_OUT = (),  # "winpostinstall.py",
+    FILTER_OUT = []  # "winpostinstall.py",
+
+    if os.name != "nt":
+        FILTER_OUT.append("pytango_winpostinstall.py")
 
-    scripts_dir = abspath('scripts')
+    scripts_dir = abspath("scripts")
     scripts = []
     items = os.listdir(scripts_dir)
     for item in items:
@@ -154,21 +160,23 @@ def add_lib(name, dirs, sys_libs, env_name=None, lib_name=None, inc_suffix=None)
         return
     else:
         inc_dir = os.path.join(ENV, 'include')
+        dirs['include_dirs'].append(inc_dir)
         if inc_suffix is not None:
             inc_dir = os.path.join(inc_dir, inc_suffix)
+            dirs['include_dirs'].append(inc_dir)
+
         lib_dirs = [os.path.join(ENV, 'lib')]
         if is64:
             lib64_dir = os.path.join(ENV, 'lib64')
             if os.path.isdir(lib64_dir):
                 lib_dirs.insert(0, lib64_dir)
+        dirs['library_dirs'].extend(lib_dirs)
         
         if lib_name.startswith('lib'):
             lib_name = lib_name[3:]
-        dirs['include_dirs'].append(inc_dir)
-        dirs['library_dirs'].extend(lib_dirs)
         dirs['libraries'].append(lib_name)
 
-        
+
 class build(dftbuild):
 
     user_options = dftbuild.user_options + \
@@ -202,7 +210,7 @@ class build(dftbuild):
         dftbuild.run(self)
 
         if self.strip_lib:
-            if os.name == 'posix':
+            if 'posix' in os.name:
                 has_objcopy = os.system("type objcopy") == 0
                 if has_objcopy:
                     d = abspath(self.build_lib, "PyTango")
@@ -260,7 +268,7 @@ class build_ext(dftbuild_ext):
             ext.define_macros += [ ('PYTANGO_HAS_UNIQUE_PTR', '1') ]
         dftbuild_ext.build_extension(self, ext)
 
-        
+
 if sphinx:
     class build_doc(BuildDoc):
 
@@ -323,18 +331,17 @@ class install(dftinstall):
     sub_commands = list(dftinstall.sub_commands)
     sub_commands.append(('install_html', has_html))
 
-    
-def main():
+
+def setup_args():
     macros = []
-    
+
     directories = {
         'include_dirs': [],
         'library_dirs': [],
-        'libraries':    ['tango', 'log4tango', 'zmq',
-                         'omniDynamic4', 'COS4', 'omniORB4', 'omnithread'],
+        'libraries':    ['tango'],
     }
     sys_libs = []
-    
+
     add_lib('omni', directories, sys_libs, lib_name='omniORB4')
     add_lib('zmq', directories, sys_libs, lib_name='libzmq')
     add_lib('tango', directories, sys_libs, inc_suffix='tango')
@@ -363,11 +370,11 @@ def main():
 
         directories['include_dirs'].append(inc_dir)
         directories['library_dirs'].extend(lib_dirs)
-                
+
     directories['libraries'].append(boost_library_name)
 
     # special numpy configuration
-    
+
     numpy_c_include = get_c_numpy()
     if numpy_c_include is not None:
         directories['include_dirs'].append(numpy_c_include)
@@ -379,7 +386,7 @@ def main():
 
     if 'posix' in os.name:
         directories = pkg_config(*sys_libs, **directories)
-    
+
     Release = get_release_info()
 
     author = Release.authors['Coutinho']
@@ -391,7 +398,7 @@ def main():
         'PyTango.ipython',
         'PyTango.ipython.ipython_00_10',
         'PyTango.ipython.ipython_00_11',
-        'PyTango.ipython.ipython_10_00',        
+        'PyTango.ipython.ipython_10_00',
     ]
 
     py_modules = []
@@ -412,6 +419,8 @@ def main():
     scripts = get_script_files()
 
     data_files = []
+    if os.name == 'nt':
+        data_files.append(('scripts', ['doc/_static/itango.ico']))
 
     classifiers = [
         'Development Status :: 5 - Production/Stable',
@@ -429,7 +438,7 @@ def main():
         'Topic :: Scientific/Engineering',
         'Topic :: Software Development :: Libraries',
     ]
-    
+
     # Note for PyTango developers:
     # Compilation time can be greatly reduced by compiling the file
     # src/precompiled_header.hpp as src/precompiled_header.hpp.gch
@@ -446,7 +455,7 @@ def main():
 
     if please_debug:
         extra_compile_args += ['-g', '-O0']
-        extra_link_args += ['-g' , '-O0'] 
+        extra_link_args += ['-g' , '-O0']
 
     src_dir = abspath('src', 'boost', 'cpp')
     client_dir = src_dir
@@ -485,7 +494,7 @@ def main():
     if sphinx:
         cmdclass['build_doc'] = build_doc
 
-    dist = setup(
+    opts = dict(
         name='PyTango',
         version=Release.version,
         description=Release.description,
@@ -510,7 +519,10 @@ def main():
         ext_modules=[_pytango],
         cmdclass=cmdclass)
 
-    return dist
+    return opts
+
+def main():
+    return setup(**setup_args())
 
 if __name__ == "__main__":
     main()
diff --git a/src/boost/cpp/attr_conf_event_data.cpp b/src/boost/cpp/attr_conf_event_data.cpp
index cee7201..15c5521 100644
--- a/src/boost/cpp/attr_conf_event_data.cpp
+++ b/src/boost/cpp/attr_conf_event_data.cpp
@@ -12,26 +12,55 @@
 #include "precompiled_header.hpp"
 #include <tango.h>
 
+#include "exception.h"
+
 using namespace boost::python;
 
+extern boost::python::object PyTango_DevFailed;
+
+namespace PyAttrConfEventData
+{
+    static boost::shared_ptr<Tango::AttrConfEventData> makeAttrConfEventData()
+    {
+        Tango::AttrConfEventData *result = new Tango::AttrConfEventData;
+        return boost::shared_ptr<Tango::AttrConfEventData>(result);
+    }
+
+    static void set_errors(Tango::AttrConfEventData &event_data, 
+                           boost::python::object &dev_failed)
+    {
+        Tango::DevFailed df;
+        boost::python::object errors = dev_failed.attr("args");
+        sequencePyDevError_2_DevErrorList(errors.ptr(), event_data.errors);
+    }
+};
+
 void export_attr_conf_event_data()
 {
     class_<Tango::AttrConfEventData>("AttrConfEventData",
         init<const Tango::AttrConfEventData &>())
-        // The original Tango::EventData structure has a 'device' field.
+
+        .def("__init__", boost::python::make_constructor(PyAttrConfEventData::makeAttrConfEventData))
+
+        // The original Tango::AttrConfEventData structure has a 'device' field.
         // However, if we returned this directly we would get a different
         // python device each time. So we are doing our weird things to make
         // sure the device returned is the same where the read action was
-        // performed. So we don't return Tango::EventData::device directly.
+        // performed. So we don't return Tango::AttrConfEventData::device directly.
         // See callback.cpp
         .setattr("device",object())
-        .def_readonly("attr_name", &Tango::AttrConfEventData::attr_name)
-        .def_readonly("event", &Tango::AttrConfEventData::event)
+        .def_readwrite("attr_name", &Tango::AttrConfEventData::attr_name)
+        .def_readwrite("event", &Tango::AttrConfEventData::event)
+
         .setattr("attr_conf",object())
-        .def_readonly("err", &Tango::AttrConfEventData::err)
-        .def_readonly("reception_date", &Tango::AttrConfEventData::reception_date)
-        .add_property("errors", make_getter(&Tango::AttrConfEventData::errors, 
-            return_value_policy<copy_non_const_reference>()))
+
+        .def_readwrite("err", &Tango::AttrConfEventData::err)
+        .def_readwrite("reception_date", &Tango::AttrConfEventData::reception_date)
+        .add_property("errors", 
+		      make_getter(&Tango::AttrConfEventData::errors, 
+				  return_value_policy<copy_non_const_reference>()),
+		      &PyAttrConfEventData::set_errors)
+
         .def("get_date", &Tango::AttrConfEventData::get_date,
             return_internal_reference<>())
     ;
diff --git a/src/boost/cpp/attribute_proxy.cpp b/src/boost/cpp/attribute_proxy.cpp
index 8beaf9c..3f0f7ed 100644
--- a/src/boost/cpp/attribute_proxy.cpp
+++ b/src/boost/cpp/attribute_proxy.cpp
@@ -32,6 +32,16 @@ namespace PyAttributeProxy
             return bopy::make_tuple(ret);
         }
     };
+
+    static boost::shared_ptr<Tango::AttributeProxy> makeAttributeProxy1(const std::string& name)
+    {
+        return boost::shared_ptr<Tango::AttributeProxy>(new Tango::AttributeProxy(name.c_str()));
+    }
+
+    static boost::shared_ptr<Tango::AttributeProxy> makeAttributeProxy2(const Tango::DeviceProxy *dev, const std::string& name)
+    {
+      return boost::shared_ptr<Tango::AttributeProxy>(new Tango::AttributeProxy(dev, name.c_str()));
+    }
 }
 
 void export_attribute_proxy()
@@ -46,14 +56,13 @@ void export_attribute_proxy()
     void (Tango::AttributeProxy::*delete_property_)(std::string &) =
         &Tango::AttributeProxy::delete_property;
 
-    bopy::class_<Tango::AttributeProxy> AttributeProxy(
-        "__AttributeProxy",
-        bopy::init<const char *>())
+    bopy::class_<Tango::AttributeProxy> AttributeProxy("__AttributeProxy",
+        bopy::init<const Tango::AttributeProxy &>())
     ;
 
     AttributeProxy
-        .def(bopy::init<const Tango::DeviceProxy *, const char *>())
-        .def(bopy::init<const Tango::AttributeProxy &>())
+        .def("__init__", boost::python::make_constructor(PyAttributeProxy::makeAttributeProxy1))
+        .def("__init__", boost::python::make_constructor(PyAttributeProxy::makeAttributeProxy2))
 
         //
         // Pickle
diff --git a/src/boost/cpp/base_types.cpp b/src/boost/cpp/base_types.cpp
index c1586c4..dcc9faa 100644
--- a/src/boost/cpp/base_types.cpp
+++ b/src/boost/cpp/base_types.cpp
@@ -159,11 +159,76 @@ struct convert_PySequence_to_CORBA_Sequence
     
 };
 
+#if PY_VERSION_HEX < 0x03000000
+bool is_str(PyObject* obj)
+{
+    return PyString_Check(obj) || PyUnicode_Check(obj);
+}
+
+struct StdString_from_python_str_unicode
+{
+    StdString_from_python_str_unicode()
+    {
+        boost::python::converter::registry::push_back(
+          &convertible,
+          &construct,
+          boost::python::type_id<std::string>());
+    }
+ 
+    // Determine if obj_ptr can be converted in a std::string
+    static void* convertible(PyObject* obj)
+    {
+        if (!is_str(obj))
+        {
+            return 0;
+        }
+        return obj;
+    }
+ 
+    // Convert obj_ptr into a std::string
+    static void construct(PyObject* obj,
+                          boost::python::converter::rvalue_from_python_stage1_data* data)
+    {
+      bool decref = false;
+
+      if (PyUnicode_Check(obj))
+      {
+          decref = true;
+          obj = PyUnicode_AsLatin1String(obj);
+      }
+      
+      const char* value = PyBytes_AsString(obj);
+ 
+      // Grab pointer to memory into which to construct the new std::string
+      void* storage = (
+        (boost::python::converter::rvalue_from_python_storage<std::string>*)
+        data)->storage.bytes;
+ 
+      // in-place construct the new std::string using the character data
+      // extraced from the python object
+      new (storage) std::string(value);
+ 
+      // Stash the memory chunk pointer for later use by boost.python
+      data->convertible = storage;
+
+      if (decref)
+          Py_DECREF(obj);
+    }
+};
+
+#endif // PY_VERSION_HEX < 0x03000000
+
 int raise_asynch_exception(long thread_id, boost::python::object exp_klass)
 {
     return PyThreadState_SetAsyncExc(thread_id, exp_klass.ptr());
 }
 
+void leave()
+{
+    AutoPythonAllowThreads guard;
+    Tango::leavefunc();
+}
+
 void export_base_types()
 {
     enum_<PyTango::ExtractAs>("ExtractAs")
@@ -317,7 +382,11 @@ void export_base_types()
     convert_numpy_to_integer<Tango::DEV_ULONG>();
     convert_numpy_to_integer<Tango::DEV_LONG64>();
     convert_numpy_to_integer<Tango::DEV_ULONG64>();
-    
+
+#if PY_VERSION_HEX < 0x03000000
+    StdString_from_python_str_unicode();
+#endif
+
     // from tango_const.h
     export_poll_device();
 
@@ -347,4 +416,6 @@ void export_base_types()
     def("raise_asynch_exception", &raise_asynch_exception);
 
     def("_get_tango_lib_release", &Tango::_convert_tango_lib_release);
+
+    def("_leavefunc", &leave);
 }
diff --git a/src/boost/cpp/callback.cpp b/src/boost/cpp/callback.cpp
index 59ae207..ce083d4 100644
--- a/src/boost/cpp/callback.cpp
+++ b/src/boost/cpp/callback.cpp
@@ -254,6 +254,11 @@ namespace {
 }
 
 
+boost::python::object PyCallBackPushEvent::get_override(const char* name)
+{ 
+    return boost::python::wrapper<Tango::CallBack>::get_override(name); 
+}
+
 
 void PyCallBackPushEvent::fill_py_event(Tango::EventData* ev, object & py_ev, object py_device, PyTango::ExtractAs extract_as)
 {
diff --git a/src/boost/cpp/callback.h b/src/boost/cpp/callback.h
index 7c2e21d..a149bb4 100644
--- a/src/boost/cpp/callback.h
+++ b/src/boost/cpp/callback.h
@@ -65,7 +65,8 @@ public:
 class PyCallBackPushEvent : public Tango::CallBack , public boost::python::wrapper<Tango::CallBack>
 {
 public:
-    PyCallBackPushEvent() : m_weak_device(0), m_extract_as(PyTango::ExtractAsNumpy) {}
+    PyCallBackPushEvent() : m_weak_device(0), m_extract_as(PyTango::ExtractAsNumpy)
+    {}
     virtual ~PyCallBackPushEvent();
 
     //! The object that will call this callback (DeviceProxy), so we can
@@ -78,8 +79,7 @@ public:
     void set_extract_as(PyTango::ExtractAs extract_as)
     {   this->m_extract_as = extract_as; }
 
-    boost::python::object get_override(const char* name)
-    { return boost::python::wrapper<Tango::CallBack>::get_override(name); }
+    boost::python::object get_override(const char* name);
     
 //     virtual void cmd_ended(Tango::CmdDoneEvent * ev);
 //     virtual void attr_read(Tango::AttrReadEvent *ev);
diff --git a/src/boost/cpp/data_ready_event_data.cpp b/src/boost/cpp/data_ready_event_data.cpp
index 073e395..6000598 100644
--- a/src/boost/cpp/data_ready_event_data.cpp
+++ b/src/boost/cpp/data_ready_event_data.cpp
@@ -12,14 +12,32 @@
 #include "precompiled_header.hpp"
 #include <tango.h>
 
+#include "exception.h"
+
 using namespace boost::python;
 
+extern boost::python::object PyTango_DevFailed;
+
 struct PyDataReadyEventData
 {
     static inline Tango::DeviceProxy* get_device(Tango::DataReadyEventData &self)
     {
         return self.device;
     }
+
+    static boost::shared_ptr<Tango::DataReadyEventData> makeDataReadyEventData()
+    {
+        Tango::DataReadyEventData *result = new Tango::DataReadyEventData;
+        return boost::shared_ptr<Tango::DataReadyEventData>(result);
+    }
+
+    static void set_errors(Tango::DataReadyEventData &event_data, 
+	                   boost::python::object &dev_failed)
+    {
+        Tango::DevFailed df;
+        boost::python::object errors = dev_failed.attr("args");
+        sequencePyDevError_2_DevErrorList(errors.ptr(), event_data.errors);
+    }
 };
 
 void export_data_ready_event_data()
@@ -27,6 +45,8 @@ void export_data_ready_event_data()
     class_<Tango::DataReadyEventData>("DataReadyEventData",
         init<const Tango::DataReadyEventData &>())
 
+        .def("__init__", boost::python::make_constructor(PyDataReadyEventData::makeDataReadyEventData))
+
         // The original Tango::EventData structure has a 'device' field.
         // However, if we returned this directly we would get a different
         // python device each time. So we are doing our weird things to make
@@ -34,14 +54,17 @@ void export_data_ready_event_data()
         // performed. So we don't return Tango::EventData::device directly.
         // See callback.cpp
         .setattr("device",object())
-        .def_readonly("attr_name", &Tango::DataReadyEventData::attr_name)
-        .def_readonly("event", &Tango::DataReadyEventData::event)
-        .def_readonly("attr_data_type", &Tango::DataReadyEventData::attr_data_type)
-        .def_readonly("ctr", &Tango::DataReadyEventData::ctr)
-        .def_readonly("err", &Tango::DataReadyEventData::err)
-        .def_readonly("reception_date", &Tango::DataReadyEventData::reception_date)
-        .add_property("errors", make_getter(&Tango::DataReadyEventData::errors, 
-            return_value_policy<copy_non_const_reference>()))
+        .def_readwrite("attr_name", &Tango::DataReadyEventData::attr_name)
+        .def_readwrite("event", &Tango::DataReadyEventData::event)
+        .def_readwrite("attr_data_type", &Tango::DataReadyEventData::attr_data_type)
+        .def_readwrite("ctr", &Tango::DataReadyEventData::ctr)
+        .def_readwrite("err", &Tango::DataReadyEventData::err)
+        .def_readwrite("reception_date", &Tango::DataReadyEventData::reception_date)
+        .add_property("errors", 
+		      make_getter(&Tango::DataReadyEventData::errors, 
+				  return_value_policy<copy_non_const_reference>()),
+		      &PyDataReadyEventData::set_errors)
+
         .def("get_date", &Tango::DataReadyEventData::get_date,
             return_internal_reference<>())
     ;
diff --git a/src/boost/cpp/device_attribute.cpp b/src/boost/cpp/device_attribute.cpp
index ad11024..01d6390 100644
--- a/src/boost/cpp/device_attribute.cpp
+++ b/src/boost/cpp/device_attribute.cpp
@@ -680,9 +680,9 @@ namespace PyDeviceAttribute
                         TANGO_CALL_ON_ATTRIBUTE_DATA_TYPE_ID( data_type, _fill_numpy_attribute, self, isImage, py_value );
                     else
                         TANGO_CALL_ON_ATTRIBUTE_DATA_TYPE_ID( data_type, _fill_list_attribute, self, isImage, py_value );
-                    break;
                 }
 #               endif
+                break;
             default:
                 raise_(PyExc_TypeError, "unsupported data_format.");
         }
diff --git a/src/boost/cpp/device_attribute.h b/src/boost/cpp/device_attribute.h
index 23828ee..2bf3952 100644
--- a/src/boost/cpp/device_attribute.h
+++ b/src/boost/cpp/device_attribute.h
@@ -122,6 +122,12 @@ namespace PyDeviceAttribute {
     template<typename TDeviceAttribute>
     boost::python::object convert_to_python(const unique_pointer<std::vector<TDeviceAttribute> >& dev_attr_vec, Tango::DeviceProxy & dev_proxy, PyTango::ExtractAs extract_as)
     {
+		if (dev_attr_vec->empty())
+		{
+			boost::python::list ls;
+			return ls;
+		}
+
         update_data_format(dev_proxy, &(*dev_attr_vec)[0], dev_attr_vec->size());
 
         // Convert the c++ vector of DeviceAttribute into a pythonic list
@@ -169,10 +175,6 @@ namespace PyDeviceAttribute {
     template<>
     inline void _fill_scalar_attribute<Tango::DEV_ENCODED>(Tango::DeviceAttribute & dev_attr, const boost::python::object & py_value)
     {
-        static const long tangoTypeConst = Tango::DEV_ENCODED;
-        typedef TANGO_const2type(tangoTypeConst) TangoScalarType;
-        typedef TANGO_const2arraytype(tangoTypeConst) TangoArrayType;
-
         /// @todo test it!!
 
         /// @todo Now I am accepting 2 strings: encoded_format, encoded_data. This
diff --git a/src/boost/cpp/device_proxy.cpp b/src/boost/cpp/device_proxy.cpp
index f99838c..08c5f03 100644
--- a/src/boost/cpp/device_proxy.cpp
+++ b/src/boost/cpp/device_proxy.cpp
@@ -372,6 +372,16 @@ namespace PyDeviceProxy
         return get_events__aux<Tango::DataReadyEventData, Tango::DataReadyEventDataList>
                                                 (py_self, event_id);
     }
+
+    static boost::shared_ptr<Tango::DeviceProxy> makeDeviceProxy1(const std::string& name)
+    {
+        return boost::shared_ptr<Tango::DeviceProxy>(new Tango::DeviceProxy(name.c_str()));
+    }
+
+    static boost::shared_ptr<Tango::DeviceProxy> makeDeviceProxy2(const std::string& name, bool b)
+    {
+      return boost::shared_ptr<Tango::DeviceProxy>(new Tango::DeviceProxy(name.c_str(), b));
+    }
 };
 
 void export_device_proxy()
@@ -394,6 +404,8 @@ void export_device_proxy()
         .def(bopy::init<const char *>())
         .def(bopy::init<const char *, bool>())
         .def(bopy::init<const Tango::DeviceProxy &>())
+        .def("__init__", boost::python::make_constructor(PyDeviceProxy::makeDeviceProxy1))
+        .def("__init__", boost::python::make_constructor(PyDeviceProxy::makeDeviceProxy2))
 
         //
         // Pickle
diff --git a/src/boost/cpp/event_data.cpp b/src/boost/cpp/event_data.cpp
index a9f7f49..f55405f 100644
--- a/src/boost/cpp/event_data.cpp
+++ b/src/boost/cpp/event_data.cpp
@@ -12,13 +12,44 @@
 #include "precompiled_header.hpp"
 #include <tango.h>
 
+#include "exception.h"
+
 using namespace boost::python;
 
+extern boost::python::object PyTango_DevFailed;
+
+namespace PyEventData
+{
+    static boost::shared_ptr<Tango::EventData> makeEventData()
+    {
+        Tango::EventData *result = new Tango::EventData;
+        result->attr_value = new Tango::DeviceAttribute();
+       return boost::shared_ptr<Tango::EventData>(result);
+    }
+
+    static void set_errors(Tango::EventData &event_data, boost::python::object &error)
+    {
+        PyObject* error_ptr = error.ptr();
+        if (PyObject_IsInstance(error_ptr, PyTango_DevFailed.ptr()))
+        {
+            Tango::DevFailed df;
+	    boost::python::object error_list = error.attr("args");
+	    sequencePyDevError_2_DevErrorList(error_list.ptr(), event_data.errors);
+        }
+        else
+        {
+            sequencePyDevError_2_DevErrorList(error_ptr, event_data.errors);
+        }
+    }
+};
+
 void export_event_data()
 {
     class_<Tango::EventData>("EventData",
         init<const Tango::EventData &>())
     
+        .def("__init__", boost::python::make_constructor(PyEventData::makeEventData))
+
         // The original Tango::EventData structure has a 'device' field.
         // However, if we returned this directly we would get a different
         // python device each time. So we are doing our weird things to make
@@ -27,8 +58,8 @@ void export_event_data()
         // See callback.cpp
         .setattr("device", object())
         
-        .def_readonly("attr_name", &Tango::EventData::attr_name)
-        .def_readonly("event", &Tango::EventData::event)
+        .def_readwrite("attr_name", &Tango::EventData::attr_name)
+        .def_readwrite("event", &Tango::EventData::event)
         
         // The original Tango::EventData structure has "get_attr_value" but
         // we can't refer it directly here because we have to extract value
@@ -36,10 +67,12 @@ void export_event_data()
         // See callback.cpp
         .setattr("attr_value", object())
         
-        .def_readonly("err", &Tango::EventData::err)
-        .def_readonly("reception_date", &Tango::EventData::reception_date)
-        .add_property("errors", make_getter(&Tango::EventData::errors, 
-            return_value_policy<copy_non_const_reference>()))
+        .def_readwrite("err", &Tango::EventData::err)
+        .def_readwrite("reception_date", &Tango::EventData::reception_date)
+        .add_property("errors", 
+		      make_getter(&Tango::EventData::errors, 
+				  return_value_policy<copy_non_const_reference>()),
+		      &PyEventData::set_errors)
         
         .def("get_date", &Tango::EventData::get_date, 
             return_internal_reference<>())
diff --git a/src/boost/cpp/fast_from_py.h b/src/boost/cpp/fast_from_py.h
index 4329aae..4abce71 100644
--- a/src/boost/cpp/fast_from_py.h
+++ b/src/boost/cpp/fast_from_py.h
@@ -281,8 +281,7 @@ inline typename TANGO_const2type(tangoTypeConst)*
     fast_python_to_tango_buffer_sequence(PyObject* py_val, long* pdim_x, long *pdim_y, const std::string &fname, bool isImage, long& res_dim_x, long& res_dim_y)
 {
     typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;
-    typedef typename TANGO_const2arraytype(tangoTypeConst) TangoArrayType;
-
+ 
     long dim_x;
     long dim_y = 0;
     Py_ssize_t len = PySequence_Size(py_val);
diff --git a/src/boost/cpp/fast_from_py_numpy.hpp b/src/boost/cpp/fast_from_py_numpy.hpp
index 6fd6cb8..7f24054 100644
--- a/src/boost/cpp/fast_from_py_numpy.hpp
+++ b/src/boost/cpp/fast_from_py_numpy.hpp
@@ -33,7 +33,6 @@ inline typename TANGO_const2type(tangoScalarTypeConst)*
     fast_python_to_tango_buffer_numpy(PyObject* py_val, long* pdim_x, long* pdim_y, const std::string &fname, bool isImage, long& res_dim_x, long& res_dim_y)
 {
     typedef typename TANGO_const2type(tangoScalarTypeConst) TangoScalarType;
-    typedef typename TANGO_const2arraytype(tangoScalarTypeConst) TangoArrayType;
     static const int typenum = TANGO_const2numpy(tangoScalarTypeConst);
     
     if (!PyArray_Check(py_val)) {
diff --git a/src/boost/cpp/server/attribute.cpp b/src/boost/cpp/server/attribute.cpp
index 0e88137..94c321a 100644
--- a/src/boost/cpp/server/attribute.cpp
+++ b/src/boost/cpp/server/attribute.cpp
@@ -87,36 +87,6 @@ namespace PyAttribute
     }
 
     /**
-     * ATTENTION: this template specialization is done to close a memory leak
-     *            that exists up to tango 7.1.1 for string read_write attributes
-     *
-     * Tango Attribute set_value wrapper for scalar string attributes
-     * 
-     * @param att attribute reference
-     * @param value new attribute value
-     */
-    /*
-    template<>
-    inline void __set_value_scalar<Tango::DEV_STRING>(Tango::Attribute &att,
-                                                      bopy::object &value)
-    {
-        Tango::DevString *v = new Tango::DevString;
-
-        if (TANGO_VERSION_HEX < 0x07020000 && att.get_writable() != Tango::READ)
-        { // MEMORY LEAK: use the python string directly instead of creating a
-          // string
-            v[0] = PyString_AsString(value.ptr());
-            att.set_value(v, 1, 0);
-        }
-        else
-        { // No memory leak here. Do the standard thing
-            from_py<Tango::DEV_STRING>::convert(value, *v);
-            att.set_value(v, 1, 0, true);
-        }
-    }
-    */
-    
-    /**
      * Tango Attribute set_value wrapper for DevEncoded attributes
      *
      * @param att attribute reference
@@ -143,6 +113,35 @@ namespace PyAttribute
         att.set_value(&val_str_real, (Tango::DevUChar*)val_real, (long)len(data));
     }
 
+    /**
+     * Tango Attribute set_value wrapper for DevEncoded attributes
+     *
+     * @param att attribute reference
+     * @param data_str new attribute data string
+     * @param data new attribute data
+     */
+    inline void __set_value(Tango::Attribute &att,
+                            bopy::str &data_str,
+                            bopy::object &data)
+    {
+        extract<Tango::DevString> val_str(data_str.ptr());
+        if (!val_str.check())
+        {
+            throw_wrong_python_data_type(att.get_name(), "set_value()");
+        }
+
+	PyObject* data_ptr = data.ptr();
+	Py_buffer view;
+	
+	if (PyObject_GetBuffer(data_ptr, &view, PyBUF_FULL_RO) < 0)
+	{
+	    throw_wrong_python_data_type(att.get_name(), "set_value()");
+	}
+
+	Tango::DevString val_str_real = val_str;
+        att.set_value(&val_str_real, (Tango::DevUChar*)view.buf, (long)view.len);	
+	PyBuffer_Release(&view);
+    }
 
     /**
      * Tango Attribute set_value_date_quality wrapper for scalar attributes
@@ -175,38 +174,37 @@ namespace PyAttribute
         att.set_value_date_quality(cpp_val.release(), tv, quality, 1, 0, true);
     }
 
-
     /**
-     * ATTENTION: this template specialization is done to close a memory leak
-     *            that exists up to tango 7.1.1 for string read_write attributes
+     * Tango Attribute set_value_date_quality wrapper for DevEncoded attributes
      *
-     * Tango Attribute set_value_date_quality wrapper for scalar string attributes
-     * 
      * @param att attribute reference
-     * @param value new attribute value
+     * @param data_str new attribute data string
+     * @param data new attribute data
      * @param t timestamp
      * @param quality attribute quality
      */
-    /*
-    template<>
-    inline void __set_value_date_quality_scalar<Tango::DEV_STRING>(Tango::Attribute &att,
-                                                bopy::object &value,
-                                                double t, Tango::AttrQuality quality)
+    inline void __set_value_date_quality(Tango::Attribute &att,
+                                         bopy::str &data_str,
+                                         bopy::str &data,
+                                         double t, Tango::AttrQuality quality)
     {
-        PYTG_NEW_TIME_FROM_DOUBLE(t, tv);
-        
-        Tango::DevString *v = new Tango::DevString;
-        if (att.get_writable() == Tango::READ)
-        { // No memory leak here. Do the standard thing
-            from_py<Tango::DEV_STRING>::convert(value, *v);
+        extract<Tango::DevString> val_str(data_str.ptr());
+        if (!val_str.check())
+        {
+            throw_wrong_python_data_type(att.get_name(), "set_value1()");
         }
-        else
-        { // MEMORY LEAK: use the python string directly instead of creating a string
-            v[0] = PyString_AsString(value.ptr());
+        extract<Tango::DevString> val(data.ptr());
+        if (!val.check())
+        {
+            throw_wrong_python_data_type(att.get_name(), "set_value2()");
         }
-        att.set_value_date_quality(v, tv, quality, 1, 0, true);
+
+        PYTG_NEW_TIME_FROM_DOUBLE(t, tv);
+        Tango::DevString val_str_real = val_str;
+        Tango::DevString val_real = val;
+        att.set_value_date_quality(&val_str_real, (Tango::DevUChar*)val_real,
+                                   (long)len(data), tv, quality);
     }
-    */
 
     /**
      * Tango Attribute set_value_date_quality wrapper for DevEncoded attributes
@@ -219,7 +217,7 @@ namespace PyAttribute
      */
     inline void __set_value_date_quality(Tango::Attribute &att,
                                          bopy::str &data_str,
-                                         bopy::str &data,
+                                         bopy::object &data,
                                          double t, Tango::AttrQuality quality)
     {
         extract<Tango::DevString> val_str(data_str.ptr());
@@ -227,17 +225,21 @@ namespace PyAttribute
         {
             throw_wrong_python_data_type(att.get_name(), "set_value1()");
         }
-        extract<Tango::DevString> val(data.ptr());
-        if (!val.check())
-        {
-            throw_wrong_python_data_type(att.get_name(), "set_value2()");
-        }
+
+	PyObject* data_ptr = data.ptr();
+	Py_buffer view;
+	
+	if (PyObject_GetBuffer(data_ptr, &view, PyBUF_FULL_RO) < 0)
+	{
+	    throw_wrong_python_data_type(att.get_name(), "set_value()");
+	}
 
         PYTG_NEW_TIME_FROM_DOUBLE(t, tv);
-        Tango::DevString val_str_real = val_str;
-        Tango::DevString val_real = val;
-        att.set_value_date_quality(&val_str_real, (Tango::DevUChar*)val_real,
-                                   (long)len(data), tv, quality);
+	Tango::DevString val_str_real = val_str;
+        att.set_value(&val_str_real, (Tango::DevUChar*)view.buf, (long)view.len);	
+        att.set_value_date_quality(&val_str_real, (Tango::DevUChar*)view.buf,
+                                   (long)view.len, tv, quality);
+	PyBuffer_Release(&view);
     }
 
     template<long tangoTypeConst>
@@ -252,7 +254,6 @@ namespace PyAttribute
             bool isImage)
     {
         typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;
-        typedef typename TANGO_const2arraytype(tangoTypeConst) TangoArrayType;
 
         if (!PySequence_Check(value.ptr()))
         {
@@ -288,8 +289,6 @@ namespace PyAttribute
         }
     }
 
-
-
     inline void __set_value(const std::string & fname, Tango::Attribute &att, bopy::object &value, long* x, long *y, double t = 0.0, Tango::AttrQuality* quality = 0)
     {
         long type = att.get_data_type();
@@ -337,6 +336,14 @@ namespace PyAttribute
             __set_value(att, data_str, data);
     }
 
+    inline void __set_value(const std::string & fname, Tango::Attribute &att, bopy::str &data_str, bopy::object &data, double t = 0.0, Tango::AttrQuality* quality = 0)
+    {
+        if (quality)
+            __set_value_date_quality(att, data_str, data, t, *quality);
+        else
+            __set_value(att, data_str, data);
+    }
+
     inline void set_value(Tango::Attribute &att, bopy::object &value)
     { __set_value("set_value", att, value, 0, 0); }
 
@@ -346,6 +353,9 @@ namespace PyAttribute
     inline void set_value(Tango::Attribute &att, bopy::str &data_str, bopy::str &data)
     { __set_value("set_value", att, data_str, data); }
 
+    inline void set_value(Tango::Attribute &att, bopy::str &data_str, bopy::object &data)
+    { __set_value("set_value", att, data_str, data); }
+
     inline void set_value(Tango::Attribute &att, bopy::object &value, long x)
     { __set_value("set_value", att, value, &x, 0); }
 
@@ -365,6 +375,13 @@ namespace PyAttribute
     { __set_value("set_value_date_quality", att, data_str, data, t, &quality); }
 
     inline void set_value_date_quality(Tango::Attribute &att,
+                                       bopy::str &data_str,
+                                       bopy::object &data,
+                                       double t,
+                                       Tango::AttrQuality quality)
+    { __set_value("set_value_date_quality", att, data_str, data, t, &quality); }
+
+    inline void set_value_date_quality(Tango::Attribute &att,
                                        bopy::object &value,
                                        double t, Tango::AttrQuality quality,
                                        long x)
@@ -893,6 +910,9 @@ void export_attribute()
             (void (*) (Tango::Attribute &, bopy::object &))
             &PyAttribute::set_value)
         .def("set_value",
+            (void (*) (Tango::Attribute &, bopy::str &, bopy::object &))
+            &PyAttribute::set_value)
+        .def("set_value",
             (void (*) (Tango::Attribute &, bopy::str &, bopy::str &))
             &PyAttribute::set_value)
         .def("set_value",
@@ -911,6 +931,9 @@ void export_attribute()
             (void (*) (Tango::Attribute &, bopy::str &, bopy::str &, double t, Tango::AttrQuality quality))
             &PyAttribute::set_value_date_quality)
         .def("set_value_date_quality",
+            (void (*) (Tango::Attribute &, bopy::str &, bopy::object &, double t, Tango::AttrQuality quality))
+            &PyAttribute::set_value_date_quality)
+        .def("set_value_date_quality",
             (void (*) (Tango::Attribute &, bopy::object &, double t, Tango::AttrQuality quality, long))
             &PyAttribute::set_value_date_quality)
         .def("set_value_date_quality",
diff --git a/src/boost/cpp/server/attribute.h b/src/boost/cpp/server/attribute.h
index 7c9ef38..0535ff3 100644
--- a/src/boost/cpp/server/attribute.h
+++ b/src/boost/cpp/server/attribute.h
@@ -22,6 +22,9 @@ namespace PyAttribute
     void set_value(Tango::Attribute &, boost::python::str &,
                    boost::python::str &);
 
+    void set_value(Tango::Attribute &, boost::python::str &,
+                   boost::python::object &);
+
     void set_value(Tango::Attribute &, boost::python::object &, long);
 
     void set_value(Tango::Attribute &, boost::python::object &, long, long);
@@ -33,6 +36,10 @@ namespace PyAttribute
                                 boost::python::str &, double,
                                 Tango::AttrQuality);
 
+    void set_value_date_quality(Tango::Attribute &, boost::python::str &,
+                                boost::python::object &, double,
+                                Tango::AttrQuality);
+
     void set_value_date_quality(Tango::Attribute &, boost::python::object &,
                                 double, Tango::AttrQuality , long);
 
diff --git a/src/boost/cpp/server/command.cpp b/src/boost/cpp/server/command.cpp
index 99e8134..82577e3 100644
--- a/src/boost/cpp/server/command.cpp
+++ b/src/boost/cpp/server/command.cpp
@@ -118,15 +118,24 @@ void insert_scalar<Tango::DEV_ENCODED>(boost::python::object &o, CORBA::Any &any
     bopy::object p1 = o[1];
 
     const char* encoded_format = bopy::extract<const char *> (p0.ptr());
-    const char* encoded_data = bopy::extract<const char *> (p1.ptr());
     
-    CORBA::ULong nb = static_cast<CORBA::ULong>(bopy::len(p1));
-    Tango::DevVarCharArray arr(nb, nb, (CORBA::Octet*)encoded_data, false);
+    PyObject* data_ptr = p1.ptr();
+    Py_buffer view;
+    
+    if (PyObject_GetBuffer(data_ptr, &view, PyBUF_FULL_RO) < 0)
+    {
+        throw_bad_type(Tango::CmdArgTypeName[Tango::DEV_ENCODED]);
+    }
+    
+    CORBA::ULong nb = static_cast<CORBA::ULong>(view.len);
+    Tango::DevVarCharArray arr(nb, nb, (CORBA::Octet*)view.buf, false);
     Tango::DevEncoded *data = new Tango::DevEncoded;
     data->encoded_format = CORBA::string_dup(encoded_format);
-    data->encoded_data = arr;
-
+    data->encoded_data = arr;    
+    
     any <<= data;
+
+    PyBuffer_Release(&view);
 }
 
 template<long tangoArrayTypeConst>
diff --git a/src/boost/cpp/server/device_class.cpp b/src/boost/cpp/server/device_class.cpp
index 63e4324..58d9c7b 100644
--- a/src/boost/cpp/server/device_class.cpp
+++ b/src/boost/cpp/server/device_class.cpp
@@ -70,18 +70,18 @@ void CppDeviceClass::create_command(const std::string &cmd_name,
 }
 
 void CppDeviceClass::create_attribute(vector<Tango::Attr *> &att_list,
-                                    const std::string &attr_name,
-                                    Tango::CmdArgType attr_type,
-                                    Tango::AttrDataFormat attr_format,
-                                    Tango::AttrWriteType attr_write,
-                                    long dim_x, long dim_y,
-                                    Tango::DispLevel display_level,
-                                    long polling_period,
-                                    bool memorized, bool hw_memorized,
-                                    const std::string &read_method_name,
-                                    const std::string &write_method_name,
-                                    const std::string &is_allowed_name,
-                                    Tango::UserDefaultAttrProp *att_prop)
+				      const std::string &attr_name,
+				      Tango::CmdArgType attr_type,
+				      Tango::AttrDataFormat attr_format,
+				      Tango::AttrWriteType attr_write,
+				      long dim_x, long dim_y,
+				      Tango::DispLevel display_level,
+				      long polling_period,
+				      bool memorized, bool hw_memorized,
+				      const std::string &read_method_name,
+				      const std::string &write_method_name,
+				      const std::string &is_allowed_name,
+				      Tango::UserDefaultAttrProp *att_prop)
 {
     //
     // Create the attribute objet according to attribute format
@@ -102,13 +102,15 @@ void CppDeviceClass::create_attribute(vector<Tango::Attr *> &att_list,
             break;
 
         case Tango::SPECTRUM:
-            spec_attr_ptr = new PySpecAttr(attr_name.c_str(), attr_type, attr_write, dim_x);
+            spec_attr_ptr = new PySpecAttr(attr_name.c_str(), attr_type,
+					   attr_write, dim_x);
             py_attr_ptr = spec_attr_ptr;
             attr_ptr = spec_attr_ptr;
             break;
 
         case Tango::IMAGE:
-            ima_attr_ptr = new PyImaAttr(attr_name.c_str(), attr_type, attr_write, dim_x, dim_y);
+            ima_attr_ptr = new PyImaAttr(attr_name.c_str(), attr_type, 
+					 attr_write, dim_x, dim_y);
             py_attr_ptr = ima_attr_ptr;
             attr_ptr = ima_attr_ptr;
             break;
@@ -178,7 +180,7 @@ void CppDeviceClassWrap::attribute_factory(std::vector<Tango::Attr *> &att_list)
 
     try
     {
-        boost::python::call_method<void>(m_self, "_DeviceClass__attribute_factory",
+        boost::python::call_method<void>(m_self, "_attribute_factory",
                                          py_att_list);
     }
     catch(boost::python::error_already_set &eas)
@@ -189,7 +191,7 @@ void CppDeviceClassWrap::attribute_factory(std::vector<Tango::Attr *> &att_list)
 
 void CppDeviceClassWrap::command_factory()
 {
-    CALL_DEVCLASS_METHOD(_DeviceClass__command_factory)
+    CALL_DEVCLASS_METHOD(_command_factory)
 }
 
 void CppDeviceClassWrap::device_name_factory(std::vector<std::string> &dev_list)
@@ -281,7 +283,8 @@ namespace PyDeviceClass
     {
         boost::python::list py_cmd_list;
         vector<Tango::Command *> cmd_list = self.get_command_list();
-        for(vector<Tango::Command *>::iterator it = cmd_list.begin(); it != cmd_list.end(); ++it)
+        for(vector<Tango::Command *>::iterator it = cmd_list.begin(); 
+	    it != cmd_list.end(); ++it)
         {
             object py_value = object(
                         handle<>(
@@ -292,37 +295,25 @@ namespace PyDeviceClass
         }
         return py_cmd_list;
     }
-        
-    /*
-    void add_device(CppDeviceClass &self, auto_ptr<Tango::DeviceImpl> dev)
+   
+    void register_signal(CppDeviceClass &self, long signo)
     {
-        self.add_device(dev.get());
-        dev.release();
+	self.register_signal(signo);
     }
 
-    void add_device(CppDeviceClass &self, auto_ptr<Tango::Device_4Impl> dev)
+#if (defined __linux)
+    
+    void register_signal(CppDeviceClass &self, long signo, bool own_handler)
     {
-        self.add_device(dev.get());
-        dev.release();
+	self.register_signal(signo, own_handler);
     }
 
-    void (*add_device1)(CppDeviceClass &, auto_ptr<Tango::DeviceImpl>) = &add_device;
-    void (*add_device2)(CppDeviceClass &, auto_ptr<Tango::Device_4Impl>) = &add_device;
-    */
-    
+#endif
 }
 
 BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS (export_device_overload,
                                         CppDeviceClass::export_device, 1, 2)
 
-#if !(defined __linux)
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS (register_signal_overload,
-                                        Tango::DeviceClass::register_signal, 1, 1)
-#else
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS (register_signal_overload,
-                                        Tango::DeviceClass::register_signal, 1, 2)
-#endif
-
 
 void export_device_class()
 {
@@ -335,18 +326,20 @@ void export_device_class()
     void (Tango::DeviceClass::*add_wiz_class_prop__)(string &,string &,string &) =
         &Tango::DeviceClass::add_wiz_class_prop;
 
-    class_<CppDeviceClass, auto_ptr<CppDeviceClassWrap>, boost::noncopyable>("_DeviceClass",
+    class_<CppDeviceClass, auto_ptr<CppDeviceClassWrap>, boost::noncopyable>("DeviceClass",
         init<const std::string &>())
 
         .def("device_factory", &CppDeviceClassWrap::device_factory)
         .def("device_name_factory", &CppDeviceClassWrap::device_name_factory)
         .def("export_device", &CppDeviceClass::export_device,
             export_device_overload())
-        //.def("_add_device", PyDeviceClass::add_device1)
-        //.def("_add_device", PyDeviceClass::add_device2)
         .def("_add_device", &CppDeviceClass::add_device)
-        .def("register_signal",&Tango::DeviceClass::register_signal,
-            register_signal_overload())
+        .def("register_signal", 
+	     (void (*) (CppDeviceClass &, long)) &PyDeviceClass::register_signal)
+#if defined __linux
+        .def("register_signal", 
+	     (void (*) (CppDeviceClass &, long, bool)) &PyDeviceClass::register_signal)
+#endif
         .def("unregister_signal", &Tango::DeviceClass::unregister_signal)
         .def("signal_handler", &Tango::DeviceClass::signal_handler,
             &CppDeviceClassWrap::default_signal_handler)
diff --git a/src/boost/cpp/server/device_impl.cpp b/src/boost/cpp/server/device_impl.cpp
index a5472e7..f6e76dc 100644
--- a/src/boost/cpp/server/device_impl.cpp
+++ b/src/boost/cpp/server/device_impl.cpp
@@ -235,6 +235,15 @@ namespace PyDeviceImpl
         attr.fire_change_event();
     }
 
+    // Special variantion for encoded data type
+    inline void push_change_event(Tango::DeviceImpl &self, str &name, str &str_data,
+                                  object &data)
+    {
+        SAFE_PUSH(self, attr, name)
+        PyAttribute::set_value(attr, str_data, data);
+        attr.fire_change_event();
+    }
+
     inline void push_change_event(Tango::DeviceImpl &self, str &name, object &data,
                                   long x)
     {
@@ -266,6 +275,15 @@ namespace PyDeviceImpl
         attr.fire_change_event();
     }
 
+    // Special variantion for encoded data type
+    inline void push_change_event(Tango::DeviceImpl &self, str &name, str &str_data,
+                                  object &data, double t, Tango::AttrQuality quality)
+    {
+        SAFE_PUSH(self, attr, name)
+        PyAttribute::set_value_date_quality(attr, str_data, data, t, quality);
+        attr.fire_change_event();
+    }
+
     inline void push_change_event(Tango::DeviceImpl &self, str &name, object &data,
                                   double t, Tango::AttrQuality quality, long x)
     {
@@ -299,7 +317,7 @@ namespace PyDeviceImpl
         SAFE_PUSH_ARCHIVE_EVENT(self, name, data);
     }
 
-    // Special variantion for encoded data type
+    // Special variation for encoded data type
     inline void push_archive_event(Tango::DeviceImpl &self, str &name, str &str_data,
                                    str &data)
     {
@@ -308,6 +326,15 @@ namespace PyDeviceImpl
         attr.fire_archive_event();
     }
 
+    // Special variation for encoded data type
+    inline void push_archive_event(Tango::DeviceImpl &self, str &name, str &str_data,
+                                   object &data)
+    {
+        SAFE_PUSH(self, attr, name)
+        PyAttribute::set_value(attr, str_data, data);
+        attr.fire_archive_event();
+    }
+
     inline void push_archive_event(Tango::DeviceImpl &self, str &name, object &data,
                            long x)
     {
@@ -339,6 +366,15 @@ namespace PyDeviceImpl
         attr.fire_archive_event();
     }
 
+    // Special variantion for encoded data type
+    inline void push_archive_event(Tango::DeviceImpl &self, str &name, str &str_data,
+                                   object &data, double t, Tango::AttrQuality quality)
+    {
+        SAFE_PUSH(self, attr, name)
+        PyAttribute::set_value_date_quality(attr, str_data, data, t, quality);
+        attr.fire_archive_event();
+    }
+
     inline void push_archive_event(Tango::DeviceImpl &self, str &name, object &data,
                                   double t, Tango::AttrQuality quality, long x)
     {
@@ -377,6 +413,16 @@ namespace PyDeviceImpl
         attr.fire_event(filt_names_, filt_vals_);
     }
 
+    // Special variantion for encoded data type
+    inline void push_event(Tango::DeviceImpl &self, str &name,
+                           object &filt_names, object &filt_vals,
+                           str &str_data, object &data)
+    {
+        AUX_SAFE_PUSH_EVENT(self, name, filt_names, filt_vals)
+        PyAttribute::set_value(attr, str_data, data);
+        attr.fire_event(filt_names_, filt_vals_);
+    }
+
     inline void push_event(Tango::DeviceImpl &self, str &name,
                            object &filt_names, object &filt_vals, object &data,
                            long x)
@@ -413,6 +459,17 @@ namespace PyDeviceImpl
         attr.fire_event(filt_names_, filt_vals_);
     }
 
+    // Special variantion for encoded data type
+    inline void push_event(Tango::DeviceImpl &self, str &name,
+                           object &filt_names, object &filt_vals,
+                           str &str_data, object &data,
+                           double t, Tango::AttrQuality quality)
+    {
+        AUX_SAFE_PUSH_EVENT(self, name, filt_names, filt_vals)
+        PyAttribute::set_value_date_quality(attr, str_data, data, t, quality);
+        attr.fire_event(filt_names_, filt_vals_);
+    }
+
     inline void push_event(Tango::DeviceImpl &self, str &name,
                            object &filt_names, object &filt_vals, object &data,
                            double t, Tango::AttrQuality quality, long x)
@@ -1255,6 +1312,10 @@ void export_device_impl()
             &PyDeviceImpl::push_change_event)
 
         .def("push_change_event",
+            (void (*) (Tango::DeviceImpl &, str &, str &, object &))
+            &PyDeviceImpl::push_change_event)
+
+        .def("push_change_event",
             (void (*) (Tango::DeviceImpl &, str &, object &, long))
             &PyDeviceImpl::push_change_event)
 
@@ -1271,6 +1332,10 @@ void export_device_impl()
             &PyDeviceImpl::push_change_event)
 
         .def("push_change_event",
+            (void (*) (Tango::DeviceImpl &, str &, str &, object &, double, Tango::AttrQuality))
+            &PyDeviceImpl::push_change_event)
+
+        .def("push_change_event",
             (void (*) (Tango::DeviceImpl &, str &, object &, double, Tango::AttrQuality, long))
             &PyDeviceImpl::push_change_event)
 
@@ -1292,6 +1357,10 @@ void export_device_impl()
             &PyDeviceImpl::push_archive_event)
 
         .def("push_archive_event",
+            (void (*) (Tango::DeviceImpl &, str &, str &, object &))
+            &PyDeviceImpl::push_archive_event)
+
+        .def("push_archive_event",
             (void (*) (Tango::DeviceImpl &, str &, object &, long))
             &PyDeviceImpl::push_archive_event)
 
@@ -1308,6 +1377,10 @@ void export_device_impl()
             &PyDeviceImpl::push_archive_event)
 
         .def("push_archive_event",
+            (void (*) (Tango::DeviceImpl &, str &, str &, object &, double, Tango::AttrQuality))
+            &PyDeviceImpl::push_archive_event)
+
+        .def("push_archive_event",
             (void (*) (Tango::DeviceImpl &, str &, object &, double, Tango::AttrQuality, long))
             &PyDeviceImpl::push_archive_event)
 
@@ -1328,6 +1401,10 @@ void export_device_impl()
             &PyDeviceImpl::push_event)
 
         .def("push_event",
+            (void (*) (Tango::DeviceImpl &, str &, object &, object &, str &, object &))
+            &PyDeviceImpl::push_event)
+
+        .def("push_event",
             (void (*) (Tango::DeviceImpl &, str &, object &, object &, object &, long))
             &PyDeviceImpl::push_event)
 
@@ -1344,6 +1421,10 @@ void export_device_impl()
             &PyDeviceImpl::push_event)
 
         .def("push_event",
+            (void (*) (Tango::DeviceImpl &, str &, object &, object &, str &, object &, double, Tango::AttrQuality))
+            &PyDeviceImpl::push_event)
+
+        .def("push_event",
             (void (*) (Tango::DeviceImpl &, str &, object &, object &, object &, double, Tango::AttrQuality, long))
             &PyDeviceImpl::push_event)
 
diff --git a/src/boost/cpp/server/device_impl.h b/src/boost/cpp/server/device_impl.h
index b998d80..eca1630 100644
--- a/src/boost/cpp/server/device_impl.h
+++ b/src/boost/cpp/server/device_impl.h
@@ -15,7 +15,7 @@
 #include <boost/python.hpp>
 #include <tango.h>
 
-#include <server/device_class.h>
+#include "server/device_class.h"
 
 /**
  * A wrapper around the Tango::DeviceImpl class
diff --git a/src/boost/cpp/server/dserver.cpp b/src/boost/cpp/server/dserver.cpp
index 904ba61..2394ff6 100644
--- a/src/boost/cpp/server/dserver.cpp
+++ b/src/boost/cpp/server/dserver.cpp
@@ -127,12 +127,6 @@ namespace PyDServer
         delete ret;
         return py_ret;
     }
-    
-    void duplicate_d_var(Tango::DServer &self)
-    {
-        Tango::Device_var d = self._this();
-        self.set_d_var(Tango::Device::_duplicate(d));
-    }
 }
 
 BOOST_PYTHON_FUNCTION_OVERLOADS(add_obj_polling_overload, PyDServer::add_obj_polling, 2, 4)
@@ -192,7 +186,6 @@ void export_dserver()
         .def("get_poll_th_pool_size", &Tango::DServer::get_poll_th_pool_size)
         .def("get_opt_pool_usage", &Tango::DServer::get_opt_pool_usage)
         .def("get_poll_th_conf", &Tango::DServer::get_poll_th_conf)
-        .def("duplicate_d_var", &PyDServer::duplicate_d_var)
     ;
     
 }
diff --git a/src/boost/cpp/server/tango_util.cpp b/src/boost/cpp/server/tango_util.cpp
index d6594af..b8a0aa1 100644
--- a/src/boost/cpp/server/tango_util.cpp
+++ b/src/boost/cpp/server/tango_util.cpp
@@ -184,9 +184,11 @@ namespace PyUtil
     
     boost::python::str get_dserver_ior(Tango::Util& self, Tango::DServer* dserver)
     {
-        const char *ior = self.get_orb()->object_to_string(dserver->_this());
-        boost::python::str ret = ior;
-        delete [] ior;
+        Tango::Device_var d = dserver->_this();
+	dserver->set_d_var(Tango::Device::_duplicate(d));
+        const char *dserver_ior = self.get_orb()->object_to_string(d);
+        boost::python::str ret = dserver_ior;
+        delete [] dserver_ior;
         return ret;
     }
 
@@ -200,8 +202,21 @@ namespace PyUtil
     
     void orb_run(Tango::Util& self)
     {
+        AutoPythonAllowThreads guard;
         self.get_orb()->run();
     }
+
+    boost::python::str get_pid_str(Tango::Util& self)
+    {
+        boost::python::str ret = self.get_pid_str().c_str();
+        return ret;
+    }
+
+    boost::python::str get_version_str(Tango::Util& self)
+    {
+        boost::python::str ret = self.get_version_str().c_str();
+        return ret;
+    }
 }
 
 void init_python()
@@ -242,12 +257,10 @@ void export_util()
             return_value_policy<copy_non_const_reference>())
         .def("get_host_name", &Tango::Util::get_host_name,
             return_value_policy<copy_non_const_reference>())
-        .def("get_pid_str", &Tango::Util::get_pid_str,
-            return_value_policy<copy_non_const_reference>())
+        .def("get_pid_str", &PyUtil::get_pid_str)
         .def("get_pid", &Tango::Util::get_pid)
         .def("get_tango_lib_release", &Tango::Util::get_tango_lib_release)
-        .def("get_version_str", &Tango::Util::get_version_str,
-            return_value_policy<copy_non_const_reference>())
+        .def("get_version_str", &PyUtil::get_version_str)
         .def("get_server_version", &Tango::Util::get_server_version,
             return_value_policy<copy_non_const_reference>())
         .def("set_server_version", &Tango::Util::set_server_version)
@@ -260,6 +273,7 @@ void export_util()
         .def("server_init", &PyUtil::server_init, server_init_overload())
         .def("server_run", &PyUtil::server_run)
         .def("server_cleanup", &Tango::Util::server_cleanup)
+        .def("shutdown_server", &Tango::Util::shutdown_server)
         .def("trigger_cmd_polling", &Tango::Util::trigger_cmd_polling)
         .def("trigger_attr_polling", &Tango::Util::trigger_attr_polling)
         .def("set_polling_threads_pool_size", &Tango::Util::set_polling_threads_pool_size)
diff --git a/src/boost/cpp/server/wattribute.cpp b/src/boost/cpp/server/wattribute.cpp
index 5d63198..d672d86 100644
--- a/src/boost/cpp/server/wattribute.cpp
+++ b/src/boost/cpp/server/wattribute.cpp
@@ -305,6 +305,54 @@ namespace PyWAttribute
     }
 
     template<>
+    inline void __set_write_value_array<Tango::DEV_STRING>(Tango::WAttribute &att,
+							   boost::python::object &seq,
+							   long x_dim, long y_dim)
+    {
+        PyObject *seq_ptr = seq.ptr();
+        long len = (long) PySequence_Size(seq_ptr);
+        twod2oned(len, x_dim, y_dim);
+
+	Tango::DevString* tg_ptr = Tango::DevVarStringArray::allocbuf(len);
+
+        for (long idx = 0; idx < len; ++idx)
+        {
+            PyObject *elt_ptr = PySequence_GetItem(seq_ptr, idx);
+
+            // The boost extract could be used:
+            // TangoScalarType val = boost::python::extract<TangoScalarType>(elt_ptr);
+            // instead of the code below.
+            // the problem is that extract is considerably slower than our
+            // convert function which only has to deal with the specific tango
+            // data types
+            try
+            {
+		Tango::DevString tg_scalar;
+                from_py<Tango::DEV_STRING>::convert(elt_ptr, tg_scalar);
+                tg_ptr[idx] = Tango::string_dup(tg_scalar);
+                Py_DECREF(elt_ptr);
+            }
+            catch(...)
+            {
+                Py_DECREF(elt_ptr);
+                delete [] tg_ptr;
+                throw;
+            }
+        }
+
+        try
+        {
+            att.set_write_value(tg_ptr, x_dim, y_dim);
+//            delete [] tg_ptr;
+        }
+        catch(...)
+        {
+            delete [] tg_ptr;
+            throw;
+        }
+    }
+
+    template<>
     inline void __set_write_value_array<Tango::DEV_ENCODED>(Tango::WAttribute &att,
                                                             boost::python::object &seq,
                                                             long x_dim, long y_dim)
@@ -452,14 +500,16 @@ namespace PyWAttribute
     void __get_write_value_pytango3<Tango::DEV_STRING>(Tango::WAttribute &att,
                                               boost::python::list &seq)
     {
-        const Tango::ConstDevString *ptr;
-
-        long length = att.get_write_value_length();
+        const Tango::ConstDevString *ptr = NULL;
 
         att.get_write_value(ptr);
 
-        for (long l = 0; l < length; ++l)
-        {
+	if (ptr == NULL) {
+	    return;
+	}
+
+        long length = att.get_write_value_length();
+        for (long l = 0; l < length; ++l) {
             seq.append(ptr[l]);
         }
     }
@@ -484,9 +534,15 @@ namespace PyWAttribute
     template<>
     void __get_write_value_scalar<Tango::DEV_STRING>(Tango::WAttribute &att, boost::python::object* obj)
     {
-        const Tango::ConstDevString *v = NULL;
+        Tango::DevString v = NULL;
         att.get_write_value(v);
-        *obj = boost::python::object(v[0]);
+
+	if(v == NULL) {
+	    *obj = boost::python::object();
+	}
+	else {
+	    *obj = boost::python::object((const char*)v);
+	}
     }
 
     template<long tangoTypeConst>
@@ -494,25 +550,39 @@ namespace PyWAttribute
     {
         typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;
 
-        const TangoScalarType * buffer;
+        const TangoScalarType *buffer = NULL;
         att.get_write_value(buffer);
+
+	if (buffer == NULL) {
+	    *obj = boost::python::object();
+	    return;
+	}
+
         size_t length = att.get_write_value_length();
         
         boost::python::list o;
-        for (size_t n = 0; n < length; ++n)
+        for (size_t n = 0; n < length; ++n) {
             o.append(buffer[n]);
+	}
         *obj = o;
     }
 
     template<>
     void __get_write_value_array_pytango3<Tango::DEV_STRING>(Tango::WAttribute &att, boost::python::object* obj)
     {
-        const Tango::ConstDevString *ptr;
+        const Tango::ConstDevString *ptr = NULL;
+
+	if (ptr == NULL) {
+	    *obj = boost::python::object();
+	    return;
+	}
+
         long length = att.get_write_value_length();
         att.get_write_value(ptr);
         boost::python::list o;
-        for (long l = 0; l < length; ++l)
+        for (long l = 0; l < length; ++l) {
             o.append(ptr[l]);
+	}
     }
 
     template<long tangoTypeConst>
@@ -520,8 +590,14 @@ namespace PyWAttribute
     {
         typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;
 
-        const TangoScalarType *buffer;
+        const TangoScalarType *buffer = NULL;
         att.get_write_value(buffer);
+
+	if (buffer == NULL) {
+	    *obj = boost::python::object();
+	    return;
+	}	
+
         size_t dim_x = att.get_w_dim_x();
         size_t dim_y = att.get_w_dim_y();
         
@@ -546,8 +622,14 @@ namespace PyWAttribute
     template<>
     void __get_write_value_array_lists<Tango::DEV_STRING>(Tango::WAttribute &att, boost::python::object* obj)
     {
-        const Tango::ConstDevString* buffer;
+        const Tango::ConstDevString* buffer= NULL;
         att.get_write_value(buffer);
+
+	if (buffer == NULL) {
+	    *obj = boost::python::object();
+	    return;
+	}
+
         size_t dim_x = att.get_w_dim_x();
         size_t dim_y = att.get_w_dim_y();
         
diff --git a/src/boost/cpp/to_py_numpy.hpp b/src/boost/cpp/to_py_numpy.hpp
index 2574d76..f5a49c2 100644
--- a/src/boost/cpp/to_py_numpy.hpp
+++ b/src/boost/cpp/to_py_numpy.hpp
@@ -17,8 +17,6 @@
 template <long tangoArrayTypeConst>
 inline boost::python::object to_py_numpy(const typename TANGO_const2type(tangoArrayTypeConst)* tg_array, boost::python::object parent)
 {
-    typedef typename TANGO_const2type(tangoArrayTypeConst) TangoArrayType;
-
     static const int typenum = TANGO_const2scalarnumpy(tangoArrayTypeConst);
 
     if (tg_array == 0) {
diff --git a/src/boost/python/__init__.py b/src/boost/python/__init__.py
index 38a9163..66a38d9 100644
--- a/src/boost/python/__init__.py
+++ b/src/boost/python/__init__.py
@@ -78,36 +78,47 @@ import os
 import sys
 
 def __prepare_nt():
-    PATH = os.environ['PATH']
+    import struct
+    PATH = os.environ.get('PATH')
+    if PATH is None:
+        os.environ["PATH"] = PATH = ""
     tango_root = os.environ.get("TANGO_ROOT")
     if tango_root is None:
         tango_root = os.path.join(os.environ["ProgramFiles"], "tango")
+    tango_root = tango_root.lower()
+    
     if sys.hexversion < 0x03030000:
         vc = "vc9_dll"
     else:
         vc = "vc10_dll"
-    tango_dll_path = os.path.join(tango_root,"win32","lib",vc)
-    if tango_dll_path.lower() not in PATH.lower():
-        os.environ['PATH'] += ";" + tango_dll_path
-
-try:
-    from ._PyTango import DeviceProxy
-except ImportError as ie:
-    if ie.args[0].count("_PyTango"):
-        print(80*"-")
-        print(ie)
-        print(80*"-")
-        print("Probably your current directory is the PyTango's source installation directory.")
-        print("You must leave this directory first before using PyTango, otherwise the")
-        print("source distribution will conflict with the installed PyTango")
-        print(80*"-")
-        raise
-    
-    # in windows try to find the location for tango
-    if os.name == 'nt':
-        __prepare_nt()
-        from ._PyTango import DeviceProxy
+    is64 = 8 * struct.calcsize("P") == 64
+    if is64:
+        arch = "win64"
+    else:
+        arch = "win32"
+    tango_dll_path = os.path.join(tango_root, arch, "lib", vc)
+    tango_dll_path = tango_dll_path.lower()
+    if os.path.exists(tango_dll_path) and \
+       tango_dll_path not in PATH.lower():
+            os.environ['PATH'] += ";" + tango_dll_path
+    else:
+        # Tango C++ could not be found on the system... 
+        # ... use PyTango's private Tango C++ library
+        tango_dll_path = os.path.dirname(os.path.abspath(__file__))
+        tango_dll_path = os.path.join(tango_dll_path, "_tango_dll_")
+        if os.path.exists(tango_dll_path):
+            os.environ['PATH'] += ";" + tango_dll_path
 
+if os.name == 'nt':
+    try:
+        from . import _PyTango
+    except ImportError as ie:
+        # in windows try to find the location for tango
+        __prepare_nt()
+        from . import _PyTango
+else:
+    from . import _PyTango
+        
 from ._PyTango import (AccessControlType, ApiUtil, ArchiveEventInfo,
     AsynCall, AsynReplyNotArrived, AttReqType, Attr, AttrConfEventData,
     AttrDataFormat, AttrList, AttrProperty, AttrQuality, AttrReadEvent,
@@ -128,7 +139,7 @@ from ._PyTango import (AccessControlType, ApiUtil, ArchiveEventInfo,
     DevVarULong64Array, DevVarULongArray, DevVarUShortArray, DevVoid,
     DeviceAttribute, DeviceAttributeConfig, DeviceAttributeHistory,
     DeviceData, DeviceDataList, DeviceDataHistory, DeviceDataHistoryList,
-    DeviceImpl, DeviceInfo, DeviceUnlocked, Device_2Impl,
+    DeviceImpl, DeviceInfo, DeviceProxy, DeviceUnlocked, Device_2Impl,
     Device_3Impl, Device_4Impl, DispLevel, EncodedAttribute, ErrSeverity,
     EventData, EventSystemFailed, EventType,
     Except, ExtractAs, GreenMode, FMT_UNKNOWN, GroupAttrReply, GroupAttrReplyList,
@@ -172,7 +183,9 @@ from .globals import get_class, get_classes, get_cpp_class, get_cpp_classes, \
     get_constructed_class, get_constructed_classes, class_factory, \
     delete_class_list, class_list, cpp_class_list, constructed_class
 from .utils import is_scalar_type, is_array_type, is_numerical_type, \
-    is_int_type, is_float_type, obj_2_str, seqStr_2_obj
+    is_int_type, is_float_type, is_bool_type, is_str_type, \
+    obj_2_str, str_2_obj, seqStr_2_obj, \
+    requires_pytango, requires_tango
 from .green import set_green_mode, get_green_mode
 from .device_proxy import get_device_proxy
 from .tango_numpy import NumpyType, numpy_type, numpy_spectrum, numpy_image
@@ -180,3 +193,10 @@ from .tango_numpy import NumpyType, numpy_type, numpy_spectrum, numpy_image
 from .pytango_init import init as __init
 __init()
 
+
+def __leave():
+    _PyTango._leavefunc()
+
+import atexit
+atexit.register(__leave)
+
diff --git a/src/boost/python/attr_data.py b/src/boost/python/attr_data.py
index c75c6d6..5812e04 100644
--- a/src/boost/python/attr_data.py
+++ b/src/boost/python/attr_data.py
@@ -274,8 +274,13 @@ class AttrData(object):
             throw_ex("Wrong polling period in attribute information for "
                      "attribute %s in class %s\nAttribute information for "
                      "polling period is not an integer" % (attr_name, name))
-        
-        memorized = extra_info.get("memorized", "false").lower()
+
+        try:
+            memorized = extra_info.get("memorized", "false").lower()
+        except:
+            throw_ex("Wrong memorized value. for attribute %s in class %s."
+                     "Allowed valued are the strings \"true\", \"false\" and "
+                     "\"true_without_hard_applied\" (case incensitive)")
         if memorized == "true":
             self.memorized = True
             self.hw_memorized = True
diff --git a/src/boost/python/client.py b/src/boost/python/client.py
index de55252..69a093a 100644
--- a/src/boost/python/client.py
+++ b/src/boost/python/client.py
@@ -9,10 +9,232 @@
 # See LICENSE.txt for more info.
 # ------------------------------------------------------------------------------
 
-"""High Level API for writting Tango clients"""
+"""
+High Level API for writting Tango clients
 
+This is an experimental module. Not part of the official API.
+"""
+
+import functools
+
+import PyTango
 from PyTango import DeviceProxy as Device
-from PyTango import AttributeProxy as Attribute
-from PyTango import Database
-from PyTango import Group
-from PyTango import DeviceAttribute
+from PyTango import CmdArgType
+from PyTango.codec import loads
+from PyTango.codec import dumps as _dumps
+
+_FMT = "pickle"
+
+dumps = functools.partial(_dumps, _FMT)
+
+def _command(device, cmd_info, *args, **kwargs):
+    name = cmd_info.cmd_name
+    if cmd_info.in_type == CmdArgType.DevEncoded:
+        result = device.command_inout(name, dumps((args, kwargs)))
+    else:
+        result = device.command_inout(name, *args, **kwargs)
+    if cmd_info.out_type == CmdArgType.DevEncoded:
+        result = loads(*result)
+    return result
+
+
+class _DeviceHelper(object):
+
+    __CMD_FILTER = set(("init", "state", "status"))
+    __ATTR_FILTER = set(("state", "status"))
+
+    def __init__(self, dev_name, *args, **kwargs):
+        self.dev_name = dev_name
+        self.device = Device(dev_name, *args, **kwargs)
+
+    def get_attr_cache(self, refresh=False):
+        try:
+            cache = self.__attr_cache
+            if not cache:
+                refresh = True
+        except AttributeError:
+            refresh = True
+        if refresh:
+            cache = {}
+            dev = self.device
+            try:
+                for attr_info in dev.attribute_list_query_ex():
+                    attr_name = attr_info.name
+                    if attr_name.lower() in self.__ATTR_FILTER:
+                        continue
+                    cache[attr_name] = attr_info
+            except PyTango.DevFailed:
+                pass
+            self.__attr_cache = cache
+        return cache
+
+    def get_attr_info(self, name):
+        cache = self.get_attr_cache()
+        result = cache.get(name)
+        if result:
+            return result
+        else:
+            cache = self.get_attr_cache(refresh=True)
+            return cache.get(name)
+
+    def get_cmd_cache(self, refresh=False):
+        try:
+            cache = self.__cmd_cache
+            if not cache:
+                refresh = True
+        except AttributeError:
+            refresh = True
+        if refresh:
+            cache = {}
+            dev = self.device
+            try:
+                for cmd_info in dev.command_list_query():
+                    cmd_name = cmd_info.cmd_name
+                    if cmd_name.lower() in self.__CMD_FILTER:
+                        continue
+                    cmd_func = functools.partial(_command, dev, cmd_info)
+                    cmd_func.__name__ = cmd_name
+                    cmd_func.__doc__ = cmd_info.in_type_desc
+                    cmd_info.func = cmd_func
+                    cache[cmd_name] = cmd_info
+            except PyTango.DevFailed:
+                pass
+            self.__cmd_cache = cache
+        return cache        
+
+    def get_cmd_info(self, name):
+        cache = self.get_cmd_cache()
+        result = cache.get(name)
+        if result:
+            return result
+        else:
+            cache = self.get_cmd_cache(refresh=True)
+            return cache.get(name)
+
+    def is_cmd(self, name):
+        return name.lower() in self.get_cmd_cache()
+
+    def members(self):
+        result = self.get_attr_cache().keys()
+        result.extend(self.get_cmd_cache().keys())
+        return result
+
+    def get(self, name):
+        dev = self.device
+        result = self.get_attr_info(name)
+        if result:
+            result = dev.read_attribute(name)
+            value = result.value
+            if result.type == PyTango.DevEncoded:
+                result = loads(*value)
+            else:
+                result = value
+            return result
+        result = self.get_cmd_info(name)
+        if result is None:
+            raise KeyError("Unknown %s" % name)
+        return result
+
+    def set(self, name, value):
+        result = self.get_attr_info(name)
+        if result is None:
+            raise KeyError("Unknown attribute %s" % name)
+        if result.data_type == PyTango.DevEncoded:
+            self.device.write_attribute(name, dumps(value))
+        else:
+            self.device.write_attribute(name, value)
+        
+    def get_info(self):
+        try:
+            return self.__info
+        except AttributeError:
+            pass
+        try:
+            info = self.device.info()
+            self.__dict__["__info"] = info
+            return info
+        except PyTango.DevFailed:
+            return None
+    
+    def __str__(self):
+        return self.dstr()
+
+    def __repr__(self):
+        return str(self)
+
+    def dstr(self):
+        info = self.get_info()
+        klass = "Device"
+        if info:
+            klass = info.dev_class
+        return "{0}({1})".format(klass, self.dev_name)
+
+class _Device(object):
+    """Tango object"""
+    
+    def __init__(self, dev_name, *args, **kwargs):
+        helper = _DeviceHelper(dev_name, *args, **kwargs)
+        self.__dict__["_helper"] = helper
+    
+    def __getattr__(self, name):
+        r = self._helper.get(name)
+        if isinstance(r, PyTango.CommandInfo):
+            self.__dict__[name] = r.func
+            return r.func
+        return r
+
+    def __setattr__(self, name, value):
+        try:
+            return self._helper.set(name, value)
+        except KeyError:
+            object.__setattr__(self, name, value)
+
+    def __str__(self):
+        return str(self._helper)
+
+    def __repr__(self):
+        return repr(self._helper)
+
+    def __dir__(self):
+        return self._helper.members()
+
+
+def Object(*args, **kwargs):
+    """Experimental class. Not part of the official API"""
+    return _Device(*args, **kwargs)
+
+
+def get_object_proxy(obj):
+    """Experimental function. Not part of the official API"""    
+    return obj._helper.device
+
+
+def get_object_db(obj):
+    """Experimental function. Not part of the official API"""    
+    return get_object_proxy(obj).get_device_db()
+
+
+def get_object_name(obj):
+    """Experimental function. Not part of the official API"""    
+    return get_object_proxy(obj).get_name()
+
+
+def get_object_info(obj):
+    """Experimental function. Not part of the official API"""    
+    return get_object_proxy(obj).info()
+
+
+def get_attributes_config(obj, refresh=False):
+    """Experimental function. Not part of the official API"""    
+    return obj._helper.get_attr_cache(refresh=refresh)
+
+
+def get_commands_config(obj, refresh=False):
+    """Experimental function. Not part of the official API"""
+    return obj._helper.get_cmd_cache(refresh=refresh)
+
+
+def connect(obj, signal, slot, event_type=None):
+    """Experimental function. Not part of the official API"""    
+    raise NotImplementedError
+    
diff --git a/src/boost/python/codec.py b/src/boost/python/codec.py
new file mode 100644
index 0000000..5f99d49
--- /dev/null
+++ b/src/boost/python/codec.py
@@ -0,0 +1,22 @@
+__all__ = ["loads", "dumps"]
+
+def loads(fmt, data):
+    if fmt.startswith("pickle"):
+        import pickle
+        loads = pickle.loads
+    elif fmt.startswith("json"):
+        import json
+        loads = json.loads
+    else:
+        raise TypeError("Format '{0}' not supported".format(fmt))
+    return loads(data)
+
+def dumps(fmt, obj):
+    if fmt.startswith("pickle"):
+        import pickle
+        ret = fmt, pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
+        return ret
+    elif fmt.startswith("json"):
+        import json
+        return fmt, json.dumps(obj)
+    raise TypeError("Format '{0}' not supported".format(fmt))
diff --git a/src/boost/python/connection.py b/src/boost/python/connection.py
index a576e3f..5850b1c 100644
--- a/src/boost/python/connection.py
+++ b/src/boost/python/connection.py
@@ -130,7 +130,9 @@ def __Connection__command_inout_raw(self, cmd_name, cmd_param = None):
 
 def __Connection__command_inout_asynch(self, cmd_name, *args):
     """
-    command_inout_asynch(self, cmd_name, cmd_param=None, forget=False) -> id
+    command_inout_asynch(self, cmd_name) -> id
+    command_inout_asynch(self, cmd_name, cmd_param) -> id    
+    command_inout_asynch(self, cmd_name, cmd_param, forget) -> id
     
             Execute asynchronously (polling model) a command on a device
             
@@ -139,11 +141,11 @@ def __Connection__command_inout_asynch(self, cmd_name, *args):
                 - cmd_param : (any) It should be a value of the type expected by the
                               command or a DeviceData object with this value inserted.
                               It can be ommited if the command should not get any argument.
-                              If the command sould get no argument and you want
+                              If the command should get no argument and you want
                               to set the 'forget' param, use None for cmd_param.
                 - forget    : (bool) If this flag is set to true, this means that the client
                               does not care at all about the server answer and will even
-                              not try to get it. A false default value is provided. Please,
+                              not try to get it. Default value is False. Please,
                               note that device re-connection will not take place (in case
                               it is needed) if the fire and forget mode is used. Therefore,
                               an application using only fire and forget requests is not able
diff --git a/src/boost/python/constants.py~ b/src/boost/python/constants.py~
new file mode 100644
index 0000000..e69de29
diff --git a/src/boost/python/databaseds/DataBaseds b/src/boost/python/databaseds/DataBaseds
index 2f8cf3c..cb35c0f 100755
--- a/src/boost/python/databaseds/DataBaseds
+++ b/src/boost/python/databaseds/DataBaseds
@@ -1,9 +1,6 @@
 #!/usr/bin/env python
 
-import database
-
-def main():
-    database.main()
+from database import main
     
 if __name__ == '__main__':
     main()
diff --git a/src/boost/python/databaseds/create_db.sql b/src/boost/python/databaseds/create_db.sql
index 34f6703..3ffdd7a 100644
--- a/src/boost/python/databaseds/create_db.sql
+++ b/src/boost/python/databaseds/create_db.sql
@@ -20,6 +20,11 @@ INSERT INTO object_history_id VALUES (0);
 
 INSERT INTO device VALUES ('sys/database/2',NULL,'sys','database','2','nada','nada','nada','DataBaseds/2','nada','DataBase','nada','nada','nada','nada');
 INSERT INTO device VALUES ('dserver/DataBaseds/2',NULL,'dserver','DataBaseds','2','nada','nada','nada','DataBaseds/2','nada','DServer','nada','nada','nada','nada');
+INSERT INTO server VALUES ('databaseds/2','',0,0);
+
+INSERT INTO device VALUES ('sys/database/pydb-test',NULL,'sys','database','pydb-test','nada','nada','nada','DataBaseds/pydb-test','nada','DataBase','nada','nada','nada','nada');
+INSERT INTO device VALUES ('dserver/DataBaseds/pydb-test',NULL,'dserver','DataBaseds','pydb-test','nada','nada','nada','DataBaseds/pydb-test','nada','DServer','nada','nada','nada','nada');
+INSERT INTO server VALUES ('databaseds/pydb-test','',0,0);
 
 #
 # Create entry for test device server in device table
@@ -27,6 +32,7 @@ INSERT INTO device VALUES ('dserver/DataBaseds/2',NULL,'dserver','DataBaseds','2
 
 INSERT INTO device VALUES ('sys/tg_test/1',NULL,'sys','tg_test','1','nada','nada','nada','TangoTest/test','nada','TangoTest','nada','nada','nada','nada');
 INSERT INTO device VALUES ('dserver/TangoTest/test',NULL,'dserver','TangoTest','test','nada','nada','nada','TangoTest/test','nada','DServer','nada','nada','nada','nada');
+INSERT INTO server VALUES ('tangotest/test','',0,0);
 
 #
 # Create entry for Tango Control Access in device table
diff --git a/src/boost/python/databaseds/database.py b/src/boost/python/databaseds/database.py
index 6092884..d9ee34c 100644
--- a/src/boost/python/databaseds/database.py
+++ b/src/boost/python/databaseds/database.py
@@ -33,10 +33,25 @@ __docformat__ = 'restructuredtext'
 import PyTango
 import sys
 # Add additional import
+
+import time
+
 #----- PROTECTED REGION ID(DataBase.additionnal_import) ENABLED START -----#
 
 import logging
 
+try:
+    import argparse
+except ImportError:
+    argparse = None
+    from optparse import OptionParser
+
+from PyTango.globals import get_class, get_class_by_class, \
+    get_constructed_class_by_class
+
+#Argument Options
+global options
+
 class DbInter(PyTango.Interceptors):
 
     def create_thread(self):
@@ -61,11 +76,6 @@ th_exc = PyTango.Except.throw_exception
 
 from db_errors import *
 
-DB_FRONTED = {
-    "sqlite3" : "db2",
-    "pyodbc" : "pyodbc"
-}
-
 def check_device_name(dev_name):
     if '*' in dev_name:
         return False, None, None
@@ -99,6 +109,18 @@ def replace_wildcard(text):
     text = text.replace("*", "%")
     return text
 
+class TimeStructure:
+    def __init__(self):
+        self.average = 0
+        self.minimum = 0
+        self.maximum = 0
+        self.maximum = 0
+        self.total_elapsed = 0
+        self.calls = 0
+        self.index = ''
+
+
+cmd_list = ["DbImportDevice", "DbExportDevice", "DbGetHostServerList", "DbGetHostList","DbGetServerList", "DbGetDevicePropertyList", "DbGetClassPropertyList", "DbGetDeviceMemberList", "DbGetDeviceFamilyList", "DbGetDeviceDomainList", "DbGetDeviceProperty", "DbPutDeviceProperty",  "DbDeleteDeviceProperty", "DbInfo", "DbGetDeviceClassList", "DbGetDeviceAttributeProperty", "DbPutDeviceAttributeProperty", "DbGetDeviceAttributeProperty2", "DbPutDeviceAttributeProperty2", "DbUnExportServer", " [...]
 
 #----- PROTECTED REGION END -----#	//	DataBase.additionnal_import
 
@@ -111,6 +133,24 @@ def replace_wildcard(text):
 class DataBase (PyTango.Device_4Impl):
 
 #--------- Add you global variables here --------------------------
+
+    def  update_timing_stats(self, time_before, time_after, cmd_name):
+        tmp_time = self.timing_maps[cmd_name]
+        time_elapsed = (time_after - time_before) * 1000.
+        tmp_time.total_elapsed = tmp_time.total_elapsed + time_elapsed
+        if time_elapsed > tmp_time.maximum:
+            tmp_time.maximum = time_elapsed
+        if time_elapsed < tmp_time.minimum or tmp_time.minimum == 0:
+            tmp_time.minimum = time_elapsed
+        tmp_time.calls = tmp_time.calls + 1
+        tmp_time.average = tmp_time.total_elapsed/tmp_time.calls
+
+    def init_timing_stats(self):
+        self.timing_maps = {}
+        for cmd in cmd_list:
+            self.timing_maps[cmd] = TimeStructure()
+            self.timing_maps[cmd].index = cmd
+
 #----- PROTECTED REGION ID(DataBase.global_variables) ENABLED START -----#
 
     def get_device_properties(self, device_klass):
@@ -147,10 +187,13 @@ class DataBase (PyTango.Device_4Impl):
         self.attr_Timing_calls_read = [0.0]
         self.attr_Timing_index_read = ['']
         self.attr_Timing_info_read = ['']
+        self.init_timing_stats()
         #----- PROTECTED REGION ID(DataBase.init_device) ENABLED START -----#
+        m = __import__('db_access.%s' % (options.db_access),None,None,
+                       'db_access.%s' % (options.db_access))
+        self.db = m.get_db()
 
-        self.db = db_access.Tango_sqlite3()
-
+        self.set_state(PyTango.DevState.ON)
 
         #----- PROTECTED REGION END -----#	//	DataBase.init_device
 
@@ -186,6 +229,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".read_Timing_average()")
         #----- PROTECTED REGION ID(DataBase.Timing_average_read) ENABLED START -----#
 
+        self.attr_Timing_average_read[:] = []
+        for tmp_name in cmd_list:
+            self.attr_Timing_average_read.append(self.timing_maps[tmp_name].average)
+        
         #----- PROTECTED REGION END -----#	//	DataBase.Timing_average_read
         attr.set_value(self.attr_Timing_average_read)
 
@@ -196,6 +243,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".read_Timing_minimum()")
         #----- PROTECTED REGION ID(DataBase.Timing_minimum_read) ENABLED START -----#
 
+        self.attr_Timing_minimum_read[:] = []
+        for tmp_name in cmd_list:
+            self.attr_Timing_minimum_read.append(self.timing_maps[tmp_name].minimum)
+
         #----- PROTECTED REGION END -----#	//	DataBase.Timing_minimum_read
         attr.set_value(self.attr_Timing_minimum_read)
 
@@ -206,6 +257,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".read_Timing_maximum()")
         #----- PROTECTED REGION ID(DataBase.Timing_maximum_read) ENABLED START -----#
 
+        self.attr_Timing_maximum_read[:] = []
+        for tmp_name in cmd_list:
+            self.attr_Timing_maximum_read.append(self.timing_maps[tmp_name].maximum)
+
         #----- PROTECTED REGION END -----#	//	DataBase.Timing_maximum_read
         attr.set_value(self.attr_Timing_maximum_read)
 
@@ -216,6 +271,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".read_Timing_calls()")
         #----- PROTECTED REGION ID(DataBase.Timing_calls_read) ENABLED START -----#
 
+        self.attr_Timing_calls_read[:] = []
+        for tmp_name in cmd_list:
+            self.attr_Timing_calls_read.append(self.timing_maps[tmp_name].calls)
+
         #----- PROTECTED REGION END -----#	//	DataBase.Timing_calls_read
         attr.set_value(self.attr_Timing_calls_read)
 
@@ -226,6 +285,9 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".read_Timing_index()")
         #----- PROTECTED REGION ID(DataBase.Timing_index_read) ENABLED START -----#
 
+        for tmp_name in cmd_list:
+            self.attr_Timing_index_read.append(self.timing_maps[tmp_name].index)
+
         #----- PROTECTED REGION END -----#	//	DataBase.Timing_index_read
         attr.set_value(self.attr_Timing_index_read)
 
@@ -235,7 +297,17 @@ class DataBase (PyTango.Device_4Impl):
     def read_Timing_info(self, attr):
         self.debug_stream("In " + self.get_name() + ".read_Timing_info()")
         #----- PROTECTED REGION ID(DataBase.Timing_info_read) ENABLED START -----#
-
+        self.attr_Timing_info_read[:] = []   
+        util = PyTango.Util.instance()
+        self.attr_Timing_info_read.append("TANGO Database Timing info on host " + util.get_host_name())
+        self.attr_Timing_info_read.append(" ")
+        self.attr_Timing_info_read.append("command	average	minimum	maximum	calls")
+        self.attr_Timing_info_read.append(" ")
+
+                                          
+        for tmp_name in cmd_list:
+            tmp_info = "%41s\t%6.3f\t%6.3f\t%6.3f\t%.0f"%(tmp_name, self.timing_maps[tmp_name].average, self.timing_maps[tmp_name].minimum, self.timing_maps[tmp_name].maximum, self.timing_maps[tmp_name].calls)
+            self.attr_Timing_info_read.append(tmp_info)
         #----- PROTECTED REGION END -----#	//	DataBase.Timing_info_read
         attr.set_value(self.attr_Timing_info_read)
 
@@ -318,8 +390,6 @@ class DataBase (PyTango.Device_4Impl):
                    "DataBase::AddServer()")
         server_name = argin[0]
 
-        db = self.db
-        cursor = db.get_cursor()
         for i in range((len(argin) - 1) / 2):
             d_name, klass_name = argin[i * 2 + 1], argin[i * 2 + 2]
             ret, dev_name, dfm = check_device_name(d_name)
@@ -327,10 +397,9 @@ class DataBase (PyTango.Device_4Impl):
                 th_exc(DB_IncorrectDeviceName,
                       "device name (" + d_name + ") syntax error (should be [tango:][//instance/]domain/family/member)",
                       "DataBase::AddServer()")
-            db.add_device(server_name, (dev_name, dfm) , klass_name, cursor=cursor)
+            self.db.add_device(server_name, (dev_name, dfm) , klass_name)
+
 
-        cursor.connection.commit()
-        cursor.close()
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbAddServer
 
@@ -401,12 +470,8 @@ class DataBase (PyTango.Device_4Impl):
 
         klass_name, attr_name = argin[:2]
 
-        db = self.db
-        cursor = db.get_cursor()
         for prop_name in argin[2:]:
-            db.delete_class_attribute_property(klass_name, attr_name, prop_name, cursor=cursor)
-        cursor.connection.commit()
-        cursor.close()
+            self.db.delete_class_attribute_property(klass_name, attr_name, prop_name)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbDeleteClassAttributeProperty
 
@@ -427,12 +492,8 @@ class DataBase (PyTango.Device_4Impl):
 
         klass_name = argin[0]
 
-        db = self.db
-        cursor = db.get_cursor()
         for prop_name in argin[1:]:
-            db.delete_class_property(prop_name, cursor=cursor)
-        cursor.connection.commit()
-        cursor.close()
+            self.db.delete_class_property(prop_name)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbDeleteClassProperty
 
@@ -500,7 +561,7 @@ class DataBase (PyTango.Device_4Impl):
 
         ret, dev_name, dfm = check_device_name(argin)
         if not ret:
-            self.warn_stream("DataBase::db_delete_device_attribute(): device name " + dev_name + " incorrect ")
+            self.warn_stream("DataBase::db_delete_device_attribute(): device name " + argin + " incorrect ")
             th_exc(DB_IncorrectDeviceName,
                    "failed to delete device attribute, device name incorrect",
                    "DataBase::DeleteDeviceAttribute()")
@@ -535,17 +596,14 @@ class DataBase (PyTango.Device_4Impl):
 
         ret, dev_name, dfm = check_device_name(argin)
         if not ret:
-            self.warn_stream("DataBase::db_delete_device_attribute_property(): device name " + dev_name + " incorrect ")
+            self.warn_stream("DataBase::db_delete_device_attribute_property(): device name " + argin + " incorrect ")
             th_exc(DB_IncorrectDeviceName,
                    "failed to delete device attribute property, device name incorrect",
                    "DataBase::DeleteDeviceAttributeProperty()")
 
-        db = self.db
-        cursor = db.get_cursor()
+ 
         for prop_name in argin[2:]:
-            self.db.delete_device_attribute_property(dev_name, attr_name, prop_name, cursor=cursor)
-        cursor.connection.commit()
-        cursor.close()
+            self.db.delete_device_attribute_property(dev_name, attr_name, prop_name)
 
 
 
@@ -566,13 +624,14 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbDeleteDeviceProperty()")
         #----- PROTECTED REGION ID(DataBase.DbDeleteDeviceProperty) ENABLED START -----#
 
+        time_before = time.time()
+
         dev_name = argin[0]
-        db = self.db
-        cursor = db.get_cursor()
         for prop_name in argin[1:]:
             self.db.delete_device_property(dev_name, prop_name)
-        cursor.connection.commit()
-        cursor.close()
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbDeleteDeviceProperty")
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbDeleteDeviceProperty
 
@@ -591,13 +650,9 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbDeleteProperty()")
         #----- PROTECTED REGION ID(DataBase.DbDeleteProperty) ENABLED START -----#
 
-        obj_name = argin[0];
-        db = self.db
-        cursor = db.get_cursor()
+        obj_name = argin[0]
         for prop_name in argin[1:]:
             self.db.delete_property(obj_name, prop_name)
-        cursor.connection.commit()
-        cursor.close()
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbDeleteProperty
 
@@ -657,7 +712,7 @@ class DataBase (PyTango.Device_4Impl):
         :rtype: PyTango.DevVoid """
         self.debug_stream("In " + self.get_name() + ".DbExportDevice()")
         #----- PROTECTED REGION ID(DataBase.DbExportDevice) ENABLED START -----#
-
+        time_before = time.time()
         if len(argin) < 5:
             self.warn_stream("DataBase::DbExportDevice(): insufficient export info for device ")
             th_exc(DB_IncorrectArguments,
@@ -665,9 +720,12 @@ class DataBase (PyTango.Device_4Impl):
                    "DataBase::ExportDevice()")
 
         dev_name, IOR, host, pid, version = argin[:5]
+        dev_name = dev_name.lower()
         if pid.lower() == 'null':
             pid = "-1"
         self.db.export_device(dev_name, IOR, host, pid, version)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbExportDevice")
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbExportDevice
 
@@ -688,6 +746,7 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbExportEvent()")
         #----- PROTECTED REGION ID(DataBase.DbExportEvent) ENABLED START -----#
 
+        time_before = time.time()
         if len(argin) < 5:
             self.warn_stream("DataBase::db_export_event(): insufficient export info for event ")
             th_exc(DB_IncorrectArguments,
@@ -697,6 +756,8 @@ class DataBase (PyTango.Device_4Impl):
         event, IOR, host, pid, version = argin[:5]
         event = replace_wildcard(event.lower())
         self.db.export_event(event, IOR, host, pid, version)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbExportEvent")
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbExportEvent
 
@@ -785,10 +846,7 @@ class DataBase (PyTango.Device_4Impl):
         #----- PROTECTED REGION ID(DataBase.DbGetClassAttributeList) ENABLED START -----#
 
         class_name = argin[0]
-        if len(argin) > 1:
-            wildcard = replace_wildcard(argin[1])
-        else:
-            wildcard = "%"
+        wildcard = replace_wildcard(argin[1])
 
         argout = self.db.get_class_attribute_list(class_name, wildcard)
 
@@ -817,7 +875,7 @@ class DataBase (PyTango.Device_4Impl):
         #----- PROTECTED REGION ID(DataBase.DbGetClassAttributeProperty) ENABLED START -----#
 
         class_name = argin[0]
-        argout = self.db.get_class_attribute_property(clas_name, argout[1:])
+        argout = self.db.get_class_attribute_property(class_name, argin[1:])
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassAttributeProperty
         return argout
@@ -850,7 +908,7 @@ class DataBase (PyTango.Device_4Impl):
         #----- PROTECTED REGION ID(DataBase.DbGetClassAttributeProperty2) ENABLED START -----#
 
         class_name = argin[0]
-        argout = self.db.get_class_attribute_property2(clas_name, argout[1:])
+        argout = self.db.get_class_attribute_property2(class_name, argin[1:])
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassAttributeProperty2
         return argout
@@ -875,7 +933,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetClassAttributePropertyHist()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetClassAttributePropertyHist) ENABLED START -----#
-
+        class_name = argin[0]
+        attribute = replace_wildcard(argin[1])
+        prop_name = replace_wildcard(argin[2])
+        argout = self.db.get_class_attribute_property_hist(class_name, attribute, prop_name)
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassAttributePropertyHist
         return argout
 
@@ -894,7 +955,7 @@ class DataBase (PyTango.Device_4Impl):
         #----- PROTECTED REGION ID(DataBase.DbGetClassForDevice) ENABLED START -----#
 
         argout = self.db.get_class_for_device(argin)
-
+        print argout
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassForDevice
         return argout
 
@@ -962,6 +1023,8 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetClassProperty) ENABLED START -----#
 
+        class_name = argin[0]
+        argout = self.db.get_class_property(class_name,argint[1:])
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassProperty
         return argout
 
@@ -984,6 +1047,10 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetClassPropertyHist) ENABLED START -----#
 
+        class_name = argin[0]
+        prop_name = argin[1]
+        argout = self.db.get_class_property_hist(class_name, prop_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassPropertyHist
         return argout
 
@@ -1000,6 +1067,16 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetClassPropertyList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetClassPropertyList) ENABLED START -----#
+        time_before = time.time()
+        if not argin:
+            argin = "%"
+        else:
+            argin = replace_wildcard(argin)
+
+        argout = self.db.get_class_property_list(argin)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbDeleteDeviceProperty")
+        
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetClassPropertyList
         return argout
@@ -1018,6 +1095,15 @@ class DataBase (PyTango.Device_4Impl):
         argout = ''
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceAlias) ENABLED START -----#
 
+        ret, dev_name, dfm = check_device_name(argin)
+        if not ret:
+            th_exc(DB_IncorrectDeviceName,
+                  "device name (" + argin + ") syntax error (should be [tango:][//instance/]domain/family/member)",
+                  "DataBase::DbGetDeviceAlias()")
+
+        argout = self.db.get_device_alias(dev_name)
+
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceAlias
         return argout
 
@@ -1034,6 +1120,12 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetDeviceAliasList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceAliasList) ENABLED START -----#
+        if not argin:
+            argin = "%"
+        else:
+            argin = replace_wildcard(argin)
+
+        argout = self.db.get_device_alias_list(argin)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceAliasList
         return argout
@@ -1054,6 +1146,15 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributeList) ENABLED START -----#
 
+        dev_name = argin[0]
+        wildcard = argin[1]
+        if not wildcard:
+            wildcard = "%"
+        else:
+            wildcard = replace_wildcard(wildcard)
+
+        argout = self.db.get_device_attribute_list(dev_name, wildcard)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceAttributeList
         return argout
 
@@ -1077,6 +1178,11 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetDeviceAttributeProperty()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributeProperty) ENABLED START -----#
+        time_before = time.time()
+        dev_name = argin[0]
+        argout = self.db.get_device_attribute_property(class_name, argin[1:])
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceAttributeProperty")
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceAttributeProperty
         return argout
@@ -1109,6 +1215,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributeProperty2) ENABLED START -----#
 
+        time_before = time.time()
+
+        dev_name = argin[0]
+        argout = self.db.get_device_attribute_property2(class_name, argin[1:])
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceAttributeProperty2")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceAttributeProperty2
         return argout
 
@@ -1133,6 +1246,11 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributePropertyHist) ENABLED START -----#
 
+        dev_name = argin[0]
+        attribute = replace_wildcard(argin[1])
+        prop_name = replace_wildcard(argin[2])
+        argout = self.db.get_device_attribute_property_hist(dev_name, attribute, prop_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceAttributePropertyHist
         return argout
 
@@ -1153,6 +1271,12 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceClassList) ENABLED START -----#
 
+        time_before = time.time()
+        
+        argout = self.db.get_device_class_list(argin)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceClassList")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceClassList
         return argout
 
@@ -1170,6 +1294,12 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceDomainList) ENABLED START -----#
 
+        time_before = time.time()
+        argin = replace_wildcard(argin)
+        argout = self.db.get_device_domain_list(argin)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceDomainList")
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceDomainList
         return argout
 
@@ -1187,6 +1317,14 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceExportedList) ENABLED START -----#
 
+        time_before = time.time()
+        
+        argin = replace_wildcard(argin)
+        argout = self.db.get_device_exported_list(argin)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceExportedList")
+
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceExportedList
         return argout
 
@@ -1205,6 +1343,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceFamilyList) ENABLED START -----#
 
+        time_before = time.time()
+
+        argin = replace_wildcard(argin)
+        argout = self.db.get_device_family_list(argin)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceFamilyList")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceFamilyList
         return argout
 
@@ -1230,8 +1375,16 @@ class DataBase (PyTango.Device_4Impl):
         :rtype: PyTango.DevVarLongStringArray """
         self.debug_stream("In " + self.get_name() + ".DbGetDeviceInfo()")
         argout = [0], ['']
+
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceInfo) ENABLED START -----#
+        ret, dev_name, dfm = check_device_name(argin)
+        if not ret:
+            th_exc(DB_IncorrectDeviceName,
+                  "device name (" + argin + ") syntax error (should be [tango:][//instance/]domain/family/member)",
+                  "DataBase::DbGetDeviceAlias()")
 
+        argout = self.db.get_device_info(dev_name)
+ 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceInfo
         return argout
 
@@ -1249,7 +1402,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetDeviceList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceList) ENABLED START -----#
-
+        server_name = replace_wildcard(argin[0])
+        class_name = replace_wildcard(argin[1])
+        argout = self.db.get_device_list(server_name, class_name)
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceList
         return argout
 
@@ -1266,7 +1422,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetDeviceWideList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceWideList) ENABLED START -----#
-
+        
+        argin = replace_wildcard(argin)
+        argout = self.db.get_device_wide_list(argin)
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceWideList
         return argout
 
@@ -1285,6 +1444,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceMemberList) ENABLED START -----#
 
+        time_before = time.time()
+
+        argin = replace_wildcard(argin)
+        argout = self.db.get_device_member_list(argin)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceMemberList")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceMemberList
         return argout
 
@@ -1313,6 +1479,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceProperty) ENABLED START -----#
 
+        time_before = time.time()
+
+        device_name = argin[0]
+        argout = self.db.get_device_property(device_name, argin[1:])
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDeviceProperty")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceProperty
         return argout
 
@@ -1323,7 +1496,7 @@ class DataBase (PyTango.Device_4Impl):
         """ Retrieve device  property history
         
         :param argin: Str[0] = Device name
-        Str[2] = Property name
+        Str[1] = Property name
         :type: PyTango.DevVarStringArray
         :return: Str[0] = Property name
         Str[1] = date
@@ -1335,6 +1508,11 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDevicePropertyHist) ENABLED START -----#
 
+        device_name = argin[0]
+        prop_name = argin[1]
+        argout = self.db.get_device_property_hist(device_name, prop_name)
+        
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDevicePropertyHist
         return argout
 
@@ -1354,6 +1532,17 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDevicePropertyList) ENABLED START -----#
 
+        time_before = time.time()
+        
+        device_name = argin[0]
+        prop_filter = argin[1]
+    
+        prop_filter = replace_wildcard(prop_filter)
+            
+        argout = self.db.get_device_property_list(device_name, prop_filter)
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetDevicePropertyList")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDevicePropertyList
         return argout
 
@@ -1370,6 +1559,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetDeviceServerClassList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDeviceServerClassList) ENABLED START -----#
+        
+        argin = replace_wildcard(argin)
+            
+        argout = self.db.get_server_class_list(argin)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDeviceServerClassList
         return argout
@@ -1387,6 +1580,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetExportdDeviceListForClass()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetExportdDeviceListForClass) ENABLED START -----#
+        
+        argin = replace_wildcard(argin)
+            
+        argout = self.db.get_exported_device_list_for_class(argin)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetExportdDeviceListForClass
         return argout
@@ -1405,6 +1602,14 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetHostList) ENABLED START -----#
 
+        time_before = time.time()
+        
+        argin = replace_wildcard(argin)           
+        argout = self.db.get_host_list(argin)
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetHostList")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetHostList
         return argout
 
@@ -1423,6 +1628,15 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetHostServerList) ENABLED START -----#
 
+        time_before = time.time()
+        
+        argin = replace_wildcard(argin)            
+        argout = self.db.get_host_server_list(argin)
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetHostServerList")
+
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetHostServerList
         return argout
 
@@ -1439,6 +1653,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetHostServersInfo()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetHostServersInfo) ENABLED START -----#
+        
+        argin = replace_wildcard(argin)
+            
+        argout = self.db.get_host_servers_info(argin)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetHostServersInfo
         return argout
@@ -1456,7 +1674,9 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetInstanceNameList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetInstanceNameList) ENABLED START -----#
-
+        
+        argout = self.db.get_instance_name_list(argin)
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetInstanceNameList
         return argout
 
@@ -1474,7 +1694,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetObjectList()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetObjectList) ENABLED START -----#
-
+        
+        argin = replace_wildcard(argin)
+        argout = self.db.get_object_list(argin)
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetObjectList
         return argout
 
@@ -1503,6 +1726,9 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetProperty) ENABLED START -----#
 
+        object_name = argin[0]
+        argout = self.db.get_property(object_name, argin[1:])
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetProperty
         return argout
 
@@ -1525,6 +1751,10 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetPropertyHist) ENABLED START -----#
 
+        object_name = argin[0]
+        prop_name = argin[1]
+        argout = self.db.get_property_hist(object_name, prop_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetPropertyHist
         return argout
 
@@ -1544,6 +1774,11 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetPropertyList) ENABLED START -----#
 
+        object_name = argin[0]
+        wildcard = replace_wildcard(argin[1])
+
+        argout = self.db.get_property_list(object_name, wildcard)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetPropertyList
         return argout
 
@@ -1560,6 +1795,8 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbGetServerInfo()")
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetServerInfo) ENABLED START -----#
+        
+        argout = self.db.get_server_info(argin)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetServerInfo
         return argout
@@ -1579,6 +1816,14 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetServerList) ENABLED START -----#
 
+        time_before = time.time()
+
+        argin = replace_wildcard(argin)
+        argout = self.db.get_server_list(argin)
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbGetServerList")
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetServerList
         return argout
 
@@ -1595,7 +1840,10 @@ class DataBase (PyTango.Device_4Impl):
         :rtype: PyTango.DevVarStringArray """
         self.debug_stream("In " + self.get_name() + ".DbGetServerNameList()")
         argout = ['']
-        #----- PROTECTED REGION ID(DataBase.DbGetServerNameList) ENABLED START -----#
+        #----- PROTECTED REGION ID(DataBase.DbGetServerNameList) ENABLED START ----
+
+        argin = replace_wildcard(argin)
+        argout = self.db.get_server_name_list(argin)
 
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetServerNameList
         return argout
@@ -1622,6 +1870,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = [0], ['']
         #----- PROTECTED REGION ID(DataBase.DbImportDevice) ENABLED START -----#
 
+        time_before = time.time()
+        
+        argout = self.db.import_device(argin.lower())
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbImportDevice")
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbImportDevice
         return argout
 
@@ -1639,6 +1894,16 @@ class DataBase (PyTango.Device_4Impl):
         argout = [0], ['']
         #----- PROTECTED REGION ID(DataBase.DbImportEvent) ENABLED START -----#
 
+        time_before = time.time()
+        
+        argin = replace_wildcard(argin.lower())
+
+        argout = self.db.import_event(argin)
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbImportEvent")
+        
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbImportEvent
         return argout
 
@@ -1666,6 +1931,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbInfo) ENABLED START -----#
 
+        time_before = time.time()
+
+        argout = self.db.info()
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbInfo")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbInfo
         return argout
 
@@ -1683,6 +1955,16 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutAttributeAlias()")
         #----- PROTECTED REGION ID(DataBase.DbPutAttributeAlias) ENABLED START -----#
 
+        if len(argin) < 2:
+            self.warn_stream("DataBase::DbPutAttributeAlias(): insufficient number of arguments ")
+            th_exc(DB_IncorrectArguments,
+                   "insufficient number of arguments to put attribute alias",
+                   "DataBase::DbPutAttributeAlias()")
+
+        attribute_name = argin[0]
+        attribute_alias = argin[1]
+        self.db.put_attribute_alias(attribute_name, attribute_alias)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutAttributeAlias
 
 #------------------------------------------------------------------
@@ -1703,7 +1985,16 @@ class DataBase (PyTango.Device_4Impl):
         :rtype: PyTango.DevVoid """
         self.debug_stream("In " + self.get_name() + ".DbPutClassAttributeProperty()")
         #----- PROTECTED REGION ID(DataBase.DbPutClassAttributeProperty) ENABLED START -----#
-
+        
+        class_name = argin[0]
+        nb_attributes = int(argin[1])
+        
+        
+        self.info_stream("DataBase::DbPutClassAttributeProperty(): put " + str(nb_attributes) + " attributes for class " + class_name)
+        
+        self.db.put_class_attribute_property(class_name, nb_attributes, argin[2:])
+        
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutClassAttributeProperty
 
 #------------------------------------------------------------------
@@ -1727,7 +2018,13 @@ class DataBase (PyTango.Device_4Impl):
         :rtype: PyTango.DevVoid """
         self.debug_stream("In " + self.get_name() + ".DbPutClassAttributeProperty2()")
         #----- PROTECTED REGION ID(DataBase.DbPutClassAttributeProperty2) ENABLED START -----#
-
+        class_name = argin[0]
+        nb_attributes = int(argin[1])
+        
+        self.info_stream("DataBase::DbPutClassAttributeProperty2(): put " + str(nb_attributes) + " attributes for class " + class_name)
+        
+        self.db.put_class_attribute_property2(class_name, nb_attributes, argin[2:])
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutClassAttributeProperty2
 
 #------------------------------------------------------------------
@@ -1749,6 +2046,19 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutClassProperty()")
         #----- PROTECTED REGION ID(DataBase.DbPutClassProperty) ENABLED START -----#
 
+        time_before = time.time()
+
+        class_name = argin[0]
+        nb_properties = int(argin[1])
+        
+        self.info_stream("DataBase::DbPutClassProperty(): put " + str(nb_properties) + " attributes for class " + class_name)
+        
+        self.db.put_class_property(class_name, properties, argin[2:])
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbPutClassProperty")
+
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutClassProperty
 
 #------------------------------------------------------------------
@@ -1765,6 +2075,18 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutDeviceAlias()")
         #----- PROTECTED REGION ID(DataBase.DbPutDeviceAlias) ENABLED START -----#
 
+        if len(argin) < 2:
+            self.warn_stream("DataBase::DbPutDeviceAlias(): insufficient number of arguments ")
+            th_exc(DB_IncorrectArguments,
+                   "insufficient number of arguments to put device alias",
+                   "DataBase::DbPutDeviceAlias()")
+
+        device_name = argin[0]
+        device_alias = argin[1]
+        self.db.put_device_alias(device_name, device_alias)
+
+
+        
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutDeviceAlias
 
 #------------------------------------------------------------------
@@ -1786,6 +2108,18 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutDeviceAttributeProperty()")
         #----- PROTECTED REGION ID(DataBase.DbPutDeviceAttributeProperty) ENABLED START -----#
 
+        time_before = time.time()
+
+        device_name = argin[0]
+        nb_attributes = int(argin[1])
+              
+        self.info_stream("DataBase::DbPutDeviceAttributeProperty(): put " + str(nb_attributes) + " attributes for device " + device_name)
+        
+        self.db.put_device_attribute_property(device_name, nb_attributes, argin[2:])
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbPutDeviceAttributeProperty")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutDeviceAttributeProperty
 
 #------------------------------------------------------------------
@@ -1811,6 +2145,19 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutDeviceAttributeProperty2()")
         #----- PROTECTED REGION ID(DataBase.DbPutDeviceAttributeProperty2) ENABLED START -----#
 
+        time_before = time.time()
+
+        device_name = argin[0]
+        nb_attributes = int(argin[1])
+        
+        self.info_stream("DataBase::DbPutDeviceAttributeProperty2(): put " + str(nb_attributes) + " attributes for device " + device_name)
+        
+        self.db.put_device_attribute_property2(device_name, nb_attributes, argin[2:])
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbPutDeviceAttributeProperty2")
+
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutDeviceAttributeProperty2
 
 #------------------------------------------------------------------
@@ -1832,6 +2179,18 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutDeviceProperty()")
         #----- PROTECTED REGION ID(DataBase.DbPutDeviceProperty) ENABLED START -----#
 
+        time_before = time.time()
+
+        device_name = argin[0]
+        nb_properties = int(argin[1])
+        
+        self.info_stream("DataBase::DbPutDeviceProperty(): put " + str(nb_properties) + " attributes for device " + device_name)
+        
+        self.db.put_device_property(device_name, properties, argin[2:])
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbPutDeviceProperty")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutDeviceProperty
 
 #------------------------------------------------------------------
@@ -1853,6 +2212,13 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutProperty()")
         #----- PROTECTED REGION ID(DataBase.DbPutProperty) ENABLED START -----#
 
+        object_name = argin[0]
+        nb_properties = int(argin[1])
+        
+        self.info_stream("DataBase::DbPutProperty(): put " + str(nb_properties) + " attributes for object " + object_name)
+        
+        self.db.put_property(object_name, properties, argin[2:])
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutProperty
 
 #------------------------------------------------------------------
@@ -1868,6 +2234,25 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbPutServerInfo()")
         #----- PROTECTED REGION ID(DataBase.DbPutServerInfo) ENABLED START -----#
 
+        if len(argin) < 4:
+            self.warn_stream("DataBase::DbPutServerInfo(): insufficient number of arguments ")
+            th_exc(DB_IncorrectArguments,
+                   "insufficient server info",
+                   "DataBase::DbPutServerInfo()")
+
+        tmp_server = argin[0].lower()
+        tmp_host = argin[1]
+        tmp_mode = argin[2]
+        tmp_level = argin[3]
+        tmp_extra = []
+        if len(argin) > 4:
+            tmp_extra = argin[4:]
+        
+        tmp_len = len(argin) - 1
+        self.info_stream("DataBase::DbPutServerInfo(): put " + tmp_len + "  export info for device " + tmp_server)
+
+        self.db.put_server_info(tmp_server, tmp_host, tmp_mode, tmp_level, tmp_extra)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbPutServerInfo
 
 #------------------------------------------------------------------
@@ -1883,6 +2268,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbUnExportDevice()")
         #----- PROTECTED REGION ID(DataBase.DbUnExportDevice) ENABLED START -----#
 
+        dev_name = argin[0].lower()
+
+        self.db.unexport_device(dev_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbUnExportDevice
 
 #------------------------------------------------------------------
@@ -1898,6 +2287,10 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbUnExportEvent()")
         #----- PROTECTED REGION ID(DataBase.DbUnExportEvent) ENABLED START -----#
 
+        event_name = argin[0].lower()
+
+        self.db.unexport_event(event_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbUnExportEvent
 
 #------------------------------------------------------------------
@@ -1914,6 +2307,15 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbUnExportServer()")
         #----- PROTECTED REGION ID(DataBase.DbUnExportServer) ENABLED START -----#
 
+        time_before = time.time()
+
+        server_name = argin[0].lower()
+
+        self.db.unexport_server(server_name)
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbUnExportServer")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbUnExportServer
 
 #------------------------------------------------------------------
@@ -1928,6 +2330,12 @@ class DataBase (PyTango.Device_4Impl):
         :rtype: PyTango.DevVoid """
         self.debug_stream("In " + self.get_name() + ".ResetTimingValues()")
         #----- PROTECTED REGION ID(DataBase.ResetTimingValues) ENABLED START -----#
+        for tmp_timing in timing_maps.itervalues():
+            tmp_timing.average = 0.
+            tmp_timing.minimum = 0.
+            tmp_timing.maximum = 0.
+            tmp_timing.total_elapsed = 0.
+            tmp_timing.calls = 0.
 
         #----- PROTECTED REGION END -----#	//	DataBase.ResetTimingValues
 
@@ -1947,6 +2355,13 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetDataForServerCache) ENABLED START -----#
 
+        time_before = time.time()
+
+        ##  TODO
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbDbGetDataForServerCache")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetDataForServerCache
         return argout
 
@@ -1964,6 +2379,23 @@ class DataBase (PyTango.Device_4Impl):
         self.debug_stream("In " + self.get_name() + ".DbDeleteAllDeviceAttributeProperty()")
         #----- PROTECTED REGION ID(DataBase.DbDeleteAllDeviceAttributeProperty) ENABLED START -----#
 
+        if len(argin) < 2:
+            self.warn_stream("DataBase::DbDeleteAllDeviceAttributeProperty(): insufficient number of arguments ")
+            th_exc(DB_IncorrectArguments,
+                   "insufficient number of arguments to delete all device attribute(s) property",
+                   "DataBase::DbDeleteAllDeviceAttributeProperty()")
+
+        dev_name = argin[0]
+
+        ret, d_name, dfm = check_device_name(dev_name)
+        
+        if not ret:
+            th_exc(DB_IncorrectDeviceName,
+                  "device name (" + argin + ") syntax error (should be [tango:][//instance/]domain/family/member)",
+                  "DataBase::DbDeleteAllDeviceAttributeProperty()")
+
+        self.db.delete_all_device_attribute_property(dev_name, argin[1:])
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbDeleteAllDeviceAttributeProperty
 
 #------------------------------------------------------------------
@@ -1984,6 +2416,30 @@ class DataBase (PyTango.Device_4Impl):
         argout = [0], ['']
         #----- PROTECTED REGION ID(DataBase.DbMySqlSelect) ENABLED START -----#
 
+        time_before = time.time()
+
+        tmp_argin = argin.lower()
+        
+        #  Check if SELECT key is alread inside command
+
+        cmd = argin
+        tmp_argin = argin.lower()
+        pos = tmp_argin.find('select')
+        if pos == -1:
+            cmd = "SELECT " + cmd
+
+        pos = tmp_argin.find(';')
+        if pos != -1 and len(tmp_argin) > (pos + 1):
+            th_exc(DB_IncorrectArguments,
+                   "SQL command not valid: " + argin,
+                   "DataBase::ExportDevice()")
+        logging.info("DataBase::db_my_sql_select(): \n %s" % cmd)
+        
+        argout = self.db.my_sql_select(cmd)
+
+        time_after = time.time()
+        self.update_timing_stats(time_before, time_after, "DbMySqlSelect")
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbMySqlSelect
         return argout
 
@@ -2001,6 +2457,8 @@ class DataBase (PyTango.Device_4Impl):
         argout = ['']
         #----- PROTECTED REGION ID(DataBase.DbGetCSDbServerList) ENABLED START -----#
 
+        argout = self.db.get_csdb_server_list()
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetCSDbServerList
         return argout
 
@@ -2019,6 +2477,9 @@ class DataBase (PyTango.Device_4Impl):
         argout = ''
         #----- PROTECTED REGION ID(DataBase.DbGetAttributeAlias2) ENABLED START -----#
 
+        attr_name = argin[0]
+        argout = self.db.get_attribute_alias2(attr_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetAttributeAlias2
         return argout
 
@@ -2037,9 +2498,47 @@ class DataBase (PyTango.Device_4Impl):
         argout = ''
         #----- PROTECTED REGION ID(DataBase.DbGetAliasAttribute) ENABLED START -----#
 
+        alias_name = argin[0]
+        argout = self.db.get_alias_attribute(alias_name)
+
         #----- PROTECTED REGION END -----#	//	DataBase.DbGetAliasAttribute
         return argout
 
+#------------------------------------------------------------------
+#    DbRenameServer command:
+#------------------------------------------------------------------
+    def DbRenameServer(self, argin):
+        """ Rename a device server process
+        
+        :param argin: str[0] = old device server name (exec/instance)
+        str[1] =  new device server name (exec/instance)
+        :type: PyTango.DevVarStringArray
+        :return: 
+        :rtype: PyTango.DevVoid """
+        self.debug_stream("In " + self.get_name() + ".DbRenameServer()")
+        #----- PROTECTED REGION ID(DataBase.DbRenameServer) ENABLED START -----#
+
+        if len(argin) < 2:
+            self.warn_stream("DataBase::DbRenameServer(): insufficient number of arguments ")
+            th_exc(DB_IncorrectArguments,
+                   "insufficient number of arguments (two required: old name and new name",
+                   "DataBase::DbRenameServer")
+        
+
+        old_name = argin[0]
+        new_name = argin[1]
+        
+        if ('/' not in argin[0]) or ('/' not in argin[1]):
+            self.warn_stream("DataBase::DbRenameServer(): wrong syntax in command args ")
+            th_exc(DB_IncorrectArguments,
+                   "Wrong syntax in command args (ds_exec_name/inst_name)",
+                   "DataBase::DbRenameServer")
+
+        self.db.rename_server(old_name, new_name)
+            
+        
+
+        #----- PROTECTED REGION END -----#	//	DataBase.DbRenameServer
 
 #==================================================================
 #
@@ -2306,6 +2805,9 @@ class DataBaseClass(PyTango.DeviceClass):
         'DbGetAliasAttribute':
             [[PyTango.DevString, "The attribute alias"],
             [PyTango.DevString, "The attribute name (dev_name/att_name)"]],
+        'DbRenameServer':
+            [[PyTango.DevVoid, "none"],
+            [PyTango.DevVarStringArray, "s[0] = old device server name (exec/instance)\ns[1] = new device server name (exec/instance)"]],
         }
 
 
@@ -2348,55 +2850,123 @@ class DataBaseClass(PyTango.DeviceClass):
     def __init__(self, name):
         PyTango.DeviceClass.__init__(self, name)
         self.set_type(name);
-        print "In DataBase Class  constructor"
 
     def device_factory(self, names):
         names = [get_db_name()]
         return PyTango.DeviceClass.device_factory(self, names)
 
+    def device_factory(self, device_list):
+        """for internal usage only"""
+
+        dev_name = get_db_name()
+        
+        klass = self.__class__
+        klass_name = klass.__name__
+        info, klass = get_class_by_class(klass), get_constructed_class_by_class(klass)
+        
+        if info is None:
+            raise RuntimeError("Device class '%s' is not registered" % klass_name)
+
+        if klass is None:
+            raise RuntimeError("Device class '%s' as not been constructed" % klass_name)
+        
+        deviceClassClass, deviceImplClass, deviceImplName = info
+        deviceImplClass._device_class_instance = klass
+
+        device = deviceImplClass(klass, dev_name)
+        self._add_device(device)
+        tmp_dev_list = [device]
+
+        self.dyn_attr(tmp_dev_list)
+
+        self.export_device(device, "database")
+        self.py_dev_list += tmp_dev_list
+        
 #==================================================================
 #
 #    DataBase class main method
 #
 #==================================================================
 def main():
+    #Parameters management
+    global options
+    if argparse:
+        parser = argparse.ArgumentParser()
+        parser.add_argument("--db_access",dest="db_access",default="sqlite3",
+                            help="database type")
+        parser.add_argument("-e", "--embedded",dest="embedded",default=False,
+                            action="store_true")
+        parser.add_argument('argv',nargs=argparse.REMAINDER)
+        options = parser.parse_args()
+        options.argv = ["DataBaseds"] + options.argv
+    else:
+        parser = OptionParser()
+        parser.add_option("--db_access",dest="db_access",default="sqlite3",
+                          help="database type")
+        parser.add_option("-e","--embedded",dest="embedded",default=False,
+                          action="store_true")
+        (options,args) = parser.parse_args()
+        options.argv = ["DataBaseds"] + args
+
     log_fmt = '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'
     logging.basicConfig(format=log_fmt, stream=sys.stdout, level=logging.INFO)
     try:
-        db_name = "sys/database/" + sys.argv[1]
+        db_name = "sys/database/" + options.argv[1]
         set_db_name(db_name)
-        PyTango.Util.set_use_db(False)
-        py = PyTango.Util(sys.argv)
-        py.add_class(DataBaseClass, DataBase, 'DataBase')
-
-        U = PyTango.Util.instance()
-        dbi = DbInter()
-        U.set_interceptors(dbi)
-        U.set_serial_model(PyTango.SerialModel.NO_SYNC)
-        U.server_init()
-        dserver = U.get_dserver_device()
-        dbase = U.get_device_by_name(db_name)
-
-        dserver.duplicate_d_var()
-
-        pars = [dserver.get_name(), U.get_dserver_ior(dserver),
-                U.get_host_name(), U.get_pid_str(), U.get_version_str()]
-        dbase.DbExportDevice(pars)
-
-        pars[0] = dbase.get_name()
-        pars[1] = U.get_device_ior(dbase)
-        dbase.DbExportDevice(pars)
-
-        print "Ready to accept request"
-        U.orb_run()
-
-
-    except PyTango.DevFailed as df:
-        print '-------> Received a DevFailed exception:', df
-        import traceback;traceback.print_exc()
+        if options.embedded:
+            __run_embedded(db_name, options.argv)
+        else:
+            __run(db_name, options.argv)
     except Exception as e:
-        print '-------> An unforeseen exception occured....', e
-        import traceback;traceback.print_exc()
-
+        import traceback
+        traceback.print_exc()
+
+def __run(db_name,argv):
+    """
+    Runs the Database DS as a standalone database. Run it with::
+    
+        ./DataBaseds pydb-test -ORBendPoint giop:tcp::11000
+    """
+    PyTango.Util.set_use_db(False)
+    py_util = PyTango.Util(argv)
+    py_util.add_class(DataBaseClass, DataBase, 'DataBase')
+
+    util = PyTango.Util.instance()
+    dbi = DbInter()
+    util.set_interceptors(dbi)
+    #util.set_serial_model(PyTango.SerialModel.NO_SYNC)
+    util.server_init()
+
+    dserver = util.get_dserver_device()
+    dserver_name = dserver.get_name()
+    dserver_ior = util.get_dserver_ior(dserver)
+
+    dbase = util.get_device_by_name(db_name)
+    dbase_name = dbase.get_name()
+    dbase_ior = util.get_device_ior(dbase)
+        
+    host = util.get_host_name()
+    pid = util.get_pid_str()
+    version = util.get_version_str()
+        
+    dbase.DbExportDevice([dserver_name, dserver_ior, host, pid, version])
+    dbase.DbExportDevice([dbase_name, dbase_ior, host, pid, version])
+
+    print("Ready to accept request")
+    util.orb_run()
+#    util.server_run()
+
+def __run_embedded(db_name,argv):
+    """Runs the Database device server embeded in another TANGO Database
+    (just like any other TANGO device server)"""
+
+    py_util = PyTango.Util(argv)
+    py_util.add_class(DataBaseClass, DataBase, 'DataBase')
+
+    util = PyTango.Util.instance()
+    util.server_init()
+    print("Ready to accept request")
+    util.server_run()
+    
 if __name__ == '__main__':
     main()
diff --git a/src/boost/python/databaseds/db_access.py b/src/boost/python/databaseds/db_access.py
deleted file mode 100644
index 6faa291..0000000
--- a/src/boost/python/databaseds/db_access.py
+++ /dev/null
@@ -1,517 +0,0 @@
-from __future__ import print_function
-
-import os
-import logging
-import functools
-
-import PyTango
-
-th_exc = PyTango.Except.throw_exception
-
-from db_errors import *
-
-def get_create_db_statements():
-    statements = []
-    with open("create_db_tables.sql") as f:
-        lines = f.readlines()
-    # strip comments
-    lines = (line for line in lines if not line.startswith('#'))
-    lines = (line for line in lines if not line.lower().strip().startswith('key'))
-    lines = (line for line in lines if not line.lower().strip().startswith('key'))
-    lines = "".join(lines)
-    lines = lines.replace("ENGINE=MyISAM", "")
-    statements += lines.split(";")
-
-    with open("create_db.sql") as f:
-        lines = f.readlines()
-    # strip comments
-    lines = (line for line in lines if not line.lower().startswith('#'))
-    lines = (line for line in lines if not line.lower().startswith('create database'))
-    lines = (line for line in lines if not line.lower().startswith('use'))
-    lines = (line for line in lines if not line.lower().startswith('source'))
-    lines = "".join(lines)
-    statements += lines.split(";")
-
-    return statements
-
-def replace_wildcard(text):
-    # escape '%' with '\'
-    text = text.replace("%", "\\%")
-    # escape '_' with '\'
-    text = text.replace("_", "\\_")
-    # escape '"' with '\'
-    text = text.replace('"', '\\"')
-    # escape ''' with '\'
-    text = text.replace("'", "\\'")
-    # replace '*' with '%'
-    text = text.replace("*", "%")
-    return text
-
-def use_cursor(f):
-    @functools.wraps(f)
-    def wrap(*args, **kwargs):
-        self = args[0]
-        has_cursor = 'cursor' in kwargs
-        cursor = kwargs.pop('cursor', None)
-        if not has_cursor:
-            cursor = self.get_cursor()
-        self.cursor = cursor
-        try:
-            ret = f(*args, **kwargs)
-            if not has_cursor:
-                cursor.connection.commit()
-            return ret
-        finally:
-            if not has_cursor:
-                cursor.close()
-                del self.cursor
-    return wrap
-
-class Tango_dbapi2(object):
-
-    DB_API_NAME = 'sqlite3'
-
-    def __init__(self, db_name="tango_database.db", history_depth=10, fire_to_starter=True):
-        self._db_api = None
-        self._db_conn = None
-        self.db_name = db_name
-        self.history_depth = history_depth;
-        self.fire_to_starter = fire_to_starter
-        self._logger = logging.getLogger(self.__class__.__name__)
-        self._debug = self._logger.info
-        self._info = self._logger.info
-        self._warn = self._logger.warn
-        self._error = self._logger.error
-        self._critical = self._logger.critical
-        self.initialize()
-
-    def close_db(self):
-        if self._db_conn is not None:
-            self._db_conn.commit()
-            self._db_conn.close()
-        self._db_api = None
-        self._db_conn = None
-
-    def get_db_api(self):
-        if self._db_api is None:
-            self._db_api = __import__(self.DB_API_NAME)
-        return self._db_api
-
-    @property
-    def db_api(self):
-        return self.get_db_api()
-
-    @property
-    def db_conn(self):
-        if self._db_conn is None:
-            self._db_conn = self.db_api.connect(self.db_name)
-        return self._db_conn
-
-    def get_cursor(self):
-        return self.db_conn.cursor()
-
-    def initialize(self):
-        self._info("Initializing database...")
-        if not os.path.isfile(self.db_name):
-            self.create_db()
-
-    @use_cursor
-    def create_db(self):
-        self._info("Creating database...")
-        statements = get_create_db_statements()
-        cursor = self.cursor
-        for statement in statements:
-            cursor.execute(statement)
-
-    @use_cursor
-    def get_id(self, name):
-        cursor = self.cursor
-        name += '"_history_id'
-        _id = cursor.execute('SELECT id FROM ?', (name,)).fetchone()[0] + 1
-        cursor.execute('UPDATE ? SET id=?', (name, _id))
-        return _id
-
-    @use_cursor
-    def purge_att_property(self, table, field, obj, attr, name):
-        cursor = self.cursor
-        cursor.execute(\
-            'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? AND ' \
-            'attribute = ? ORDER BY date', (table, field, obj, name, attr))
-        rows = cursor.fetchall()
-        to_del = len(rows) - self.history_depth
-        if to_del > 0:
-            for row in rows[:to_del]:
-                cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
-
-    @use_cursor
-    def purge_property(self, table, field, obj, name):
-        cursor = self.cursor
-        cursor.execute(\
-            'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? ' \
-            'ORDER BY date', (table, field, obj, name))
-        rows = cursor.fetchall()
-        to_del = len(rows) - self.history_depth
-        if to_del > 0:
-            for row in rows[:to_del]:
-                cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
-
-    @use_cursor
-    def get_device_host(self, name):
-        cursor = self.cursor
-        name = replace_wildcard(name)
-        cursor.execute('SELECT host FROM device WHERE name LIKE ?', (name,))
-        row = cursor.fetchone()
-        if row is None:
-            raise Exception("No host for device '" + name + "'")
-        else:
-            return row[0]
-
-    # TANGO API
-
-    def get_stored_procedure_release(self):
-        return 'release 1.8'
-
-    @use_cursor
-    def add_device(self, server_name, dev_info, klass_name, alias=None):
-        self._info("delete_attribute_alias(server_name=%s, dev_info=%s, klass_name=%s, alias=%s)",
-                   server_name, dev_info, klass_name, alias)
-        dev_name, (domain, family, member) = dev_info
-        cursor = self.cursor
-
-        # first delete the tuple (device,name) from the device table
-        cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
-
-        # then insert the new value for this tuple
-        cursor.execute(\
-            'INSERT INTO device (name, alias, domain, family, member, exported, ' \
-            'ior, host, server, pid, class, version, started, stopped) ' \
-            'VALUES (?, ?, ?, ?, ?, 0, "nada", "nada", ?, 0, ?, "0", NULL, NULL)',
-            (dev_name, alias, domain, family, member, server_name, klass_name))
-
-        # Check if a DServer device entry for the process already exists
-        cursor.execute('SELECT name FROM device WHERE server LIKE ? AND class LIKE "DServer"', (server_name,))
-        if cursor.fetchone() is None:
-            dev_name = "dserver/" + server_name
-            domain, family, member = dev_name.split("/", 2)
-            cursor.execute(\
-            'INSERT INTO device (name, domain, family, member, exported, ior, ' \
-            'host, server, pid, class, version, started, stopped) ' \
-            'VALUES (?, ?, ?, ?, 0, "nada", "nada", ?, 0, "DServer", "0", NULL, NULL)',
-            (dev_name, domain, family, member, server_name))
-
-    @use_cursor
-    def delete_attribute_alias(self, alias):
-        self._info("delete_attribute_alias(alias=%s)", alias)
-        self.cursor.execute('DELETE FROM attribute_alias WHERE alias=?', (alias,))
-
-    @use_cursor
-    def delete_class_attribute(self, klass_name, attr_name):
-        self.cursor.execute(\
-            'DELETE FROM property_attribute_class WHERE class LIKE ? AND ' \
-            'attribute LIKE ?', (klass_name, attr_name))
-
-    @use_cursor
-    def delete_class_attribute_property(self, klass_name, attr_name, prop_name):
-        cursor = self.cursor
-
-        # Is there something to delete ?
-        cursor.execute(\
-            'SELECT count(*) FROM property_attribute_class WHERE class = ? ' \
-            'AND attribute = ? AND name = ?', (klass_name, attr_name, prop_name))
-        if cursor.fetchone()[0] > 0:
-            # then delete property from the property_attribute_class table
-            cursor.execute(\
-                'DELETE FROM property_attribute_class WHERE class = ? AND ' \
-                'attribute = ? and name = ?', (klass_name, attr_name, prop_name))
-            # mark this property as deleted
-            hist_id = self.get_id('class_attibute', cursor=cursor)
-            cursor.execute(\
-                'INSERT INTO property_attribute_class_hist (class, attribute, ' \
-                'name, id, count, value) VALUES ' \
-                '(?, ?, ?, ?, "0", "DELETED")',
-                (klass_name, attr_name, prop_name, hist_id))
-            self.purge_att_property("property_attribute_class_hist", "class",
-                                    klass_name, attr_name, prop_name, cursor=cursor)
-
-    @use_cursor
-    def delete_class_property(self, klass_name, prop_name):
-        cursor = self.cursor
-
-        prop_name = replace_wildcard(prop_name)
-        # Is there something to delete ?
-        cursor.execute(\
-            'SELECT DISTINCT name FROM property_class WHERE class=? AND ' \
-            'name LIKE ?', (klass_name, prop_name))
-        for row in cursor.fetchall():
-            # delete the tuple (device,name,count) from the property table
-            name = row[0]
-            cursor.execute(\
-                'DELETE FROM property_class WHERE class=? AND name=?',
-                (klass_name, name))
-            # Mark this property as deleted
-            hist_id = self.get_id("class", cursor=cursor)
-            cursor.execute(\
-                'INSERT INTO property_class_hist (class, name, id, count, value) ' \
-                'VALUES (?, ?, ?, "0", "DELETED")',
-                (klass_name, name, hist_id))
-            self.purge_property("property_class_hist", "class", klass_name,
-                                name, cursor=cursor)
-
-    @use_cursor
-    def delete_device(self, dev_name):
-        self._info("delete_device(dev_name=%s)", dev_name)
-        cursor = self.cursor
-        dev_name = replace_wildcard(dev_name)
-
-        # delete the device from the device table
-        cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
-
-        # delete device from the property_device table
-        cursor.execute('DELETE FROM property_device WHERE device LIKE ?', (dev_name,))
-
-        # delete device from the property_attribute_device table
-        cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ?', (dev_name,))
-
-    @use_cursor
-    def delete_device_alias(self, dev_alias):
-        self._info("delete_device_alias(dev_alias=%s)", dev_alias)
-        self.cursor.execute('UPDATE device SET alias=NULL WHERE alias=?', (dev_alias,))
-
-    @use_cursor
-    def delete_device_attribute(self, dev_name, attr_name):
-        dev_name = replace_wildcard(dev_name)
-        self.cursor.execute(\
-            'DELETE FROM property_attribute_device WHERE device LIKE ? AND ' \
-            'attribute LIKE ?', (dev_name, attr_name))
-
-    @use_cursor
-    def delete_device_attribute_property(self, dev_name, attr_name, prop_name):
-        cursor = self.cursor
-        # Is there something to delete ?
-        cursor.execute(\
-            'SELECT count(*) FROM property_attribute_device WHERE device = ?' \
-            'AND attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
-        if cursor.fetchone()[0] > 0:
-            # delete property from the property_attribute_device table
-            cursor.execute(\
-                'DELETE FROM property_attribute_device WHERE device = ? AND '
-                'attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
-            # Mark this property as deleted
-            hist_id = self.get_id("device_attribute", cursor=cursor)
-            cursor.execute(\
-                'INSERT INTO property_attribute_device_hist ' \
-                '(device, attribute, name, id, count, value) VALUES ' \
-                '(?, ?, ?, ?, "0", "DELETED")', (dev_name, attr_name, prop_name, hist_id))
-            self.purge_att_property("property_attribute_device_hist", "device",
-                                    dev_name, attr_name, prop_name, cursor=cursor)
-
-    @use_cursor
-    def delete_device_property(self, dev_name, prop_name):
-        cursor = self.cursor
-        prop_name = replace_wildcard(prop_name)
-
-        # Is there something to delete ?
-        cursor.execute(\
-            'SELECT DISTINCT name FROM property_device WHERE device=? AND ' \
-            'name LIKE ?', (dev_name, prop_name))
-        for row in cursor.fetchall():
-            # delete the tuple (device,name,count) from the property table
-            cursor.execute(\
-                'DELETE FROM property_device WHERE device=? AND name LIKE ?',
-                (dev_name, prop_name))
-            # Mark this property as deleted
-            hist_id = self.get_id("device", cursor=cursor)
-            cursor.execute(\
-                'INSERT INTO property_device_hist (device, id, name, count, value) ' \
-                'VALUES (?, ?, ?, "0", "DELETED")', (dev_name, hist_id, row[0]))
-            self.purge_property("property_device_hist", "device", dev_name, row[0])
-
-    @use_cursor
-    def delete_property(self, obj_name, prop_name):
-        cursor = self.cursor
-        prop_name = replace_wildcard(prop_name)
-
-        # Is there something to delete ?
-        cursor.execute(\
-            'SELECT DISTINCT name FROM property WHERE object=? AND ' \
-            'name LIKE ?', (obj_name, prop_name))
-        for row in cursor.fetchall():
-            # delete the tuple (object,name,count) from the property table
-            cursor.execute(\
-                'DELETE FROM property_device WHERE device=? AND name LIKE ?',
-                (obj_name, prop_name))
-            # Mark this property as deleted
-            hist_id = self.get_id("object", cursor=cursor)
-            cursor.execute(\
-                'INSERT INTO property_hist (object, name, id, count, value) ' \
-                'VALUES (?, ?, ?, "0", "DELETED")', (obj_name, row[0], hist_id))
-            self.purge_property("property_hist", "object", obj_name, row[0])
-
-    @use_cursor
-    def delete_server(self, server_instance):
-        cursor = self.cursor
-        server_instance = replace_wildcard(server_instance)
-
-        previous_host = None
-        # get host where running
-        if self.fire_to_starter:
-            adm_dev_name = "dserver/" + server_instance
-            previous_host = self.get_device_host(adm_dev_name)
-
-        # then delete the device from the device table
-        cursor.execute('DELETE FROM device WHERE server LIKE ?', (server_instance,))
-
-        # Update host's starter to update controlled servers list
-        if self.fire_to_starter and previous_host:
-            # TODO send to starter
-            pass
-
-    @use_cursor
-    def delete_server_info(self, server_instance):
-        self.cursor.execute('DELETE FROM server WHERE name=?', (server_instance,))
-
-    @use_cursor
-    def export_device(self, dev_name, IOR, host, pid, version):
-        self._info("export_device(dev_name=%s, IOR=%s, host=%s, pid=%s, version=%s)",
-                   dev_name, IOR, host, pid, version)
-        cursor = self.cursor
-        do_fire = False
-        previous_host = None
-        if self.fire_to_starter:
-            # TODO send to starter
-            pass
-        cursor.execute('SELECT server FROM device WHERE name LIKE ?', (dev_name,))
-        row = cursor.fetchone()
-        if row is None:
-            th_exc(DB_DeviceNotDefined,
-                   "device " + dev_name + " not defined in the database !",
-                   "DataBase::ExportDevice()")
-        server = row[0]
-
-        # update the new value for this tuple
-        cursor.execute(\
-            'UPDATE device SET exported=1, ior=?, host=?, pid=?, version=?, ' \
-            'started=datetime("now") WHERE name LIKE ?',
-            (IOR, host, pid, version, dev_name))
-
-        # update host name in server table
-        cursor.execute('UPDATE server SET host=? WHERE name LIKE ?', (host, server))
-
-        if do_fire:
-            # TODO send to starter
-            pass
-
-    @use_cursor
-    def export_event(self, event, IOR, host, pid, version):
-        cursor = self.cursor
-        cursor.execute(\
-            'INSERT event (name,exported,ior,host,server,pid,version,started) ' \
-            'VALUES (?, 1, ?, ?, ?, ?, ?, datetime("now")',
-            (event, IOR, host, event, pid, version))
-
-    @use_cursor
-    def get_alias_device(self, dev_alias):
-        cursor = self.cursor
-        cursor.execute('SELECT name FROM device WHERE alias LIKE ?',
-                       (dev_alias,))
-        row = cursor.fetchone()
-        if row is None:
-            th_exc(DB_DeviceNotDefined,
-                   "No device found for alias '" + dev_alias + "'",
-                   "DataBase::GetAliasDevice()")
-        return row[0]
-
-    @use_cursor
-    def get_attribute_alias(self, attr_alias):
-        cursor = self.cursor
-        cursor.execute('SELECT name from attribute_alias WHERE alias LIKE ?',
-                       (attr_alias,))
-        row = cursor.fetchone()
-        if row is None:
-            th_exc(DB_SQLError,
-                   "No attribute found for alias '" + attr_alias + "'",
-                   "DataBase::GetAttributeAlias()")
-        return row[0]
-
-    @use_cursor
-    def get_attribute_alias_list(self, attr_alias):
-        cursor = self.cursor
-        cursor.execute('SELECT DISTINCT alias FROM attribute_alias WHERE alias LIKE ? ORDER BY attribute',
-                       (attr_alias,))
-        return [ row[0] for row in cursor.fetchall() ]
-
-    @use_cursor
-    def get_class_attribute_list(self, class_name, wildcard):
-        cursor = self.cursor
-        cursor.execute('SELECT DISTINCT attribute FROM property_attribute_class WHERE class=? and attribute like ?',
-                       (class_name, wildcard))
-        return [ row[0] for row in cursor.fetchall() ]
-
-    @use_cursor
-    def get_class_attribute_property(self, clas_name, attributes):
-        cursor = self.cursor
-        stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ?'
-        result = [class_name, str(len(attributes))]
-        for attribute in attributes:
-            cursor.execute(stmt, (class_name, attribute))
-            rows = cursor.fetchall()
-            result.append(attribute)
-            result.append(str(len(rows)))
-            for row in rows:
-                result.append(row[0])
-                result.append(row[1])
-        return result
-
-    @use_cursor
-    def get_class_attribute_property2(self, clas_name, attributes):
-        cursor = self.cursor
-        stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ? ORDER BY name,count'
-        result = [class_name, str(len(attributes))]
-        # TODO: NOT DONE YET!
-        for attribute in attributes:
-            cursor.execute(stmt, (class_name, attribute))
-            rows = cursor.fetchall()
-            result.append(attribute)
-            result.append(str(len(rows)))
-            for row in rows:
-                result.append(row[0])
-                result.append(row[1])
-        return result
-
-    @use_cursor
-    def get_class_for_device(self, dev_name):
-        cursor = self.cursor
-        cursor.execute('SELECT DISTINCT class FROM device WHERE name=?', (dev_name,))
-        row = cursor.fetchone()
-        if row is None:
-            th_exc(DB_IncorrectArguments, "Class not found for " + dev_name,
-                   "Database.GetClassForDevice")
-        return row
-
-    @use_cursor
-    def get_class_inheritance_for_device(self, dev_name):
-        cursor = self.cursor
-        class_name = self.get_class_for_device(dev_name, cursor=cursor)
-        props = self.get_class_property(class_name, "InheritedFrom", cursor=cursor)
-        return [class_name] + props[4:]
-
-    @use_cursor
-    def get_class_list(self, server):
-        cursor = self.cursor
-        cursor.execute('SELECT DISTINCT class FROM device WHERE class LIKE ? ORDER BY class', (server,))
-        return [ row[0] for row in cursor.fetchall() ]
-
-
-
-class Tango_sqlite3(Tango_dbapi2):
-
-    DB_API_NAME = 'sqlite3'
-
-
-def main():
-    db = Tango_sqlite3()
-    db.add_device("MyServer/my1", ("a/b/c", ("a", "b", "c")), "MyClass")
-    db.close_db()
-
-if __name__ == "__main__":
-    main()
diff --git a/src/boost/python/databaseds/db_access/__init__.py b/src/boost/python/databaseds/db_access/__init__.py
new file mode 100644
index 0000000..0a06f03
--- /dev/null
+++ b/src/boost/python/databaseds/db_access/__init__.py
@@ -0,0 +1,14 @@
+__all__ = []
+def _init_module() :
+    import os
+    for root,dirs,files in os.walk(__path__[0]) :
+        for file_name in files :
+            if file_name.startswith('__') : continue
+            base,ext = os.path.splitext(file_name)
+            if ext == '.py' :
+                subdir = root[len(__path__[0]) + 1:]
+                if subdir:
+                    base = '%s.%s' % (subdir,base)
+                __all__.append(base)
+_init_module()
+
diff --git a/src/boost/python/databaseds/db_access/sqlite3.py b/src/boost/python/databaseds/db_access/sqlite3.py
new file mode 100644
index 0000000..f637355
--- /dev/null
+++ b/src/boost/python/databaseds/db_access/sqlite3.py
@@ -0,0 +1,1575 @@
+from __future__ import print_function
+
+import os
+import logging
+import functools
+import threading
+import Queue
+
+import PyTango
+
+th_exc = PyTango.Except.throw_exception
+
+from db_errors import *
+
+from concurrent.futures import ThreadPoolExecutor
+
+Executor = ThreadPoolExecutor(1)
+
+def get_create_db_statements():
+    statements = []
+    with open("create_db_tables.sql") as f:
+        lines = f.readlines()
+    # strip comments
+    lines = (line for line in lines if not line.startswith('#'))
+    lines = (line for line in lines if not line.lower().strip().startswith('key'))
+    lines = (line for line in lines if not line.lower().strip().startswith('key'))
+    lines = "".join(lines)
+    lines = lines.replace("ENGINE=MyISAM", "")
+    statements += lines.split(";")
+
+    with open("create_db.sql") as f:
+        lines = f.readlines()
+    # strip comments
+    lines = (line for line in lines if not line.lower().startswith('#'))
+    lines = (line for line in lines if not line.lower().startswith('create database'))
+    lines = (line for line in lines if not line.lower().startswith('use'))
+    lines = (line for line in lines if not line.lower().startswith('source'))
+    lines = "".join(lines)
+    statements += lines.split(";")
+
+    return statements
+
+def replace_wildcard(text):
+    # escape '%' with '\'
+    text = text.replace("%", "\\%")
+    # escape '_' with '\'
+    text = text.replace("_", "\\_")
+    # escape '"' with '\'
+    text = text.replace('"', '\\"')
+    # escape ''' with '\'
+    text = text.replace("'", "\\'")
+    # replace '*' with '%'
+    text = text.replace("*", "%")
+    return text
+
+def use_cursor(f):
+    @functools.wraps(f)
+    def wrap(*args, **kwargs):
+        self = args[0]
+        has_cursor = 'cursor' in kwargs
+        cursor = kwargs.pop('cursor', None)
+        if not has_cursor:
+            cursor = Executor.submit(self.get_cursor).result()
+        self.cursor = cursor
+        try:
+            ret = Executor.submit(f, *args, **kwargs).result()
+            if not has_cursor:
+                Executor.submit(cursor.connection.commit).result()
+            return ret
+        finally:
+            if not has_cursor:
+                Executor.submit(cursor.close).result()
+                del self.cursor
+    return wrap
+
+
+class Tango_dbapi2(object):
+
+    DB_API_NAME = 'sqlite3'
+
+    def __init__(self, db_name="tango_database.db", history_depth=10, fire_to_starter=True):
+        self._db_api = None
+        self._db_conn = None
+        self.db_name = db_name
+        self.history_depth = history_depth
+        self.fire_to_starter = fire_to_starter
+        self._logger = logging.getLogger(self.__class__.__name__)
+        self._debug = self._logger.debug
+        self._info = self._logger.info
+        self._warn = self._logger.warn
+        self._error = self._logger.error
+        self._critical = self._logger.critical
+        self.initialize()
+
+    def close_db(self):
+        if self._db_conn is not None:
+            self._db_conn.commit()
+            self._db_conn.close()
+        self._db_api = None
+        self._db_conn = None
+
+    def get_db_api(self):
+        if self._db_api is None:
+            self._db_api = __import__(self.DB_API_NAME)
+        return self._db_api
+
+    @property
+    def db_api(self):
+        return self.get_db_api()
+
+    @property
+    def db_conn(self):
+        if self._db_conn is None:
+            self._db_conn = self.db_api.connect(self.db_name)
+        return self._db_conn
+
+    def get_cursor(self):
+        return self.db_conn.cursor()
+
+    def initialize(self):
+        self._info("Initializing database...")
+        if not os.path.isfile(self.db_name):
+            self.create_db()
+        else:
+            # trigger connection
+            self.db_conn
+
+    @use_cursor
+    def create_db(self):
+        self._info("Creating database...")
+        statements = get_create_db_statements()
+        cursor = self.cursor
+        for statement in statements:
+            cursor.execute(statement)
+
+    @use_cursor
+    def get_id(self, name):
+        cursor = self.cursor
+        name += '"_history_id'
+        _id = cursor.execute('SELECT id FROM ?', (name,)).fetchone()[0] + 1
+        cursor.execute('UPDATE ? SET id=?', (name, _id))
+        return _id
+
+    @use_cursor
+    def purge_att_property(self, table, field, obj, attr, name):
+        cursor = self.cursor
+        cursor.execute(\
+            'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? AND ' \
+            'attribute = ? ORDER BY date', (table, field, obj, name, attr))
+        rows = cursor.fetchall()
+        to_del = len(rows) - self.history_depth
+        if to_del > 0:
+            for row in rows[:to_del]:
+                cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
+
+    @use_cursor
+    def purge_property(self, table, field, obj, name):
+        cursor = self.cursor
+        cursor.execute(\
+            'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? ' \
+            'ORDER BY date', (table, field, obj, name))
+        rows = cursor.fetchall()
+        to_del = len(rows) - self.history_depth
+        if to_del > 0:
+            for row in rows[:to_del]:
+                cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
+
+    @use_cursor
+    def get_device_host(self, name):
+        cursor = self.cursor
+        name = replace_wildcard(name)
+        cursor.execute('SELECT host FROM device WHERE name LIKE ?', (name,))
+        row = cursor.fetchone()
+        if row is None:
+            raise Exception("No host for device '" + name + "'")
+        else:
+            return row[0]
+
+
+    def send_starter_cmd(self, starter_dev_names):
+        for name in starter_dev_names:
+            pos = name.find('.')
+            if pos != -1:
+                name = name[0:pos]
+            dev = PyTango.DeviceProxy(name)
+            dev.UpdateServersInfo()
+            
+            
+
+    # TANGO API
+
+    def get_stored_procedure_release(self):
+        return 'release 1.8'
+
+    @use_cursor
+    def add_device(self, server_name, dev_info, klass_name, alias=None):
+        self._info("delete_attribute_alias(server_name=%s, dev_info=%s, klass_name=%s, alias=%s)",
+                   server_name, dev_info, klass_name, alias)
+        dev_name, (domain, family, member) = dev_info
+        cursor = self.cursor
+
+        # first delete the tuple (device,name) from the device table
+        cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
+
+        # then insert the new value for this tuple
+        cursor.execute(\
+            'INSERT INTO device (name, alias, domain, family, member, exported, ' \
+            'ior, host, server, pid, class, version, started, stopped) ' \
+            'VALUES (?, ?, ?, ?, ?, 0, "nada", "nada", ?, 0, ?, "0", NULL, NULL)',
+            (dev_name, alias, domain, family, member, server_name, klass_name))
+
+        # Check if a DServer device entry for the process already exists
+        cursor.execute('SELECT name FROM device WHERE server LIKE ? AND class LIKE "DServer"', (server_name,))
+        if cursor.fetchone() is None:
+            dev_name = "dserver/" + server_name
+            domain, family, member = dev_name.split("/", 2)
+            cursor.execute(\
+            'INSERT INTO device (name, domain, family, member, exported, ior, ' \
+            'host, server, pid, class, version, started, stopped) ' \
+            'VALUES (?, ?, ?, ?, 0, "nada", "nada", ?, 0, "DServer", "0", NULL, NULL)',
+            (dev_name, domain, family, member, server_name))
+
+    @use_cursor
+    def delete_attribute_alias(self, alias):
+        self._info("delete_attribute_alias(alias=%s)", alias)
+        self.cursor.execute('DELETE FROM attribute_alias WHERE alias=?', (alias,))
+
+    @use_cursor
+    def delete_class_attribute(self, klass_name, attr_name):
+        self.cursor.execute(\
+            'DELETE FROM property_attribute_class WHERE class LIKE ? AND ' \
+            'attribute LIKE ?', (klass_name, attr_name))
+
+    @use_cursor
+    def delete_class_attribute_property(self, klass_name, attr_name, prop_name):
+        cursor = self.cursor
+
+        # Is there something to delete ?
+        cursor.execute(\
+            'SELECT count(*) FROM property_attribute_class WHERE class = ? ' \
+            'AND attribute = ? AND name = ?', (klass_name, attr_name, prop_name))
+        if cursor.fetchone()[0] > 0:
+            # then delete property from the property_attribute_class table
+            cursor.execute(\
+                'DELETE FROM property_attribute_class WHERE class = ? AND ' \
+                'attribute = ? and name = ?', (klass_name, attr_name, prop_name))
+            # mark this property as deleted
+            hist_id = self.get_id('class_attribute', cursor=cursor)
+            cursor.execute(\
+                'INSERT INTO property_attribute_class_hist (class, attribute, ' \
+                'name, id, count, value) VALUES ' \
+                '(?, ?, ?, ?, "0", "DELETED")',
+                (klass_name, attr_name, prop_name, hist_id))
+            self.purge_att_property("property_attribute_class_hist", "class",
+                                    klass_name, attr_name, prop_name, cursor=cursor)
+
+    @use_cursor
+    def delete_class_property(self, klass_name, prop_name):
+        cursor = self.cursor
+
+        prop_name = replace_wildcard(prop_name)
+        # Is there something to delete ?
+        cursor.execute(\
+            'SELECT DISTINCT name FROM property_class WHERE class=? AND ' \
+            'name LIKE ?', (klass_name, prop_name))
+        for row in cursor.fetchall():
+            # delete the tuple (device,name,count) from the property table
+            name = row[0]
+            cursor.execute(\
+                'DELETE FROM property_class WHERE class=? AND name=?',
+                (klass_name, name))
+            # Mark this property as deleted
+            hist_id = self.get_id("class", cursor=cursor)
+            cursor.execute(\
+                'INSERT INTO property_class_hist (class, name, id, count, value) ' \
+                'VALUES (?, ?, ?, "0", "DELETED")',
+                (klass_name, name, hist_id))
+            self.purge_property("property_class_hist", "class", klass_name,
+                                name, cursor=cursor)
+
+    @use_cursor
+    def delete_device(self, dev_name):
+        self._info("delete_device(dev_name=%s)", dev_name)
+        cursor = self.cursor
+        dev_name = replace_wildcard(dev_name)
+
+        # delete the device from the device table
+        cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
+
+        # delete device from the property_device table
+        cursor.execute('DELETE FROM property_device WHERE device LIKE ?', (dev_name,))
+
+        # delete device from the property_attribute_device table
+        cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ?', (dev_name,))
+
+    @use_cursor
+    def delete_device_alias(self, dev_alias):
+        self._info("delete_device_alias(dev_alias=%s)", dev_alias)
+        self.cursor.execute('UPDATE device SET alias=NULL WHERE alias=?', (dev_alias,))
+
+    @use_cursor
+    def delete_device_attribute(self, dev_name, attr_name):
+        dev_name = replace_wildcard(dev_name)
+        self.cursor.execute(\
+            'DELETE FROM property_attribute_device WHERE device LIKE ? AND ' \
+            'attribute LIKE ?', (dev_name, attr_name))
+
+    @use_cursor
+    def delete_device_attribute_property(self, dev_name, attr_name, prop_name):
+        cursor = self.cursor
+        # Is there something to delete ?
+        cursor.execute(\
+            'SELECT count(*) FROM property_attribute_device WHERE device = ?' \
+            'AND attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
+        if cursor.fetchone()[0] > 0:
+            # delete property from the property_attribute_device table
+            cursor.execute(\
+                'DELETE FROM property_attribute_device WHERE device = ? AND '
+                'attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
+            # Mark this property as deleted
+            hist_id = self.get_id("device_attribute", cursor=cursor)
+            cursor.execute(\
+                'INSERT INTO property_attribute_device_hist ' \
+                '(device, attribute, name, id, count, value) VALUES ' \
+                '(?, ?, ?, ?, "0", "DELETED")', (dev_name, attr_name, prop_name, hist_id))
+            self.purge_att_property("property_attribute_device_hist", "device",
+                                    dev_name, attr_name, prop_name, cursor=cursor)
+
+    @use_cursor
+    def delete_device_property(self, dev_name, prop_name):
+        cursor = self.cursor
+        prop_name = replace_wildcard(prop_name)
+
+        # Is there something to delete ?
+        cursor.execute(\
+            'SELECT DISTINCT name FROM property_device WHERE device=? AND ' \
+            'name LIKE ?', (dev_name, prop_name))
+        for row in cursor.fetchall():
+            # delete the tuple (device,name,count) from the property table
+            cursor.execute(\
+                'DELETE FROM property_device WHERE device=? AND name LIKE ?',
+                (dev_name, prop_name))
+            # Mark this property as deleted
+            hist_id = self.get_id("device", cursor=cursor)
+            cursor.execute(\
+                'INSERT INTO property_device_hist (device, id, name, count, value) ' \
+                'VALUES (?, ?, ?, "0", "DELETED")', (dev_name, hist_id, row[0]))
+            self.purge_property("property_device_hist", "device", dev_name, row[0])
+
+    @use_cursor
+    def delete_property(self, obj_name, prop_name):
+        cursor = self.cursor
+        prop_name = replace_wildcard(prop_name)
+
+        # Is there something to delete ?
+        cursor.execute(\
+            'SELECT DISTINCT name FROM property WHERE object=? AND ' \
+            'name LIKE ?', (obj_name, prop_name))
+        for row in cursor.fetchall():
+            # delete the tuple (object,name,count) from the property table
+            cursor.execute(\
+                'DELETE FROM property_device WHERE device=? AND name LIKE ?',
+                (obj_name, prop_name))
+            # Mark this property as deleted
+            hist_id = self.get_id("object", cursor=cursor)
+            cursor.execute(\
+                'INSERT INTO property_hist (object, name, id, count, value) ' \
+                'VALUES (?, ?, ?, "0", "DELETED")', (obj_name, row[0], hist_id))
+            self.purge_property("property_hist", "object", obj_name, row[0])
+
+    @use_cursor
+    def delete_server(self, server_instance):
+        cursor = self.cursor
+        server_instance = replace_wildcard(server_instance)
+
+        previous_host = None
+        # get host where running
+        if self.fire_to_starter:
+            adm_dev_name = "dserver/" + server_instance
+            previous_host = self.get_device_host(adm_dev_name)
+
+        # then delete the device from the device table
+        cursor.execute('DELETE FROM device WHERE server LIKE ?', (server_instance,))
+
+        # Update host's starter to update controlled servers list
+        if self.fire_to_starter and previous_host:
+            self.send_starter_cmd(previous_host)
+            pass
+
+    @use_cursor
+    def delete_server_info(self, server_instance):
+        self.cursor.execute('DELETE FROM server WHERE name=?', (server_instance,))
+
+    @use_cursor
+    def export_device(self, dev_name, IOR, host, pid, version):
+        self._info("export_device(dev_name=%s, host=%s, pid=%s, version=%s)",
+                   dev_name, host, pid, version)
+        self._info("export_device(IOR=%s)", IOR)
+        cursor = self.cursor
+        do_fire = False
+        previous_host = None
+        
+        if self.fire_to_starter:
+            if dev_name[0:8] == "dserver/":
+                # Get database server name
+                tango_util = PyTango.Util.instance()
+                db_serv = tango_util.get_ds_name()
+                adm_dev_name = "dserver/" + db_serv.lower()
+                if dev_name != adm_dev_name and dev_name[0:16] != "dserver/starter/":
+                    do_fire = True
+                    previous_host = self.get_device_host(dev_name)
+
+        cursor.execute('SELECT server FROM device WHERE name LIKE ?', (dev_name,))
+        row = cursor.fetchone()
+        if row is None:
+            th_exc(DB_DeviceNotDefined,
+                   "device " + dev_name + " not defined in the database !",
+                   "DataBase::ExportDevice()")
+        server = row[0]
+
+        # update the new value for this tuple
+        cursor.execute(\
+            'UPDATE device SET exported=1, ior=?, host=?, pid=?, version=?, ' \
+            'started=datetime("now") WHERE name LIKE ?',
+            (IOR, host, pid, version, dev_name))
+
+        # update host name in server table
+        cursor.execute('UPDATE server SET host=? WHERE name LIKE ?', (host, server))
+
+        if do_fire:
+            hosts = []
+            hosts.append(host)
+            if previous_host != "" and previous_host != "nada" and previous_host != host:
+                hosts.append(previous_host)
+            self.send_starter_cmd(hosts)
+
+    @use_cursor
+    def export_event(self, event, IOR, host, pid, version):
+        cursor = self.cursor
+        cursor.execute(\
+            'INSERT event (name,exported,ior,host,server,pid,version,started) ' \
+            'VALUES (?, 1, ?, ?, ?, ?, ?, datetime("now")',
+            (event, IOR, host, event, pid, version))
+
+    @use_cursor
+    def get_alias_device(self, dev_alias):
+        cursor = self.cursor
+        cursor.execute('SELECT name FROM device WHERE alias LIKE ?',
+                       (dev_alias,))
+        row = cursor.fetchone()
+        if row is None:
+            th_exc(DB_DeviceNotDefined,
+                   "No device found for alias '" + dev_alias + "'",
+                   "DataBase::GetAliasDevice()")
+        return row[0]
+
+    @use_cursor
+    def get_attribute_alias(self, attr_alias):
+        cursor = self.cursor
+        cursor.execute('SELECT name from attribute_alias WHERE alias LIKE ?',
+                       (attr_alias,))
+        row = cursor.fetchone()
+        if row is None:
+            th_exc(DB_SQLError,
+                   "No attribute found for alias '" + attr_alias + "'",
+                   "DataBase::GetAttributeAlias()")
+        return row[0]
+
+    @use_cursor
+    def get_attribute_alias_list(self, attr_alias):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT alias FROM attribute_alias WHERE alias LIKE ? ORDER BY attribute',
+                       (attr_alias,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    @use_cursor
+    def get_class_attribute_list(self, class_name, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT attribute FROM property_attribute_class WHERE class=? and attribute like ?',
+                       (class_name, wildcard))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    @use_cursor
+    def get_class_attribute_property(self, class_name, attributes):
+        cursor = self.cursor
+        stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ?'
+        result = [class_name, str(len(attributes))]
+        for attribute in attributes:
+            cursor.execute(stmt, (class_name, attribute))
+            rows = cursor.fetchall()
+            result.append(attribute)
+            result.append(str(len(rows)))
+            for row in rows:
+                result.append(row[0])
+                result.append(row[1])
+        return result
+
+    @use_cursor
+    def get_class_attribute_property2(self, class_name, attributes):
+        cursor = self.cursor
+        stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ? ORDER BY name,count'
+        result = [class_name, str(len(attributes))]
+        for attribute in attributes:
+            cursor.execute(stmt, (class_name, attribute))
+            rows = cursor.fetchall()
+            result.append(attribute) 
+            j = 0
+            new_prop = True
+            nb_props = 0
+            prop_size = 0
+            prop_names = []
+            prop_sizes = []
+            prop_values = []
+            for row in rows:
+                prop_values.append(row[1])
+                if j == 0:
+                    old_name = row[0]
+                else:
+                    name = row[0]
+                    if name != old_name:
+                        new_prop = True
+                        old_name = name
+                    else:
+                        new_prop = False
+                j  = j + 1
+                if new_prop == True:
+                    nb_props = nb_props + 1
+                    prop_names.append(row[0])
+                    if prop_size != 0:
+                        prop_sizes.append(prop_size)
+                    prop_size = 1
+                else:
+                    prop_size = prop_size + 1
+                    
+            result.append(str(nb_props))
+            j = 0
+            k = 0
+            for name in prop_names:
+                result.append(name)
+                result.append(prop_sizes[j])
+                for i in range(0, prop_sizes[j]):
+                    result.append(prop_values[k])
+                    k = k + 1
+                j = j + 1
+        return result
+
+    @use_cursor
+    def get_class_attribute_property_hist(self, class_name, attribute, prop_name):
+        cursor = self.cursor
+        stmt = 'SELECT  DISTINCT id FROM property_attribute_class_hist WHERE class=? AND attribute LIKE ? AND name LIKE ? ORDER by date ASC'
+        
+        result = []
+        
+        cursor.execute(stmt, (class_name, attribute, prop_name))
+        
+        for row in cursor.fetchall():
+            idr = row[0]
+        
+            stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,attribute,name,count FROM property_attribute_class_hist WHERE id =? AND class =?'
+            
+            cursor.execute(stmt, (idr, class_name))
+        
+            rows = cursor.fetchall()
+        
+            result.append(rows[2])
+            result.append(rows[3])
+            result.append(rows[0])
+            result.append(str(rows[4]))
+            for value in rows[1]:
+                result.append(value)
+
+        return result
+
+    @use_cursor
+    def get_class_for_device(self, dev_name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT class FROM device WHERE name=?', (dev_name,))
+        row = cursor.fetchone()
+        if row is None:
+            th_exc(DB_IncorrectArguments, "Class not found for " + dev_name,
+                   "Database.GetClassForDevice")
+        return row
+
+    @use_cursor
+    def get_class_inheritance_for_device(self, dev_name):
+        cursor = self.cursor
+        class_name = self.get_class_for_device(dev_name, cursor=cursor)
+        props = self.get_class_property(class_name, "InheritedFrom", cursor=cursor)
+        return [class_name] + props[4:]
+
+    @use_cursor
+    def get_class_list(self, server):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT class FROM device WHERE class LIKE ? ORDER BY class', (server,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+
+    @use_cursor
+    def get_class_property(self, class_name, properties):        
+        cursor = self.cursor
+        stmt = 'SELECT count,value FROM property_class WHERE class=? AND name LIKE ? ORDER BY count'
+        result.append(class_name)
+        result.append(len(properties))
+        for prop_name in properties:
+            cursor.execute(stmt, (class_name, prop_name))
+            rows = cursor.fetchall()
+            result.append(prop_name)
+            result.append(str(len(rows)))
+            for row in rows:
+                result.append(row[1])
+        return result
+        
+    @use_cursor
+    def get_class_property_hist(self, class_name, prop_name):
+        cursor = self.cursor
+        stmt = 'SELECT  DISTINCT id FROM property_class_hist WHERE class=? AND AND name LIKE ? ORDER by date ASC'
+        
+        result = []
+        
+        cursor.execute(stmt, (class_name, prop_name))
+        
+        for row in cursor.fetchall():
+            idr = row[0]
+        
+            stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,name,count FROM property_class_hist WHERE id =? AND class =?'
+            
+            cursor.execute(stmt, (idr, class_name))
+        
+            rows = cursor.fetchall()
+        
+            result.append(rows[2])
+            result.append(rows[0])
+            result.append(str(rows[3]))
+            for value in rows[1]:
+                result.append(value)
+
+        return result      
+        
+    @use_cursor
+    def get_class_property_list(self, class_name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT name FROM property_class WHERE class LIKE ? order by NAME',
+                       (class_name,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    @use_cursor
+    def get_device_alias(self, dev_name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT alias FROM device WHERE name LIKE ?',
+                       (dev_name,))
+        row = cursor.fetchone()
+        if row is None:
+            th_exc(DB_DeviceNotDefined,
+                   "No alias found for device '" + dev_name + "'",
+                   "DataBase::GetDeviceAlias()")
+        return row[0]
+
+    @use_cursor
+    def get_device_alias_list(self, alias):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT alias FROM device WHERE alias LIKE ? ORDER BY alias',
+                       (alias,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    @use_cursor
+    def get_device_attribute_list(self, dev_name, attribute):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT  attribute FROM property_attribute_device WHERE device=?  AND attribute LIKE ? ORDER BY attribute',
+                       (dev_name, attribute,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+
+    @use_cursor
+    def get_device_attribute_property(self, dev_name, attributes):
+        cursor = self.cursor
+        stmt = 'SELECT name,value FROM property_attribute_device WHERE device=? AND attribute LIKE ?'
+        result = [dev_name, str(len(attributes))]
+        for attribute in attributes:
+            cursor.execute(stmt, (dev_name, attribute))
+            rows = cursor.fetchall()
+            result.append(attribute)
+            result.append(str(len(rows)))
+            for row in rows:
+                result.append(row[0])
+                result.append(row[1])
+        return result  
+
+    @use_cursor
+    def get_device_attribute_property2(self, dev_name, attributes):
+        cursor = self.cursor
+        stmt = 'SELECT name,value FROM property_attribute_device WHERE device=? AND attribute LIKE ? ORDER BY name,count' 
+        result = [dev_name, str(len(attributes))]
+        for attribute in attributes:
+            cursor.execute(stmt, (dev_name, attribute))
+            rows = cursor.fetchall()
+            result.append(attribute) 
+            j = 0
+            new_prop = True
+            nb_props = 0
+            prop_size = 0
+            prop_names = []
+            prop_sizes = []
+            prop_values = []
+            for row in rows:
+                prop_values.append(row[1])
+                if j == 0:
+                    old_name = row[0]
+                else:
+                    name = row[0]
+                    if name != old_name:
+                        new_prop = True
+                        old_name = name
+                    else:
+                        new_prop = False
+                j  = j + 1
+                if new_prop == True:
+                    nb_props = nb_props + 1
+                    prop_names.append(row[0])
+                    if prop_size != 0:
+                        prop_sizes.append(prop_size)
+                    prop_size = 1
+                else:
+                    prop_size = prop_size + 1
+                    
+            result.append(str(nb_props))
+            j = 0
+            k = 0
+            for name in prop_names:
+                result.append(name)
+                result.append(prop_sizes[j])
+                for i in range(0, prop_sizes[j]):
+                    result.append(prop_values[k])
+                    k = k + 1
+                j = j + 1
+        return result
+
+    @use_cursor
+    def get_device_attribute_property_hist(self, dev_name, attribute, prop_name):
+        cursor = self.cursor
+        stmt = 'SELECT  DISTINCT id FROM property_attribute_device_hist WHERE device=? AND attribute LIKE ? AND name LIKE ? ORDER by date ASC'
+        
+        result = []
+        
+        cursor.execute(stmt, (dev_name, attribute, prop_name))
+        
+        for row in cursor.fetchall():
+            idr = row[0]
+        
+            stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,attribute,name,count FROM property_attribute_device_hist WHERE id =? AND device =? ORDER BY count ASC'
+            
+            cursor.execute(stmt, (idr, class_name))
+        
+            rows = cursor.fetchall()
+        
+            result.append(rows[2])
+            result.append(rows[3])
+            result.append(rows[0])
+            result.append(str(rows[4]))
+            for value in rows[1]:
+                result.append(value)
+
+        return result
+
+    
+    @use_cursor
+    def get_device_class_list(self, server_name):
+        cursor = self.cursor
+        result = []
+        cursor.execute('SELECT name,class FROM device WHERE server =?  ORDER BY name',
+                       (server_name,))
+        for row in cursor.fetchall():
+            result.append(row[0])
+            result.append(row[1])
+        
+        return result
+
+    @use_cursor
+    def get_device_domain_list(self, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT domain FROM device WHERE name LIKE ? OR alias LIKE ? ORDER BY domain',
+                       (wildcard,wildcard))
+        return [ row[0] for row in cursor.fetchall() ]
+
+   
+    @use_cursor
+    def get_device_exported_list(self, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT name FROM device WHERE (name LIKE ? OR alias LIKE ?) AND exported=1 ORDER BY name',
+                       (wildcard,wildcard))
+        return [ row[0] for row in cursor.fetchall() ]   
+
+    @use_cursor
+    def get_device_family_list(self, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT family FROM device WHERE name LIKE ? OR alias LIKE ? ORDER BY family',
+                       (wildcard,wildcard))
+        return [ row[0] for row in cursor.fetchall() ]   
+    
+    @use_cursor
+    def get_device_info(self, dev_name):
+        cursor = self.cursor
+        cursor.execute('SELECT exported,ior,version,pid,server,host,started,stopped,class FROM device WHERE name =?  or alias =?',
+                       (dev_name,dev_name))
+        result_long = []
+        result_str = []
+        for row in cursor.fetchall():
+            if ((row[4] == None) or (row[5] == None)):
+                th_exc(DB_SQLError,
+                       "Wrong info in database for device '" + dev_name + "'",
+                       "DataBase::GetDeviceInfo()")
+            result_str.append(dev_name)
+            if raw[1] != None:
+                result_str.append(str(raw[1]))
+            else:
+               result_str.append("")
+            result_str.append(str(raw[2]))
+            result_str.append(str(raw[4]))
+            result_str.append(str(raw[5]))
+           
+            for i in range(0,2):
+                cursor.execute('SELECT DATE_FORMAT(?,\'%D-%M-%Y at %H:%i:%s\')', raw[6 + i])
+                tmp_date = cursor.fetchone()
+                if tmp_date == None:
+                    result_str.append("?")
+                else:               
+                    result_str.append(str(tmp_date))
+
+            for i in range(0,2):
+                if raw[i] != None:
+                    result_long.append(raw[i])
+
+        result = (result_long, result_str)
+        return result
+
+    @use_cursor
+    def get_device_list(self,server_name, class_name ):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT name FROM device WHERE server LIKE ? AND class LIKE ? ORDER BY name',
+                       (server_name, class_name))
+        return [ row[0] for row in cursor.fetchall() ]
+    
+    @use_cursor
+    def get_device_wide_list(self, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT name FROM device WHERE name LIKE ? ORDER BY name',
+                       (wildcard,))
+        return [ row[0] for row in cursor.fetchall() ]
+    
+    @use_cursor
+    def get_device_member_list(self, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT  member FROM device WHERE name LIKE ? ORDER BY member',
+                       (wildcard,))
+        return [ row[0] for row in cursor.fetchall() ]    
+
+    
+    @use_cursor
+    def get_device_property(self, dev_name, properties):
+        cursor = self.cursor
+        stmt = 'SELECT count,value,name FROM property_device WHERE device = ? AND name LIKE ?  ORDER BY count'
+        result = []
+        result.append(dev_name)
+        result.append(str(len(properties)))
+        for prop in properties:
+            result.append(prop)
+            tmp_name = replace_wildcard(prop)
+            cursor.execute(stmt, (dev_name, tmp_name))
+            rows = cursor.fetchall()
+            result.append(attribute)
+            result.append(str(len(rows)))
+            for row in rows:
+                result.append(row[1])
+        return result    
+
+    @use_cursor
+    def get_device_property_hist(self, device_name, prop_name):
+        cursor = self.cursor
+        stmt = 'SELECT  DISTINCT id FROM property_device_hist WHERE device=? AND name LIKE ? ORDER by date ASC'
+        
+        result = []
+	
+        tmp_name   = replace_wildcard(prop_name);
+        
+        cursor.execute(stmt, (class_name, device_name, tmp_name))
+
+        stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,name,count FROM property_device_hist WHERE id =? AND device =? ORDER BY count ASC'
+
+        for row in cursor.fetchall():
+            idr = row[0]
+            cursor.execute(stmt, (idr, device_name))
+            rows = cursor.fetchall()
+            result.append(rows[2])
+            result.append(rows[0])
+            result.append(str(rows[3]))
+            for value in rows[1]:
+                result.append(value)
+
+        return result
+
+    @use_cursor
+    def get_device_server_class_list(self, server_name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT  class FROM device WHERE server LIKE ? ORDER BY class',
+                       (sever_name,))
+        return [ row[0] for row in cursor.fetchall() ]
+   
+    @use_cursor
+    def get_exported_device_list_for_class(self, class_name):
+        cursor = self.cursor
+        cursor.execute('SELECT  DISTINCT name FROM device WHERE class LIKE ? AND exported=1 ORDER BY name',
+                       (class_name,))
+        return [ row[0] for row in cursor.fetchall() ]   
+    
+    @use_cursor
+    def get_host_list(self, host_name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT host FROM device WHERE host LIKE ?  ORDER BY host',
+                       (host_name,))
+        return [ row[0] for row in cursor.fetchall() ]       
+    
+    @use_cursor
+    def get_host_server_list(self, host_name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT server FROM device WHERE host LIKE ?  ORDER BY server',
+                       (host_name,))
+        return [ row[0] for row in cursor.fetchall() ]     
+     
+     
+    def get_host_servers_info(self, host_name):
+        servers = self.get_host_server_list(host_name)
+        result = []
+        for server in servers:
+            result.append(server)
+            info = self.get_server_info(server)
+            result.append(info[2]) 
+            result.append(info[3])
+        return result
+
+     
+    def get_instance_name_list(self, server_name):
+        server_name = server_name + "\*"
+        server_list = self.get_server_list(server_name)
+        result = []
+        for server in server_list:
+            names = server.split("/")
+            result.append(names[1])
+        return result
+
+    @use_cursor
+    def get_object_list(self, name):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT object FROM property WHERE object LIKE ?  ORDER BY object',
+                       (name,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    @use_cursor
+    def get_property(self, object_name, properties):
+        cursor = self.cursor
+        result = []
+        result.append(object_name)
+        result.append(str(len(properties))) 
+        stmt = 'SELECT count,value,name FROM property WHERE object LIKE ?  AND name LIKE ? ORDER BY count'
+        for prop_name in properties:
+            result.append(prop_name)
+            prop_name = replace_wildcard(prop_name)
+            cursor.execute(stmt, (object_name,prop_name))
+            rows = cursor.fetchall()
+            n_rows = len(rows)
+            result.append(n_rows)
+            if n_rows:
+                for row in rows:
+                    result.append(row[1])
+                else:
+                    result.append(" ")
+        return result
+
+
+    @use_cursor
+    def get_property_hist(self, object_name, prop_name):
+        cursor = self.cursor
+        result = []
+        
+        stmt = 'SELECT  DISTINCT id FROM property_hist WHERE object=? AND name LIKE ? ORDER by date'        
+        prop_name = replace_wildcard(prop_name)        
+        cursor.execute(stmt, (object_name, prop_name))
+
+        stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,name,count FROM property_hist WHERE id =? AND object =?'
+        for row in cursor.fetchall():
+            idr = row[0]
+            
+            cursor.execute(stmt, (idr, object_name))
+            rows = cursor.fetchall()
+            count = len(rows)
+            if rows[3] == 0:
+                count = 0
+            result.append(rows[2])
+            result.append(rows[0])
+            result.append(str(count))
+            for tmp_row in rows:
+                result.append(tmp_row[1])
+
+        return result
+
+    @use_cursor
+    def get_property_list(self, object_name, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT name FROM property WHERE object LIKE ? AND name LIKE ? ORDER BY name',
+                       (object_name,wildcard))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    @use_cursor
+    def get_server_info(self, server_name):
+        cursor = self.cursor
+        cursor.execute('SELECT host,mode,level FROM server WHERE name =?',
+                       (server_name,))
+        result = []
+        result.append(server_name)
+        row = cursor.fetchone()
+        if row is None:
+            result.append(" ")
+            result.append(" ")
+            result.append(" ")
+        else:
+            result.append(row[0])
+            result.append(row[1])
+            result.append(row[2])
+            
+        return result
+     
+    @use_cursor
+    def get_server_list(self, wildcard):
+        cursor = self.cursor
+        cursor.execute('SELECT DISTINCT server FROM device WHERE server LIKE ? ORDER BY server',
+                       (wildcard,))
+        return [ row[0] for row in cursor.fetchall() ]
+
+    def get_server_list(self, wildcard):
+        result = []
+        server_list = self.get_server_list(wildcard)
+        for server in server_list:
+            found = 0
+            server_name = server.split("/")[0]
+            for res in result:
+                if server_name.lower() == res.lower():
+                    found = 1
+            if not found:
+                result.append(server_name)
+        return result
+
+    @use_cursor
+    def import_device(self, dev_name):
+        cursor = self.cursor
+        result_long = []
+        result_str = []
+        # Search first by server name and if nothing found by alias
+        # Using OR takes much more time
+        cursor.execute('"SELECT exported,ior,version,pid,server,host,class FROM device WHERE name =?',
+                       (dev_name,))
+        rows = cursor.fetchall()
+        if len(rows) == 0:
+            cursor.execute('"SELECT exported,ior,version,pid,server,host,class FROM device WHERE alias =?',
+                           (dev_name,))
+            rows = cursor.fetchall()
+            if len(rows) == 0:
+                th_exc(DB_DeviceNotDefined,
+                       "device " + dev_name + " not defined in the database !",
+                       "DataBase::ImportDevice()")
+        for row in rows:
+            result_str.append(dev_name)
+            result_str.append(row[2])
+            result_str.append(row[4])
+            result_str.append(row[5])
+            result_str.append(row[6])
+            if row[1] != None:
+                result_str.append(row[1])
+            else:
+                result_str.append("")
+            result_long.append(row[0])
+            result_long.append(row[3])
+        result = (result_long, result_str)
+        return result    
+     
+    @use_cursor
+    def import_event(self, event_name):
+        cursor = self.cursor
+        result_long = []
+        result_str = []
+        cursor.execute('"SELECT exported,ior,version,pid,host FROM event WHERE name =?',
+                       (event_name,))
+        rows = cursor.fetchall()
+        if len(rows) == 0:
+            th_exc(DB_DeviceNotDefined,
+                   "event " + event_name + " not defined in the database !",
+                   "DataBase::ImportEvent()")
+        for row in rows:
+            result_str.append(event_name)
+            result_str.append(row[1])
+            result_str.append(row[2])
+            result_str.append(row[4])
+            exported = -1
+            if row[0] != None:
+                exported = row[0]
+            result_long.append(exported)
+            result_long.append(row[3])
+        result = (result_long, result_str)
+        return result
+
+     
+    @use_cursor
+    def info(self):
+        cursor = self.cursor
+        result = []
+         # db name
+        info_str = "TANGO Database " + self.db_name
+        result.append(info_str)
+         # new line
+        result.append("")
+         # get start time of database
+        cursor.execute('SELECT started FROM device WHERE name =?',
+                       (self.db_name,))
+        row = cursor.fetchone()
+        info_str = "Running since ..." + str(row[0])
+        result.append(info_str)
+        # new line
+        result.append("")
+        # get number of devices defined
+        cursor.execute('SELECT COUNT(*) FROM device')
+        row = cursor.fetchone()
+        info_str = "Devices defined = " + str(row[0])
+        result.append(info_str)
+        # get number of devices exported
+        cursor.execute('SELECT COUNT(*) FROM device WHERE exported = 1')
+        row = cursor.fetchone()
+        info_str = "Devices exported = " + str(row[0])
+        result.append(info_str)
+        # get number of device servers defined
+        cursor.execute('SELECT COUNT(*) FROM device WHERE class = \"DServer\" ')
+        row = cursor.fetchone()
+        info_str = "Device servers defined = " + str(row[0])
+        result.append(info_str)
+        # get number of device servers exported
+        cursor.execute('SELECT COUNT(*) FROM device WHERE class = \"DServer\"  AND exported = 1')
+        row = cursor.fetchone()
+        info_str = "Device servers exported = " + str(row[0])
+        result.append(info_str)
+        # new line
+        result.append("")
+        # get number of device properties
+        cursor.execute('SELECT COUNT(*) FROM property_device')
+        row = cursor.fetchone()
+        info_str = "Device properties defineed = " + str(row[0])
+        cursor.execute('SELECT COUNT(*) FROM property_device_hist')
+        row = cursor.fetchone()
+        info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+        result.append(info_str)
+        # get number of class properties
+        cursor.execute('SELECT COUNT(*) FROM property_class')
+        row = cursor.fetchone()
+        info_str = "Class properties defined = " + str(row[0])
+        cursor.execute('SELECT COUNT(*) FROM property_class_hist')
+        row = cursor.fetchone()
+        info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+        result.append(info_str)
+        # get number of device attribute properties
+        cursor.execute('SELECT COUNT(*) FROM property_attribute_device')
+        row = cursor.fetchone()
+        info_str = "Device attribute properties defined = " + str(row[0])
+        cursor.execute('SELECT COUNT(*) FROM property_attribute_device_hist')
+        row = cursor.fetchone()
+        info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+        result.append(info_str)
+        # get number of class attribute properties
+        cursor.execute('SELECT COUNT(*) FROM property_attribute_class')
+        row = cursor.fetchone()
+        info_str = "Class attribute properties defined = " + str(row[0])
+        cursor.execute('SELECT COUNT(*) FROM property_attribute_class_hist')
+        row = cursor.fetchone()
+        info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+        result.append(info_str)
+        # get number of object properties
+        cursor.execute('SELECT COUNT(*) FROM property')
+        row = cursor.fetchone()
+        info_str = "Object properties defined = " + str(row[0])
+        cursor.execute('SELECT COUNT(*) FROM property_hist')
+        row = cursor.fetchone()
+        info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+        result.append(info_str)
+        
+        return result
+         
+    @use_cursor
+    def put_attribute_alias(self, attribute_name, attribute_alias):
+        cursor = self.cursor
+        attribute_name = attribute_name.lower()
+        # first check if this alias exists
+        cursor.execute('SELECT alias from attribute_alias WHERE alias=? AND name <> ? ',
+                       (attribute_alias,attribute_name))
+        rows = cursor.fetchall()
+        if len(rows) > 0:
+            self.warn_stream("DataBase::DbPutAttributeAlias(): this alias exists already ")
+            th_exc(DB_SQLError,
+                   "alias " + attribute_alias + " already exists !",
+                   "DataBase::DbPutAttributeAlias()")
+        tmp_names = attribute_name.split("/")
+        if len(tmp_names) != 4:
+            self.warn_stream("DataBase::DbPutAttributeAlias(): attribute name has bad syntax, must have 3 / in it")
+            th_exc(DB_SQLError,
+                   "attribute name " + attribute_name + " has bad syntax, must have 3 / in it",
+                   "DataBase::DbPutAttributeAlias()")
+         # first delete the current entry (if any)
+        cursor.execute('DELETE FROM attribute_alias WHERE name=?',
+                       (attribute_name,))
+         # update the new value for this tuple
+        tmp_device = tmp_names[0] + "/" + tmp_names[1] + "/" + tmp_names[2]
+        tmp_attribute = tmp_names[3]
+        cursor.execute('INSERT attribute_alias SET alias=? ,name=?, device=?,updated=NOW()',
+                       (attribute_alias, tmp_device, tmp_attribute)) 
+
+         
+    @use_cursor
+    def put_class_attribute_property(self, class_name, nb_attributes, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        for i in range(0,nb_attributes):
+            tmp_attribute = attr_prop_list[k]
+            nb_properties = int(attr_prop_list[k+1])
+            for j in range(k+2,k+nb_properties*2+2,2):
+                tmp_name = attr_prop_list[j]
+                tmp_value = attr_prop_list[j+1]
+                 # first delete the tuple (device,name,count) from the property table
+                cursor.execute('DELETE FROM property_attribute_class WHERE class LIKE ? AND attribute LIKE ? AND name LIKE ?', (class_name, tmp_attribute, tmp_name))
+                # then insert the new value for this tuple
+                cursor.execute('INSERT INTO property_attribute_class SET class=? ,attribute=?,name=?,count=\'1\',value=?,updated=NULL,accessed=NULL', (class_name, tmp_attribute, tmp_name, tmp_value))
+                # then insert the new value into the history table
+                hist_id = self.get_id("class_attribute", cursor=cursor)
+                cursor.execute('INSERT INTO property_attribute_class_hist SET class=?,attribute=?,name=?,id=?,count=\'1\',value=?', (class_name, tmp_attribute, tmp_name,hist_id,tmp_value))
+
+                self.purge_att_property("property_attribute_class_hist", "class",
+                                        class_name, tmp_attribute, tmp_name, cursor=cursor)
+            k = k + nb_properties*2+2
+
+    @use_cursor
+    def put_class_attribute_property2(self, class_name, nb_attributes, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        for i in range(0,nb_attributes):
+            tmp_attribute = attr_prop_list[k]
+            nb_properties = int(attr_prop_list[k+1])
+            for jj in range(0,nb_properties,1):
+                j = k + 2
+                tmp_name = attr_prop_list[j]
+                # first delete the tuple (device,name,count) from the property table
+                cursor.execute('DELETE FROM property_attribute_class WHERE class LIKE ? AND attribute LIKE ? AND name LIKE ?', (class_name, tmp_attribute, tmp_name))
+                n_rows = attr_prop_list[j+1]
+                tmp_count = 0
+                for l in range(j+1,j+n_rows+1,1):
+                    tmp_value = attr_prop_list[l+1]
+                    tmp_count = tmp_count + 1
+                    # then insert the new value for this tuple
+                    cursor.execute('INSERT INTO property_attribute_class SET class=? ,attribute=?,name=?,count=?,value=?,updated=NULL,accessed=NULL', (class_name, tmp_attribute, tmp_name, str(tmp_count), tmp_value))
+                    # then insert the new value into the history table
+                    hist_id = self.get_id("class_attribute", cursor=cursor)
+                    cursor.execute('INSERT INTO property_attribute_class_hist SET class=?,attribute=?,name=?,id=?,count=?,value=?', (class_name, tmp_attribute, tmp_name,hist_id, str(tmp_count),tmp_value))
+
+                    self.purge_att_property("property_attribute_class_hist", "class",
+                                            class_name, tmp_attribute, tmp_name, cursor=cursor)
+                k = k + n_rows + 2
+            k = k + 2    
+
+    @use_cursor
+    def put_class_property(self, class_name, nb_properties, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        for i in range(0,nb_properties):
+            tmp_count = 0
+            tmp_name = attr_prop_list[k]
+            n_rows = attr_prop_list[k+1]
+             # first delete all tuples (device,name) from the property table
+            cursor.execute('DELETE FROM property_class WHERE class LIKE ? AND name LIKE ?', (class_name, tmp_name))
+
+            for j in range(k+2,k+n_rows+2,1):
+                tmp_value = attr_prop_list[j]
+                tmp_count = tmp_count+1
+                # then insert the new value for this tuple
+                cursor.execute('INSERT INTO property_class SET class=? ,name=?,count=?,value=?,updated=NULL,accessed=NULL', (class_name, tmp_name, str(tmp_count), tmp_value))
+                # then insert the new value into the history table
+                hist_id = self.get_id("class", cursor=cursor)
+                cursor.execute('INSERT INTO property_class_hist SET class=?,name=?,id=?,count=?,value=?', (class_name, tmp_name,hist_id, str(tmp_count),tmp_value))
+                self.purge_att_property("property_class_hist", "class",
+                                        class_name, tmp_name, cursor=cursor)
+            k = k + n_rows + 2
+
+    @use_cursor
+    def put_device_alias(self, device_name, device_alias):
+        cursor = self.cursor
+        device_name = device_name.lower()
+        # first check if this alias exists
+        cursor.execute('SELECT alias from device WHERE alias=? AND name <>?',
+                       (device_alias, device_name))
+        rows = cursor.fetchall()
+        if len(rows) > 0:
+            self.warn_stream("DataBase::DbPutDeviceAlias(): this alias exists already ")
+            th_exc(DB_SQLError,
+                   "alias " + device_alias + " already exists !",
+                   "DataBase::DbPutDeviceAlias()")
+        # update the new value for this tuple
+        cursor.execute('UPDATE device SET alias=? ,started=NOW() where name LIKE ?',
+                       (device_alias, device_name)) 
+
+    @use_cursor
+    def put_device_attribute_property(self, device_name, nb_attributes, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        for i in range(0,nb_attributes):
+            tmp_attribute = attr_prop_list[k]
+            nb_properties = int(attr_prop_list[k+1])
+            for j in range(k+2,k+nb_properties*2+2,2):
+                tmp_name = attr_prop_list[j]
+                tmp_value = attr_prop_list[j+1]
+                # first delete the tuple (device,name,count) from the property table
+                cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ? AND attribute LIKE ? AND name LIKE ?', (device_name, tmp_attribute, tmp_name))
+                # then insert the new value for this tuple
+                cursor.execute('INSERT INTO property_attribute_device SET device=? ,attribute=?,name=?,count=\'1\',value=?,updated=NULL,accessed=NULL', (device_name, tmp_attribute, tmp_name, tmp_value))
+                # then insert the new value into the history table
+                hist_id = self.get_id("device_attribute", cursor=cursor)
+                cursor.execute('INSERT INTO property_attribute_device_hist SET device=?,attribute=?,name=?,id=?,count=\'1\',value=?', (device_name, tmp_attribute, tmp_name,hist_id,tmp_value))
+
+                self.purge_att_property("property_attribute_device_hist", "device",
+                                         device_name, tmp_attribute, tmp_name, cursor=cursor)
+            k = k + nb_properties*2+2         
+
+
+    @use_cursor
+    def put_device_attribute_property2(self, device_name, nb_attributes, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        for i in range(0,nb_attributes):
+            tmp_attribute = attr_prop_list[k]
+            nb_properties = int(attr_prop_list[k+1])
+            for jj in range(0,nb_properties,1):
+                j = k + 2
+                tmp_name = attr_prop_list[j]
+                # first delete the tuple (device,name,count) from the property table
+                cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ? AND attribute LIKE ? AND name LIKE ?', (device_name, tmp_attribute, tmp_name))
+                n_rows = attr_prop_list[j+1]
+                tmp_count = 0
+                for l in range(j+1,j+n_rows+1,1):
+                    tmp_value = attr_prop_list[l+1]
+                    tmp_count = tmp_count + 1
+                    # then insert the new value for this tuple
+                    cursor.execute('INSERT INTO property_attribute_device SET device=? ,attribute=?,name=?,count=?,value=?,updated=NULL,accessed=NULL', (device_name, tmp_attribute, tmp_name, str(tmp_count), tmp_value))
+                    # then insert the new value into the history table
+                    hist_id = self.get_id("device_attribute", cursor=cursor)
+                    cursor.execute('INSERT INTO property_attribute_device_hist SET device=?,attribute=?,name=?,id=?,count=?,value=?', (device_name, tmp_attribute, tmp_name,hist_id, str(tmp_count),tmp_value))
+
+                    self.purge_att_property("property_attribute_device_hist", "device",
+                                            device_name, tmp_attribute, tmp_name, cursor=cursor)
+                k = k + n_rows + 2
+            k = k + 2    
+
+    @use_cursor
+    def put_device_property(self, device_name, nb_properties, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        hist_id = self.get_id("device", cursor=cursor)
+        for i in range(0,nb_properties):
+            tmp_count = 0
+            tmp_name = attr_prop_list[k]
+            n_rows = attr_prop_list[k+1]
+            # first delete all tuples (device,name) from the property table
+            cursor.execute('DELETE FROM property_device WHERE device LIKE ? AND name LIKE ?', (device_name, tmp_name))
+
+            for j in range(k+2,k+n_rows+2,1):
+                tmp_value = attr_prop_list[j]
+                tmp_count = tmp_count+1
+                # then insert the new value for this tuple
+                cursor.execute('INSERT INTO property_device SET device=? ,name=?,count=?,value=?,updated=NULL,accessed=NULL', (device_name, tmp_name, str(tmp_count), tmp_value))
+                # then insert the new value into the history table
+                cursor.execute('INSERT INTO property_device_hist SET device=?,name=?,id=?,count=?,value=?', (device_name, tmp_name,hist_id, str(tmp_count),tmp_value))
+            self.purge_att_property("property_device_hist", "device",
+                                    device_name, tmp_name, cursor=cursor)
+            k = k + n_rows + 2
+
+    @use_cursor
+    def put_property(self, object_name, nb_properties, attr_prop_list):
+        cursor = self.cursor
+        k = 0
+        hist_id = self.get_id("object", cursor=cursor)
+        for i in range(0,nb_properties):
+            tmp_count = 0
+            tmp_name = attr_prop_list[k]
+            n_rows = attr_prop_list[k+1]
+            # first delete the property from the property table
+            cursor.execute('DELETE FROM property WHERE object =? AND name =?', (object_name, tmp_name))
+
+            for j in range(k+2,k+n_rows+2,1):
+                tmp_value = attr_prop_list[j]
+                tmp_count = tmp_count+1
+                # then insert the new value for this tuple
+                cursor.execute('INSERT INTO property SET object=? ,name=?,count=?,value=?,updated=NULL,accessed=NULL', (object_name, tmp_name, str(tmp_count), tmp_value))
+                # then insert the new value into the history table
+                cursor.execute('INSERT INTO property_hist SET object=?,name=?,id=?,count=?,value=?', (object_name, tmp_name,hist_id, str(tmp_count),tmp_value))
+            self.purge_att_property("property_hist", "object",
+                                    object_name, tmp_name, cursor=cursor)
+            k = k + n_rows + 2
+
+    @use_cursor
+    def put_server_info(self, tmp_server, tmp_host, tmp_mode, tmp_level, tmp_extra):
+        cursor = self.cursor         
+         # If it is an empty host name -> get previous host where running
+        previous_host = ""
+        if self.fire_to_starter:
+            if tmp_host == "":
+                adm_dev_name = "dserver/" + tmp_server
+                previous_host = self.get_device_host(adm_dev_name)
+        # first delete the server from the server table         
+        cursor.execute('DELETE FROM server WHERE name=?', (tmp_server,))
+        # insert the new info for this server
+        cursor.execute('INSERT INTO server SET name=? ,host=? ,mode=? ,level=?', ( tmp_server, tmp_host, tmp_mode, tmp_level))
+        #  Update host's starter to update controlled servers list
+        if self.fire_to_starter:
+            hosts = []
+            if previous_host == "":
+                hosts.append(tmp_host)
+            else:
+                hosts.append(previous_host)
+            self.send_starter_cmd(hosts)
+                 
+    @use_cursor
+    def uexport_device(self, dev_name):
+        cursor = self.cursor         
+        self._info("un-export device(dev_name=%s)", dev_name)
+        cursor.execute('UPDATE device SET exported=0,stopped=NOW() WHERE name LIKE ?', (dev_name,))
+        
+    @use_cursor
+    def uexport_event(self, event_name):
+        cursor = self.cursor         
+        self._info("un-export event (event_name=%s)", event_name)
+        cursor.execute('UPDATE event SET exported=0,stopped=NOW() WHERE name LIKE ?', (event_name,))
+                               
+    @use_cursor
+    def uexport_server(self, server_name):
+        cursor = self.cursor         
+        self._info("un-export all devices from server ", server_name)
+        cursor.execute('UPDATE device SET exported=0,stopped=NOW() WHERE server LIKE ?', (server_name,))
+        
+    @use_cursor
+    def delete_all_device_attribute_property(self, dev_name, attr_list):
+        cursor = self.cursor  
+        for attr_name in attr_list:
+            self._info("_delete_all_device_attribute_property(): delete device %s attribute %s property(ies) from database", dev_name, attr_name)
+             #Is there something to delete ?   
+            cursor.execute('SELECT DISTINCT name FROM property_attribute_device WHERE device =? AND attribute = ?', (dev_name,attr_name))
+            rows = cursor.fetchall()
+            if len(rows) != 0:
+                cursor.execute('DELETE FROM property_attribute_device WHERE device = ? AND attribute = ?', (dev_name,attr_name))
+            # Mark this property as deleted
+            for row in rows:
+                hist_id = self.get_id('device_attribute', cursor=cursor)
+                cursor.execute('INSERT INTO property_attribute_device_hist SET device=?,attribute=?,name=?,id=?,count=\'0\',value=\'DELETED\'', (dev_name,attr_name,row[0], hist_id))
+                self.purge_att_property("property_attribute_device_hist", "device",
+                                         dev_name, attr_name, row[0], cursor=cursor)
+                
+    @use_cursor
+    def my_sql_select(self, cmd):
+        cursor = self.cursor
+        cursor.execute(cmd)
+        result_long = []
+        result_str = []
+        rows = cursor.fetchall()
+        nb_fields = 0
+        for row in rows:
+            if row == None:
+                result_str.append("")
+                result_long.append(0)
+            else:
+                for field in row:
+                    nb_fields = nb_fields + 1
+                    if field != None:
+                        result_str.append(str(field))
+                        result_long.append(1)
+                    else:                        
+                        result_str.append("")
+                        result_long.append(0)
+        result_long.append(len(rows))
+        result_long.append(nb_fields)
+                    
+        result = (result_long, result_str)
+        return result
+
+
+
+    @use_cursor
+    def get_csdb_server_list(self):
+        cursor = self.cursor
+        
+        cursor.execute('SELECT DISTINCT ior FROM device WHERE exported=1 AND domain=\'sys\' AND family=\'database\'')
+        return [ row[0] for row in cursor.fetchall() ]
+     
+    @use_cursor
+    def get_attribute_alias2(self, attr_name):
+        cursor = self.cursor
+        cursor.execute('SELECT alias from attribute_alias WHERE name LIKE ? ',(attr_name,))        
+        return [ row[0] for row in cursor.fetchall() ]
+    
+    @use_cursor
+    def get_alias_attribute(self, alias_name):
+        cursor = self.cursor
+        cursor.execute('SELECT name from attribute_alias WHERE alias LIKE ? ',(alias_name,))        
+        return [ row[0] for row in cursor.fetchall() ]     
+    
+    @use_cursor
+    def rename_server(self, old_name, new_name):
+        cursor = self.cursor
+        # Check that the new name is not already used
+        new_adm_name = "dserver/" + new_name
+        cursor.execute('SELECT name from device WHERE name = ? ',(new_adm_name,))
+        rows = cursor.fetchall()
+        if len(rows) != 0:
+            th_exc(DB_SQLError,
+                   "Device server process name " + attribute_alias + "is already used !",
+                   "DataBase::DbRenameServer()")
+            
+        # get host where running
+        previous_host = ""
+        if self.fire_to_starter:
+            try:
+                adm_dev = "dserver/" + old_name
+                previous_host = self.get_device_host(adm_dev)
+            except:
+                th_exc(DB_IncorrectServerName,
+                       "Server " + old_name + "not defined in database!",
+                       "DataBase::DbRenameServer()")
+        # Change ds exec name. This means
+        #  1 - Update the device's server column
+        #  2 - Change the ds admin device name
+        #  3 - Change admin device property (if any)
+        #  4 - Change admin device attribute property (if any)
+     
+        old_adm_name = "dserver/" + old_name
+        tmp_new = new_name.split('/')
+        new_exec = tmp_new[0]
+        new_inst = tmp_new[1]    
+        new_adm_name = "dserver/" + new_name
+        
+        cursor.execute('UPDATE device SET name =?, family =?, mamber =? WHERE name =?', (new_adm_name, new_exec, new_inst, old_adm_name))
+        
+        cursor.execute('UPDATE property_device set device=? WHERE device=?', (new_adm_name, old_adm_name))
+        
+        cursor.execute('UPDATE property_attribute_device set device=? WHERE device=?', (new_adm_name, old_adm_name))
+              
+        #  Update host's starter to update controlled servers list
+        if self.fire_to_starter:
+            hosts = []
+            if previous_host == "":
+                hosts.append(tmp_host)
+            else:
+                hosts.append(previous_host)
+            self.send_starter_cmd(hosts)
+
+   
+class sqlite3(Tango_dbapi2):
+
+    DB_API_NAME = 'sqlite3'
+
+def main():
+    db = Tango_sqlite3()
+    db.add_device("MyServer/my1", ("a/b/c", ("a", "b", "c")), "MyClass")
+    db.close_db()
+
+def get_db():
+    return Executor.submit(sqlite3).result()
+
+if __name__ == "__main__":
+    main()
diff --git a/src/boost/python/db.py b/src/boost/python/db.py
index 68352d5..416e021 100644
--- a/src/boost/python/db.py
+++ b/src/boost/python/db.py
@@ -1044,7 +1044,7 @@ def __doc_Database():
             Example :
                 dev_info = DbDevInfo()
                 dev_info.name = 'my/own/device'
-                dev_info.class = 'MyDevice'
+                dev_info._class = 'MyDevice'
                 dev_info.server = 'MyServer/test'
                 db.add_device(dev_info)
 
diff --git a/src/boost/python/device_class.py b/src/boost/python/device_class.py
index f35eef4..38888c1 100644
--- a/src/boost/python/device_class.py
+++ b/src/boost/python/device_class.py
@@ -21,7 +21,7 @@ __docformat__ = "restructuredtext"
 
 import collections
 
-from ._PyTango import Except, DevFailed, _DeviceClass, CmdArgType, \
+from ._PyTango import Except, DevFailed, DeviceClass, CmdArgType, \
     DispLevel, UserDefaultAttrProp
 from .pyutil import Util
 
@@ -261,393 +261,401 @@ class PropUtil:
         return obj_2_str(argin, argout_type)
 
 
-class DeviceClass(_DeviceClass):
-    """Base class for all TANGO device-class class.
-       A TANGO device-class class is a class where is stored all
-       data/method common to all devices of a TANGO device class"""
+def __DeviceClass__init__(self, name):
+    DeviceClass.__init_orig__(self, name)
+    self.dyn_att_added_methods = []
+    try:
+        pu = self.prop_util = PropUtil()
+        self.py_dev_list = []
+        pu.set_default_property_values(self, self.class_property_list,
+                                       self.device_property_list)
+        pu.get_class_properties(self, self.class_property_list)
+        for prop_name in self.class_property_list:
+            if not hasattr(self, prop_name):
+                setattr(self, prop_name, pu.get_property_values(prop_name,
+                        self.class_property_list))
+    except DevFailed as df:
+        print("PyDS: %s: A Tango error occured in the constructor:" % name)
+        Except.print_exception(df)
+    except Exception as e:
+        print("PyDS: %s: An error occured in the constructor:" % name)
+        print(str(e))
+
+def __DeviceClass__str__(self):
+    return '%s(%s)' % (self.__class__.__name__, self.get_name())
+
+def __DeviceClass__repr__(self):
+    return '%s(%s)' % (self.__class__.__name__, self.get_name())    
+
+def __throw_create_attribute_exception(msg):
+    """
+    Helper method to throw DevFailed exception when inside
+    create_attribute
+    """
+    Except.throw_exception("PyDs_WrongAttributeDefinition", msg,
+                           "create_attribute()")
+
+def __throw_create_command_exception(msg):
+    """
+    Helper method to throw DevFailed exception when inside
+    create_command
+    """
+    Except.throw_exception("PyDs_WrongCommandDefinition", msg,
+                           "create_command()")
+
+def __DeviceClass__attribute_factory(self, attr_list):
+    """for internal usage only"""
+    for attr_name, attr_info in self.attr_list.items():
+        if isinstance(attr_info, AttrData):
+            attr_data = attr_info
+        else:
+            attr_data = AttrData(attr_name, self.get_name(), attr_info)
+        self._create_attribute(attr_list, attr_data.attr_name,
+                               attr_data.attr_type,
+                               attr_data.attr_format,
+                               attr_data.attr_write,
+                               attr_data.dim_x, attr_data.dim_y,
+                               attr_data.display_level,
+                               attr_data.polling_period,
+                               attr_data.memorized,
+                               attr_data.hw_memorized,
+                               attr_data.read_method_name,
+                               attr_data.write_method_name,
+                               attr_data.is_allowed_name,
+                               attr_data.att_prop)
+
+def __DeviceClass__command_factory(self):
+    """for internal usage only"""
+    name = self.get_name()
+    class_info = get_class(name)
+    deviceimpl_class = class_info[1]
+
+    if not hasattr(deviceimpl_class, "init_device"):
+        msg = "Wrong definition of class %s\n" \
+              "The init_device() method does not exist!" % name
+        Except.throw_exception("PyDs_WrongCommandDefinition", msg, "command_factory()")
+
+    for cmd_name, cmd_info in self.cmd_list.items():
+        __create_command(self, deviceimpl_class, cmd_name, cmd_info)
+
+def __create_command(self, deviceimpl_class, cmd_name, cmd_info):
+    """for internal usage only"""
+    name = self.get_name()
+
+    # check for well defined command info
+
+    # check parameter
+    if not isinstance(cmd_info, collections.Sequence):
+        msg = "Wrong data type for value for describing command %s in " \
+              "class %s\nMust be a sequence with 2 or 3 elements" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    if len(cmd_info) < 2 or len(cmd_info) > 3:
+        msg = "Wrong number of argument for describing command %s in " \
+              "class %s\nMust be a sequence with 2 or 3 elements" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    param_info, result_info = cmd_info[0], cmd_info[1]
+
+    if not isinstance(param_info, collections.Sequence):
+        msg = "Wrong data type in command argument for command %s in " \
+              "class %s\nCommand parameter (first element) must be a sequence" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    if len(param_info) < 1 or len(param_info) > 2:
+        msg = "Wrong data type in command argument for command %s in " \
+              "class %s\nSequence describing command parameters must contain " \
+              "1 or 2 elements"
+        __throw_create_command_exception(msg)
+
+    param_type = CmdArgType.DevVoid
+    try:
+        param_type = CmdArgType(param_info[0])
+    except:
+        msg = "Wrong data type in command argument for command %s in " \
+              "class %s\nCommand parameter type (first element in first " \
+              "sequence) must be a PyTango.CmdArgType"
+        __throw_create_command_exception(msg)
+
+    param_desc = ""
+    if len(param_info) > 1:
+        param_desc = param_info[1]
+        if not is_pure_str(param_desc):
+            msg = "Wrong data type in command parameter for command %s in " \
+                  "class %s\nCommand parameter description (second element " \
+                  "in first sequence), when given, must be a string"
+            __throw_create_command_exception(msg)
+
+    # Check result
+    if not isinstance(result_info, collections.Sequence):
+        msg = "Wrong data type in command result for command %s in " \
+              "class %s\nCommand result (second element) must be a sequence" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    if len(result_info) < 1 or len(result_info) > 2:
+        msg = "Wrong data type in command result for command %s in " \
+              "class %s\nSequence describing command result must contain " \
+              "1 or 2 elements" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    result_type = CmdArgType.DevVoid
+    try:
+        result_type = CmdArgType(result_info[0])
+    except:
+        msg = "Wrong data type in command result for command %s in " \
+              "class %s\nCommand result type (first element in second " \
+              "sequence) must be a PyTango.CmdArgType" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    result_desc = ""
+    if len(result_info) > 1:
+        result_desc = result_info[1]
+        if not is_pure_str(result_desc):
+            msg = "Wrong data type in command result for command %s in " \
+                  "class %s\nCommand parameter description (second element " \
+                  "in second sequence), when given, must be a string" % (cmd_name, name)
+            __throw_create_command_exception(msg)
+
+    # If it is defined, get addictional dictionnary used for optional parameters
+    display_level, default_command, polling_period = DispLevel.OPERATOR, False, -1
+
+    if len(cmd_info) == 3:
+        extra_info = cmd_info[2]
+        if not isinstance(extra_info, collections.Mapping):
+            msg = "Wrong data type in command information for command %s in " \
+                  "class %s\nCommand information (third element in sequence), " \
+                  "when given, must be a dictionary" % (cmd_name, name)
+            __throw_create_command_exception(msg)
+
+        if len(extra_info) > 3:
+            msg = "Wrong data type in command information for command %s in " \
+                  "class %s\nThe optional dictionary can not have more than " \
+                  "three elements" % (cmd_name, name)
+            __throw_create_command_exception(msg)
+
+        for info_name, info_value in extra_info.items():
+            info_name_lower = info_name.lower()
+            if info_name_lower == "display level":
+                try:
+                    display_level = DispLevel(info_value)
+                except:
+                    msg = "Wrong data type in command information for command %s in " \
+                          "class %s\nCommand information for display level is not a " \
+                          "PyTango.DispLevel" % (cmd_name, name)
+                    __throw_create_command_exception(msg)
+            elif info_name_lower == "default command":
+                if not is_pure_str(info_value):
+                    msg = "Wrong data type in command information for command %s in " \
+                          "class %s\nCommand information for default command is not a " \
+                          "string" % (cmd_name, name)
+                    __throw_create_command_exception(msg)
+                v = info_value.lower()
+                default_command = v == 'true'
+            elif info_name_lower == "polling period":
+                try:
+                    polling_period = int(info_value)
+                except:
+                    msg = "Wrong data type in command information for command %s in " \
+                          "class %s\nCommand information for polling period is not an " \
+                          "integer" % (cmd_name, name)
+                    __throw_create_command_exception(msg)
+            else:
+                msg = "Wrong data type in command information for command %s in " \
+                      "class %s\nCommand information has unknown key " \
+                      "%s" % (cmd_name, name, info_name)
+                __throw_create_command_exception(msg)
+
+    # check that the method to be executed exists
+    try:
+        cmd = getattr(deviceimpl_class, cmd_name)
+        if not isinstance(cmd, collections.Callable):
+            msg = "Wrong definition of command %s in " \
+                  "class %s\nThe object exists in class but is not " \
+                  "a method!" % (cmd_name, name)
+            __throw_create_command_exception(msg)
+    except AttributeError:
+        msg = "Wrong definition of command %s in " \
+              "class %s\nThe command method does not exist!" % (cmd_name, name)
+        __throw_create_command_exception(msg)
+
+    is_allowed_name = "is_%s_allowed" % cmd_name
+    try:
+        is_allowed = getattr(deviceimpl_class, is_allowed_name)
+        if not isinstance(is_allowed, collections.Callable):
+            msg = "Wrong definition of command %s in " \
+                  "class %s\nThe object '%s' exists in class but is " \
+                  "not a method!" % (cmd_name, name, is_allowed_name)
+            __throw_create_command_exception(msg)
+    except:
+        is_allowed_name = ""
 
-    class_property_list = {}
-    device_property_list = {}
-    cmd_list = {}
-    attr_list = {}
+    self._create_command(cmd_name, param_type, result_type,
+                         param_desc, result_desc,
+                         display_level, default_command,
+                         polling_period, is_allowed_name)
 
-    def __init__(self, name):
-        _DeviceClass.__init__(self, name)
-        self.dyn_att_added_methods = []
-        try:
-            pu = self.prop_util = PropUtil()
-            self.py_dev_list = []
-            pu.set_default_property_values(self, self.class_property_list,
-                                           self.device_property_list)
-            pu.get_class_properties(self, self.class_property_list)
-            for prop_name in self.class_property_list:
-                if not hasattr(self, prop_name):
-                    setattr(self, prop_name, pu.get_property_values(prop_name,
-                            self.class_property_list))
-        except DevFailed as df:
-            print("PyDS: %s: A Tango error occured in the constructor:" % name)
-            Except.print_exception(df)
-        except Exception as e:
-            print("PyDS: %s: An error occured in the constructor:" % name)
-            print(str(e))
-            
-    def __str__(self):
-        return '%s(%s)' % (self.__class__.__name__, self.get_name())
-    
-    def __repr__(self):
-        return '%s(%s)' % (self.__class__.__name__, self.get_name())    
-    
-    def __throw_create_attribute_exception(self, msg):
-        """Helper method to throw DevFailed exception when inside create_attribute"""
-        Except.throw_exception("PyDs_WrongAttributeDefinition", msg, "create_attribute()")
+def __DeviceClass__new_device(self, klass, dev_class, dev_name):
+    return klass(dev_class, dev_name)
 
-    def __throw_create_command_exception(self, msg):
-        """Helper method to throw DevFailed exception when inside create_command"""
-        Except.throw_exception("PyDs_WrongCommandDefinition", msg, "create_command()")
+def __DeviceClass__device_factory(self, device_list):
+    """for internal usage only"""
 
-    def __attribute_factory(self, attr_list):
-        """for internal usage only"""
+    klass = self.__class__
+    klass_name = klass.__name__
+    info, klass = get_class_by_class(klass), get_constructed_class_by_class(klass)
 
-        for attr_name, attr_info in self.attr_list.items():
-            if isinstance(attr_info, AttrData):
-                attr_data = attr_info
-            else:
-                attr_data = AttrData(attr_name, self.get_name(), attr_info)
-            self.__create_attribute(attr_list, attr_data)
+    if info is None:
+        raise RuntimeError("Device class '%s' is not registered" % klass_name)
 
-    def __create_attribute(self, attr_list, attr_data):
-        """for internal usage only"""
-        self._create_attribute(attr_list, attr_data.attr_name,
-                               attr_data.attr_type, attr_data.attr_format,
-                               attr_data.attr_write, attr_data.dim_x,
-                               attr_data.dim_y, attr_data.display_level,
-                               attr_data.polling_period, attr_data.memorized,
-                               attr_data.hw_memorized,
-                               attr_data.read_method_name,
-                               attr_data.write_method_name,
-                               attr_data.is_allowed_name, attr_data.att_prop)
-
-    def __create_user_default_attr_prop(self, attr_name, extra_info):
-        """for internal usage only"""
-        p = UserDefaultAttrProp()
-        for k, v in extra_info.items():
-            k_lower = k.lower()
-            method_name = "set_%s" % k_lower.replace(' ','_')
-            if hasattr(p, method_name):
-                method = getattr(p, method_name)
-                method(str(v))
-            elif k == 'delta_time':
-                p.set_delta_t(str(v))
-            elif not k_lower in ('display level', 'polling period', 'memorized'):
-                name = self.get_name()
-                msg = "Wrong definition of attribute %s in " \
-                      "class %s\nThe object extra information '%s' " \
-                      "is not recognized!" % (attr_name, name, k)
-                self.__throw_create_attribute_exception(msg)
-        return p
-
-    def __command_factory(self):
-        """for internal usage only"""
-        name = self.get_name()
-        class_info = get_class(name)
-        deviceimpl_class = class_info[1]
-
-        if not hasattr(deviceimpl_class, "init_device"):
-            msg = "Wrong definition of class %s\n" \
-                  "The init_device() method does not exist!" % name
-            Except.throw_exception("PyDs_WrongCommandDefinition", msg, "command_factory()")
-
-        for cmd_name, cmd_info in self.cmd_list.items():
-            self.__create_command(deviceimpl_class, cmd_name, cmd_info)
-
-    def __create_command(self, deviceimpl_class, cmd_name, cmd_info):
-        """for internal usage only"""
-        name = self.get_name()
-
-        # check for well defined command info
-
-        # check parameter
-        if not isinstance(cmd_info, collections.Sequence):
-            msg = "Wrong data type for value for describing command %s in " \
-                  "class %s\nMust be a sequence with 2 or 3 elements" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
-
-        if len(cmd_info) < 2 or len(cmd_info) > 3:
-            msg = "Wrong number of argument for describing command %s in " \
-                  "class %s\nMust be a sequence with 2 or 3 elements" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
-
-        param_info, result_info = cmd_info[0], cmd_info[1]
-
-        if not isinstance(param_info, collections.Sequence):
-            msg = "Wrong data type in command argument for command %s in " \
-                  "class %s\nCommand parameter (first element) must be a sequence" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
-
-        if len(param_info) < 1 or len(param_info) > 2:
-            msg = "Wrong data type in command argument for command %s in " \
-                  "class %s\nSequence describing command parameters must contain " \
-                  "1 or 2 elements"
-            self.__throw_create_command_exception(msg)
-
-        param_type = CmdArgType.DevVoid
-        try:
-            param_type = CmdArgType(param_info[0])
-        except:
-            msg = "Wrong data type in command argument for command %s in " \
-                  "class %s\nCommand parameter type (first element in first " \
-                  "sequence) must be a PyTango.CmdArgType"
-            self.__throw_create_command_exception(msg)
-
-        param_desc = ""
-        if len(param_info) > 1:
-            param_desc = param_info[1]
-            if not is_pure_str(param_desc):
-                msg = "Wrong data type in command parameter for command %s in " \
-                      "class %s\nCommand parameter description (second element " \
-                      "in first sequence), when given, must be a string"
-                self.__throw_create_command_exception(msg)
-
-        # Check result
-        if not isinstance(result_info, collections.Sequence):
-            msg = "Wrong data type in command result for command %s in " \
-                  "class %s\nCommand result (second element) must be a sequence" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
+    if klass is None:
+        raise RuntimeError("Device class '%s' as not been constructed" % klass_name)
 
-        if len(result_info) < 1 or len(result_info) > 2:
-            msg = "Wrong data type in command result for command %s in " \
-                  "class %s\nSequence describing command result must contain " \
-                  "1 or 2 elements" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
+    deviceClassClass, deviceImplClass, deviceImplName = info
+    deviceImplClass._device_class_instance = klass
 
-        result_type = CmdArgType.DevVoid
-        try:
-            result_type = CmdArgType(result_info[0])
-        except:
-            msg = "Wrong data type in command result for command %s in " \
-                  "class %s\nCommand result type (first element in second " \
-                  "sequence) must be a PyTango.CmdArgType" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
-
-        result_desc = ""
-        if len(result_info) > 1:
-            result_desc = result_info[1]
-            if not is_pure_str(result_desc):
-                msg = "Wrong data type in command result for command %s in " \
-                      "class %s\nCommand parameter description (second element " \
-                      "in second sequence), when given, must be a string" % (cmd_name, name)
-                self.__throw_create_command_exception(msg)
-
-        # If it is defined, get addictional dictionnary used for optional parameters
-        display_level, default_command, polling_period = DispLevel.OPERATOR, False, -1
-
-        if len(cmd_info) == 3:
-            extra_info = cmd_info[2]
-            if not isinstance(extra_info, collections.Mapping):
-                msg = "Wrong data type in command information for command %s in " \
-                      "class %s\nCommand information (third element in sequence), " \
-                      "when given, must be a dictionary" % (cmd_name, name)
-                self.__throw_create_command_exception(msg)
+    tmp_dev_list = []
+    for dev_name in device_list:
+        device = self._new_device(deviceImplClass, klass, dev_name)
+        self._add_device(device)
+        tmp_dev_list.append(device)
 
-            if len(extra_info) > 3:
-                msg = "Wrong data type in command information for command %s in " \
-                      "class %s\nThe optional dictionary can not have more than " \
-                      "three elements" % (cmd_name, name)
-                self.__throw_create_command_exception(msg)
-
-            for info_name, info_value in extra_info.items():
-                info_name_lower = info_name.lower()
-                if info_name_lower == "display level":
-                    try:
-                        display_level = DispLevel(info_value)
-                    except:
-                        msg = "Wrong data type in command information for command %s in " \
-                              "class %s\nCommand information for display level is not a " \
-                              "PyTango.DispLevel" % (cmd_name, name)
-                        self.__throw_create_command_exception(msg)
-                elif info_name_lower == "default command":
-                    if not is_pure_str(info_value):
-                        msg = "Wrong data type in command information for command %s in " \
-                              "class %s\nCommand information for default command is not a " \
-                              "string" % (cmd_name, name)
-                        self.__throw_create_command_exception(msg)
-                    v = info_value.lower()
-                    default_command = v == 'true'
-                elif info_name_lower == "polling period":
-                    try:
-                        polling_period = int(info_value)
-                    except:
-                        msg = "Wrong data type in command information for command %s in " \
-                              "class %s\nCommand information for polling period is not an " \
-                              "integer" % (cmd_name, name)
-                        self.__throw_create_command_exception(msg)
-                else:
-                    msg = "Wrong data type in command information for command %s in " \
-                          "class %s\nCommand information has unknown key " \
-                          "%s" % (cmd_name, name, info_name)
-                    self.__throw_create_command_exception(msg)
+    self.dyn_attr(tmp_dev_list)
 
-        # check that the method to be executed exists
-        try:
-            cmd = getattr(deviceimpl_class, cmd_name)
-            if not isinstance(cmd, collections.Callable):
-                msg = "Wrong definition of command %s in " \
-                      "class %s\nThe object exists in class but is not " \
-                      "a method!" % (cmd_name, name)
-                self.__throw_create_command_exception(msg)
-        except AttributeError:
-            msg = "Wrong definition of command %s in " \
-                  "class %s\nThe command method does not exist!" % (cmd_name, name)
-            self.__throw_create_command_exception(msg)
+    for dev in tmp_dev_list:
+        if Util._UseDb and not Util._FileDb:
+            self.export_device(dev)
+        else:
+            self.export_device(dev, dev.get_name())
+    self.py_dev_list += tmp_dev_list
 
-        is_allowed_name = "is_%s_allowed" % cmd_name
-        try:
-            is_allowed = getattr(deviceimpl_class, is_allowed_name)
-            if not isinstance(is_allowed, collections.Callable):
-                msg = "Wrong definition of command %s in " \
-                      "class %s\nThe object '%s' exists in class but is " \
-                      "not a method!" % (cmd_name, name, is_allowed_name)
-                self.__throw_create_command_exception(msg)
-        except:
-            is_allowed_name = ""
-
-        self._create_command(cmd_name, param_type, result_type,
-                             param_desc, result_desc,
-                             display_level, default_command,
-                             polling_period, is_allowed_name)
-
-    def device_factory(self, device_list):
-        """for internal usage only"""
-       
-        klass = self.__class__
-        klass_name = klass.__name__
-        info, klass = get_class_by_class(klass), get_constructed_class_by_class(klass)
-        
-        if info is None:
-            raise RuntimeError("Device class '%s' is not registered" % klass_name)
+def __DeviceClass__create_device(self, device_name, alias=None, cb=None):
+    """
+        create_device(self, device_name, alias=None, cb=None) -> None
 
-        if klass is None:
-            raise RuntimeError("Device class '%s' as not been constructed" % klass_name)
-        
-        deviceClassClass, deviceImplClass, deviceImplName = info
-        deviceImplClass._device_class_instance = klass
+            Creates a new device of the given class in the database, creates a new
+            DeviceImpl for it and calls init_device (just like it is done for
+            existing devices when the DS starts up)
 
-        tmp_dev_list = []
-        for dev_name in device_list:
-            device = deviceImplClass(klass, dev_name)
-            self._add_device(device)
-            tmp_dev_list.append(device)
+            An optional parameter callback is called AFTER the device is 
+            registered in the database and BEFORE the init_device for the
+            newly created device is called
 
-        self.dyn_attr(tmp_dev_list)
+        Throws PyTango.DevFailed:
+            - the device name exists already or
+            - the given class is not registered for this DS.
+            - the cb is not a callable
 
-        for dev in tmp_dev_list:
-            if Util._UseDb and not Util._FileDb:
-                self.export_device(dev)
-            else:
-                self.export_device(dev, dev.get_name())
-        self.py_dev_list += tmp_dev_list
+        New in PyTango 7.1.2
+
+        Parameters :
+            - device_name : (str) the device name
+            - alias : (str) optional alias. Default value is None meaning do not create device alias
+            - cb : (callable) a callback that is called AFTER the device is registered
+                   in the database and BEFORE the init_device for the newly created
+                   device is called. Typically you may want to put device and/or attribute
+                   properties in the database here. The callback must receive a parameter:
+                   device name (str). Default value is None meaning no callback
+
+        Return     : None"""
+    util = Util.instance()
+    util.create_device(self.get_name(), device_name, alias=alias, cb=cb)
+
+def __DeviceClass__delete_device(self, device_name):
+    """
+        delete_device(self, klass_name, device_name) -> None
+
+            Deletes an existing device from the database and from this running
+            server
 
-    def create_device(self, device_name, alias=None, cb=None):
-        """
-            create_device(self, device_name, alias=None, cb=None) -> None
-            
-                Creates a new device of the given class in the database, creates a new
-                DeviceImpl for it and calls init_device (just like it is done for
-                existing devices when the DS starts up)
-        
-                An optional parameter callback is called AFTER the device is 
-                registered in the database and BEFORE the init_device for the
-                newly created device is called
-                
             Throws PyTango.DevFailed:
-                - the device name exists already or
-                - the given class is not registered for this DS.
-                - the cb is not a callable
-                
-            New in PyTango 7.1.2
-            
-            Parameters :
-                - device_name : (str) the device name
-                - alias : (str) optional alias. Default value is None meaning do not create device alias
-                - cb : (callable) a callback that is called AFTER the device is registered
-                       in the database and BEFORE the init_device for the newly created
-                       device is called. Typically you may want to put device and/or attribute
-                       properties in the database here. The callback must receive a parameter:
-                       device name (str). Default value is None meaning no callback
-            
-            Return     : None"""
-        util = Util.instance()
-        util.create_device(self.get_name(), device_name, alias=alias, cb=cb)
-
-    def delete_device(self, device_name):
-        """
-            delete_device(self, klass_name, device_name) -> None
-            
-                Deletes an existing device from the database and from this running
-                server
-        
-                Throws PyTango.DevFailed:
-                    - the device name doesn't exist in the database
-                    - the device name doesn't exist in this DS.
-            
-            New in PyTango 7.1.2
-            
-            Parameters :
-                - klass_name : (str) the device class name
-                - device_name : (str) the device name
-            
-            Return     : None"""
-        util = Util.instance()
-        util.delete_device(self.get_name(), device_name)
-        
-    def dyn_attr(self,device_list):
-        """
-            dyn_attr(self,device_list) -> None
+                - the device name doesn't exist in the database
+                - the device name doesn't exist in this DS.
 
-                Default implementation does not do anything
-                Overwrite in order to provide dynamic attributes
+        New in PyTango 7.1.2
 
-            Parameters :
-                - device_list : (seq<DeviceImpl>) sequence of devices of this class
-
-            Return     : None"""
-        pass
-
-    def device_destroyer(self,name):
-        """for internal usage only"""
-        name = name.lower()
-        for d in self.py_dev_list:
-            dname = d.get_name().lower()
-            if dname == name:
-                dev_cl = d.get_device_class()
-                # the internal C++ device_destroyer isn't case sensitive so we
-                # use the internal DeviceImpl name to make sure the DeviceClass
-                # finds it
-                dev_cl._device_destroyer(d.get_name())
-                self.py_dev_list.remove(d)
-                return
-        err_mess = "Device " + name + " not in Tango class device list!"
-        Except.throw_exception("PyAPI_CantDestroyDevice",err_mess,"DeviceClass.device_destroyer")
-    
-    def device_name_factory(self, dev_name_list):
-        """
-            device_name_factory(self, dev_name_list) ->  None
-                
-                Create device(s) name list (for no database device server).
-                This method can be re-defined in DeviceClass sub-class for
-                device server started without database. Its rule is to
-                initialise class device name. The default method does nothing.
-            
-            Parameters :
-                - dev_name_list : (seq<str>) sequence of devices to be filled
+        Parameters :
+            - klass_name : (str) the device class name
+            - device_name : (str) the device name
+
+        Return     : None"""
+    util = Util.instance()
+    util.delete_device(self.get_name(), device_name)
+
+def __DeviceClass__dyn_attr(self,device_list):
+    """
+        dyn_attr(self,device_list) -> None
+
+            Default implementation does not do anything
+            Overwrite in order to provide dynamic attributes
+
+        Parameters :
+            - device_list : (seq<DeviceImpl>) sequence of devices of this class
+
+        Return     : None"""
+    pass
+
+def __DeviceClass__device_destroyer(self,name):
+    """for internal usage only"""
+    name = name.lower()
+    for d in self.py_dev_list:
+        dname = d.get_name().lower()
+        if dname == name:
+            dev_cl = d.get_device_class()
+            # the internal C++ device_destroyer isn't case sensitive so we
+            # use the internal DeviceImpl name to make sure the DeviceClass
+            # finds it
+            dev_cl._device_destroyer(d.get_name())
+            self.py_dev_list.remove(d)
+            return
+    err_mess = "Device " + name + " not in Tango class device list!"
+    Except.throw_exception("PyAPI_CantDestroyDevice",err_mess,"DeviceClass.device_destroyer")
+
+
+def __DeviceClass__device_name_factory(self, dev_name_list):
+    """
+        device_name_factory(self, dev_name_list) ->  None
+
+            Create device(s) name list (for no database device server).
+            This method can be re-defined in DeviceClass sub-class for
+            device server started without database. Its rule is to
+            initialise class device name. The default method does nothing.
+
+        Parameters :
+            - dev_name_list : (seq<str>) sequence of devices to be filled
+
+        Return     : None"""
+    pass
 
-            Return     : None"""
-        pass
     
 def __init_DeviceClass():
-    pass
+    DeviceClass.class_property_list = {}
+    DeviceClass.device_property_list = {}
+    DeviceClass.cmd_list = {}
+    DeviceClass.attr_list = {}
+    DeviceClass.__init_orig__ = DeviceClass.__init__
+    DeviceClass.__init__ = __DeviceClass__init__
+    DeviceClass.__str__ = __DeviceClass__str__
+    DeviceClass.__repr__ = __DeviceClass__repr__
+    DeviceClass._attribute_factory = __DeviceClass__attribute_factory
+    DeviceClass._command_factory = __DeviceClass__command_factory
+    DeviceClass._new_device = __DeviceClass__new_device
+    
+    DeviceClass.device_factory = __DeviceClass__device_factory
+    DeviceClass.create_device = __DeviceClass__create_device
+    DeviceClass.delete_device = __DeviceClass__delete_device
+    DeviceClass.dyn_attr = __DeviceClass__dyn_attr
+    DeviceClass.device_destroyer = __DeviceClass__device_destroyer
+    DeviceClass.device_name_factory = __DeviceClass__device_name_factory
+
 
 def __doc_DeviceClass():
+
+    DeviceClass.__doc__ = """
+    Base class for all TANGO device-class class.
+    A TANGO device-class class is a class where is stored all
+    data/method common to all devices of a TANGO device class
+    """
+    
     def document_method(method_name, desc, append=True):
         return __document_method(DeviceClass, method_name, desc, append)
 
@@ -672,6 +680,10 @@ def __doc_DeviceClass():
             is sent to to the device server process.
             The second version of the method is available only under Linux.
 
+        Throws PyTango.DevFailed:
+            - if the signal number is out of range
+            - if the operating system failed to register a signal for the process.
+
         Parameters :
             - signo : (int) signal identifier
             - own_handler : (bool) true if you want the device signal handler
@@ -680,10 +692,6 @@ def __doc_DeviceClass():
                             is set to true, care should be taken on how the
                             handler is written. A default false value is provided
         
-        Throws PyTango.DevFailed:
-            - if the signal number is out of range
-            - if the operating system failed to register a signal for the process.
-
         Return     : None
     """ )
 
diff --git a/src/boost/python/device_proxy.py b/src/boost/python/device_proxy.py
index 19223f8..ad1abc7 100644
--- a/src/boost/python/device_proxy.py
+++ b/src/boost/python/device_proxy.py
@@ -19,6 +19,7 @@ __all__ = ["device_proxy_init", "get_device_proxy"]
 
 __docformat__ = "restructuredtext"
 
+import time
 import threading
 import collections
 
@@ -33,6 +34,7 @@ from .utils import seq_2_DbData, DbData_2_dict
 from .utils import document_method as __document_method
 from .green import result, submit, green, get_green_mode
 
+_UNSUBSCRIBE_LIFETIME = 60
 
 def get_device_proxy(*args, **kwargs):
     """get_device_proxy(self, dev_name, green_mode=None, wait=True, timeout=True) -> DeviceProxy
@@ -121,6 +123,7 @@ def __check_read_attribute(dev_attr):
 def __DeviceProxy__init__(self, *args, **kwargs):
     self.__dict__['_green_mode'] = kwargs.pop('green_mode', None)
     self.__dict__['_executors'] = executors = {}
+    self.__dict__['_pending_unsubscribe'] = {}
     executors[GreenMode.Futures] = kwargs.pop('executor', None)
     executors[GreenMode.Gevent] = kwargs.pop('threadpool', None)
     return DeviceProxy.__init_orig__(self, *args, **kwargs)
@@ -863,11 +866,25 @@ def __DeviceProxy__unsubscribe_event(self, event_id):
 
         Throws     : EventSystemFailed
     """
+    events_del = set()
+    timestamp = time.time()
+    se = self.__get_event_map()
+
     with self.__get_event_map_lock():
-        se = self.__get_event_map()
-        if event_id not in se:
-            raise IndexError("This device proxy does not own this subscription " + str(event_id))
+        # first delete event callbacks that have expire
+        for evt_id, (_, expire_time) in self._pending_unsubscribe.items():
+            if expire_time <= timestamp:
+                events_del.add(evt_id)
+        for evt_id in events_del:
+            del self._pending_unsubscribe[evt_id]
+            
+        # unsubscribe and put the callback in the pending unsubscribe callbacks
+        try:
+            evt_info = se[event_id]
+        except KeyError:
+            raise KeyError("This device proxy does not own this subscription " + str(event_id))
         del se[event_id]
+        self._pending_unsubscribe[event_id] = evt_info[0], timestamp + _UNSUBSCRIBE_LIFETIME
     self.__unsubscribe_event(event_id)
 
 def __DeviceProxy__unsubscribe_event_all(self):
diff --git a/src/boost/python/device_server.py b/src/boost/python/device_server.py
index e19cb5b..068b567 100644
--- a/src/boost/python/device_server.py
+++ b/src/boost/python/device_server.py
@@ -413,12 +413,10 @@ def __DeviceImpl___remove_attr_meth(self,attr_name):
             delattr(self.__class__, allo_meth_name)
         cl.dyn_att_added_methods.remove(attr_name)
 
-def __join_msg(msg):
-    return ' '.join(map(str, msg))
 
-def __DeviceImpl__debug_stream(self, *msg):
+def __DeviceImpl__debug_stream(self, msg, *args):
     """
-    debug_stream(self, *msg) -> None
+    debug_stream(self, msg, *args) -> None
 
             Sends the given message to the tango debug stream.
 
@@ -430,11 +428,11 @@ def __DeviceImpl__debug_stream(self, *msg):
             - msg : (str) the message to be sent to the debug stream
         Return     : None
     """
-    self.__debug_stream(__join_msg(msg))
+    self.__debug_stream(msg % args)
 
-def __DeviceImpl__info_stream(self, *msg):
+def __DeviceImpl__info_stream(self, msg, *args):
     """
-    info_stream(self, *msg) -> None
+    info_stream(self, msg, *args) -> None
 
             Sends the given message to the tango info stream.
 
@@ -446,11 +444,11 @@ def __DeviceImpl__info_stream(self, *msg):
             - msg : (str) the message to be sent to the info stream
         Return     : None
     """
-    self.__info_stream(__join_msg(msg))
+    self.__info_stream(msg % args)
     
-def __DeviceImpl__warn_stream(self, *msg):
+def __DeviceImpl__warn_stream(self, msg, *args):
     """
-    warn_stream(self, *msg) -> None
+    warn_stream(self, msg, *args) -> None
 
             Sends the given message to the tango warn stream.
 
@@ -462,11 +460,11 @@ def __DeviceImpl__warn_stream(self, *msg):
             - msg : (str) the message to be sent to the warn stream
         Return     : None
     """
-    self.__warn_stream(__join_msg(msg))
+    self.__warn_stream(msg % args)
     
-def __DeviceImpl__error_stream(self, *msg):
+def __DeviceImpl__error_stream(self, msg, *args):
     """
-    error_stream(self, *msg) -> None
+    error_stream(self, msg, *args) -> None
 
             Sends the given message to the tango error stream.
 
@@ -478,11 +476,11 @@ def __DeviceImpl__error_stream(self, *msg):
             - msg : (str) the message to be sent to the error stream
         Return     : None
     """
-    self.__error_stream(__join_msg(msg))
+    self.__error_stream(msg % args)
     
-def __DeviceImpl__fatal_stream(self, *msg):
+def __DeviceImpl__fatal_stream(self, msg, *args):
     """
-    fatal_stream(self, *msg) -> None
+    fatal_stream(self, msg, *args) -> None
 
             Sends the given message to the tango fatal stream.
 
@@ -494,7 +492,7 @@ def __DeviceImpl__fatal_stream(self, *msg):
             - msg : (str) the message to be sent to the fatal stream
         Return     : None
     """
-    self.__fatal_stream(__join_msg(msg))
+    self.__fatal_stream(msg % args)
 
 @property
 def __DeviceImpl__debug(self):
@@ -549,22 +547,25 @@ def __init_DeviceImpl():
     DeviceImpl.log_error = __DeviceImpl__error
     DeviceImpl.log_fatal = __DeviceImpl__fatal
 
-def __Logger__log(self, level, *msg):
+def __Logger__log(self, level, msg, *args):
     """
-    log(self, level, *msg) -> None
+    log(self, level, msg, *args) -> None
 
             Sends the given message to the tango the selected stream.
 
         Parameters :
             - level: (Level.LevelLevel) Log level
             - msg : (str) the message to be sent to the stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
+
+    .. versionchanged: 
     """
-    self.__log(level, __join_msg(msg))
+    self.__log(level, msg % args)
 
-def __Logger__log_unconditionally(self, level, *msg):
+def __Logger__log_unconditionally(self, level, msg, *args):
     """
-    log_unconditionally(self, level, *msg) -> None
+    log_unconditionally(self, level, msg, *args) -> None
 
             Sends the given message to the tango the selected stream,
             without checking the level.
@@ -572,69 +573,75 @@ def __Logger__log_unconditionally(self, level, *msg):
         Parameters :
             - level: (Level.LevelLevel) Log level
             - msg : (str) the message to be sent to the stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
     """
-    self.__log_unconditionally(level, __join_msg(msg))
+    self.__log_unconditionally(level, msg % args)
 
-def __Logger__debug(self, *msg):
+def __Logger__debug(self, msg, *args):
     """
-    debug(self, *msg) -> None
+    debug(self, msg, *args) -> None
 
             Sends the given message to the tango debug stream.
 
         Parameters :
             - msg : (str) the message to be sent to the debug stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
     """
-    self.__debug(__join_msg(msg))
+    self.__debug(msg % args)
 
-def __Logger__info(self, *msg):
+def __Logger__info(self, msg, *args):
     """
-    info(self, *msg) -> None
+    info(self, msg, *args) -> None
 
             Sends the given message to the tango info stream.
 
         Parameters :
             - msg : (str) the message to be sent to the info stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
     """
-    self.__info(__join_msg(msg))
+    self.__info(msg % args)
 
-def __Logger__warn(self, *msg):
+def __Logger__warn(self, msg, *args):
     """
-    warn(self, *msg) -> None
+    warn(self, msg, *args) -> None
 
             Sends the given message to the tango warn stream.
 
         Parameters :
             - msg : (str) the message to be sent to the warn stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
     """
-    self.__warn(__join_msg(msg))
+    self.__warn(msg % args)
 
-def __Logger__error(self, *msg):
+def __Logger__error(self, msg, *args):
     """
-    error(self, *msg) -> None
+    error(self, msg, *args) -> None
 
             Sends the given message to the tango error stream.
 
         Parameters :
             - msg : (str) the message to be sent to the error stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
     """
-    self.__error(__join_msg(msg))
+    self.__error(msg % args)
 
-def __Logger__fatal(self, *msg):
+def __Logger__fatal(self, msg, *args):
     """
-    fatal(self, *msg) -> None
+    fatal(self, msg, *args) -> None
 
             Sends the given message to the tango fatal stream.
 
         Parameters :
             - msg : (str) the message to be sent to the fatal stream
+            - args: (seq<str>) list of optional message arguments
         Return     : None
     """
-    self.__fatal(__join_msg(msg))
+    self.__fatal(msg % args)
 
 def __Attr__str(self):
     return '%s(%s)' % (self.__class__.__name__, self.get_name())
@@ -866,8 +873,8 @@ def __doc_DeviceImpl():
                      attribute type and format.
                      for SPECTRUM and IMAGE attributes, data can be any type of sequence of elements
                      compatible with the attribute type
-            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
-                         be a str.
+            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+                         be a str or an object with the buffer interface.
             - except: (DevFailed) Instead of data, you may want to send an exception.
             - dim_x : (int) the attribute x length. Default value is 1
             - dim_y : (int) the attribute y length. Default value is 0
@@ -894,8 +901,8 @@ def __doc_DeviceImpl():
                      attribute type and format.
                      for SPECTRUM and IMAGE attributes, data can be any type of sequence of elements
                      compatible with the attribute type
-            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
-                         be a str.
+            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+                         be a str or an object with the buffer interface.
             - except: (DevFailed) Instead of data, you may want to send an exception.
             - dim_x : (int) the attribute x length. Default value is 1
             - dim_y : (int) the attribute y length. Default value is 0
@@ -923,8 +930,8 @@ def __doc_DeviceImpl():
                      attribute type and format.
                      for SPECTRUM and IMAGE attributes, data can be any type of sequence of elements
                      compatible with the attribute type
-            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
-                         be a str.
+            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+                         be a str or an object with the buffer interface.
             - dim_x : (int) the attribute x length. Default value is 1
             - dim_y : (int) the attribute y length. Default value is 0
             - time_stamp : (double) the time stamp
@@ -1061,6 +1068,32 @@ def __doc_DeviceImpl():
         New in PyTango 7.1.2
     """ )
 
+    document_method("get_attribute_poll_period", """
+    get_attribute_poll_period(self, attr_name) -> int
+
+            Returns the attribute polling period (ms) or 0 if the attribute
+            is not polled.
+
+        Parameters :
+            - attr_name : (str) attribute name
+        Return     : (int) attribute polling period (ms) or 0 if it is not polled
+        
+        New in PyTango 8.0.0
+    """ )
+
+    document_method("get_command_poll_period", """
+    get_command_poll_period(self, cmd_name) -> int
+
+            Returns the command polling period (ms) or 0 if the command
+            is not polled.
+
+        Parameters :
+            - cmd_name : (str) command name
+        Return     : (int) command polling period (ms) or 0 if it is not polled
+        
+        New in PyTango 8.0.0
+    """ )
+    
     document_method("check_command_exists", """
     check_command_exists(self) -> None
 
@@ -1529,8 +1562,8 @@ def __doc_Attribute():
                      for IMAGE attributes.
                      The recommended sequence is a C continuous and aligned numpy
                      array, as it can be optimized.
-            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
-                         be a str.
+            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+                         be a str or an object with the buffer interface.
             - dim_x : (int) [DEPRECATED] the attribute x length. Default value is 1
             - dim_y : (int) [DEPRECATED] the attribute y length. Default value is 0
         Return     : None
@@ -1555,8 +1588,8 @@ def __doc_Attribute():
                      for IMAGE attributes.
                      The recommended sequence is a C continuous and aligned numpy
                      array, as it can be optimized.
-            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
-                         be a str.
+            - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+                         be a str or an object with the buffer interface.
             - dim_x : (int) [DEPRECATED] the attribute x length. Default value is 1
             - dim_y : (int) [DEPRECATED] the attribute y length. Default value is 0
             - time_stamp : (double) the time stamp
diff --git a/src/boost/python/group.py b/src/boost/python/group.py
index cbd75b7..d97e9c4 100644
--- a/src/boost/python/group.py
+++ b/src/boost/python/group.py
@@ -20,7 +20,7 @@ __docformat__ = "restructuredtext"
 import operator
 
 from ._PyTango import __Group as _RealGroup, StdStringVector
-from .utils import seq_2_StdStringVector
+from .utils import seq_2_StdStringVector, is_pure_str
 from .utils import document_method as __document_method
 import collections
 
@@ -54,7 +54,7 @@ class Group:
     same attribute(s)/command(s) to be able to do parallel requests."""
     
     def __init__(self, name):
-        if isinstance(name, str):
+        if is_pure_str(name):
             name = _RealGroup(name)
         if not isinstance(name, _RealGroup):
             raise TypeError("Constructor expected receives a str")
diff --git a/src/boost/python/ipython/__init__.py b/src/boost/python/ipython/__init__.py
index 7b7b29b..360eb89 100644
--- a/src/boost/python/ipython/__init__.py
+++ b/src/boost/python/ipython/__init__.py
@@ -10,7 +10,7 @@
 # -----------------------------------------------------------------------------
 
 __all__ = ["init_ipython", "install", "load_ipython_extension",
-           "unload_ipython_extension", "load_config"]
+           "unload_ipython_extension", "load_config", "run", "run_qt"]
 
 from .common import get_python_version
 from .common import get_ipython_version
@@ -33,6 +33,9 @@ init_ipython = default_init_ipython
 install = default_install
 is_installed = lambda : False
 
+__run = None
+__run_qt = None
+
 ipv = get_ipython_version()
 
 if ipv >= "0.10" and ipv < "0.11":
@@ -67,3 +70,8 @@ def run():
     if not is_installed():
         install(verbose=False)
     __run()
+
+def run_qt():
+    if not is_installed():
+        install(verbose=False)
+    __run(qt=True)
\ No newline at end of file
diff --git a/src/boost/python/ipython/common.py b/src/boost/python/ipython/common.py
index 1ae516f..2ee7ada 100644
--- a/src/boost/python/ipython/common.py
+++ b/src/boost/python/ipython/common.py
@@ -35,16 +35,19 @@ def get_ipython_version():
     """Returns the current IPython version"""
     import IPython
     v = None
-    try:
+    if hasattr(IPython, "version_info"):
+        v = '.'.join(map(str, IPython.version_info[:3]))
+    else:
         try:
-            v = IPython.Release.version
-        except:
             try:
-                v = IPython.release.version
+                v = IPython.Release.version
             except:
-                pass
-    except:
-        pass
+                try:
+                    v = IPython.release.version
+                except:
+                    pass
+        except:
+            pass
     return StrictVersion(v)
 
 #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
diff --git a/src/boost/python/ipython/ipython_00_10/ipy_install.py b/src/boost/python/ipython/ipython_00_10/ipy_install.py
index 8fb2144..a8bc514 100644
--- a/src/boost/python/ipython/ipython_00_10/ipy_install.py
+++ b/src/boost/python/ipython/ipython_00_10/ipy_install.py
@@ -13,7 +13,6 @@
 
 import sys
 import os
-import io
 
 import IPython.genutils
 import PyTango
@@ -38,13 +37,16 @@ def is_installed(ipydir=None):
     return os.path.isfile(f_name)
     
 
-def install(ipydir=None,verbose=True):
+def install(ipydir=None, verbose=True, profile='tango'):
     install_dir = ipydir or IPython.genutils.get_ipython_dir()
     f_name = os.path.join(install_dir, 'ipy_profile_tango.py')
     if verbose:
-        out = sys.stdout
+        def out(msg):
+            sys.stdout.write(msg)
+            sys.stdout.flush()
     else:
-        out = io.BytesIO()
+        out = lambda x : None
+            
     if ipydir is None and os.path.isfile(f_name):
         print("Warning: The file '%s' already exists." % f_name)
         r = ''
@@ -55,19 +57,18 @@ def install(ipydir=None,verbose=True):
             return
     profile = __PROFILE.format(pytangover=PyTango.Release.version, ipyver=IPython.Release.version)
     
-    out.write("Installing tango extension to ipython... ")
-    out.flush()
+    out("Installing tango extension to ipython... ")
     try:
         f = open(f_name, "w")
         f.write(profile)
         f.close()
-        out.write("[DONE]\n\n")
+        out("[DONE]\n\n")
     except:
-        out.write("[FAILED]\n\n")
+        out("[FAILED]\n\n")
         raise
     
     ipy_user_config = os.path.join(IPython.genutils.get_ipython_dir(), 'ipy_user_conf.py')
-    out.write("""\
+    out("""\
 To start ipython with tango interface simply type on the command line:
 %% ipython -p tango
 
diff --git a/src/boost/python/ipython/ipython_00_11/ipy_install.py b/src/boost/python/ipython/ipython_00_11/ipy_install.py
index d246cb9..cbf09a0 100644
--- a/src/boost/python/ipython/ipython_00_11/ipy_install.py
+++ b/src/boost/python/ipython/ipython_00_11/ipy_install.py
@@ -53,10 +53,12 @@ def is_installed(ipydir=None, profile='tango'):
 
 def install(ipydir=None, verbose=True, profile='tango'):
     if verbose:
-        out = sys.stdout
+        def out(msg):
+            sys.stdout.write(msg)
+            sys.stdout.flush()
     else:
-        out = io.BytesIO()
-    
+        out = lambda x : None
+            
     ipython_dir = ipydir or get_ipython_dir()
     try:
         p_dir = ProfileDir.find_profile_dir_by_name(ipython_dir, profile)
@@ -71,16 +73,15 @@ def install(ipydir=None, verbose=True, profile='tango'):
     if not create_config:
         return
 
-    out.write("Installing tango extension to ipython... ")
-    out.flush()
+    out("Installing tango extension to ipython... ")
 
     profile = __PROFILE.format(pytangover=PyTango.Release.version,
                                ipyver=IPython.release.version)
     with open(abs_config_file_name, "w") as f:
         f.write(profile)
         f.close()
-    out.write("[DONE]\n\n")
-    out.write("""\
+    out("[DONE]\n\n")
+    out("""\
 To start ipython with tango interface simply type on the command line:
 %% ipython --profile=tango
 
diff --git a/src/boost/python/ipython/ipython_00_11/ipython_00_11.py b/src/boost/python/ipython/ipython_00_11/ipython_00_11.py
index bbf5911..ac3a8d2 100644
--- a/src/boost/python/ipython/ipython_00_11/ipython_00_11.py
+++ b/src/boost/python/ipython/ipython_00_11/ipython_00_11.py
@@ -536,7 +536,7 @@ def __exc_handler(ip, etype, value, tb, tb_offset=None):
         global _TANGO_ERR
         user_ns[_TANGO_ERR] = etype, value, tb, tb_offset
         if len(value.args):
-            v = value[0]
+            v = value.args[0]
             print("%s: %s" % (v.reason ,v.desc))
         else:
             print("Empty DevFailed")
diff --git a/src/boost/python/ipython/ipython_10_00/ipy_install.py b/src/boost/python/ipython/ipython_10_00/ipy_install.py
index d246cb9..b0f7bc4 100644
--- a/src/boost/python/ipython/ipython_10_00/ipy_install.py
+++ b/src/boost/python/ipython/ipython_10_00/ipy_install.py
@@ -13,9 +13,8 @@
 
 from __future__ import with_statement
 
-import sys
 import os
-import io
+import sys
 
 import IPython
 from IPython.core.profiledir import ProfileDirError, ProfileDir
@@ -51,11 +50,14 @@ def is_installed(ipydir=None, profile='tango'):
     abs_config_file_name = os.path.join(p_dir.location, _CONFIG_FILE_NAME)
     return os.path.isfile(abs_config_file_name)
 
+
 def install(ipydir=None, verbose=True, profile='tango'):
     if verbose:
-        out = sys.stdout
+        def out(msg):
+            sys.stdout.write(msg)
+            sys.stdout.flush()
     else:
-        out = io.BytesIO()
+        out = lambda x : None
     
     ipython_dir = ipydir or get_ipython_dir()
     try:
@@ -71,16 +73,15 @@ def install(ipydir=None, verbose=True, profile='tango'):
     if not create_config:
         return
 
-    out.write("Installing tango extension to ipython... ")
-    out.flush()
+    out("Installing tango extension to ipython... ")
 
     profile = __PROFILE.format(pytangover=PyTango.Release.version,
                                ipyver=IPython.release.version)
     with open(abs_config_file_name, "w") as f:
         f.write(profile)
         f.close()
-    out.write("[DONE]\n\n")
-    out.write("""\
+    out("[DONE]\n\n")
+    out("""\
 To start ipython with tango interface simply type on the command line:
 %% ipython --profile=tango
 
diff --git a/src/boost/python/ipython/ipython_10_00/ipython_10_00.py b/src/boost/python/ipython/ipython_10_00/ipython_10_00.py
index 926f119..9137bc8 100644
--- a/src/boost/python/ipython/ipython_10_00/ipython_10_00.py
+++ b/src/boost/python/ipython/ipython_10_00/ipython_10_00.py
@@ -13,12 +13,13 @@
 
 from __future__ import print_function
 
-__all__ = ["load_config", "load_ipython_extension", "unload_ipython_extension"]
+__all__ = ["load_config", "load_ipython_extension", "unload_ipython_extension",
+           "run"]
 
-import sys
 import os
 import re
 import io
+import sys
 import operator
 import textwrap
 
@@ -398,12 +399,12 @@ def mon(self, parameter_s=''):
             raise UsageError("%mon -d: must provide an attribute to unmonitor")
         else:
             try:
-                dev, sep, attr = todel.rpartition("/")
+                dev, _, attr = todel.rpartition("/")
                 subscriptions = __get_device_subscriptions(dev)
-                id = subscriptions[attr.lower()]
+                attr_id = subscriptions[attr.lower()]
                 del subscriptions[attr.lower()]
                 d = __get_device_proxy(dev)
-                d.unsubscribe_event(id)
+                d.unsubscribe_event(attr_id)
                 print("Stopped monitoring '%s'" % todel)
             except KeyError:
                 raise UsageError("%%mon -d: Not monitoring '%s'" % todel)
@@ -413,22 +414,23 @@ def mon(self, parameter_s=''):
             toadd = args[0]
         except IndexError:
             raise UsageError("%mon -a: must provide an attribute to monitor")
-        dev, sep, attr = toadd.rpartition("/")
+        dev, _, attr = toadd.rpartition("/")
         subscriptions = __get_device_subscriptions(dev)
-        id = subscriptions.get(attr.lower())
-        if id is not None:
+        attr_id = subscriptions.get(attr.lower())
+        if attr_id is not None:
             raise UsageError("%%mon -a: Already monitoring '%s'" % toadd)
         d = __get_device_proxy(dev)
         w = __get_event_log()
         model = w.model()
-        id = d.subscribe_event(attr, PyTango.EventType.CHANGE_EVENT, model, [])
-        subscriptions[attr.lower()] = id
+        attr_id = d.subscribe_event(attr, PyTango.EventType.CHANGE_EVENT,
+                                    model, [])
+        subscriptions[attr.lower()] = attr_id
         print("'%s' is now being monitored. Type 'mon' to see all events" % toadd)
     elif 'r' in opts:
         for d, v in db._db_cache.devices.items():
             d, subs = v[3], v[4]
-            for id in subs.values():
-                d.unsubscribe_event(id)
+            for _id in subs.values():
+                d.unsubscribe_event(_id)
             v[4] = {}
     elif 'i' in opts:
         try:
@@ -535,7 +537,7 @@ def __exc_handler(ip, etype, value, tb, tb_offset=None):
         global _TANGO_ERR
         user_ns[_TANGO_ERR] = etype, value, tb, tb_offset
         if len(value.args):
-            v = value[0]
+            v = value.args[0]
             print("%s: %s" % (v.reason ,v.desc))
         else:
             print("Empty DevFailed")
@@ -742,7 +744,7 @@ def init_db(parameter_s=''):
     
     r = db.command_inout("DbMySqlSelect", query)
     row_nb, column_nb = r[0][-2], r[0][-1]
-    results, data = r[0][:-2], r[1]
+    data = r[1]
     assert row_nb == len(data) / column_nb
     devices, aliases, servers, klasses = data[0::4], data[1::4], data[2::4], data[3::4]
 
@@ -795,7 +797,7 @@ def init_db(parameter_s=''):
 
     r = db.command_inout("DbMySqlSelect", query)
     row_nb, column_nb = r[0][-2], r[0][-1]
-    results, data = r[0][:-2], r[1]
+    data = r[1]
     assert row_nb == len(data) / column_nb
     attributes, aliases = data[0::2], data[1::2]
     
@@ -879,7 +881,7 @@ def complete(text):
 __DIRNAME = os.path.dirname(os.path.abspath(__file__))
 __RES_DIR = os.path.join(__DIRNAME, os.path.pardir, 'resource')
 
-class __TangoInfo(object):
+class __TangoDeviceInfo(object):
     """Helper class for when DeviceProxy.info() is not available"""
     
     def __init__(self, dev):
@@ -893,6 +895,69 @@ class __TangoInfo(object):
         self.server_host = 'Unknown'
         self.server_id = 'Unknown'
         self.server_version = 1
+    
+        
+def __get_device_class_icon(klass="Device"):
+    icon_prop = "__icon"
+    db = __get_db()
+    try:
+        icon_filename = db.get_class_property(klass, icon_prop)[icon_prop]
+        if icon_filename:
+            icon_filename = icon_filename[0]
+        else:            
+            icon_filename = klass.lower() + os.path.extsep + "png"
+    except:
+        icon_filename = klass.lower() + os.path.extsep + "png"
+    
+    if os.path.isabs(icon_filename):
+        icon = icon_filename
+    else:
+        icon = os.path.join(__RES_DIR, icon_filename)
+    if not os.path.isfile(icon):
+        icon = os.path.join(__RES_DIR, "_class.png")
+    return icon
+
+
+__DEV_CLASS_HTML_TEMPLATE = """\
+<table border="0" cellpadding="2" width="100%">
+<tr><td width="140" rowspan="7" valign="middle" align="center"><img src="{icon}" height="128"/></td>
+    <td width="140">Name:</td><td><b>{name}</b></td></tr>
+<tr><td width="140">Super class:</td><td>{super_class}</td></tr>
+<tr><td width="140">Database:</td><td>{database}</td></tr>
+<tr><td width="140">Description:</td><td>{description}</td></tr>
+<tr><td width="140">Documentation:</td><td><a target="_blank" href="{doc_url}">{doc_url}</a></td></tr>
+</table>"""
+
+def __get_class_property_str(dev_class, prop_name, default=""):
+    data = __get_db().get_class_property(dev_class, prop_name)[prop_name]
+    if len(data):
+        return data[0]
+    else:
+        return default
+
+def display_deviceclass_html(dev_class):
+    """displayhook function for PyTango.DeviceProxy, rendered as HTML"""
+    fmt = dict(name=dev_class)
+    db = __get_db()
+    try:
+        fmt["database"] = db.get_db_host() + ":" + db.get_db_port()
+    except:
+        try:
+            fmt["database"] = db.get_file_name()
+        except:
+            fmt["database"]  = "Unknown"
+
+    doc_url = __get_class_property_str(dev_class, "doc_url", "www.tango-controls.org")
+    try:
+        fmt["doc_url"] = doc_url[doc_url.index("http"):]
+    except ValueError:
+        fmt["doc_url"] = doc_url
+    
+    fmt['icon'] = __get_device_class_icon(dev_class)
+    fmt['super_class'] = __get_class_property_str(dev_class, "InheritedFrom", "DeviceImpl")
+    fmt['description'] = __get_class_property_str(dev_class, "Description", "A Tango device class")
+    return __DEV_CLASS_HTML_TEMPLATE.format(**fmt)
+
 
 def __get_device_icon(dev_proxy, klass="Device"):
     icon_prop = "__icon"
@@ -935,7 +1000,7 @@ def display_deviceproxy_html(dev_proxy):
     try:
         info = dev_proxy.info()
     except:
-        info = __TangoInfo(dev_proxy)
+        info = __TangoDeviceInfo(dev_proxy)
     name = dev_proxy.dev_name()
     fmt = dict(dev_class=info.dev_class, server_id=info.server_id,
                server_host=info.server_host, name=name)
@@ -1153,18 +1218,12 @@ def load_config(config):
     i_shell = config.InteractiveShell
     i_shell.colors = 'Linux'
 
-    if ipy_ver >= "0.12":
-        # ------------------------------------
-        # PromptManager (ipython >= 0.12)
-        # ------------------------------------
-        prompt = config.PromptManager
-        prompt.in_template = 'ITango [\\#]: '
-        prompt.out_template = 'Result [\\#]: '
-    else:
-        # (Deprecated in ipython >= 0.12 use PromptManager.in_template)
-        i_shell.prompt_in1 = 'ITango [\\#]: '
-        # (Deprecated in ipython >= 0.12 use PromptManager.out_template)
-        i_shell.prompt_out = 'Result [\\#]: '
+    # ------------------------------------
+    # PromptManager (ipython >= 0.12)
+    # ------------------------------------
+    prompt = config.PromptManager
+    prompt.in_template = 'ITango [\\#]: '
+    prompt.out_template = 'Result [\\#]: '
     
     # ------------------------------------
     # InteractiveShellApp
@@ -1232,7 +1291,7 @@ def unload_ipython_extension(ipython):
     #print "Unloading PyTango IPython extension"
     pass
 
-def run():
+def run(qt=False):
 
     # overwrite the original IPython Qt widget with our own so we can put a
     # customized banner. IPython may have been installed without Qt support so we
@@ -1265,6 +1324,10 @@ def run():
             argv.append("--profile=tango")
     except:
         pass    
-        
-    launch_new_instance()
     
+    if qt:
+        if not 'qtconsole' in argv:
+            argv.insert(1, 'qtconsole')
+            argv.append('--pylab=inline')
+    
+    launch_new_instance()
diff --git a/src/boost/python/ipython/resource/serial.png b/src/boost/python/ipython/resource/serial.png
index 789d999..a7eef8d 100644
Binary files a/src/boost/python/ipython/resource/serial.png and b/src/boost/python/ipython/resource/serial.png differ
diff --git a/src/boost/python/pytango_init.py b/src/boost/python/pytango_init.py
index 3dc2573..3512914 100644
--- a/src/boost/python/pytango_init.py
+++ b/src/boost/python/pytango_init.py
@@ -64,8 +64,8 @@ def init_constants():
         #UNAME = tuple(map(str, json.loads(constants.UNAME)))
 
     tg_rt_ver_nb = _get_tango_lib_release()
-    tg_rt_major_ver = tg_rt_ver_nb / 100
-    tg_rt_minor_ver = tg_rt_ver_nb / 10 % 10
+    tg_rt_major_ver = tg_rt_ver_nb // 100
+    tg_rt_minor_ver = tg_rt_ver_nb // 10 % 10
     tg_rt_patch_ver = tg_rt_ver_nb % 10
     tg_rt_ver = ".".join(map(str, (tg_rt_major_ver, tg_rt_minor_ver,
                                    tg_rt_patch_ver)))
diff --git a/src/boost/python/release.py b/src/boost/python/release.py
index b2d7a64..dbbc234 100644
--- a/src/boost/python/release.py
+++ b/src/boost/python/release.py
@@ -40,7 +40,7 @@ class Release:
             - keywords : (seq<str>) list of keywords
             - license : (str) the license"""
     name = 'PyTango'
-    version_info = (8, 1, 1, 'final', 0)
+    version_info = (8, 1, 4, 'final', 0)
     version = '.'.join(map(str, version_info[:3]))
     version_long = version + ''.join(map(str, version_info[3:]))
     version_description = 'This version implements the C++ Tango 8.1 API.'
diff --git a/src/boost/python/server.py b/src/boost/python/server.py
index 3384ef3..40096a6 100644
--- a/src/boost/python/server.py
+++ b/src/boost/python/server.py
@@ -9,165 +9,34 @@
 # See LICENSE.txt for more info.
 # ------------------------------------------------------------------------------
 
-"""High Level API for writing Tango device servers.
-
-.. _pytango-hlapi-datatypes:
-
-.. rubric:: Data types
-
-When declaring attributes, properties or commands, one of the most important
-information is the data type. It is given by the keyword argument *dtype*.
-In order to provide a more *pythonic* interface, this argument is not restricted
-to the :obj:`~PyTango.CmdArgType` options.
-
-For example, to define a *SCALAR* :obj:`~PyTango.CmdArgType.DevLong`
-attribute you have several possibilities:
-
-#. :obj:`int`
-#. 'int'
-#. 'int32'
-#. 'integer' 
-#. :obj:`PyTango.CmdArgType.DevLong`
-#. 'DevLong' 
-#. :obj:`numpy.int32`
-
-To define a *SPECTRUM* attribute simply wrap the scalar data type in any
-python sequence:
-
-* using a *tuple*: ``(:obj:`int`,)`` or
-* using a *list*: ``[:obj:`int`]`` or
-* any other sequence type
-
-To define an *IMAGE* attribute simply wrap the scalar data type in any
-python sequence of sequences:
-
-* using a *tuple*: ``((:obj:`int`,),)`` or
-* using a *list*: ``[[:obj:`int`]]`` or
-* any other sequence type
-
-Below is the complete table of equivalences.
-
-========================================  ========================================
- type                                      tango type                             
-========================================  ========================================
- ``None``                                  ``DevVoid``                            
- ``DevVoid``                               ``DevVoid``                            
- ``DevBoolean``                            ``DevBoolean``                         
- ``DevShort``                              ``DevShort``                           
- ``DevLong``                               ``DevLong``                            
- ``DevFloat``                              ``DevFloat``                           
- ``DevDouble``                             ``DevDouble``                          
- ``DevUShort``                             ``DevUShort``                          
- ``DevULong``                              ``DevULong``                           
- ``DevString``                             ``DevString``                          
- ``DevVarCharArray``                       ``DevVarCharArray``                    
- ``DevVarShortArray``                      ``DevVarShortArray``                   
- ``DevVarLongArray``                       ``DevVarLongArray``                    
- ``DevVarFloatArray``                      ``DevVarFloatArray``                   
- ``DevVarDoubleArray``                     ``DevVarDoubleArray``                  
- ``DevVarUShortArray``                     ``DevVarUShortArray``                  
- ``DevVarULongArray``                      ``DevVarULongArray``                   
- ``DevVarStringArray``                     ``DevVarStringArray``                  
- ``DevVarLongStringArray``                 ``DevVarLongStringArray``              
- ``DevVarDoubleStringArray``               ``DevVarDoubleStringArray``            
- ``DevState``                              ``DevState``                           
- ``DevVarBooleanArray``                    ``DevVarBooleanArray``                 
- ``DevUChar``                              ``DevUChar``                           
- ``DevLong64``                             ``DevLong64``                          
- ``DevULong64``                            ``DevULong64``                         
- ``DevVarLong64Array``                     ``DevVarLong64Array``                  
- ``DevVarULong64Array``                    ``DevVarULong64Array``                 
- ``DevInt``                                ``DevInt``                             
- ``DevEncoded``                            ``DevEncoded``                         
- ``chr``                                   ``DevUChar``                           
- ``'DevBoolean'``                          ``DevBoolean``                         
- ``'DevDouble'``                           ``DevDouble``                          
- ``'DevEncoded'``                          ``DevEncoded``                         
- ``'DevFloat'``                            ``DevFloat``                           
- ``'DevInt'``                              ``DevInt``                             
- ``'DevLong'``                             ``DevLong``                            
- ``'DevLong64'``                           ``DevLong64``                          
- ``'DevShort'``                            ``DevShort``                           
- ``'DevState'``                            ``DevState``                           
- ``'DevString'``                           ``DevString``                          
- ``'DevUChar'``                            ``DevUChar``                           
- ``'DevULong'``                            ``DevULong``                           
- ``'DevULong64'``                          ``DevULong64``                         
- ``'DevUShort'``                           ``DevUShort``                          
- ``'DevVarBooleanArray'``                  ``DevVarBooleanArray``                 
- ``'DevVarCharArray'``                     ``DevVarCharArray``                    
- ``'DevVarDoubleArray'``                   ``DevVarDoubleArray``                  
- ``'DevVarDoubleStringArray'``             ``DevVarDoubleStringArray``            
- ``'DevVarFloatArray'``                    ``DevVarFloatArray``                   
- ``'DevVarLong64Array'``                   ``DevVarLong64Array``                  
- ``'DevVarLongArray'``                     ``DevVarLongArray``                    
- ``'DevVarLongStringArray'``               ``DevVarLongStringArray``              
- ``'DevVarShortArray'``                    ``DevVarShortArray``
- ``'DevVarStringArray'``                   ``DevVarStringArray``                  
- ``'DevVarULong64Array'``                  ``DevVarULong64Array``                 
- ``'DevVarULongArray'``                    ``DevVarULongArray``                   
- ``'DevVarUShortArray'``                   ``DevVarUShortArray``                  
- ``'DevVoid'``                             ``DevVoid``                            
- ``'None'``                                ``DevVoid``                            
- ``'bool'``                                ``DevBoolean``                         
- ``'boolean'``                             ``DevBoolean``                         
- ``'byte'``                                ``DevUChar``                           
- ``'bytearray'``                           ``DevEncoded``                         
- ``'bytes'``                               ``DevEncoded``                         
- ``'char'``                                ``DevUChar``                           
- ``'chr'``                                 ``DevUChar``                           
- ``'double'``                              ``DevDouble``                          
- ``'float'``                               ``DevDouble``                          
- ``'float32'``                             ``DevFloat``                           
- ``'float64'``                             ``DevDouble``                          
- ``'int'``                                 ``DevLong``                            
- ``'int16'``                               ``DevShort``                           
- ``'int32'``                               ``DevLong``                            
- ``'int64'``                               ``DevLong64``                          
- ``'str'``                                 ``DevString``                          
- ``'string'``                              ``DevString``                          
- ``'text'``                                ``DevString``                          
- ``'uint'``                                ``DevULong``                           
- ``'uint16'``                              ``DevUShort``                          
- ``'uint32'``                              ``DevULong``                           
- ``'uint64'``                              ``DevULong64``                         
- :py:obj:`float`                           ``DevDouble``                          
- :py:obj:`int`                             ``DevLong``                            
- :py:obj:`str`                             ``DevString``                          
- :py:obj:`bool`                            ``DevBoolean``                         
- :py:obj:`bytearray`                       ``DevEncoded``                         
- :py:obj:`numpy.bool_`                     ``DevBoolean``                         
- :py:obj:`numpy.int16`                     ``DevShort``                           
- :py:obj:`numpy.int32`                     ``DevLong``                            
- :py:obj:`numpy.int64`                     ``DevLong64``                          
- :py:obj:`numpy.uint8`                     ``DevUChar``                           
- :py:obj:`numpy.uint16`                    ``DevUShort``                          
- :py:obj:`numpy.uint32`                    ``DevULong``                           
- :py:obj:`numpy.uint64`                    ``DevULong64``                         
- :py:obj:`numpy.float32`                   ``DevFloat``                           
- :py:obj:`numpy.float64`                   ``DevDouble``                          
-========================================  ========================================
-"""
+"""Server helper classes for writing Tango device servers."""
 
 from __future__ import with_statement
 from __future__ import print_function
+from __future__ import absolute_import
 
-__all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute", "command",
-           "device_property", "class_property", "server_run"]
+__all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute",
+           "command", "device_property", "class_property",
+           "run", "server_run", "Server"]
 
-import __builtin__
+import os
 import sys
 import inspect
+import logging
+import weakref
+import operator
 import functools
 import traceback
 
-from ._PyTango import DeviceImpl, Attribute, WAttribute, CmdArgType
-from ._PyTango import AttrDataFormat, AttrWriteType, DispLevel, constants
-from ._PyTango import DevFailed
+from ._PyTango import (CmdArgType, AttrDataFormat, AttrWriteType,
+                       DevFailed, Except, GreenMode, constants,
+                       Database, DbDevInfo, DevState, CmdArgType,
+                       Attr)
 from .attr_data import AttrData
 from .device_class import DeviceClass
-from .utils import get_tango_device_classes, is_seq, is_non_str_seq
-from .utils import scalar_to_array_type
+from .utils import (get_tango_device_classes, is_seq, is_non_str_seq,
+                    scalar_to_array_type)
+from .codec import loads, dumps
 
 API_VERSION = 2
 
@@ -207,13 +76,14 @@ def __build_to_tango_type():
         'chr'       : CmdArgType.DevUChar,
         'char'      : CmdArgType.DevUChar,
         'None'      : CmdArgType.DevVoid,
+        'state'     : CmdArgType.DevState,
     }
 
     for key in dir(CmdArgType):
         if key.startswith("Dev"):
             value = getattr(CmdArgType, key)
             ret[key] = ret[value] = value
-            
+
     if constants.NUMPY_SUPPORT:
         import numpy
         FROM_TANGO_TO_NUMPY_TYPE = { \
@@ -230,14 +100,14 @@ def __build_to_tango_type():
                      CmdArgType.DevFloat : numpy.float32,
         }
 
-        for key,value in FROM_TANGO_TO_NUMPY_TYPE.items():
+        for key, value in FROM_TANGO_TO_NUMPY_TYPE.items():
             ret[value] = key
     return ret
-    
+
 TO_TANGO_TYPE = __build_to_tango_type()
 
 
-def get_tango_type_format(dtype=None, dformat=None):
+def _get_tango_type_format(dtype=None, dformat=None):
     if dformat is None:
         dformat = AttrDataFormat.SCALAR
         if is_non_str_seq(dtype):
@@ -256,7 +126,7 @@ def from_typeformat_to_type(dtype, dformat):
         raise TypeError("Cannot translate IMAGE to tango type")
     return scalar_to_array_type(dtype)
 
-    
+
 def set_complex_value(attr, value):
     is_tuple = isinstance(value, tuple)
     dtype, fmt = attr.get_data_type(), attr.get_data_format()
@@ -264,7 +134,9 @@ def set_complex_value(attr, value):
         if is_tuple and len(value) == 4:
             attr.set_value_date_quality(*value)
         elif is_tuple and len(value) == 3 and is_non_str_seq(value[0]):
-            attr.set_value_date_quality(value[0][0], value[0][1], *value[1:])
+            attr.set_value_date_quality(value[0][0],
+                                        value[0][1],
+                                        *value[1:])
         else:
             attr.set_value(*value)
     else:
@@ -287,60 +159,110 @@ def set_complex_value(attr, value):
         else:
             attr.set_value(value)
 
-            
-def check_tango_device_klass_attribute_read_method(tango_device_klass, method_name):
-    """Checks if method given by it's name for the given DeviceImpl class has
-    the correct signature. If a read/write method doesn't have a parameter
-    (the traditional Attribute), then the method is wrapped into another method
-    which has correct parameter definition to make it work.
-    
+
+def check_dev_klass_attr_read_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
     :param tango_device_klass: a DeviceImpl class
     :type tango_device_klass: class
-    :param method_name: method to be cheched
-    :type attr_data: str"""
-    read_method = getattr(tango_device_klass, method_name)
-
-    @functools.wraps(read_method)
-    def read_attr(self, attr):
-        ret = read_method(self)
-        if not attr.get_value_flag() and ret is not None:
-            set_complex_value(attr, ret)
-        return ret
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    read_method = getattr(attribute, "fget", None)
+    if read_method:
+        method_name = "__read_{0}__".format(attribute.attr_name)
+        attribute.read_method_name = method_name
+    else:
+        method_name = attribute.read_method_name
+        read_method = getattr(tango_device_klass, method_name)
+
+    read_args = inspect.getargspec(read_method)
+
+    if len(read_args.args) < 2:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            runner = _get_runner()
+            if runner:
+                ret = runner.execute(read_method, self)
+            else:
+                ret = read_method(self)
+            if not attr.get_value_flag() and ret is not None:
+                set_complex_value(attr, ret)
+            return ret
+    else:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            runner = _get_runner()
+            if runner:
+                ret = runner.execute(read_method, self, attr)
+            else:            
+                ret = read_method(self, attr)
+            return ret
+        
+    method_name = "__read_{0}_wrapper__".format(attribute.attr_name)
+    attribute.read_method_name = method_name        
+
     setattr(tango_device_klass, method_name, read_attr)
 
-    
-def check_tango_device_klass_attribute_write_method(tango_device_klass, method_name):
-    """Checks if method given by it's name for the given DeviceImpl class has
-    the correct signature. If a read/write method doesn't have a parameter
-    (the traditional Attribute), then the method is wrapped into another method
-    which has correct parameter definition to make it work.
-    
+
+def check_dev_klass_attr_write_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
     :param tango_device_klass: a DeviceImpl class
     :type tango_device_klass: class
-    :param method_name: method to be cheched
-    :type attr_data: str"""
-    write_method = getattr(tango_device_klass, method_name)
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    write_method = getattr(attribute, "fset", None)
+    if write_method:
+        method_name = "__write_{0}__".format(attribute.attr_name)
+        attribute.write_method_name = method_name
+    else:
+        method_name = attribute.write_method_name
+        write_method = getattr(tango_device_klass, method_name)
 
     @functools.wraps(write_method)
     def write_attr(self, attr):
         value = attr.get_write_value()
-        return write_method(self, value)
+        runner = _get_runner()
+        if runner:
+            ret = runner.execute(write_method, self, value)
+        else:
+            ret = write_method(self, value)
+        return ret
     setattr(tango_device_klass, method_name, write_attr)
 
-    
-def check_tango_device_klass_attribute_methods(tango_device_klass, attr_data):
-    """Checks if the read and write methods have the correct signature. If a 
-    read/write method doesn't have a parameter (the traditional Attribute),
-    then the method is wrapped into another method to make this work
-    
+
+def check_dev_klass_attr_methods(tango_device_klass, attribute):
+    """
+    Checks if the read and write methods have the correct signature.
+    If a read/write method doesn't have a parameter (the traditional
+    Attribute), then the method is wrapped into another method to make
+    this work.
+
     :param tango_device_klass: a DeviceImpl class
     :type tango_device_klass: class
-    :param attr_data: the attribute data information
-    :type attr_data: AttrData"""
-    if attr_data.attr_write in (AttrWriteType.READ, AttrWriteType.READ_WRITE):
-        check_tango_device_klass_attribute_read_method(tango_device_klass, attr_data.read_method_name)
-    if attr_data.attr_write in (AttrWriteType.WRITE, AttrWriteType.READ_WRITE):
-        check_tango_device_klass_attribute_write_method(tango_device_klass, attr_data.write_method_name)
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    if attribute.attr_write in (AttrWriteType.READ,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_read_method(tango_device_klass,
+                                         attribute)
+    if attribute.attr_write in (AttrWriteType.WRITE,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_write_method(tango_device_klass,
+                                          attribute)
 
 
 class _DeviceClass(DeviceClass):
@@ -349,34 +271,47 @@ class _DeviceClass(DeviceClass):
         DeviceClass.__init__(self, name)
         self.set_type(name)
 
+    def _new_device(self, klass, dev_class, dev_name):
+        runner = _get_runner()
+        if runner:
+            return runner.execute(DeviceClass._new_device, self,
+                                  klass, dev_class, dev_name)
+        else:
+            return DeviceClass._new_device(self, klass, dev_class,
+                                           dev_name)
+
     def dyn_attr(self, dev_list):
         """Invoked to create dynamic attributes for the given devices.
         Default implementation calls
         :meth:`TT.initialize_dynamic_attributes` for each device
-    
+
         :param dev_list: list of devices
         :type dev_list: :class:`PyTango.DeviceImpl`"""
 
         for dev in dev_list:
-            init_dyn_attrs = getattr(dev, "initialize_dynamic_attributes", None)
+            init_dyn_attrs = getattr(dev,
+                                     "initialize_dynamic_attributes",
+                                     None)
             if init_dyn_attrs and callable(init_dyn_attrs):
                 try:
                     init_dyn_attrs()
                 except Exception:
-                    import traceback
-                    dev.warn_stream("Failed to initialize dynamic attributes")
-                    dev.debug_stream("Details: " + traceback.format_exc())
-    
-        
+                    dev.warn_stream("Failed to initialize dynamic " \
+                                    "attributes")
+                    dev.debug_stream("Details: " + \
+                                     traceback.format_exc())
+
+
 def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
     klass_name = tango_device_klass.__name__
     if not issubclass(tango_device_klass, (Device)):
-        msg = "{0} device must inherit from PyTango.server.Device".format(klass_name)
+        msg = "{0} device must inherit from " \
+              "PyTango.server.Device".format(klass_name)
         raise Exception(msg)
-    
+
     if attrs is None:
         attrs = tango_device_klass.__dict__
-        
+
     attr_list = {}
     class_property_list = {}
     device_property_list = {}
@@ -384,34 +319,47 @@ def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
 
     for attr_name, attr_obj in attrs.items():
         if isinstance(attr_obj, attribute):
-            attr_obj._set_name(attr_name)
+            if attr_obj.attr_name is None:
+                attr_obj._set_name(attr_name)
+            else:
+                attr_name = attr_obj.attr_name
             attr_list[attr_name] = attr_obj
-            check_tango_device_klass_attribute_methods(tango_device_klass, attr_obj)
+            check_dev_klass_attr_methods(tango_device_klass, attr_obj)
         elif isinstance(attr_obj, device_property):
-            device_property_list[attr_name] = [attr_obj.dtype, attr_obj.doc, attr_obj.default_value]
+            attr_obj.name = attr_name
+            device_property_list[attr_name] = [attr_obj.dtype,
+                                               attr_obj.doc,
+                                               attr_obj.default_value]
         elif isinstance(attr_obj, class_property):
-            class_property_list[attr_name] = [attr_obj.dtype, attr_obj.doc, attr_obj.default_value]
+            attr_obj.name = attr_name
+            class_property_list[attr_name] = [attr_obj.dtype,
+                                              attr_obj.doc,
+                                              attr_obj.default_value]
         elif inspect.isroutine(attr_obj):
             if hasattr(attr_obj, "__tango_command__"):
                 cmd_name, cmd_info = attr_obj.__tango_command__
                 cmd_list[cmd_name] = cmd_info
-    
+
     devclass_name = klass_name + "Class"
-    
+
     devclass_attrs = dict(class_property_list=class_property_list,
                           device_property_list=device_property_list,
                           cmd_list=cmd_list, attr_list=attr_list)
     return type(devclass_name, (_DeviceClass,), devclass_attrs)
 
 
-def init_tango_device_klass(tango_device_klass, attrs=None, tango_class_name=None):
+def init_tango_device_klass(tango_device_klass, attrs=None,
+                            tango_class_name=None):
     klass_name = tango_device_klass.__name__
-    tango_deviceclass_klass = create_tango_deviceclass_klass(tango_device_klass,
-                                                             attrs=attrs)
+    tango_deviceclass_klass = create_tango_deviceclass_klass(
+        tango_device_klass, attrs=attrs)
     if tango_class_name is None:
-        tango_klass_name = klass_name
-    tango_device_klass._DeviceClass = tango_deviceclass_klass
-    tango_device_klass._DeviceClassName = tango_klass_name
+        if hasattr(tango_device_klass, "TangoClassName"):
+            tango_class_name = tango_device_klass.TangoClassName
+        else:
+            tango_class_name = klass_name
+    tango_device_klass.TangoClassClass = tango_deviceclass_klass
+    tango_device_klass.TangoClassName = tango_class_name
     tango_device_klass._api = API_VERSION
     return tango_device_klass
 
@@ -426,19 +374,21 @@ def create_tango_device_klass(name, bases, attrs):
 
 
 def DeviceMeta(name, bases, attrs):
-    """The :py:data:`metaclass` callable for :class:`Device`. Every subclass of
-    :class:`Device` must have associated this metaclass to itself in order to
-    work properly (boost-python internal limitation).
-    
+    """
+    The :py:data:`metaclass` callable for :class:`Device`.Every
+    sub-class of :class:`Device` must have associated this metaclass
+    to itself in order to work properly (boost-python internal
+    limitation).
+
     Example (python 2.x)::
-    
+
         from PyTango.server import Device, DeviceMeta
 
         class PowerSupply(Device):
             __metaclass__ = DeviceMeta
 
     Example (python 3.x)::
-    
+
         from PyTango.server import Device, DeviceMeta
 
         class PowerSupply(Device, metaclass=DeviceMeta):
@@ -448,370 +398,1174 @@ def DeviceMeta(name, bases, attrs):
 
 
 class Device(LatestDeviceImpl):
-    """High level DeviceImpl API. All Device specific classes should inherit
-    from this class."""
-    
+    """
+    High level DeviceImpl API. All Device specific classes should
+    inherit from this class."""
+
     def __init__(self, cl, name):
+        self._tango_properties = {}
         LatestDeviceImpl.__init__(self, cl, name)
         self.init_device()
 
     def init_device(self):
-        """Tango init_device method. Default implementation calls
+        """
+        Tango init_device method. Default implementation calls
         :meth:`get_device_properties`"""
         self.get_device_properties()
-    
+
     def always_executed_hook(self):
-        """Tango always_executed_hook. Default implementation does nothing"""
+        """
+        Tango always_executed_hook. Default implementation does
+        nothing
+        """
         pass
 
     def initialize_dynamic_attributes(self):
-        """Method executed at initializion phase to create dynamic attributes.
-        Default implementation does nothing. Overwrite when necessary."""
+        """
+        Method executed at initializion phase to create dynamic
+        attributes. Default implementation does nothing. Overwrite
+        when necessary.
+        """
         pass
 
 
 class attribute(AttrData):
-    """declares a new tango attribute in a :class:`Device`. To be used like
-the python native :obj:`property` function. For example, to declare a
-scalar, `PyTango.DevDouble`, read-only attribute called *voltage* in a
-*PowerSupply* :class:`Device` do::
+    '''
+    Declares a new tango attribute in a :class:`Device`. To be used
+    like the python native :obj:`property` function. For example, to
+    declare a scalar, `PyTango.DevDouble`, read-only attribute called
+    *voltage* in a *PowerSupply* :class:`Device` do::
 
-    class PowerSupply(Device):
-        
-        voltage = attribute()
-        
-        def read_voltage(self):
-            self.voltage = 1.0
-
-It receives multiple keyword arguments.
-
-===================== ================================ ======================================= =======================================================================================
-parameter              type                                       default value                                 description
-===================== ================================ ======================================= =======================================================================================
-name                   :obj:`str`                       class member name                       alternative attribute name
-dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
-dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
-max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format) 
-max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats) 
-display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
-polling_period         :obj:`int`                       -1                                      polling period
-memorized              :obj:`bool`                      False                                   attribute should or not be memorized
-hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
-access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
-fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
-fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
-is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
-label                  :obj:`str`                       '<attr_name>'                           attribute label
-doc (or description)   :obj:`str`                       ''                                      attribute description
-unit                   :obj:`str`                       ''                                      physical units the attribute value is in
-standard_unit          :obj:`str`                       ''                                      physical standard unit
-display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
-format                 :obj:`str`                       '6.2f'                                  attribute representation format
-min_value              :obj:`str`                       None                                    minimum allowed value
-max_value              :obj:`str`                       None                                    maximum allowed value
-min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
-max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
-min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
-max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
-delta_val              :obj:`str`                       None
-delta_t                :obj:`str`                       None
-abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
-rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
-period                 :obj:`str`                       None
-archive_abs_change     :obj:`str`                       None
-archive_rel_change     :obj:`str`                       None
-archive_period         :obj:`str`                       None
-===================== ================================ ======================================= ======================================================================================="""
-
-    def __init__(self, **kwargs):
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            voltage = attribute()
+
+            def read_voltage(self):
+                return 999.999
+
+    The same can be achieved with::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @attribute
+            def voltage(self):
+                return 999.999
+
+
+    It receives multiple keyword arguments.
+
+    ===================== ================================ ======================================= =======================================================================================
+    parameter              type                                       default value                                 description
+    ===================== ================================ ======================================= =======================================================================================
+    name                   :obj:`str`                       class member name                       alternative attribute name
+    dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
+    dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
+    max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format)
+    max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
+    display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
+    polling_period         :obj:`int`                       -1                                      polling period
+    memorized              :obj:`bool`                      False                                   attribute should or not be memorized
+    hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
+    access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
+    fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
+    fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
+    is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
+    label                  :obj:`str`                       '<attr_name>'                           attribute label
+    doc (or description)   :obj:`str`                       ''                                      attribute description
+    unit                   :obj:`str`                       ''                                      physical units the attribute value is in
+    standard_unit          :obj:`str`                       ''                                      physical standard unit
+    display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
+    format                 :obj:`str`                       '6.2f'                                  attribute representation format
+    min_value              :obj:`str`                       None                                    minimum allowed value
+    max_value              :obj:`str`                       None                                    maximum allowed value
+    min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
+    max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
+    min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
+    max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
+    delta_val              :obj:`str`                       None
+    delta_t                :obj:`str`                       None
+    abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
+    rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
+    period                 :obj:`str`                       None
+    archive_abs_change     :obj:`str`                       None
+    archive_rel_change     :obj:`str`                       None
+    archive_period         :obj:`str`                       None
+    ===================== ================================ ======================================= =======================================================================================
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    Example of a integer writable attribute with a customized label,
+    unit and description::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            current = attribute(label="Current", unit="mA", dtype=int,
+                                access=AttrWriteType.READ_WRITE,
+                                doc="the power supply current")
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            def read_current(self):
+                return self._current
+
+            def write_current(self, current):
+                self._current = current
+
+    The same, but using attribute as a decorator::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            @attribute(label="Current", unit="mA", dtype=int)
+            def current(self):
+                """the power supply current"""
+                return 999.999
+
+            @current.write
+            def current(self, current):
+                self._current = current
+
+    In this second format, defining the `write` implies setting the
+    attribute access to READ_WRITE.
+    '''
+
+    def __init__(self, fget=None, **kwargs):
+        self._kwargs = dict(kwargs)
         name = kwargs.pop("name", None)
         class_name = kwargs.pop("class_name", None)
+
+        if fget:
+            if inspect.isroutine(fget):
+                self.fget = fget
+                if 'doc' not in kwargs and 'description' not in kwargs:
+                    kwargs['doc'] = fget.__doc__
+            else:
+                kwargs['fget'] = fget
+
         super(attribute, self).__init__(name, class_name)
         if 'dtype' in kwargs:
             kwargs['dtype'], kwargs['dformat'] = \
-                get_tango_type_format(kwargs['dtype'], kwargs.get('dformat'))
+                _get_tango_type_format(kwargs['dtype'],
+                                      kwargs.get('dformat'))
         self.build_from_dict(kwargs)
-   
+
     def get_attribute(self, obj):
         return obj.get_device_attr().get_attr_by_name(self.attr_name)
 
     # --------------------
     # descriptor interface
     # --------------------
-    
+
     def __get__(self, obj, objtype):
         return self.get_attribute(obj)
 
     def __set__(self, obj, value):
         attr = self.get_attribute(obj)
         set_complex_value(attr, value)
-    
+
     def __delete__(self, obj):
         obj.remove_attribute(self.attr_name)
 
-        
-def _attribute(**kwargs):
-    """declares a new tango attribute in a :class:`Device`. To be used like
-the python native :obj:`property` function. For example, to declare a
-scalar, `PyTango.DevDouble`, read-only attribute called *voltage* in a
-*PowerSupply* :class:`Device` do::
+    def setter(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        self.fset = fset
+        if self.attr_write == AttrWriteType.READ:
+            if getattr(self, 'fget', None):
+                self.attr_write = AttrWriteType.READ_WRITE
+            else:
+                self.attr_write = AttrWriteType.WRITE
+        return self
 
-    class PowerSupply(Device):
-        
-        voltage = attribute()
-        
-        def read_voltage(self):
-            self.voltage = 1.0
-
-It receives multiple keyword arguments.
-
-===================== ================================ ======================================= =======================================================================================
-parameter              type                                       default value                                 description
-===================== ================================ ======================================= =======================================================================================
-name                   :obj:`str`                       class member name                       alternative attribute name
-dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
-dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
-max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format) 
-max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats) 
-display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
-polling_period         :obj:`int`                       -1                                      polling period
-memorized              :obj:`bool`                      False                                   attribute should or not be memorized
-hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
-access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
-fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
-fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
-is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
-label                  :obj:`str`                       '<attr_name>'                           attribute label
-doc (or description)   :obj:`str`                       ''                                      attribute description
-unit                   :obj:`str`                       ''                                      physical units the attribute value is in
-standard_unit          :obj:`str`                       ''                                      physical standard unit
-display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
-format                 :obj:`str`                       '6.2f'                                  attribute representation format
-min_value              :obj:`str`                       None                                    minimum allowed value
-max_value              :obj:`str`                       None                                    maximum allowed value
-min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
-max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
-min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
-max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
-delta_val              :obj:`str`                       None
-delta_t                :obj:`str`                       None
-abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
-rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
-period                 :obj:`str`                       None
-archive_abs_change     :obj:`str`                       None
-archive_rel_change     :obj:`str`                       None
-archive_period         :obj:`str`                       None
-===================== ================================ ======================================= ======================================================================================="""
-    if 'dtype' in kwargs:
-        kwargs['dtype'], kwargs['dformat'] = \
-          get_tango_type_format(kwargs['dtype'], kwargs.get('dformat'))
-    return attribute.from_dict(kwargs)
+    def write(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        return self.setter(fset)
+
+    def __call__(self, fget):
+        return type(self)(fget=fget, **self._kwargs)
 
 
 def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
             dtype_out=None, dformat_out=None, doc_out="",):
-    """declares a new tango command in a :class:`Device`.
-    To be used like a decorator in the methods you want to declare as tango
-    commands. For example, to declare a *ramp* command that receives a
-    `PyTango.DevDouble` parameter called *current*, do::
+    """
+    Declares a new tango command in a :class:`Device`.
+    To be used like a decorator in the methods you want to declare as
+    tango commands. The following example declares commands:
+
+        * `void TurnOn(void)`
+        * `void Ramp(DevDouble current)`
+        * `DevBool Pressurize(DevDouble pressure)`
+
+    ::
 
         class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @command
+            def TurnOn(self):
+                self.info_stream('Turning on the power supply')
 
             @command(dtype_in=float)
-            def ramp(self, current):
-                self.info_stream("Ramping on %f..." % current)
-
-            # Another more elaborate command
-            
-            @command(dtype_in=float, doc_in="the pressure to be set",
-                     dtype_out=(bool, doc_out="True if it worked, False otherwise")
-            def setPressure(self, pressure):
-                self.info_stream("Setting pressure on %f..." % pressure)
-                
-    :param dtype_in: a :ref:`data type <pytango-hlapi-datatypes>`
-                     describing the type of parameter. Default is None meaning
-                     no parameter.
+            def Ramp(self, current):
+                self.info_stream('Ramping on %f...' % current)
+
+            @command(dtype_in=float, doc_in='the pressure to be set',
+                     dtype_out=bool, doc_out='True if it worked, False otherwise')
+            def Pressurize(self, pressure):
+                self.info_stream('Pressurizing to %f...' % pressure)
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    :param dtype_in:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of parameter. Default is None meaning no parameter.
     :param dformat_in: parameter data format. Default is None.
     :type dformat_in: AttrDataFormat
     :param doc_in: parameter documentation
     :type doc_in: str
 
-    :param dtype_out: a :ref:`data type <pytango-hlapi-datatypes>`
-                      describing the type of return value. Default is None
-                      meaning no return value.
+    :param dtype_out:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of return value. Default is None meaning no return value.
     :param dformat_out: return value data format. Default is None.
     :type dformat_out: AttrDataFormat
     :param doc_out: return value documentation
     :type doc_out: str
-
     """
     if f is None:
         return functools.partial(command,
             dtype_in=dtype_in, dformat_in=dformat_in, doc_in=doc_in,
-            dtype_out=dtype_out, dformat_out=dformat_out, doc_out=doc_out)
+            dtype_out=dtype_out, dformat_out=dformat_out,
+            doc_out=doc_out)
     name = f.__name__
-    
-    dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
-    dtype_out, dformat_out = get_tango_type_format(dtype_out, dformat_out)
+
+    dtype_in, dformat_in = _get_tango_type_format(dtype_in, dformat_in)
+    dtype_out, dformat_out = _get_tango_type_format(dtype_out,
+                                                    dformat_out)
 
     din = [from_typeformat_to_type(dtype_in, dformat_in), doc_in]
     dout = [from_typeformat_to_type(dtype_out, dformat_out), doc_out]
-    f.__tango_command__ = name, [din, dout]
-    return f
+
+    @functools.wraps(f)
+    def cmd(self, value):
+        runner = _get_runner()
+        if runner:
+            ret = runner.execute(f, self, value)
+        else:
+            ret = f(self, value)
+        return ret
+    cmd.__tango_command__ = name, [din, dout]
+    return cmd
 
 
 class _property(object):
 
     def __init__(self, dtype, doc='', default_value=None):
+        self.name = None
         self.__value = None
-        dtype = from_typeformat_to_type(*get_tango_type_format(dtype))
+        dtype = from_typeformat_to_type(*_get_tango_type_format(dtype))
         self.dtype = dtype
         self.doc = doc
         self.default_value = default_value
-    
+
     def __get__(self, obj, objtype):
-        return self.__value
+        return obj._tango_properties.get(self.name)
 
     def __set__(self, obj, value):
-        self.__value = value
-    
+        obj._tango_properties[self.name] = value
+
     def __delete__(self, obj):
-        del self.__value
+        del obj._tango_properties[self.name]
 
 
 class device_property(_property):
+    """
+    Declares a new tango device property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, device property called
+    *host* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import device_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            host = device_property(dtype=str)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
     pass
 
 
 class class_property(_property):
+    """
+    Declares a new tango class property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, class property called
+    *port* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import class_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            port = class_property(dtype=int, default_value=9788)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
     pass
 
 
-def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
-                 event_loop=None):
-    import PyTango
-    if msg_stream is None:
-        import io
-        msg_stream = io.BytesIO()
+def __to_cb(post_init_callback):
+    if post_init_callback is None:
+        return lambda : None
+
+    err_msg = "post_init_callback must be a callable or " \
+              "sequence <callable [, args, [, kwargs]]>"
+    if operator.isCallable(post_init_callback):
+        f = post_init_callback
+    elif is_non_str_seq(post_init_callback):
+        length = len(post_init_callback)
+        if length < 1 or length > 3:
+            raise TypeError(err_msg)
+        cb = post_init_callback[0]
+        if not operator.isCallable(cb):
+            raise TypeError(err_msg)
+        args, kwargs = [], {}
+        if length > 1:
+            args = post_init_callback[1]
+        if length > 2:
+            kwargs = post_init_callback[2]
+        f = functools.partial(cb, *args, **kwargs)
+    else:
+        raise TypeError(err_msg)
 
-    if args is None:
-        args = sys.argv
+    return f
 
-    if util is None:
-        util = PyTango.Util(args)
 
+def _to_classes(classes):
+    uclasses = []
     if is_seq(classes):
         for klass_info in classes:
-            if not hasattr(klass_info, '_api') or klass_info._api < 2:
-                raise Exception("When giving a single class, it must implement HLAPI (see PyTango.server)")
-            klass_klass = klass_info._DeviceClass
-            klass_name = klass_info._DeviceClassName
-            klass = klass_info
-            util.add_class(klass_klass, klass, klass_name)
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            uclasses.append((klass_klass, klass, klass_name))
     else:
         for klass_name, klass_info in classes.items():
             if is_seq(klass_info):
-                klass_klass, klass = klass_info
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
             else:
                 if not hasattr(klass_info, '_api') or klass_info._api < 2:
-                    raise Exception("When giving a single class, it must implement HLAPI (see PyTango.server)")
-                klass_klass = klass_info._DeviceClass
-                klass_name = klass_info._DeviceClassName
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
                 klass = klass_info
-            util.add_class(klass_klass, klass, klass_name)
+            uclasses.append((klass_klass, klass, klass_name))
+    return uclasses
+
+
+def _add_classes(util, classes):
+    for class_info in _to_classes(classes):
+        util.add_class(*class_info)
+
+
+def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
+                 event_loop=None, post_init_callback=None,
+                 green_mode=None):
+    if green_mode is None:
+        from PyTango import get_green_mode
+        green_mode = get_green_mode()
+    gevent_mode = green_mode == GreenMode.Gevent
+
+    import PyTango
+    if msg_stream is None:
+        write = lambda msg: None
+    else:
+        write = msg_stream.write
+
+    if args is None:
+        args = sys.argv
+
+    post_init_callback = __to_cb(post_init_callback)
+
+    if util is None:
+        util = PyTango.Util(args)
     u_instance = PyTango.Util.instance()
+
+    if gevent_mode:
+        runner = _create_runner()
+        if event_loop:
+            event_loop = functools.partial(runner.execute, event_loop)
+
     if event_loop is not None:
         u_instance.server_set_event_loop(event_loop)
-    u_instance.server_init()
-    msg_stream.write("Ready to accept request\n")
-    u_instance.server_run()
+
+    log = logging.getLogger("PyTango")
+
+    def tango_loop(runner=None):
+        _add_classes(util, classes)
+        u_instance.server_init()
+        if runner:
+            runner.execute(post_init_callback)
+        else:
+            post_init_callback()
+        write("Ready to accept request\n")
+        u_instance.server_run()
+        if runner:
+            runner.stop()
+        log.debug("Tango loop exit")
+        
+    if gevent_mode:
+        runner = _create_runner()
+        start_new_thread = runner._threading.start_new_thread
+        tango_thread_id = start_new_thread(tango_loop, (runner,))
+        runner.run()
+        log.debug("Runner finished")
+    else:
+        tango_loop()
+        
     return util
 
+def run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, green_mode=None):
+    """
+    Provides a simple way to run a tango server. It handles exceptions
+    by writting a message to the msg_stream.
+
+    The `classes` parameter can be either a sequence of:
+
+    * :class:`~PyTango.server.Device` or
+    * a sequence of two elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl` or
+    * a sequence of three elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+      tango class name (str)
+
+    or a dictionary where:
+
+    * key is the tango class name
+    * value is either:
+        * a :class:`~PyTango.server.Device` class or
+        * a sequence of two elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
+          or
+        * a sequence of three elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+          tango class name (str)
+
+    The optional `post_init_callback` can be a callable (without
+    arguments) or a tuple where the first element is the callable,
+    the second is a list of arguments (optional) and the third is a
+    dictionary of keyword arguments (also optional).
+
+    .. note::
+       the order of registration of tango classes defines the order
+       tango uses to initialize the corresponding devices.
+       if using a dictionary as argument for classes be aware that the
+       order of registration becomes arbitrary. If you need a
+       predefined order use a sequence or an OrderedDict.
+
+    Example 1: registering and running a PowerSupply inheriting from
+    :class:`~PyTango.server.Device`::
+
+        from PyTango.server import Device, DeviceMeta, run
 
-def server_run(classes, args=None, msg_stream=sys.stdout,
-               verbose=False, util=None, event_loop=None):
-    """Provides a simple way to run a tango server. It handles exceptions
-       by writting a message to the msg_stream.
-
-       The `classes` parameter can be either a sequence of :class:`~PyTango.server.Device`
-       classes or a dictionary where:
-       
-       * key is the tango class name
-       * value is either:
-           #. a :class:`~PyTango.server.Device` class or
-           #. a a sequence of two elements :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
-           
-       Example 1: registering and running a PowerSupply inheriting from :class:`~PyTango.server.Device`::
-       
-           from PyTango import server_run
-           from PyTango.server import Device, DeviceMeta
-       
-           class PowerSupply(Device):
-               __metaclass__ = DeviceMeta
-               
-           server_run((PowerSupply,))
-           
-       Example 2: registering and running a MyServer defined by tango classes 
-       `MyServerClass` and `MyServer`::
-       
-           import PyTango
-
-           class MyServer(PyTango.Device_4Impl):
-               pass
-               
-           class MyServerClass(PyTango.DeviceClass):
-               pass
-       
-           PyTango.server_run({"MyServer": (MyServerClass, MyServer)})
-       
-       :param classes:
-           a sequence of :class:`~PyTango.server.Device` classes or
-           a dictionary where keyword is the tango class name and value is a 
-           sequence of Tango Device Class python class, and Tango Device python class
-       :type classes: sequence or dict
-       
-       :param args:
-           list of command line arguments [default: None, meaning use sys.argv]
-       :type args: list
-       
-       :param msg_stream:
-           stream where to put messages [default: sys.stdout]
-       
-       :param util:
-           PyTango Util object [default: None meaning create a Util instance]
-       :type util: :class:`~PyTango.Util`
-
-       :param event_loop: event_loop callable
-       :type event_loop: callable
-       
-       :return: The Util singleton object
-       :rtype: :class:`~PyTango.Util`
-       
-       .. versionadded:: 8.0.0
-       
-       .. versionchanged:: 8.0.3
-           Added `util` keyword parameter.
-           Returns util object
-
-       .. versionchanged:: 8.1.1
-           Changed default msg_stream from *stderr* to *stdout*
-           Added `event_loop` keyword parameter.
-           Returns util object"""
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        run((PowerSupply,))
+
+    Example 2: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import run
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run({'MyServer': (MyServerClass, MyServer)})
+
+    Example 3: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run([PowerSupply, [MyServerClass, MyServer]])
+        # or: run({'MyServer': (MyServerClass, MyServer)})
+
+    :param classes:
+        a sequence of :class:`~PyTango.server.Device` classes or
+        a dictionary where keyword is the tango class name and value
+        is a sequence of Tango Device Class python class, and Tango
+        Device python class
+    :type classes: sequence or dict
+
+    :param args:
+        list of command line arguments [default: None, meaning use
+        sys.argv]
+    :type args: list
+
+    :param msg_stream:
+        stream where to put messages [default: sys.stdout]
 
+    :param util:
+        PyTango Util object [default: None meaning create a Util
+        instance]
+    :type util: :class:`~PyTango.Util`
+
+    :param event_loop: event_loop callable
+    :type event_loop: callable
+
+    :param post_init_callback:
+        an optional callback that is executed between the calls
+        Util.server_init and Util.server_run
+    :type post_init_callback:
+        callable or tuple (see description above)
+
+    :return: The Util singleton object
+    :rtype: :class:`~PyTango.Util`
+
+    .. versionadded:: 8.1.2
+
+    .. versionchanged:: 8.1.4
+        when classes argument is a sequence, the items can also be
+        a sequence <TangoClass, TangoClassClass>[, tango class name]
+    """
     if msg_stream is None:
-        import io
-        msg_stream = io.BytesIO()
-    write = msg_stream.write
+        write = lambda msg : None
+    else:
+        write = msg_stream.write
     try:
-        return __server_run(classes, args=args, util=util, event_loop=event_loop)
+        return __server_run(classes, args=args, msg_stream=msg_stream,
+                            util=util, event_loop=event_loop,
+                            post_init_callback=post_init_callback,
+                            green_mode=green_mode)
     except KeyboardInterrupt:
         write("Exiting: Keyboard interrupt\n")
     except DevFailed as df:
-        write("Exiting: Server exited with PyTango.DevFailed:\n" + str(df) + "\n")
+        write("Exiting: Server exited with PyTango.DevFailed:\n" + \
+              str(df) + "\n")
         if verbose:
             write(traceback.format_exc())
     except Exception as e:
-        write("Exiting: Server exited with unforseen exception:\n" + str(e) + "\n")
+        write("Exiting: Server exited with unforseen exception:\n" + \
+              str(e) + "\n")
         if verbose:
             write(traceback.format_exc())
     write("\nExited\n")
+
+def server_run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, green_mode=None):
+    """
+    Since PyTango 8.1.2 it is just an alias to
+    :func:`~PyTango.server.run`. Use :func:`~PyTango.server.run`
+    instead.
+
+    .. versionadded:: 8.0.0
+
+    .. versionchanged:: 8.0.3
+        Added `util` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.1
+        Changed default msg_stream from *stderr* to *stdout*
+        Added `event_loop` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.2
+        Added `post_init_callback` keyword parameter
+
+    .. deprecated:: 8.1.2
+        Use :func:`~PyTango.server.run` instead.
+
+    """
+    return run(classes, args=args, msg_stream=msg_stream,
+               verbose=verbose, util=util, event_loop=event_loop,
+               post_init_callback=post_init_callback,
+               green_mode=green_mode)
+
+
+__RUNNER = None
+
+def _get_runner():
+    return __RUNNER
+
+def _create_runner():
+    global __RUNNER
+    if __RUNNER:
+        return __RUNNER
+    
+    try:
+        from queue import Queue
+    except:
+        from Queue import Queue
+
+    import gevent
+    import gevent.event
+
+    class Runner:
+
+        from gevent import _threading
+
+        class Task:
+
+            def __init__(self, event, func, *args, **kwargs):
+                self.__event = event
+                self.__func = func
+                self.__args = args
+                self.__kwargs = kwargs
+                self.value = None
+                self.exception = None
+
+            def __call__(self):
+                func = self.__func
+                if func:
+                    try:
+                        self.value = func(*self.__args, **self.__kwargs)
+                    except:
+                        self.exception = sys.exc_info()
+                self.__event.set()
+
+            def run(self):
+                return gevent.spawn(self)
+
+        def __init__(self, max_queue_size=0):
+            self.__tasks = Queue(max_queue_size)
+            self.__stop_event = gevent.event.Event()
+            self.__watcher = gevent.get_hub().loop.async()
+            self.__watcher.start(self.__step)
+
+        def __step(self):
+            task = self.__tasks.get()
+            return task.run()
+
+        def run(self, timeout=None):
+            return gevent.wait(objects=(self.__stop_event,),
+                               timeout=timeout)
+
+        def execute(self, func, *args, **kwargs):
+            event = self._threading.Event() 
+            task = self.Task(event, func, *args, **kwargs)
+            self.__tasks.put(task)
+            self.__watcher.send()
+            event.wait()
+            if task.exception:
+                Except.throw_python_exception(*task.exception)
+            return task.value
+
+        def stop(self):
+            task = self.Task(self.__stop_event, None)
+            self.__tasks.put(task)
+            self.__watcher.send()
+
+    __RUNNER = Runner()
+    return __RUNNER
+
+
+_CLEAN_UP_TEMPLATE = """
+import sys
+from PyTango import Database
+
+db = Database()
+server_instance = '{server_instance}'
+try:
+    devices = db.get_device_class_list(server_instance)[::2]
+    for device in devices:
+        db.delete_device(device)
+        try:
+            db.delete_device_alias(db.get_alias(device))
+        except:
+            pass
+except:
+    print ('Failed to cleanup!')
+"""
+
+import numpy
+
+def __to_tango_type_fmt(value):
+    dfmt = AttrDataFormat.SCALAR
+    value_t = type(value)
+    dtype = TO_TANGO_TYPE.get(value_t)
+    max_dim_x, max_dim_y = 1, 0
+    if dtype is None:
+        if isinstance(value, numpy.ndarray):
+            dtype = TO_TANGO_TYPE.get(value.dtype.name)
+            shape_l = len(value.shape)
+            if shape_l == 1:
+                dfmt = AttrDataFormat.SPECTRUM
+                max_dim_x = max(2**16, value.shape[0])
+            elif shape_l == 2:
+                dfmt = AttrDataFormat.IMAGE
+                max_dim_x = max(2**16, value.shape[0])
+                max_dim_y = max(2**16, value.shape[1])      
+        else:
+            dtype = CmdArgType.DevEncoded
+    return dtype, dfmt, max_dim_x, max_dim_y
+
+
+def create_tango_class(obj, tango_class_name=None):
+
+    obj_klass = obj.__class__
+    obj_klass_name = obj_klass.__name__
+
+    if tango_class_name is None:
+        tango_class_name = obj_klass_name
+
+    class DeviceDispatcher(Device):
+        __metaclass__ = DeviceMeta
+
+        TangoClassName = tango_class_name
+
+        def __init__(self, tango_class_obj, name):
+            Device.__init__(self, tango_class_obj, name)
+            self.__tango_obj = Server().get_tango_object(self.get_name())
+            self.__tango_obj.device = self
+
+        def init_device(self):
+            Device.init_device(self)
+            self.set_state(DevState.ON)
+
+    DeviceDispatcher.__name__ = tango_class_name
+    DeviceDispatcherClass = DeviceDispatcher.TangoClassClass
+
+    for name in dir(obj):
+        if name.startswith("_"):
+            continue
+#        logging.info("inspecting %s.%s", obj_klass_name, name)
+        try:
+            member = getattr(obj, name)
+        except:
+            logging.warning("Failed to inspect member '%s.%s'",
+                            obj_klass_name, name)
+            logging.debug("Details:", exc_info=1)
+        if inspect.isclass(member) or inspect.ismodule(member):
+            continue
+
+        if inspect.isroutine(member):
+            func = member
+            func_name = name
+            def _command(obj, func_name, param):
+                server = Server()
+                runner = server.runner
+                args, kwargs = loads(*param)
+                f = getattr(obj, func_name)
+                if runner:
+                    result = runner.execute(f, *args, **kwargs)
+                else:
+                    result = f(*args, **kwargs)
+                return server.dumps(result)
+            cmd = functools.partial(_command, obj, func_name)
+            cmd.__name__ = name
+            doc = func.__doc__
+            if doc is None:
+                doc = ""
+            cmd.__doc__ = doc
+            setattr(DeviceDispatcher, func_name, cmd)
+            DeviceDispatcherClass.cmd_list[name] = \
+                [[CmdArgType.DevEncoded, doc],
+                 [CmdArgType.DevEncoded, ""]]
+        else:
+            read_only = False
+            if hasattr(obj_klass, name):
+                kmember = getattr(obj_klass, name)
+                if inspect.isdatadescriptor(kmember):
+                    if kmember.fset is None:
+                        read_only = True
+                else:
+                    continue
+            value = member
+            dtype, fmt, x, y = __to_tango_type_fmt(value)
+            if dtype is None or dtype == CmdArgType.DevEncoded:
+                dtype = CmdArgType.DevEncoded
+                fmt = AttrDataFormat.SCALAR
+                def read(dev, attr):
+                    server = Server()
+                    runner = server.runner
+                    name = attr.get_name()
+                    if runner:
+                        value = runner.execute(getattr, obj, name)
+                    else:
+                        value = getattr(obj, name)
+                    attr.set_value(*server.dumps(value))
+                def write(dev, attr):
+                    name = attr.get_name()
+                    value = attr.get_write_value()
+                    value = loads(*value)
+                    server = Server()
+                    runner = server.runner
+                    if runner:
+                        runner.execute(setattr, obj, name, value)
+                    else:
+                        setattr(obj, name, value)
+            else:
+                def read(dev, attr):
+                    server = Server()
+                    runner = server.runner
+                    name = attr.get_name()
+                    if runner:
+                        value = runner.execute(getattr, obj, name)
+                    else:
+                        value = getattr(obj, name)                        
+                    attr.set_value(value)
+                def write(dev, attr):
+                    server = Server()
+                    runner = server.runner
+                    name = attr.get_name()                        
+                    value = attr.get_write_value()
+                    if runner:
+                        runner.execute(setattr, obj, name, value)
+                    else:
+                        setattr(obj, name, value)                        
+            read.__name__ = "_read_" + name
+            setattr(DeviceDispatcher, read.__name__, read)
+
+            pars = dict(name=name, dtype=dtype, dformat=fmt,
+                        max_dim_x=x, max_dim_y=y, fget=read)
+            if read_only:
+                write = None
+            else:
+                write.__name__ = "_write" + name
+                pars['fset'] = write
+                setattr(DeviceDispatcher, write.__name__, write)
+            attr_data = AttrData.from_dict(pars)
+            DeviceDispatcherClass.attr_list[name] = attr_data
+    return DeviceDispatcher
+
+
+class _Server:
+
+    class TangoObject:
+
+        def __init__(self, obj, full_name, alias=None,
+                     tango_class_name=None):
+            self.full_name = full_name
+            self.alias = alias
+            self.class_name = obj.__class__.__name__
+            if tango_class_name is None:
+                tango_class_name = self.class_name
+            self.tango_class_name = tango_class_name
+            self.__obj = weakref.ref(obj)
+            self.__device = None
+
+        @property
+        def device(self):
+            if self.__device is None:
+                return None
+            return self.__device()
+
+        @device.setter
+        def device(self, dev):
+            self.__device = weakref.ref(dev)
+
+        @property
+        def obj(self):
+            return self.__obj()
+    
+    def __init__(self, server_name, server_type=None, port=None,
+                 event_loop_callback=None, post_init_callback=None,
+                 auto_clean=True, green_mode=None, tango_classes=None,
+                 protocol="pickle"):
+        self.__server_name = server_name
+        self.__server_type = server_type
+        self.__port = port
+        self.__event_loop_callback = event_loop_callback
+        self.__post_init_callback = post_init_callback
+        self.__util = None
+        self.__objects = {}
+        self.__running = False
+        self.__auto_clean = auto_clean
+        self.__green_mode = green_mode
+        self.__protocol = protocol
+        self.__tango_classes = _to_classes(tango_classes or [])
+        self.__tango_devices = []
+        if self.gevent_mode:
+            self.__runner = _create_runner()
+        else:
+            self.__runner = None
+        self.log = logging.getLogger("PyTango")
+    
+    def __build_args(self):
+        args = [self.server_type, self.__server_name]
+        if self.__port is not None:
+            args.extend(["-ORBendPoint",
+                         "giop:tcp::{0}".format(self.__port)])
+        return args
+
+    @property
+    def server_type(self):
+        server_type = self.__server_type
+        if server_type is None:
+            server_file = os.path.basename(sys.argv[0])
+            server_type = os.path.splitext(server_file)[0]
+        return server_type
+
+    @property
+    def server_instance(self):
+        return "{0}/{1}".format(self.server_type, self.__server_name)
+
+    @property
+    def tango_util(self):
+        if self.__util is None:
+            import PyTango
+            self.__util = PyTango.Util(self.__build_args())
+        return self.__util
+
+    @property
+    def green_mode(self):
+        gm = self.__green_mode
+        if gm is None:
+            from PyTango import get_green_mode            
+            gm = get_green_mode()
+        return gm
+
+    @green_mode.setter
+    def green_mode(self, gm):
+        if gm == self.__green_mode:
+            return
+        if self.__running:
+            raise RuntimeError("Cannot change green mode while "
+                               "server is running")
+        self.__green_mode = gm
+
+    @property
+    def gevent_mode(self):
+        return self.green_mode == GreenMode.Gevent
+
+    @property
+    def runner(self):
+        return self.__runner
+
+    def dumps(self, obj):
+        return dumps(self.__protocol, obj)
+
+    def get_tango_object(self, name):
+        return self.__objects.get(name.lower())
+    
+    def get_tango_class(self, tango_class_name):
+        for klass in self.__tango_classes:
+            if klass.TangoClassName == tango_class_name:
+                return klass
+
+    def __find_tango_class(self, key):
+        pass
+
+    def register_tango_device(self, klass, name):
+        if inspect.isclass(klass):
+            if isinstance(klass, Device):
+                kk, k, kname = Device.TangoClassClass, Device, Device.TangoClassName
+            else:
+                raise ValueError
+        else:
+            raise NotImplementedError
+        
+    def register_tango_class(self, klass):
+        if self.__running:
+            raise RuntimeError("Cannot create new Tango class while "
+                               "while server is running")
+        self.__tango_classes.append(klass)
+
+    def register_object(self, obj, name, tango_class_name=None):
+        slash_count = name.count("/")
+        if slash_count == 0:
+            alias = name
+            full_name = "{0}/{1}".format(self.server_instance, name)
+        elif slash_count == 2:
+            alias = None
+            full_name = name
+        else:
+            raise
+        tango_object = self.TangoObject(obj, full_name, alias,
+                                        tango_class_name=tango_class_name)
+        tango_class_name = tango_object.tango_class_name
+        tango_class = self.get_tango_class(tango_class_name)
+        if tango_class is None:
+            tango_class = create_tango_class(obj, tango_class_name)
+            self.register_tango_class(tango_class)
+        self.__objects[full_name.lower()] = tango_object
+        return tango_object
+
+    def _post_init_callback(self):
+        cb = self.__post_init_callback
+        if not cb:
+            return
+        if self.gevent_mode:
+            self.__runner.execute(cb)
+        else:
+            cb()            
+
+    def __clean_up(self):
+        self.log.debug("clean up")
+        server_instance = self.server_instance
+        db = Database()
+        if server_instance in db.get_server_list():
+            dserver_name = "dserver/{0}".format(server_instance)
+            if db.import_device(dserver_name).exported:
+                import PyTango
+                dserver = PyTango.DeviceProxy(dserver_name)
+                try:
+                    dserver.ping()
+                    raise Exception("Server already running")
+                except:
+                    logging.warning("Last time server was not properly shutdown!")
+            devices = db.get_device_class_list(server_instance)[::2]
+            for device in devices:
+                db.delete_device(device)
+                try:
+                    db.delete_device_alias(db.get_alias(device))
+                except:
+                    pass
+
+    def __clean_up_process(self):
+        if not self.__auto_clean:
+            return 
+        clean_up = _CLEAN_UP_TEMPLATE.format(server_instance=self.server_instance)
+        import subprocess
+        res = subprocess.call([sys.executable, "-c", clean_up])
+        if res:
+            self.log.error("Failed to cleanup")
+                
+    def __prepare(self):
+        self.log.debug("prepare")
+        self.__clean_up()
+        server_instance = self.server_instance
+        db = Database()
+        db_dev_infos = []
+        for obj_name, obj in self.__objects.items():
+            db_dev_info = DbDevInfo()
+            db_dev_info.server = server_instance
+            db_dev_info._class = obj.tango_class_name
+            db_dev_info.name = obj.full_name
+            db_dev_infos.append(db_dev_info)
+            db.add_device(db_dev_info)
+            if obj.alias:
+                db.put_device_alias(obj.full_name, obj.alias)
+
+    def __initialize(self):
+        self.log.debug("initialize")        
+        gevent_mode = self.gevent_mode
+        event_loop = self.__event_loop_callback
+        
+        util = self.tango_util
+        u_instance = util.instance()
+        
+        if gevent_mode:
+            if event_loop:
+                event_loop = functools.partial(self.__runner.execute,
+                                               event_loop)
+        if event_loop:
+            u_instance.server_set_event_loop(event_loop)
+
+        _add_classes(util, self.__tango_classes)
+        
+        if gevent_mode:
+            start_new_thread = self.__runner._threading.start_new_thread
+            tango_thread_id = start_new_thread(self.__tango_loop, ())
+
+    def __run(self, timeout=None):
+        if self.gevent_mode:
+            return self.__runner.run(timeout=timeout)
+        else:
+            self.__tango_loop()
+        
+    def __tango_loop(self):
+        self.log.debug("tango_loop")
+        self.__running = True
+        u_instance = self.tango_util.instance()
+        u_instance.server_init()
+        self._post_init_callback()
+        self.log.info("Ready to accept request")
+        u_instance.server_run()
+        if self.gevent_mode:
+            self.__runner.stop()
+        if self.__auto_clean:
+            self.__clean_up_process()
+        self.log.debug("Tango loop exit")        
+        
+    def run(self, timeout=None):
+        self.log.debug("run")        
+        gevent_mode = self.gevent_mode
+        running = self.__running
+        if not running:
+            self.__prepare()
+            self.__initialize()
+        else:
+            if not gevent_mode:
+                raise RuntimeError("Server is already running")
+        self.__run(timeout=timeout)
+
+
+__SERVER = None
+def Server(server_name=None, server_type=None, port=None,
+           event_loop_callback=None, post_init_callback=None,
+           auto_clean=True, green_mode=None):
+    """Experimental server class. Not part of the official API"""
+    
+    global __SERVER
+    if __SERVER is None:
+        if server_name is None:
+            raise ValueError("Must give a valid server name")
+        __SERVER = _Server(server_name,
+                           server_type=server_type, port=port,
+                           event_loop_callback=event_loop_callback,
+                           post_init_callback=post_init_callback,
+                           auto_clean=auto_clean,
+                           green_mode=green_mode)
+    return __SERVER
diff --git a/src/boost/python/server.py.single_threaded.v1 b/src/boost/python/server.py.single_threaded.v1
new file mode 100644
index 0000000..2a70955
--- /dev/null
+++ b/src/boost/python/server.py.single_threaded.v1
@@ -0,0 +1,1080 @@
+# ------------------------------------------------------------------------------
+# This file is part of PyTango (http://www.tinyurl.com/PyTango)
+#
+# Copyright 2006-2012 CELLS / ALBA Synchrotron, Bellaterra, Spain
+# Copyright 2013-2014 European Synchrotron Radiation Facility, Grenoble, France
+#
+# Distributed under the terms of the GNU Lesser General Public License,
+# either version 3 of the License, or (at your option) any later version.
+# See LICENSE.txt for more info.
+# ------------------------------------------------------------------------------
+
+"""Server helper classes for writing Tango device servers."""
+
+from __future__ import with_statement
+from __future__ import print_function
+
+__all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute",
+           "command", "device_property", "class_property",
+           "run", "server_run"]
+
+import sys
+import inspect
+import logging
+import operator
+import functools
+import traceback
+
+from ._PyTango import CmdArgType, AttrDataFormat, AttrWriteType
+from ._PyTango import DevFailed, Except, constants
+from .attr_data import AttrData
+from .device_class import DeviceClass
+from .utils import get_tango_device_classes, is_seq, is_non_str_seq
+from .utils import scalar_to_array_type
+
+API_VERSION = 2
+
+LatestDeviceImpl = get_tango_device_classes()[-1]
+
+_RUNNER = None
+
+def __build_to_tango_type():
+    ret = \
+    {
+        int         : CmdArgType.DevLong,
+        str         : CmdArgType.DevString,
+        bool        : CmdArgType.DevBoolean,
+        bytearray   : CmdArgType.DevEncoded,
+        float       : CmdArgType.DevDouble,
+        chr         : CmdArgType.DevUChar,
+        None        : CmdArgType.DevVoid,
+
+        'int'       : CmdArgType.DevLong,
+        'int16'     : CmdArgType.DevShort,
+        'int32'     : CmdArgType.DevLong,
+        'int64'     : CmdArgType.DevLong64,
+        'uint'      : CmdArgType.DevULong,
+        'uint16'    : CmdArgType.DevUShort,
+        'uint32'    : CmdArgType.DevULong,
+        'uint64'    : CmdArgType.DevULong64,
+        'str'       : CmdArgType.DevString,
+        'string'    : CmdArgType.DevString,
+        'text'      : CmdArgType.DevString,
+        'bool'      : CmdArgType.DevBoolean,
+        'boolean'   : CmdArgType.DevBoolean,
+        'bytes'     : CmdArgType.DevEncoded,
+        'bytearray' : CmdArgType.DevEncoded,
+        'float'     : CmdArgType.DevDouble,
+        'float32'   : CmdArgType.DevFloat,
+        'float64'   : CmdArgType.DevDouble,
+        'double'    : CmdArgType.DevDouble,
+        'byte'      : CmdArgType.DevUChar,
+        'chr'       : CmdArgType.DevUChar,
+        'char'      : CmdArgType.DevUChar,
+        'None'      : CmdArgType.DevVoid,
+        'state'     : CmdArgType.DevState,
+    }
+
+    for key in dir(CmdArgType):
+        if key.startswith("Dev"):
+            value = getattr(CmdArgType, key)
+            ret[key] = ret[value] = value
+
+    if constants.NUMPY_SUPPORT:
+        import numpy
+        FROM_TANGO_TO_NUMPY_TYPE = { \
+                   CmdArgType.DevBoolean : numpy.bool8,
+                     CmdArgType.DevUChar : numpy.ubyte,
+                     CmdArgType.DevShort : numpy.short,
+                    CmdArgType.DevUShort : numpy.ushort,
+                      CmdArgType.DevLong : numpy.int32,
+                     CmdArgType.DevULong : numpy.uint32,
+                    CmdArgType.DevLong64 : numpy.int64,
+                   CmdArgType.DevULong64 : numpy.uint64,
+                    CmdArgType.DevString : numpy.str,
+                    CmdArgType.DevDouble : numpy.float64,
+                     CmdArgType.DevFloat : numpy.float32,
+        }
+
+        for key, value in FROM_TANGO_TO_NUMPY_TYPE.items():
+            ret[value] = key
+    return ret
+
+TO_TANGO_TYPE = __build_to_tango_type()
+
+
+def get_tango_type_format(dtype=None, dformat=None):
+    if dformat is None:
+        dformat = AttrDataFormat.SCALAR
+        if is_non_str_seq(dtype):
+            dtype = dtype[0]
+            dformat = AttrDataFormat.SPECTRUM
+            if is_non_str_seq(dtype):
+                dtype = dtype[0]
+                dformat = AttrDataFormat.IMAGE
+    return TO_TANGO_TYPE[dtype], dformat
+
+
+def from_typeformat_to_type(dtype, dformat):
+    if dformat == AttrDataFormat.SCALAR:
+        return dtype
+    elif dformat == AttrDataFormat.IMAGE:
+        raise TypeError("Cannot translate IMAGE to tango type")
+    return scalar_to_array_type(dtype)
+
+
+def set_complex_value(attr, value):
+    is_tuple = isinstance(value, tuple)
+    dtype, fmt = attr.get_data_type(), attr.get_data_format()
+    if dtype == CmdArgType.DevEncoded:
+        if is_tuple and len(value) == 4:
+            attr.set_value_date_quality(*value)
+        elif is_tuple and len(value) == 3 and is_non_str_seq(value[0]):
+            attr.set_value_date_quality(value[0][0],
+                                        value[0][1],
+                                        *value[1:])
+        else:
+            attr.set_value(*value)
+    else:
+        if is_tuple:
+            if len(value) == 3:
+                if fmt == AttrDataFormat.SCALAR:
+                    attr.set_value_date_quality(*value)
+                elif fmt == AttrDataFormat.SPECTRUM:
+                    if is_seq(value[0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+                else:
+                    if is_seq(value[0]) and is_seq(value[0][0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+            else:
+                attr.set_value(value)
+        else:
+            attr.set_value(value)
+
+
+def check_dev_klass_attr_read_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    read_method = getattr(attribute, "fget", None)
+    if read_method:
+        method_name = "__read_{0}__".format(attribute.attr_name)
+        attribute.read_method_name = method_name
+    else:
+        method_name = attribute.read_method_name
+        read_method = getattr(tango_device_klass, method_name)
+
+    read_args = inspect.getargspec(read_method)
+
+    if len(read_args.args) < 2:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, (self,))
+            else:
+                ret = read_method(self)
+            if not attr.get_value_flag() and ret is not None:
+                set_complex_value(attr, ret)
+            return ret
+    else:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, (self, attr))
+            else:            
+                ret = read_method(self, attr)
+            return ret
+        
+    method_name = "__read_{0}_wrapper__".format(attribute.attr_name)
+    attribute.read_method_name = method_name        
+
+    setattr(tango_device_klass, method_name, read_attr)
+
+
+def check_dev_klass_attr_write_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    write_method = getattr(attribute, "fset", None)
+    if write_method:
+        method_name = "__write_{0}__".format(attribute.attr_name)
+        attribute.write_method_name = method_name
+    else:
+        method_name = attribute.write_method_name
+        write_method = getattr(tango_device_klass, method_name)
+
+    @functools.wraps(write_method)
+    def write_attr(self, attr):
+        value = attr.get_write_value()
+        return write_method(self, value)
+    setattr(tango_device_klass, method_name, write_attr)
+
+
+def check_dev_klass_attr_methods(tango_device_klass, attribute):
+    """
+    Checks if the read and write methods have the correct signature.
+    If a read/write method doesn't have a parameter (the traditional
+    Attribute), then the method is wrapped into another method to make
+    this work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    if attribute.attr_write in (AttrWriteType.READ,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_read_method(tango_device_klass,
+                                         attribute)
+    if attribute.attr_write in (AttrWriteType.WRITE,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_write_method(tango_device_klass,
+                                          attribute)
+
+
+class _DeviceClass(DeviceClass):
+
+    def __init__(self, name):
+        DeviceClass.__init__(self, name)
+        self.set_type(name)
+
+    def dyn_attr(self, dev_list):
+        """Invoked to create dynamic attributes for the given devices.
+        Default implementation calls
+        :meth:`TT.initialize_dynamic_attributes` for each device
+
+        :param dev_list: list of devices
+        :type dev_list: :class:`PyTango.DeviceImpl`"""
+
+        for dev in dev_list:
+            init_dyn_attrs = getattr(dev,
+                                     "initialize_dynamic_attributes",
+                                     None)
+            if init_dyn_attrs and callable(init_dyn_attrs):
+                try:
+                    init_dyn_attrs()
+                except Exception:
+                    dev.warn_stream("Failed to initialize dynamic " \
+                                    "attributes")
+                    dev.debug_stream("Details: " + \
+                                     traceback.format_exc())
+
+
+def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
+    klass_name = tango_device_klass.__name__
+    if not issubclass(tango_device_klass, (Device)):
+        msg = "{0} device must inherit from " \
+              "PyTango.server.Device".format(klass_name)
+        raise Exception(msg)
+
+    if attrs is None:
+        attrs = tango_device_klass.__dict__
+
+    attr_list = {}
+    class_property_list = {}
+    device_property_list = {}
+    cmd_list = {}
+
+    for attr_name, attr_obj in attrs.items():
+        if isinstance(attr_obj, attribute):
+            if attr_obj.attr_name is None:
+                attr_obj._set_name(attr_name)
+            else:
+                attr_name = attr_obj.attr_name
+            attr_list[attr_name] = attr_obj
+            check_dev_klass_attr_methods(tango_device_klass, attr_obj)
+        elif isinstance(attr_obj, device_property):
+            device_property_list[attr_name] = [attr_obj.dtype,
+                                               attr_obj.doc,
+                                               attr_obj.default_value]
+        elif isinstance(attr_obj, class_property):
+            class_property_list[attr_name] = [attr_obj.dtype,
+                                              attr_obj.doc,
+                                              attr_obj.default_value]
+        elif inspect.isroutine(attr_obj):
+            if hasattr(attr_obj, "__tango_command__"):
+                cmd_name, cmd_info = attr_obj.__tango_command__
+                cmd_list[cmd_name] = cmd_info
+
+    devclass_name = klass_name + "Class"
+
+    devclass_attrs = dict(class_property_list=class_property_list,
+                          device_property_list=device_property_list,
+                          cmd_list=cmd_list, attr_list=attr_list)
+    return type(devclass_name, (_DeviceClass,), devclass_attrs)
+
+
+def init_tango_device_klass(tango_device_klass, attrs=None,
+                            tango_class_name=None):
+    klass_name = tango_device_klass.__name__
+    tango_deviceclass_klass = create_tango_deviceclass_klass(
+        tango_device_klass, attrs=attrs)
+    if tango_class_name is None:
+        if hasattr(tango_device_klass, "TangoClassName"):
+            tango_class_name = tango_device_klass.TangoClassName
+        else:
+            tango_class_name = klass_name
+    tango_device_klass.TangoClassClass = tango_deviceclass_klass
+    tango_device_klass.TangoClassName = tango_class_name
+    tango_device_klass._api = API_VERSION
+    return tango_device_klass
+
+
+def create_tango_device_klass(name, bases, attrs):
+    klass_name = name
+
+    LatestDeviceImplMeta = type(LatestDeviceImpl)
+    klass = LatestDeviceImplMeta(klass_name, bases, attrs)
+    init_tango_device_klass(klass, attrs)
+    return klass
+
+
+def DeviceMeta(name, bases, attrs):
+    """
+    The :py:data:`metaclass` callable for :class:`Device`.Every
+    sub-class of :class:`Device` must have associated this metaclass
+    to itself in order to work properly (boost-python internal
+    limitation).
+
+    Example (python 2.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+    Example (python 3.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device, metaclass=DeviceMeta):
+            pass
+    """
+    return create_tango_device_klass(name, bases, attrs)
+
+
+class Device(LatestDeviceImpl):
+    """
+    High level DeviceImpl API. All Device specific classes should
+    inherit from this class."""
+
+    def __init__(self, cl, name):
+        LatestDeviceImpl.__init__(self, cl, name)
+#        if _RUNNER:
+#            import signal
+#            self.register_signal(signal.SIGINT)
+        self.init_device()
+        
+#    def signal_handler(self, signo):
+#        _RUNNER.stop()
+
+
+    def init_device(self):
+        """
+        Tango init_device method. Default implementation calls
+        :meth:`get_device_properties`"""
+        self.get_device_properties()
+
+    def always_executed_hook(self):
+        """
+        Tango always_executed_hook. Default implementation does
+        nothing
+        """
+        pass
+
+    def initialize_dynamic_attributes(self):
+        """
+        Method executed at initializion phase to create dynamic
+        attributes. Default implementation does nothing. Overwrite
+        when necessary.
+        """
+        pass
+
+
+class attribute(AttrData):
+    '''
+    Declares a new tango attribute in a :class:`Device`. To be used
+    like the python native :obj:`property` function. For example, to
+    declare a scalar, `PyTango.DevDouble`, read-only attribute called
+    *voltage* in a *PowerSupply* :class:`Device` do::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            voltage = attribute()
+
+            def read_voltage(self):
+                return 999.999
+
+    The same can be achieved with::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @attribute
+            def voltage(self):
+                return 999.999
+
+
+    It receives multiple keyword arguments.
+
+    ===================== ================================ ======================================= =======================================================================================
+    parameter              type                                       default value                                 description
+    ===================== ================================ ======================================= =======================================================================================
+    name                   :obj:`str`                       class member name                       alternative attribute name
+    dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
+    dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
+    max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format)
+    max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
+    display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
+    polling_period         :obj:`int`                       -1                                      polling period
+    memorized              :obj:`bool`                      False                                   attribute should or not be memorized
+    hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
+    access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
+    fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
+    fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
+    is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
+    label                  :obj:`str`                       '<attr_name>'                           attribute label
+    doc (or description)   :obj:`str`                       ''                                      attribute description
+    unit                   :obj:`str`                       ''                                      physical units the attribute value is in
+    standard_unit          :obj:`str`                       ''                                      physical standard unit
+    display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
+    format                 :obj:`str`                       '6.2f'                                  attribute representation format
+    min_value              :obj:`str`                       None                                    minimum allowed value
+    max_value              :obj:`str`                       None                                    maximum allowed value
+    min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
+    max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
+    min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
+    max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
+    delta_val              :obj:`str`                       None
+    delta_t                :obj:`str`                       None
+    abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
+    rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
+    period                 :obj:`str`                       None
+    archive_abs_change     :obj:`str`                       None
+    archive_rel_change     :obj:`str`                       None
+    archive_period         :obj:`str`                       None
+    ===================== ================================ ======================================= =======================================================================================
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    Example of a integer writable attribute with a customized label,
+    unit and description::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            current = attribute(label="Current", unit="mA", dtype=int,
+                                access=AttrWriteType.READ_WRITE,
+                                doc="the power supply current")
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            def read_current(self):
+                return self._current
+
+            def write_current(self, current):
+                self._current = current
+
+    The same, but using attribute as a decorator::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            @attribute(label="Current", unit="mA", dtype=int)
+            def current(self):
+                """the power supply current"""
+                return 999.999
+
+            @current.write
+            def current(self, current):
+                self._current = current
+
+    In this second format, defining the `write` implies setting the
+    attribute access to READ_WRITE.
+    '''
+
+    def __init__(self, fget=None, **kwargs):
+        self._kwargs = dict(kwargs)
+        name = kwargs.pop("name", None)
+        class_name = kwargs.pop("class_name", None)
+
+        if fget:
+            if inspect.isroutine(fget):
+                self.fget = fget
+                if 'doc' not in kwargs and 'description' not in kwargs:
+                    kwargs['doc'] = fget.__doc__
+            else:
+                kwargs['fget'] = fget
+
+        super(attribute, self).__init__(name, class_name)
+        if 'dtype' in kwargs:
+            kwargs['dtype'], kwargs['dformat'] = \
+                get_tango_type_format(kwargs['dtype'],
+                                      kwargs.get('dformat'))
+        self.build_from_dict(kwargs)
+
+    def get_attribute(self, obj):
+        return obj.get_device_attr().get_attr_by_name(self.attr_name)
+
+    # --------------------
+    # descriptor interface
+    # --------------------
+
+    def __get__(self, obj, objtype):
+        return self.get_attribute(obj)
+
+    def __set__(self, obj, value):
+        attr = self.get_attribute(obj)
+        set_complex_value(attr, value)
+
+    def __delete__(self, obj):
+        obj.remove_attribute(self.attr_name)
+
+    def setter(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        self.fset = fset
+        if self.attr_write == AttrWriteType.READ:
+            if getattr(self, 'fget', None):
+                self.attr_write = AttrWriteType.READ_WRITE
+            else:
+                self.attr_write = AttrWriteType.WRITE
+        return self
+
+    def write(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        return self.setter(fset)
+
+    def __call__(self, fget):
+        return type(self)(fget=fget, **self._kwargs)
+
+
+def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
+            dtype_out=None, dformat_out=None, doc_out="",):
+    """
+    Declares a new tango command in a :class:`Device`.
+    To be used like a decorator in the methods you want to declare as
+    tango commands. The following example declares commands:
+
+        * `void TurnOn(void)`
+        * `void Ramp(DevDouble current)`
+        * `DevBool Pressurize(DevDouble pressure)`
+
+    ::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @command
+            def TurnOn(self):
+                self.info_stream('Turning on the power supply')
+
+            @command(dtype_in=float)
+            def Ramp(self, current):
+                self.info_stream('Ramping on %f...' % current)
+
+            @command(dtype_in=float, doc_in='the pressure to be set',
+                     dtype_out=bool, doc_out='True if it worked, False otherwise')
+            def Pressurize(self, pressure):
+                self.info_stream('Pressurizing to %f...' % pressure)
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    :param dtype_in:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of parameter. Default is None meaning no parameter.
+    :param dformat_in: parameter data format. Default is None.
+    :type dformat_in: AttrDataFormat
+    :param doc_in: parameter documentation
+    :type doc_in: str
+
+    :param dtype_out:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of return value. Default is None meaning no return value.
+    :param dformat_out: return value data format. Default is None.
+    :type dformat_out: AttrDataFormat
+    :param doc_out: return value documentation
+    :type doc_out: str
+    """
+    if f is None:
+        return functools.partial(command,
+            dtype_in=dtype_in, dformat_in=dformat_in, doc_in=doc_in,
+            dtype_out=dtype_out, dformat_out=dformat_out,
+            doc_out=doc_out)
+    name = f.__name__
+
+    dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
+    dtype_out, dformat_out = get_tango_type_format(dtype_out,
+                                                   dformat_out)
+
+    din = [from_typeformat_to_type(dtype_in, dformat_in), doc_in]
+    dout = [from_typeformat_to_type(dtype_out, dformat_out), doc_out]
+    f.__tango_command__ = name, [din, dout]
+    return f
+
+
+class _property(object):
+
+    def __init__(self, dtype, doc='', default_value=None):
+        self.__value = None
+        dtype = from_typeformat_to_type(*get_tango_type_format(dtype))
+        self.dtype = dtype
+        self.doc = doc
+        self.default_value = default_value
+
+    def __get__(self, obj, objtype):
+        return self.__value
+
+    def __set__(self, obj, value):
+        self.__value = value
+
+    def __delete__(self, obj):
+        del self.__value
+
+
+class device_property(_property):
+    """
+    Declares a new tango device property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, device property called
+    *host* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import device_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            host = device_property(dtype=str)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+class class_property(_property):
+    """
+    Declares a new tango class property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, class property called
+    *port* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import class_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            port = class_property(dtype=int, default_value=9788)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+
+def __to_cb(post_init_callback):
+    if post_init_callback is None:
+        return lambda : None
+
+    err_msg = "post_init_callback must be a callable or " \
+              "sequence <callable [, args, [, kwargs]]>"
+    if operator.isCallable(post_init_callback):
+        f = post_init_callback
+    elif is_non_str_seq(post_init_callback):
+        length = len(post_init_callback)
+        if length < 1 or length > 3:
+            raise TypeError(err_msg)
+        cb = post_init_callback[0]
+        if not operator.isCallable(cb):
+            raise TypeError(err_msg)
+        args, kwargs = [], {}
+        if length > 1:
+            args = post_init_callback[1]
+        if length > 2:
+            kwargs = post_init_callback[2]
+        f = functools.partial(cb, *args, **kwargs)
+    else:
+        raise TypeError(err_msg)
+
+    return f
+
+def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
+                 event_loop=None, post_init_callback=None,
+                 single_threaded=False):
+
+    global _RUNNER
+    if single_threaded:
+        _RUNNER = _Runner()
+        
+    import PyTango
+    if msg_stream is None:
+        write = lambda msg: None
+    else:
+        write = msg_stream.write
+
+    if args is None:
+        args = sys.argv
+
+    post_init_callback = __to_cb(post_init_callback)
+
+    if util is None:
+        util = PyTango.Util(args)
+    u_instance = PyTango.Util.instance()
+
+    if is_seq(classes):
+        for klass_info in classes:
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+    else:
+        for klass_name, klass_info in classes.items():
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+
+    if single_threaded:
+        def single_threaded_event_loop():
+            should_exit = _RUNNER.run_step()
+            if event_loop:
+                event_loop()
+        u_instance.server_set_event_loop(single_threaded_event_loop)
+    else:
+        if event_loop is not None:
+            u_instance.server_set_event_loop(event_loop)
+
+    u_instance.server_init()
+    if single_threaded:
+        # first device will be the lucky winner of handling SIGINT
+        import signal
+        signals = signal.SIGINT, signal.SIGQUIT, signal.SIGTERM
+        d = u_instance.get_device_list("*")[0]
+        old_signal_handler = d.signal_handler
+        def signal_handler(signo):
+            if signo in signals:
+                _RUNNER.stop()
+            return old_signal_handler(signo)
+        d.signal_handler = signal_handler
+        for sig in signals:
+            d.register_signal(sig)
+        
+    post_init_callback()
+    write("Ready to accept request\n")
+    u_instance.server_run()
+    return util
+
+def run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Provides a simple way to run a tango server. It handles exceptions
+    by writting a message to the msg_stream.
+
+    The `classes` parameter can be either a sequence of:
+
+    * :class:`~PyTango.server.Device` or
+    * a sequence of two elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl` or
+    * a sequence of three elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+      tango class name (str)
+
+    or a dictionary where:
+
+    * key is the tango class name
+    * value is either:
+        * a :class:`~PyTango.server.Device` class or
+        * a sequence of two elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
+          or
+        * a sequence of three elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+          tango class name (str)
+
+    The optional `post_init_callback` can be a callable (without
+    arguments) or a tuple where the first element is the callable,
+    the second is a list of arguments (optional) and the third is a
+    dictionary of keyword arguments (also optional).
+
+    .. note::
+       the order of registration of tango classes defines the order
+       tango uses to initialize the corresponding devices.
+       if using a dictionary as argument for classes be aware that the
+       order of registration becomes arbitrary. If you need a
+       predefined order use a sequence or an OrderedDict.
+
+    Example 1: registering and running a PowerSupply inheriting from
+    :class:`~PyTango.server.Device`::
+
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        run((PowerSupply,))
+
+    Example 2: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import run
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run({'MyServer': (MyServerClass, MyServer)})
+
+    Example 3: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run([PowerSupply, [MyServerClass, MyServer]])
+        # or: run({'MyServer': (MyServerClass, MyServer)})
+
+    :param classes:
+        a sequence of :class:`~PyTango.server.Device` classes or
+        a dictionary where keyword is the tango class name and value
+        is a sequence of Tango Device Class python class, and Tango
+        Device python class
+    :type classes: sequence or dict
+
+    :param args:
+        list of command line arguments [default: None, meaning use
+        sys.argv]
+    :type args: list
+
+    :param msg_stream:
+        stream where to put messages [default: sys.stdout]
+
+    :param util:
+        PyTango Util object [default: None meaning create a Util
+        instance]
+    :type util: :class:`~PyTango.Util`
+
+    :param event_loop: event_loop callable
+    :type event_loop: callable
+
+    :param post_init_callback:
+        an optional callback that is executed between the calls
+        Util.server_init and Util.server_run
+    :type post_init_callback:
+        callable or tuple (see description above)
+
+    :return: The Util singleton object
+    :rtype: :class:`~PyTango.Util`
+
+    .. versionadded:: 8.1.2
+
+    .. versionchanged:: 8.1.4
+        when classes argument is a sequence, the items can also be
+        a sequence <TangoClass, TangoClassClass>[, tango class name]
+    """
+    if msg_stream is None:
+        write = lambda msg : None
+    else:
+        write = msg_stream.write
+    try:
+        return __server_run(classes, args=args, msg_stream=msg_stream,
+                            util=util, event_loop=event_loop,
+                            post_init_callback=post_init_callback,
+                            single_threaded=single_threaded)
+    except KeyboardInterrupt:
+        write("Exiting: Keyboard interrupt\n")
+    except DevFailed as df:
+        write("Exiting: Server exited with PyTango.DevFailed:\n" + \
+              str(df) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    except Exception as e:
+        write("Exiting: Server exited with unforseen exception:\n" + \
+              str(e) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    write("\nExited\n")
+
+def server_run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Since PyTango 8.1.2 it is just an alias to
+    :func:`~PyTango.server.run`. Use :func:`~PyTango.server.run`
+    instead.
+
+    .. versionadded:: 8.0.0
+
+    .. versionchanged:: 8.0.3
+        Added `util` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.1
+        Changed default msg_stream from *stderr* to *stdout*
+        Added `event_loop` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.2
+        Added `post_init_callback` keyword parameter
+
+    .. deprecated:: 8.1.2
+        Use :func:`~PyTango.server.run` instead.
+
+    """
+    return run(classes, args=args, msg_stream=msg_stream,
+               verbose=verbose, util=util, event_loop=event_loop,
+               post_init_callback=post_init_callback,
+               single_threaded=single_threaded)
+
+
+def _run_protect(f, *args, **kwargs):
+    if f is None:
+        return
+    try:
+        return f(*args, **kwargs)
+    except:
+        log = logging.getLogger("PyTango")
+        log.exception("Failed to execute %s", f.__name__)
+
+
+import Queue
+import threading
+
+class _Runner:
+
+    def __init__(self, max_queue_size=0):
+        self.tasks = Queue.Queue(max_queue_size)
+
+    def run_loop(self):
+        while True:
+            self.run_step()
+            
+    def run_step(self):
+        try:
+            task = self.tasks.get()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+        
+    def _exec(self, task):
+        f, args, kwargs, on_finished, on_error = task
+        try:
+            result = f(*args, **kwargs)
+            _run_protect(on_finished, result)
+        except:
+            _run_protect(on_error, *sys.exc_info)
+
+    def step(self):
+        try:
+            task = self.tasks.get_nowait()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+    
+    def execute_asynch(self, f, args=(), kwargs=None,
+                            on_finished=None, on_error=None):
+        if kwargs is None:
+            kwargs = {}
+        self.tasks.put((f, args, kwargs, on_finished, on_error))
+
+    def execute_synch(self, f, args=(), kwargs=None):
+        event = threading.Event()
+        event._err = None
+        def cb(result):
+            event._result = result
+            event.set()
+        def err_cb(*args):
+            event._err = args
+            event.set()
+        self.execute_asynch(f, args, kwargs, cb, err_cb)
+        event.wait()
+        if event._err:
+            Except.throw_python_exception(*event._err)
+        return event._result
+
+    def stop(self, wait=False):
+        if wait:
+            raise NotImplementedError
+        else:
+            self.tasks.put(None)
diff --git a/src/boost/python/server.py.single_threaded.v2 b/src/boost/python/server.py.single_threaded.v2
new file mode 100644
index 0000000..cf6f937
--- /dev/null
+++ b/src/boost/python/server.py.single_threaded.v2
@@ -0,0 +1,1096 @@
+# ------------------------------------------------------------------------------
+# This file is part of PyTango (http://www.tinyurl.com/PyTango)
+#
+# Copyright 2006-2012 CELLS / ALBA Synchrotron, Bellaterra, Spain
+# Copyright 2013-2014 European Synchrotron Radiation Facility, Grenoble, France
+#
+# Distributed under the terms of the GNU Lesser General Public License,
+# either version 3 of the License, or (at your option) any later version.
+# See LICENSE.txt for more info.
+# ------------------------------------------------------------------------------
+
+"""Server helper classes for writing Tango device servers."""
+
+from __future__ import with_statement
+from __future__ import print_function
+
+__all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute",
+           "command", "device_property", "class_property",
+           "run", "server_run"]
+
+import sys
+import inspect
+import logging
+import operator
+import functools
+import traceback
+
+from ._PyTango import CmdArgType, AttrDataFormat, AttrWriteType
+from ._PyTango import DevFailed, Except, constants
+from .attr_data import AttrData
+from .device_class import DeviceClass
+from .utils import get_tango_device_classes, is_seq, is_non_str_seq
+from .utils import scalar_to_array_type
+
+API_VERSION = 2
+
+LatestDeviceImpl = get_tango_device_classes()[-1]
+
+_RUNNER = None
+
+def __build_to_tango_type():
+    ret = \
+    {
+        int         : CmdArgType.DevLong,
+        str         : CmdArgType.DevString,
+        bool        : CmdArgType.DevBoolean,
+        bytearray   : CmdArgType.DevEncoded,
+        float       : CmdArgType.DevDouble,
+        chr         : CmdArgType.DevUChar,
+        None        : CmdArgType.DevVoid,
+
+        'int'       : CmdArgType.DevLong,
+        'int16'     : CmdArgType.DevShort,
+        'int32'     : CmdArgType.DevLong,
+        'int64'     : CmdArgType.DevLong64,
+        'uint'      : CmdArgType.DevULong,
+        'uint16'    : CmdArgType.DevUShort,
+        'uint32'    : CmdArgType.DevULong,
+        'uint64'    : CmdArgType.DevULong64,
+        'str'       : CmdArgType.DevString,
+        'string'    : CmdArgType.DevString,
+        'text'      : CmdArgType.DevString,
+        'bool'      : CmdArgType.DevBoolean,
+        'boolean'   : CmdArgType.DevBoolean,
+        'bytes'     : CmdArgType.DevEncoded,
+        'bytearray' : CmdArgType.DevEncoded,
+        'float'     : CmdArgType.DevDouble,
+        'float32'   : CmdArgType.DevFloat,
+        'float64'   : CmdArgType.DevDouble,
+        'double'    : CmdArgType.DevDouble,
+        'byte'      : CmdArgType.DevUChar,
+        'chr'       : CmdArgType.DevUChar,
+        'char'      : CmdArgType.DevUChar,
+        'None'      : CmdArgType.DevVoid,
+        'state'     : CmdArgType.DevState,
+    }
+
+    for key in dir(CmdArgType):
+        if key.startswith("Dev"):
+            value = getattr(CmdArgType, key)
+            ret[key] = ret[value] = value
+
+    if constants.NUMPY_SUPPORT:
+        import numpy
+        FROM_TANGO_TO_NUMPY_TYPE = { \
+                   CmdArgType.DevBoolean : numpy.bool8,
+                     CmdArgType.DevUChar : numpy.ubyte,
+                     CmdArgType.DevShort : numpy.short,
+                    CmdArgType.DevUShort : numpy.ushort,
+                      CmdArgType.DevLong : numpy.int32,
+                     CmdArgType.DevULong : numpy.uint32,
+                    CmdArgType.DevLong64 : numpy.int64,
+                   CmdArgType.DevULong64 : numpy.uint64,
+                    CmdArgType.DevString : numpy.str,
+                    CmdArgType.DevDouble : numpy.float64,
+                     CmdArgType.DevFloat : numpy.float32,
+        }
+
+        for key, value in FROM_TANGO_TO_NUMPY_TYPE.items():
+            ret[value] = key
+    return ret
+
+TO_TANGO_TYPE = __build_to_tango_type()
+
+
+def get_tango_type_format(dtype=None, dformat=None):
+    if dformat is None:
+        dformat = AttrDataFormat.SCALAR
+        if is_non_str_seq(dtype):
+            dtype = dtype[0]
+            dformat = AttrDataFormat.SPECTRUM
+            if is_non_str_seq(dtype):
+                dtype = dtype[0]
+                dformat = AttrDataFormat.IMAGE
+    return TO_TANGO_TYPE[dtype], dformat
+
+
+def from_typeformat_to_type(dtype, dformat):
+    if dformat == AttrDataFormat.SCALAR:
+        return dtype
+    elif dformat == AttrDataFormat.IMAGE:
+        raise TypeError("Cannot translate IMAGE to tango type")
+    return scalar_to_array_type(dtype)
+
+
+def set_complex_value(attr, value):
+    is_tuple = isinstance(value, tuple)
+    dtype, fmt = attr.get_data_type(), attr.get_data_format()
+    if dtype == CmdArgType.DevEncoded:
+        if is_tuple and len(value) == 4:
+            attr.set_value_date_quality(*value)
+        elif is_tuple and len(value) == 3 and is_non_str_seq(value[0]):
+            attr.set_value_date_quality(value[0][0],
+                                        value[0][1],
+                                        *value[1:])
+        else:
+            attr.set_value(*value)
+    else:
+        if is_tuple:
+            if len(value) == 3:
+                if fmt == AttrDataFormat.SCALAR:
+                    attr.set_value_date_quality(*value)
+                elif fmt == AttrDataFormat.SPECTRUM:
+                    if is_seq(value[0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+                else:
+                    if is_seq(value[0]) and is_seq(value[0][0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+            else:
+                attr.set_value(value)
+        else:
+            attr.set_value(value)
+
+
+def check_dev_klass_attr_read_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    read_method = getattr(attribute, "fget", None)
+    if read_method:
+        method_name = "__read_{0}__".format(attribute.attr_name)
+        attribute.read_method_name = method_name
+    else:
+        method_name = attribute.read_method_name
+        read_method = getattr(tango_device_klass, method_name)
+
+    read_args = inspect.getargspec(read_method)
+
+    if len(read_args.args) < 2:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, (self,))
+            else:
+                ret = read_method(self)
+            if not attr.get_value_flag() and ret is not None:
+                set_complex_value(attr, ret)
+            return ret
+    else:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, (self, attr))
+            else:            
+                ret = read_method(self, attr)
+            return ret
+        
+    method_name = "__read_{0}_wrapper__".format(attribute.attr_name)
+    attribute.read_method_name = method_name        
+
+    setattr(tango_device_klass, method_name, read_attr)
+
+
+def check_dev_klass_attr_write_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    write_method = getattr(attribute, "fset", None)
+    if write_method:
+        method_name = "__write_{0}__".format(attribute.attr_name)
+        attribute.write_method_name = method_name
+    else:
+        method_name = attribute.write_method_name
+        write_method = getattr(tango_device_klass, method_name)
+
+    @functools.wraps(write_method)
+    def write_attr(self, attr):
+        value = attr.get_write_value()
+        return write_method(self, value)
+    setattr(tango_device_klass, method_name, write_attr)
+
+
+def check_dev_klass_attr_methods(tango_device_klass, attribute):
+    """
+    Checks if the read and write methods have the correct signature.
+    If a read/write method doesn't have a parameter (the traditional
+    Attribute), then the method is wrapped into another method to make
+    this work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    if attribute.attr_write in (AttrWriteType.READ,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_read_method(tango_device_klass,
+                                         attribute)
+    if attribute.attr_write in (AttrWriteType.WRITE,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_write_method(tango_device_klass,
+                                          attribute)
+
+
+class _DeviceClass(DeviceClass):
+
+    def __init__(self, name):
+        DeviceClass.__init__(self, name)
+        self.set_type(name)
+
+    def dyn_attr(self, dev_list):
+        """Invoked to create dynamic attributes for the given devices.
+        Default implementation calls
+        :meth:`TT.initialize_dynamic_attributes` for each device
+
+        :param dev_list: list of devices
+        :type dev_list: :class:`PyTango.DeviceImpl`"""
+
+        for dev in dev_list:
+            init_dyn_attrs = getattr(dev,
+                                     "initialize_dynamic_attributes",
+                                     None)
+            if init_dyn_attrs and callable(init_dyn_attrs):
+                try:
+                    init_dyn_attrs()
+                except Exception:
+                    dev.warn_stream("Failed to initialize dynamic " \
+                                    "attributes")
+                    dev.debug_stream("Details: " + \
+                                     traceback.format_exc())
+
+
+def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
+    klass_name = tango_device_klass.__name__
+    if not issubclass(tango_device_klass, (Device)):
+        msg = "{0} device must inherit from " \
+              "PyTango.server.Device".format(klass_name)
+        raise Exception(msg)
+
+    if attrs is None:
+        attrs = tango_device_klass.__dict__
+
+    attr_list = {}
+    class_property_list = {}
+    device_property_list = {}
+    cmd_list = {}
+
+    for attr_name, attr_obj in attrs.items():
+        if isinstance(attr_obj, attribute):
+            if attr_obj.attr_name is None:
+                attr_obj._set_name(attr_name)
+            else:
+                attr_name = attr_obj.attr_name
+            attr_list[attr_name] = attr_obj
+            check_dev_klass_attr_methods(tango_device_klass, attr_obj)
+        elif isinstance(attr_obj, device_property):
+            device_property_list[attr_name] = [attr_obj.dtype,
+                                               attr_obj.doc,
+                                               attr_obj.default_value]
+        elif isinstance(attr_obj, class_property):
+            class_property_list[attr_name] = [attr_obj.dtype,
+                                              attr_obj.doc,
+                                              attr_obj.default_value]
+        elif inspect.isroutine(attr_obj):
+            if hasattr(attr_obj, "__tango_command__"):
+                cmd_name, cmd_info = attr_obj.__tango_command__
+                cmd_list[cmd_name] = cmd_info
+
+    devclass_name = klass_name + "Class"
+
+    devclass_attrs = dict(class_property_list=class_property_list,
+                          device_property_list=device_property_list,
+                          cmd_list=cmd_list, attr_list=attr_list)
+    return type(devclass_name, (_DeviceClass,), devclass_attrs)
+
+
+def init_tango_device_klass(tango_device_klass, attrs=None,
+                            tango_class_name=None):
+    klass_name = tango_device_klass.__name__
+    tango_deviceclass_klass = create_tango_deviceclass_klass(
+        tango_device_klass, attrs=attrs)
+    if tango_class_name is None:
+        if hasattr(tango_device_klass, "TangoClassName"):
+            tango_class_name = tango_device_klass.TangoClassName
+        else:
+            tango_class_name = klass_name
+    tango_device_klass.TangoClassClass = tango_deviceclass_klass
+    tango_device_klass.TangoClassName = tango_class_name
+    tango_device_klass._api = API_VERSION
+    return tango_device_klass
+
+
+def create_tango_device_klass(name, bases, attrs):
+    klass_name = name
+
+    LatestDeviceImplMeta = type(LatestDeviceImpl)
+    klass = LatestDeviceImplMeta(klass_name, bases, attrs)
+    init_tango_device_klass(klass, attrs)
+    return klass
+
+
+def DeviceMeta(name, bases, attrs):
+    """
+    The :py:data:`metaclass` callable for :class:`Device`.Every
+    sub-class of :class:`Device` must have associated this metaclass
+    to itself in order to work properly (boost-python internal
+    limitation).
+
+    Example (python 2.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+    Example (python 3.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device, metaclass=DeviceMeta):
+            pass
+    """
+    return create_tango_device_klass(name, bases, attrs)
+
+
+class Device(LatestDeviceImpl):
+    """
+    High level DeviceImpl API. All Device specific classes should
+    inherit from this class."""
+
+    def __init__(self, cl, name):
+        LatestDeviceImpl.__init__(self, cl, name)
+#        if _RUNNER:
+#            import signal
+#            self.register_signal(signal.SIGINT)
+        self.init_device()
+        
+#    def signal_handler(self, signo):
+#        _RUNNER.stop()
+
+
+    def init_device(self):
+        """
+        Tango init_device method. Default implementation calls
+        :meth:`get_device_properties`"""
+        self.get_device_properties()
+
+    def always_executed_hook(self):
+        """
+        Tango always_executed_hook. Default implementation does
+        nothing
+        """
+        pass
+
+    def initialize_dynamic_attributes(self):
+        """
+        Method executed at initializion phase to create dynamic
+        attributes. Default implementation does nothing. Overwrite
+        when necessary.
+        """
+        pass
+
+
+class attribute(AttrData):
+    '''
+    Declares a new tango attribute in a :class:`Device`. To be used
+    like the python native :obj:`property` function. For example, to
+    declare a scalar, `PyTango.DevDouble`, read-only attribute called
+    *voltage* in a *PowerSupply* :class:`Device` do::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            voltage = attribute()
+
+            def read_voltage(self):
+                return 999.999
+
+    The same can be achieved with::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @attribute
+            def voltage(self):
+                return 999.999
+
+
+    It receives multiple keyword arguments.
+
+    ===================== ================================ ======================================= =======================================================================================
+    parameter              type                                       default value                                 description
+    ===================== ================================ ======================================= =======================================================================================
+    name                   :obj:`str`                       class member name                       alternative attribute name
+    dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
+    dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
+    max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format)
+    max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
+    display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
+    polling_period         :obj:`int`                       -1                                      polling period
+    memorized              :obj:`bool`                      False                                   attribute should or not be memorized
+    hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
+    access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
+    fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
+    fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
+    is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
+    label                  :obj:`str`                       '<attr_name>'                           attribute label
+    doc (or description)   :obj:`str`                       ''                                      attribute description
+    unit                   :obj:`str`                       ''                                      physical units the attribute value is in
+    standard_unit          :obj:`str`                       ''                                      physical standard unit
+    display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
+    format                 :obj:`str`                       '6.2f'                                  attribute representation format
+    min_value              :obj:`str`                       None                                    minimum allowed value
+    max_value              :obj:`str`                       None                                    maximum allowed value
+    min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
+    max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
+    min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
+    max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
+    delta_val              :obj:`str`                       None
+    delta_t                :obj:`str`                       None
+    abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
+    rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
+    period                 :obj:`str`                       None
+    archive_abs_change     :obj:`str`                       None
+    archive_rel_change     :obj:`str`                       None
+    archive_period         :obj:`str`                       None
+    ===================== ================================ ======================================= =======================================================================================
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    Example of a integer writable attribute with a customized label,
+    unit and description::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            current = attribute(label="Current", unit="mA", dtype=int,
+                                access=AttrWriteType.READ_WRITE,
+                                doc="the power supply current")
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            def read_current(self):
+                return self._current
+
+            def write_current(self, current):
+                self._current = current
+
+    The same, but using attribute as a decorator::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            @attribute(label="Current", unit="mA", dtype=int)
+            def current(self):
+                """the power supply current"""
+                return 999.999
+
+            @current.write
+            def current(self, current):
+                self._current = current
+
+    In this second format, defining the `write` implies setting the
+    attribute access to READ_WRITE.
+    '''
+
+    def __init__(self, fget=None, **kwargs):
+        self._kwargs = dict(kwargs)
+        name = kwargs.pop("name", None)
+        class_name = kwargs.pop("class_name", None)
+
+        if fget:
+            if inspect.isroutine(fget):
+                self.fget = fget
+                if 'doc' not in kwargs and 'description' not in kwargs:
+                    kwargs['doc'] = fget.__doc__
+            else:
+                kwargs['fget'] = fget
+
+        super(attribute, self).__init__(name, class_name)
+        if 'dtype' in kwargs:
+            kwargs['dtype'], kwargs['dformat'] = \
+                get_tango_type_format(kwargs['dtype'],
+                                      kwargs.get('dformat'))
+        self.build_from_dict(kwargs)
+
+    def get_attribute(self, obj):
+        return obj.get_device_attr().get_attr_by_name(self.attr_name)
+
+    # --------------------
+    # descriptor interface
+    # --------------------
+
+    def __get__(self, obj, objtype):
+        return self.get_attribute(obj)
+
+    def __set__(self, obj, value):
+        attr = self.get_attribute(obj)
+        set_complex_value(attr, value)
+
+    def __delete__(self, obj):
+        obj.remove_attribute(self.attr_name)
+
+    def setter(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        self.fset = fset
+        if self.attr_write == AttrWriteType.READ:
+            if getattr(self, 'fget', None):
+                self.attr_write = AttrWriteType.READ_WRITE
+            else:
+                self.attr_write = AttrWriteType.WRITE
+        return self
+
+    def write(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        return self.setter(fset)
+
+    def __call__(self, fget):
+        return type(self)(fget=fget, **self._kwargs)
+
+
+def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
+            dtype_out=None, dformat_out=None, doc_out="",):
+    """
+    Declares a new tango command in a :class:`Device`.
+    To be used like a decorator in the methods you want to declare as
+    tango commands. The following example declares commands:
+
+        * `void TurnOn(void)`
+        * `void Ramp(DevDouble current)`
+        * `DevBool Pressurize(DevDouble pressure)`
+
+    ::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @command
+            def TurnOn(self):
+                self.info_stream('Turning on the power supply')
+
+            @command(dtype_in=float)
+            def Ramp(self, current):
+                self.info_stream('Ramping on %f...' % current)
+
+            @command(dtype_in=float, doc_in='the pressure to be set',
+                     dtype_out=bool, doc_out='True if it worked, False otherwise')
+            def Pressurize(self, pressure):
+                self.info_stream('Pressurizing to %f...' % pressure)
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    :param dtype_in:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of parameter. Default is None meaning no parameter.
+    :param dformat_in: parameter data format. Default is None.
+    :type dformat_in: AttrDataFormat
+    :param doc_in: parameter documentation
+    :type doc_in: str
+
+    :param dtype_out:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of return value. Default is None meaning no return value.
+    :param dformat_out: return value data format. Default is None.
+    :type dformat_out: AttrDataFormat
+    :param doc_out: return value documentation
+    :type doc_out: str
+    """
+    if f is None:
+        return functools.partial(command,
+            dtype_in=dtype_in, dformat_in=dformat_in, doc_in=doc_in,
+            dtype_out=dtype_out, dformat_out=dformat_out,
+            doc_out=doc_out)
+    name = f.__name__
+
+    dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
+    dtype_out, dformat_out = get_tango_type_format(dtype_out,
+                                                   dformat_out)
+
+    din = [from_typeformat_to_type(dtype_in, dformat_in), doc_in]
+    dout = [from_typeformat_to_type(dtype_out, dformat_out), doc_out]
+    f.__tango_command__ = name, [din, dout]
+    return f
+
+
+class _property(object):
+
+    def __init__(self, dtype, doc='', default_value=None):
+        self.__value = None
+        dtype = from_typeformat_to_type(*get_tango_type_format(dtype))
+        self.dtype = dtype
+        self.doc = doc
+        self.default_value = default_value
+
+    def __get__(self, obj, objtype):
+        return self.__value
+
+    def __set__(self, obj, value):
+        self.__value = value
+
+    def __delete__(self, obj):
+        del self.__value
+
+
+class device_property(_property):
+    """
+    Declares a new tango device property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, device property called
+    *host* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import device_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            host = device_property(dtype=str)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+class class_property(_property):
+    """
+    Declares a new tango class property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, class property called
+    *port* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import class_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            port = class_property(dtype=int, default_value=9788)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+
+def __to_cb(post_init_callback):
+    if post_init_callback is None:
+        return lambda : None
+
+    err_msg = "post_init_callback must be a callable or " \
+              "sequence <callable [, args, [, kwargs]]>"
+    if operator.isCallable(post_init_callback):
+        f = post_init_callback
+    elif is_non_str_seq(post_init_callback):
+        length = len(post_init_callback)
+        if length < 1 or length > 3:
+            raise TypeError(err_msg)
+        cb = post_init_callback[0]
+        if not operator.isCallable(cb):
+            raise TypeError(err_msg)
+        args, kwargs = [], {}
+        if length > 1:
+            args = post_init_callback[1]
+        if length > 2:
+            kwargs = post_init_callback[2]
+        f = functools.partial(cb, *args, **kwargs)
+    else:
+        raise TypeError(err_msg)
+
+    return f
+
+
+def __register_signals(util):
+    import signal
+    signals = signal.SIGINT, signal.SIGQUIT, signal.SIGTERM
+
+    # first device will be the lucky winner of handling SIGINT
+    d = util.get_device_list("*")[0]
+    old_signal_handler = d.signal_handler
+    def signal_handler(signo):
+        if signo in signals:
+            _RUNNER.stop()
+        return old_signal_handler(signo)
+    
+    d.signal_handler = signal_handler
+    for sig in signals:
+        d.register_signal(sig)
+    print ("Registered signals")    
+
+
+def __add_classes(util, classes):
+    if is_seq(classes):
+        for klass_info in classes:
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+    else:
+        for klass_name, klass_info in classes.items():
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+
+
+def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
+                 event_loop=None, post_init_callback=None,
+                 single_threaded=False):
+
+    global _RUNNER
+    if single_threaded:
+        _RUNNER = _Runner()
+        
+    import PyTango
+    if msg_stream is None:
+        write = lambda msg: None
+    else:
+        write = msg_stream.write
+
+    if args is None:
+        args = sys.argv
+
+    post_init_callback = __to_cb(post_init_callback)
+
+    if util is None:
+        util = PyTango.Util(args)
+    u_instance = PyTango.Util.instance()
+
+    __add_classes(util, classes)
+
+    if event_loop is not None:
+        u_instance.server_set_event_loop(event_loop)
+
+    def tango_loop(register_signals=False):
+        u_instance.server_init()
+        if register_signals:
+            __register_signals(u_instance)
+        post_init_callback()
+        write("Ready to accept request\n")
+        u_instance.server_run()
+        
+    if single_threaded:
+        tango_thread = threading.Thread(target=tango_loop,
+                                        name="PyTangoTh",
+                                        kwargs=dict(register_signals=True))
+        tango_thread.start()
+        _RUNNER.run_loop()
+        tango_thread.join()
+    else:
+        tango_loop()
+        
+    return util
+
+def run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Provides a simple way to run a tango server. It handles exceptions
+    by writting a message to the msg_stream.
+
+    The `classes` parameter can be either a sequence of:
+
+    * :class:`~PyTango.server.Device` or
+    * a sequence of two elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl` or
+    * a sequence of three elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+      tango class name (str)
+
+    or a dictionary where:
+
+    * key is the tango class name
+    * value is either:
+        * a :class:`~PyTango.server.Device` class or
+        * a sequence of two elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
+          or
+        * a sequence of three elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+          tango class name (str)
+
+    The optional `post_init_callback` can be a callable (without
+    arguments) or a tuple where the first element is the callable,
+    the second is a list of arguments (optional) and the third is a
+    dictionary of keyword arguments (also optional).
+
+    .. note::
+       the order of registration of tango classes defines the order
+       tango uses to initialize the corresponding devices.
+       if using a dictionary as argument for classes be aware that the
+       order of registration becomes arbitrary. If you need a
+       predefined order use a sequence or an OrderedDict.
+
+    Example 1: registering and running a PowerSupply inheriting from
+    :class:`~PyTango.server.Device`::
+
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        run((PowerSupply,))
+
+    Example 2: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import run
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run({'MyServer': (MyServerClass, MyServer)})
+
+    Example 3: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run([PowerSupply, [MyServerClass, MyServer]])
+        # or: run({'MyServer': (MyServerClass, MyServer)})
+
+    :param classes:
+        a sequence of :class:`~PyTango.server.Device` classes or
+        a dictionary where keyword is the tango class name and value
+        is a sequence of Tango Device Class python class, and Tango
+        Device python class
+    :type classes: sequence or dict
+
+    :param args:
+        list of command line arguments [default: None, meaning use
+        sys.argv]
+    :type args: list
+
+    :param msg_stream:
+        stream where to put messages [default: sys.stdout]
+
+    :param util:
+        PyTango Util object [default: None meaning create a Util
+        instance]
+    :type util: :class:`~PyTango.Util`
+
+    :param event_loop: event_loop callable
+    :type event_loop: callable
+
+    :param post_init_callback:
+        an optional callback that is executed between the calls
+        Util.server_init and Util.server_run
+    :type post_init_callback:
+        callable or tuple (see description above)
+
+    :return: The Util singleton object
+    :rtype: :class:`~PyTango.Util`
+
+    .. versionadded:: 8.1.2
+
+    .. versionchanged:: 8.1.4
+        when classes argument is a sequence, the items can also be
+        a sequence <TangoClass, TangoClassClass>[, tango class name]
+    """
+    if msg_stream is None:
+        write = lambda msg : None
+    else:
+        write = msg_stream.write
+    try:
+        return __server_run(classes, args=args, msg_stream=msg_stream,
+                            util=util, event_loop=event_loop,
+                            post_init_callback=post_init_callback,
+                            single_threaded=single_threaded)
+    except KeyboardInterrupt:
+        write("Exiting: Keyboard interrupt\n")
+    except DevFailed as df:
+        write("Exiting: Server exited with PyTango.DevFailed:\n" + \
+              str(df) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    except Exception as e:
+        write("Exiting: Server exited with unforseen exception:\n" + \
+              str(e) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    write("\nExited\n")
+
+def server_run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Since PyTango 8.1.2 it is just an alias to
+    :func:`~PyTango.server.run`. Use :func:`~PyTango.server.run`
+    instead.
+
+    .. versionadded:: 8.0.0
+
+    .. versionchanged:: 8.0.3
+        Added `util` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.1
+        Changed default msg_stream from *stderr* to *stdout*
+        Added `event_loop` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.2
+        Added `post_init_callback` keyword parameter
+
+    .. deprecated:: 8.1.2
+        Use :func:`~PyTango.server.run` instead.
+
+    """
+    return run(classes, args=args, msg_stream=msg_stream,
+               verbose=verbose, util=util, event_loop=event_loop,
+               post_init_callback=post_init_callback,
+               single_threaded=single_threaded)
+
+
+def _run_protect(f, *args, **kwargs):
+    if f is None:
+        return
+    try:
+        return f(*args, **kwargs)
+    except:
+        log = logging.getLogger("PyTango")
+        log.exception("Failed to execute %s", f.__name__)
+
+
+import Queue
+import threading
+
+class _Runner:
+
+    def __init__(self, max_queue_size=0):
+        self.tasks = Queue.Queue(max_queue_size)
+
+    def run_loop(self):
+        while True:
+            self.run_step()
+            
+    def run_step(self):
+        try:
+            task = self.tasks.get()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+        
+    def _exec(self, task):
+        f, args, kwargs, on_finished, on_error = task
+        try:
+            result = f(*args, **kwargs)
+            _run_protect(on_finished, result)
+        except:
+            _run_protect(on_error, *sys.exc_info)
+
+    def step(self):
+        try:
+            task = self.tasks.get_nowait()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+    
+    def execute_asynch(self, f, args=(), kwargs=None,
+                            on_finished=None, on_error=None):
+        if kwargs is None:
+            kwargs = {}
+        self.tasks.put((f, args, kwargs, on_finished, on_error))
+
+    def execute_synch(self, f, args=(), kwargs=None):
+        event = threading.Event()
+        event._err = None
+        def cb(result):
+            event._result = result
+            event.set()
+        def err_cb(*args):
+            event._err = args
+            event.set()
+        self.execute_asynch(f, args, kwargs, cb, err_cb)
+        event.wait()
+        if event._err:
+            Except.throw_python_exception(*event._err)
+        return event._result
+
+    def stop(self, wait=False):
+        if wait:
+            raise NotImplementedError
+        else:
+            self.tasks.put(None)
diff --git a/src/boost/python/server.py.single_threaded.v3 b/src/boost/python/server.py.single_threaded.v3
new file mode 100644
index 0000000..8ba397d
--- /dev/null
+++ b/src/boost/python/server.py.single_threaded.v3
@@ -0,0 +1,1124 @@
+# ------------------------------------------------------------------------------
+# This file is part of PyTango (http://www.tinyurl.com/PyTango)
+#
+# Copyright 2006-2012 CELLS / ALBA Synchrotron, Bellaterra, Spain
+# Copyright 2013-2014 European Synchrotron Radiation Facility, Grenoble, France
+#
+# Distributed under the terms of the GNU Lesser General Public License,
+# either version 3 of the License, or (at your option) any later version.
+# See LICENSE.txt for more info.
+# ------------------------------------------------------------------------------
+
+"""Server helper classes for writing Tango device servers."""
+
+from __future__ import with_statement
+from __future__ import print_function
+from __future__ import absolute_import
+
+__all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute",
+           "command", "device_property", "class_property",
+           "run", "server_run"]
+
+import sys
+import inspect
+import logging
+import operator
+import functools
+import traceback
+
+from ._PyTango import CmdArgType, AttrDataFormat, AttrWriteType
+from ._PyTango import DevFailed, Except, constants
+from .attr_data import AttrData
+from .device_class import DeviceClass
+from .utils import get_tango_device_classes, is_seq, is_non_str_seq
+from .utils import scalar_to_array_type
+
+API_VERSION = 2
+
+LatestDeviceImpl = get_tango_device_classes()[-1]
+
+_RUNNER = None
+
+def __build_to_tango_type():
+    ret = \
+    {
+        int         : CmdArgType.DevLong,
+        str         : CmdArgType.DevString,
+        bool        : CmdArgType.DevBoolean,
+        bytearray   : CmdArgType.DevEncoded,
+        float       : CmdArgType.DevDouble,
+        chr         : CmdArgType.DevUChar,
+        None        : CmdArgType.DevVoid,
+
+        'int'       : CmdArgType.DevLong,
+        'int16'     : CmdArgType.DevShort,
+        'int32'     : CmdArgType.DevLong,
+        'int64'     : CmdArgType.DevLong64,
+        'uint'      : CmdArgType.DevULong,
+        'uint16'    : CmdArgType.DevUShort,
+        'uint32'    : CmdArgType.DevULong,
+        'uint64'    : CmdArgType.DevULong64,
+        'str'       : CmdArgType.DevString,
+        'string'    : CmdArgType.DevString,
+        'text'      : CmdArgType.DevString,
+        'bool'      : CmdArgType.DevBoolean,
+        'boolean'   : CmdArgType.DevBoolean,
+        'bytes'     : CmdArgType.DevEncoded,
+        'bytearray' : CmdArgType.DevEncoded,
+        'float'     : CmdArgType.DevDouble,
+        'float32'   : CmdArgType.DevFloat,
+        'float64'   : CmdArgType.DevDouble,
+        'double'    : CmdArgType.DevDouble,
+        'byte'      : CmdArgType.DevUChar,
+        'chr'       : CmdArgType.DevUChar,
+        'char'      : CmdArgType.DevUChar,
+        'None'      : CmdArgType.DevVoid,
+        'state'     : CmdArgType.DevState,
+    }
+
+    for key in dir(CmdArgType):
+        if key.startswith("Dev"):
+            value = getattr(CmdArgType, key)
+            ret[key] = ret[value] = value
+
+    if constants.NUMPY_SUPPORT:
+        import numpy
+        FROM_TANGO_TO_NUMPY_TYPE = { \
+                   CmdArgType.DevBoolean : numpy.bool8,
+                     CmdArgType.DevUChar : numpy.ubyte,
+                     CmdArgType.DevShort : numpy.short,
+                    CmdArgType.DevUShort : numpy.ushort,
+                      CmdArgType.DevLong : numpy.int32,
+                     CmdArgType.DevULong : numpy.uint32,
+                    CmdArgType.DevLong64 : numpy.int64,
+                   CmdArgType.DevULong64 : numpy.uint64,
+                    CmdArgType.DevString : numpy.str,
+                    CmdArgType.DevDouble : numpy.float64,
+                     CmdArgType.DevFloat : numpy.float32,
+        }
+
+        for key, value in FROM_TANGO_TO_NUMPY_TYPE.items():
+            ret[value] = key
+    return ret
+
+TO_TANGO_TYPE = __build_to_tango_type()
+
+
+def get_tango_type_format(dtype=None, dformat=None):
+    if dformat is None:
+        dformat = AttrDataFormat.SCALAR
+        if is_non_str_seq(dtype):
+            dtype = dtype[0]
+            dformat = AttrDataFormat.SPECTRUM
+            if is_non_str_seq(dtype):
+                dtype = dtype[0]
+                dformat = AttrDataFormat.IMAGE
+    return TO_TANGO_TYPE[dtype], dformat
+
+
+def from_typeformat_to_type(dtype, dformat):
+    if dformat == AttrDataFormat.SCALAR:
+        return dtype
+    elif dformat == AttrDataFormat.IMAGE:
+        raise TypeError("Cannot translate IMAGE to tango type")
+    return scalar_to_array_type(dtype)
+
+
+def set_complex_value(attr, value):
+    is_tuple = isinstance(value, tuple)
+    dtype, fmt = attr.get_data_type(), attr.get_data_format()
+    if dtype == CmdArgType.DevEncoded:
+        if is_tuple and len(value) == 4:
+            attr.set_value_date_quality(*value)
+        elif is_tuple and len(value) == 3 and is_non_str_seq(value[0]):
+            attr.set_value_date_quality(value[0][0],
+                                        value[0][1],
+                                        *value[1:])
+        else:
+            attr.set_value(*value)
+    else:
+        if is_tuple:
+            if len(value) == 3:
+                if fmt == AttrDataFormat.SCALAR:
+                    attr.set_value_date_quality(*value)
+                elif fmt == AttrDataFormat.SPECTRUM:
+                    if is_seq(value[0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+                else:
+                    if is_seq(value[0]) and is_seq(value[0][0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+            else:
+                attr.set_value(value)
+        else:
+            attr.set_value(value)
+
+
+def check_dev_klass_attr_read_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    read_method = getattr(attribute, "fget", None)
+    if read_method:
+        method_name = "__read_{0}__".format(attribute.attr_name)
+        attribute.read_method_name = method_name
+    else:
+        method_name = attribute.read_method_name
+        read_method = getattr(tango_device_klass, method_name)
+
+    read_args = inspect.getargspec(read_method)
+
+    if len(read_args.args) < 2:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, (self,))
+            else:
+                ret = read_method(self)
+            if not attr.get_value_flag() and ret is not None:
+                set_complex_value(attr, ret)
+            return ret
+    else:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, (self, attr))
+            else:            
+                ret = read_method(self, attr)
+            return ret
+        
+    method_name = "__read_{0}_wrapper__".format(attribute.attr_name)
+    attribute.read_method_name = method_name        
+
+    setattr(tango_device_klass, method_name, read_attr)
+
+
+def check_dev_klass_attr_write_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    write_method = getattr(attribute, "fset", None)
+    if write_method:
+        method_name = "__write_{0}__".format(attribute.attr_name)
+        attribute.write_method_name = method_name
+    else:
+        method_name = attribute.write_method_name
+        write_method = getattr(tango_device_klass, method_name)
+
+    @functools.wraps(write_method)
+    def write_attr(self, attr):
+        value = attr.get_write_value()
+        if _RUNNER:
+            ret = _RUNNER.execute_synch(write_method, (self, value))
+        else:
+            ret = write_method(self, value)
+        return ret
+    setattr(tango_device_klass, method_name, write_attr)
+
+
+def check_dev_klass_attr_methods(tango_device_klass, attribute):
+    """
+    Checks if the read and write methods have the correct signature.
+    If a read/write method doesn't have a parameter (the traditional
+    Attribute), then the method is wrapped into another method to make
+    this work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    if attribute.attr_write in (AttrWriteType.READ,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_read_method(tango_device_klass,
+                                         attribute)
+    if attribute.attr_write in (AttrWriteType.WRITE,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_write_method(tango_device_klass,
+                                          attribute)
+
+
+class _DeviceClass(DeviceClass):
+
+    def __init__(self, name):
+        DeviceClass.__init__(self, name)
+        self.set_type(name)
+
+    def dyn_attr(self, dev_list):
+        """Invoked to create dynamic attributes for the given devices.
+        Default implementation calls
+        :meth:`Device.initialize_dynamic_attributes` for each device
+
+        :param dev_list: list of devices
+        :type dev_list: :class:`PyTango.DeviceImpl`"""
+
+        for dev in dev_list:
+            init_dyn_attrs = getattr(dev,
+                                     "initialize_dynamic_attributes",
+                                     None)
+            if init_dyn_attrs and callable(init_dyn_attrs):
+                try:
+                    init_dyn_attrs()
+                except Exception:
+                    dev.warn_stream("Failed to initialize dynamic " \
+                                    "attributes")
+                    dev.debug_stream("Details: " + \
+                                     traceback.format_exc())
+
+
+def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
+    klass_name = tango_device_klass.__name__
+    if not issubclass(tango_device_klass, (Device)):
+        msg = "{0} device must inherit from " \
+              "PyTango.server.Device".format(klass_name)
+        raise Exception(msg)
+
+    if attrs is None:
+        attrs = tango_device_klass.__dict__
+
+    attr_list = {}
+    class_property_list = {}
+    device_property_list = {}
+    cmd_list = {}
+
+    for attr_name, attr_obj in attrs.items():
+        if isinstance(attr_obj, attribute):
+            if attr_obj.attr_name is None:
+                attr_obj._set_name(attr_name)
+            else:
+                attr_name = attr_obj.attr_name
+            attr_list[attr_name] = attr_obj
+            check_dev_klass_attr_methods(tango_device_klass, attr_obj)
+        elif isinstance(attr_obj, device_property):
+            device_property_list[attr_name] = [attr_obj.dtype,
+                                               attr_obj.doc,
+                                               attr_obj.default_value]
+        elif isinstance(attr_obj, class_property):
+            class_property_list[attr_name] = [attr_obj.dtype,
+                                              attr_obj.doc,
+                                              attr_obj.default_value]
+        elif inspect.isroutine(attr_obj):
+            if hasattr(attr_obj, "__tango_command__"):
+                cmd_name, cmd_info = attr_obj.__tango_command__
+                cmd_list[cmd_name] = cmd_info
+
+    devclass_name = klass_name + "Class"
+
+    devclass_attrs = dict(class_property_list=class_property_list,
+                          device_property_list=device_property_list,
+                          cmd_list=cmd_list, attr_list=attr_list)
+    return type(devclass_name, (_DeviceClass,), devclass_attrs)
+
+
+def init_tango_device_klass(tango_device_klass, attrs=None,
+                            tango_class_name=None):
+    klass_name = tango_device_klass.__name__
+    tango_deviceclass_klass = create_tango_deviceclass_klass(
+        tango_device_klass, attrs=attrs)
+    if tango_class_name is None:
+        if hasattr(tango_device_klass, "TangoClassName"):
+            tango_class_name = tango_device_klass.TangoClassName
+        else:
+            tango_class_name = klass_name
+    tango_device_klass.TangoClassClass = tango_deviceclass_klass
+    tango_device_klass.TangoClassName = tango_class_name
+    tango_device_klass._api = API_VERSION
+    return tango_device_klass
+
+
+def create_tango_device_klass(name, bases, attrs):
+    klass_name = name
+
+    LatestDeviceImplMeta = type(LatestDeviceImpl)
+    klass = LatestDeviceImplMeta(klass_name, bases, attrs)
+    init_tango_device_klass(klass, attrs)
+    return klass
+
+
+def DeviceMeta(name, bases, attrs):
+    """
+    The :py:data:`metaclass` callable for :class:`Device`.Every
+    sub-class of :class:`Device` must have associated this metaclass
+    to itself in order to work properly (boost-python internal
+    limitation).
+
+    Example (python 2.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+    Example (python 3.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device, metaclass=DeviceMeta):
+            pass
+    """
+    return create_tango_device_klass(name, bases, attrs)
+
+
+class Device(LatestDeviceImpl):
+    """
+    High level DeviceImpl API. All Device specific classes should
+    inherit from this class."""
+
+    def __init__(self, cl, name):
+        LatestDeviceImpl.__init__(self, cl, name)
+        self.init_device()
+        
+    def init_device(self):
+        """
+        Tango init_device method. Default implementation calls
+        :meth:`get_device_properties`"""
+        self.get_device_properties()
+
+    def always_executed_hook(self):
+        """
+        Tango always_executed_hook. Default implementation does
+        nothing
+        """
+        pass
+
+    def initialize_dynamic_attributes(self):
+        """
+        Method executed at initializion phase to create dynamic
+        attributes. Default implementation does nothing. Overwrite
+        when necessary.
+        """
+        pass
+
+
+class attribute(AttrData):
+    '''
+    Declares a new tango attribute in a :class:`Device`. To be used
+    like the python native :obj:`property` function. For example, to
+    declare a scalar, `PyTango.DevDouble`, read-only attribute called
+    *voltage* in a *PowerSupply* :class:`Device` do::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            voltage = attribute()
+
+            def read_voltage(self):
+                return 999.999
+
+    The same can be achieved with::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @attribute
+            def voltage(self):
+                return 999.999
+
+
+    It receives multiple keyword arguments.
+
+    ===================== ================================ ======================================= =======================================================================================
+    parameter              type                                       default value                                 description
+    ===================== ================================ ======================================= =======================================================================================
+    name                   :obj:`str`                       class member name                       alternative attribute name
+    dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
+    dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
+    max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format)
+    max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
+    display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
+    polling_period         :obj:`int`                       -1                                      polling period
+    memorized              :obj:`bool`                      False                                   attribute should or not be memorized
+    hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
+    access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
+    fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
+    fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
+    is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
+    label                  :obj:`str`                       '<attr_name>'                           attribute label
+    doc (or description)   :obj:`str`                       ''                                      attribute description
+    unit                   :obj:`str`                       ''                                      physical units the attribute value is in
+    standard_unit          :obj:`str`                       ''                                      physical standard unit
+    display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
+    format                 :obj:`str`                       '6.2f'                                  attribute representation format
+    min_value              :obj:`str`                       None                                    minimum allowed value
+    max_value              :obj:`str`                       None                                    maximum allowed value
+    min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
+    max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
+    min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
+    max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
+    delta_val              :obj:`str`                       None
+    delta_t                :obj:`str`                       None
+    abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
+    rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
+    period                 :obj:`str`                       None
+    archive_abs_change     :obj:`str`                       None
+    archive_rel_change     :obj:`str`                       None
+    archive_period         :obj:`str`                       None
+    ===================== ================================ ======================================= =======================================================================================
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    Example of a integer writable attribute with a customized label,
+    unit and description::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            current = attribute(label="Current", unit="mA", dtype=int,
+                                access=AttrWriteType.READ_WRITE,
+                                doc="the power supply current")
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            def read_current(self):
+                return self._current
+
+            def write_current(self, current):
+                self._current = current
+
+    The same, but using attribute as a decorator::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            @attribute(label="Current", unit="mA", dtype=int)
+            def current(self):
+                """the power supply current"""
+                return 999.999
+
+            @current.write
+            def current(self, current):
+                self._current = current
+
+    In this second format, defining the `write` implies setting the
+    attribute access to READ_WRITE.
+    '''
+
+    def __init__(self, fget=None, **kwargs):
+        self._kwargs = dict(kwargs)
+        name = kwargs.pop("name", None)
+        class_name = kwargs.pop("class_name", None)
+
+        if fget:
+            if inspect.isroutine(fget):
+                self.fget = fget
+                if 'doc' not in kwargs and 'description' not in kwargs:
+                    kwargs['doc'] = fget.__doc__
+            else:
+                kwargs['fget'] = fget
+
+        super(attribute, self).__init__(name, class_name)
+        if 'dtype' in kwargs:
+            kwargs['dtype'], kwargs['dformat'] = \
+                get_tango_type_format(kwargs['dtype'],
+                                      kwargs.get('dformat'))
+        self.build_from_dict(kwargs)
+
+    def get_attribute(self, obj):
+        return obj.get_device_attr().get_attr_by_name(self.attr_name)
+
+    # --------------------
+    # descriptor interface
+    # --------------------
+
+    def __get__(self, obj, objtype):
+        return self.get_attribute(obj)
+
+    def __set__(self, obj, value):
+        attr = self.get_attribute(obj)
+        set_complex_value(attr, value)
+
+    def __delete__(self, obj):
+        obj.remove_attribute(self.attr_name)
+
+    def setter(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        self.fset = fset
+        if self.attr_write == AttrWriteType.READ:
+            if getattr(self, 'fget', None):
+                self.attr_write = AttrWriteType.READ_WRITE
+            else:
+                self.attr_write = AttrWriteType.WRITE
+        return self
+
+    def write(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        return self.setter(fset)
+
+    def __call__(self, fget):
+        return type(self)(fget=fget, **self._kwargs)
+
+
+def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
+            dtype_out=None, dformat_out=None, doc_out="",):
+    """
+    Declares a new tango command in a :class:`Device`.
+    To be used like a decorator in the methods you want to declare as
+    tango commands. The following example declares commands:
+
+        * `void TurnOn(void)`
+        * `void Ramp(DevDouble current)`
+        * `DevBool Pressurize(DevDouble pressure)`
+
+    ::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @command
+            def TurnOn(self):
+                self.info_stream('Turning on the power supply')
+
+            @command(dtype_in=float)
+            def Ramp(self, current):
+                self.info_stream('Ramping on %f...' % current)
+
+            @command(dtype_in=float, doc_in='the pressure to be set',
+                     dtype_out=bool, doc_out='True if it worked, False otherwise')
+            def Pressurize(self, pressure):
+                self.info_stream('Pressurizing to %f...' % pressure)
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    :param dtype_in:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of parameter. Default is None meaning no parameter.
+    :param dformat_in: parameter data format. Default is None.
+    :type dformat_in: AttrDataFormat
+    :param doc_in: parameter documentation
+    :type doc_in: str
+
+    :param dtype_out:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of return value. Default is None meaning no return value.
+    :param dformat_out: return value data format. Default is None.
+    :type dformat_out: AttrDataFormat
+    :param doc_out: return value documentation
+    :type doc_out: str
+    """
+    if f is None:
+        return functools.partial(command,
+            dtype_in=dtype_in, dformat_in=dformat_in, doc_in=doc_in,
+            dtype_out=dtype_out, dformat_out=dformat_out,
+            doc_out=doc_out)
+    name = f.__name__
+
+    dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
+    dtype_out, dformat_out = get_tango_type_format(dtype_out,
+                                                   dformat_out)
+
+    din = [from_typeformat_to_type(dtype_in, dformat_in), doc_in]
+    dout = [from_typeformat_to_type(dtype_out, dformat_out), doc_out]
+
+    @functools.wraps(f)
+    def cmd(self, value):
+        if _RUNNER:
+            ret = _RUNNER.execute_synch(f, (self, value))
+        else:
+            ret = f(self, value)
+        return ret
+    cmd.__tango_command__ = name, [din, dout]
+    return cmd
+
+
+class _property(object):
+
+    def __init__(self, dtype, doc='', default_value=None):
+        self.__value = None
+        dtype = from_typeformat_to_type(*get_tango_type_format(dtype))
+        self.dtype = dtype
+        self.doc = doc
+        self.default_value = default_value
+
+    def __get__(self, obj, objtype):
+        return self.__value
+
+    def __set__(self, obj, value):
+        self.__value = value
+
+    def __delete__(self, obj):
+        del self.__value
+
+
+class device_property(_property):
+    """
+    Declares a new tango device property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, device property called
+    *host* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import device_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            host = device_property(dtype=str)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+class class_property(_property):
+    """
+    Declares a new tango class property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, class property called
+    *port* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import class_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            port = class_property(dtype=int, default_value=9788)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+
+def __to_cb(post_init_callback):
+    if post_init_callback is None:
+        return lambda : None
+
+    err_msg = "post_init_callback must be a callable or " \
+              "sequence <callable [, args, [, kwargs]]>"
+    if operator.isCallable(post_init_callback):
+        f = post_init_callback
+    elif is_non_str_seq(post_init_callback):
+        length = len(post_init_callback)
+        if length < 1 or length > 3:
+            raise TypeError(err_msg)
+        cb = post_init_callback[0]
+        if not operator.isCallable(cb):
+            raise TypeError(err_msg)
+        args, kwargs = [], {}
+        if length > 1:
+            args = post_init_callback[1]
+        if length > 2:
+            kwargs = post_init_callback[2]
+        f = functools.partial(cb, *args, **kwargs)
+    else:
+        raise TypeError(err_msg)
+
+    return f
+
+
+def __register_signals(util):
+    import signal
+    signals = signal.SIGINT, signal.SIGQUIT, signal.SIGTERM
+
+    # first device class will be the lucky winner of handling signals
+    d = util.get_class_list()[0]
+    old_signal_handler = d.signal_handler
+    def signal_handler(signo):
+        if signo in signals:
+            _RUNNER.stop()
+        return old_signal_handler(signo)
+    
+    d.signal_handler = signal_handler
+    for sig in signals:
+        d.register_signal(sig)
+
+
+def __add_classes(util, classes):
+    if is_seq(classes):
+        for klass_info in classes:
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+    else:
+        for klass_name, klass_info in classes.items():
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+
+
+def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
+                 event_loop=None, post_init_callback=None,
+                 single_threaded=False):
+
+    global _RUNNER
+    if single_threaded:
+        _RUNNER = _Runner()
+        
+    import PyTango
+    if msg_stream is None:
+        write = lambda msg: None
+    else:
+        write = msg_stream.write
+
+    if args is None:
+        args = sys.argv
+
+    post_init_callback = __to_cb(post_init_callback)
+
+    if util is None:
+        util = PyTango.Util(args)
+    u_instance = PyTango.Util.instance()
+
+    __add_classes(util, classes)
+
+    if single_threaded:
+        def single_threaded_event_loop():
+            if event_loop:
+                should_exit = _RUNNER.step_nowait()
+                if not should_exit:
+                    event_loop()
+            else:
+                should_exit = _RUNNER.step()
+        u_instance.server_set_event_loop(single_threaded_event_loop)
+    else:
+        if event_loop is not None:
+            u_instance.server_set_event_loop(event_loop)
+
+    u_instance.server_init()
+    if single_threaded:
+        __register_signals(u_instance)
+        
+    post_init_callback()
+    write("Ready to accept request\n")
+    u_instance.server_run()
+    return util
+
+def run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Provides a simple way to run a tango server. It handles exceptions
+    by writting a message to the msg_stream.
+
+    The `classes` parameter can be either a sequence of:
+
+    * :class:`~PyTango.server.Device` or
+    * a sequence of two elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl` or
+    * a sequence of three elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+      tango class name (str)
+
+    or a dictionary where:
+
+    * key is the tango class name
+    * value is either:
+        * a :class:`~PyTango.server.Device` class or
+        * a sequence of two elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
+          or
+        * a sequence of three elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+          tango class name (str)
+
+    The optional `post_init_callback` can be a callable (without
+    arguments) or a tuple where the first element is the callable,
+    the second is a list of arguments (optional) and the third is a
+    dictionary of keyword arguments (also optional).
+
+    .. note::
+       the order of registration of tango classes defines the order
+       tango uses to initialize the corresponding devices.
+       if using a dictionary as argument for classes be aware that the
+       order of registration becomes arbitrary. If you need a
+       predefined order use a sequence or an OrderedDict.
+
+    Example 1: registering and running a PowerSupply inheriting from
+    :class:`~PyTango.server.Device`::
+
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        run((PowerSupply,))
+
+    Example 2: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import run
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run({'MyServer': (MyServerClass, MyServer)})
+
+    Example 3: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run([PowerSupply, [MyServerClass, MyServer]])
+        # or: run({'MyServer': (MyServerClass, MyServer)})
+
+    :param classes:
+        a sequence of :class:`~PyTango.server.Device` classes or
+        a dictionary where keyword is the tango class name and value
+        is a sequence of Tango Device Class python class, and Tango
+        Device python class
+    :type classes: sequence or dict
+
+    :param args:
+        list of command line arguments [default: None, meaning use
+        sys.argv]
+    :type args: list
+
+    :param msg_stream:
+        stream where to put messages [default: sys.stdout]
+
+    :param util:
+        PyTango Util object [default: None meaning create a Util
+        instance]
+    :type util: :class:`~PyTango.Util`
+
+    :param event_loop: event_loop callable
+    :type event_loop: callable
+
+    :param post_init_callback:
+        an optional callback that is executed between the calls
+        Util.server_init and Util.server_run
+    :type post_init_callback:
+        callable or tuple (see description above)
+
+    :return: The Util singleton object
+    :rtype: :class:`~PyTango.Util`
+
+    .. versionadded:: 8.1.2
+
+    .. versionchanged:: 8.1.4
+        when classes argument is a sequence, the items can also be
+        a sequence <TangoClass, TangoClassClass>[, tango class name]
+    """
+    if msg_stream is None:
+        write = lambda msg : None
+    else:
+        write = msg_stream.write
+    try:
+        return __server_run(classes, args=args, msg_stream=msg_stream,
+                            util=util, event_loop=event_loop,
+                            post_init_callback=post_init_callback,
+                            single_threaded=single_threaded)
+    except KeyboardInterrupt:
+        write("Exiting: Keyboard interrupt\n")
+    except DevFailed as df:
+        write("Exiting: Server exited with PyTango.DevFailed:\n" + \
+              str(df) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    except Exception as e:
+        write("Exiting: Server exited with unforseen exception:\n" + \
+              str(e) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    write("\nExited\n")
+
+def server_run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Since PyTango 8.1.2 it is just an alias to
+    :func:`~PyTango.server.run`. Use :func:`~PyTango.server.run`
+    instead.
+
+    .. versionadded:: 8.0.0
+
+    .. versionchanged:: 8.0.3
+        Added `util` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.1
+        Changed default msg_stream from *stderr* to *stdout*
+        Added `event_loop` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.2
+        Added `post_init_callback` keyword parameter
+
+    .. deprecated:: 8.1.2
+        Use :func:`~PyTango.server.run` instead.
+
+    """
+    return run(classes, args=args, msg_stream=msg_stream,
+               verbose=verbose, util=util, event_loop=event_loop,
+               post_init_callback=post_init_callback,
+               single_threaded=single_threaded)
+
+
+def _run_protect(f, *args, **kwargs):
+    if f is None:
+        return
+    try:
+        return f(*args, **kwargs)
+    except:
+        log = logging.getLogger("PyTango")
+        log.exception("Failed to execute %s", f.__name__)
+
+
+import Queue
+import threading
+
+class _BaseRunner:
+
+    def __init__(self, max_queue_size=0):
+        self.tasks = Queue.Queue(max_queue_size)
+
+    def run_loop(self):
+        while True:
+            self.step()
+            
+    def step(self):
+        try:
+            task = self.tasks.get()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+        
+    def _exec(self, task):
+        f, args, kwargs, on_finished, on_error = task
+        try:
+            result = f(*args, **kwargs)
+            _run_protect(on_finished, result)
+        except:
+            _run_protect(on_error, *sys.exc_info)
+            
+    def step_nowait(self):
+        try:
+            task = self.tasks.get_nowait()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+    
+    def execute_asynch(self, f, args=(), kwargs=None,
+                            on_finished=None, on_error=None):
+        if kwargs is None:
+            kwargs = {}
+        self.tasks.put((f, args, kwargs, on_finished, on_error))
+
+    def execute_synch(self, f, args=(), kwargs=None):
+        event = threading.Event()
+        event._err = None
+        def cb(result):
+            event._result = result
+            event.set()
+        def err_cb(*args):
+            event._err = args
+            event.set()
+        self.execute_asynch(f, args, kwargs, cb, err_cb)
+        event.wait()
+        if event._err:
+            Except.throw_python_exception(*event._err)
+        return event._result
+
+    def stop(self, wait=False):
+        if wait:
+            raise NotImplementedError
+        else:
+            self.tasks.put(None)
+
+
+
+import gevent.queue
+import gevent.event
+
+class _GRunner(_BaseRunner):
+
+    def _exec(self, task):
+        f, args, kwargs, on_finished, on_error = task
+        greenlet = gevent.spawn(f, *args, **kwargs)
+        greenlet.join()
+        if greenlet.successful():
+            _run_protect(on_finished, greenlet.value)
+        else:
+            e = greenlet.exception
+            _run_protect(on_error, e.__class__, e, None)
+            
+        greenlet.link_value(on_finished)
+        greenlet.link_exception(on_error)
+        return greenlet
+    
+
+_Runner = _GRunner
diff --git a/src/boost/python/server.py.single_threaded.v4 b/src/boost/python/server.py.single_threaded.v4
new file mode 100644
index 0000000..838899c
--- /dev/null
+++ b/src/boost/python/server.py.single_threaded.v4
@@ -0,0 +1,1102 @@
+# ------------------------------------------------------------------------------
+# This file is part of PyTango (http://www.tinyurl.com/PyTango)
+#
+# Copyright 2006-2012 CELLS / ALBA Synchrotron, Bellaterra, Spain
+# Copyright 2013-2014 European Synchrotron Radiation Facility, Grenoble, France
+#
+# Distributed under the terms of the GNU Lesser General Public License,
+# either version 3 of the License, or (at your option) any later version.
+# See LICENSE.txt for more info.
+# ------------------------------------------------------------------------------
+
+"""Server helper classes for writing Tango device servers."""
+
+from __future__ import with_statement
+from __future__ import print_function
+from __future__ import absolute_import
+
+__all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute",
+           "command", "device_property", "class_property",
+           "run", "server_run"]
+
+import sys
+import inspect
+import logging
+import operator
+import functools
+import traceback
+
+from ._PyTango import CmdArgType, AttrDataFormat, AttrWriteType
+from ._PyTango import DevFailed, Except, constants
+from .attr_data import AttrData
+from .device_class import DeviceClass
+from .utils import get_tango_device_classes, is_seq, is_non_str_seq
+from .utils import scalar_to_array_type
+
+API_VERSION = 2
+
+LatestDeviceImpl = get_tango_device_classes()[-1]
+
+_RUNNER = None
+
+def __build_to_tango_type():
+    ret = \
+    {
+        int         : CmdArgType.DevLong,
+        str         : CmdArgType.DevString,
+        bool        : CmdArgType.DevBoolean,
+        bytearray   : CmdArgType.DevEncoded,
+        float       : CmdArgType.DevDouble,
+        chr         : CmdArgType.DevUChar,
+        None        : CmdArgType.DevVoid,
+
+        'int'       : CmdArgType.DevLong,
+        'int16'     : CmdArgType.DevShort,
+        'int32'     : CmdArgType.DevLong,
+        'int64'     : CmdArgType.DevLong64,
+        'uint'      : CmdArgType.DevULong,
+        'uint16'    : CmdArgType.DevUShort,
+        'uint32'    : CmdArgType.DevULong,
+        'uint64'    : CmdArgType.DevULong64,
+        'str'       : CmdArgType.DevString,
+        'string'    : CmdArgType.DevString,
+        'text'      : CmdArgType.DevString,
+        'bool'      : CmdArgType.DevBoolean,
+        'boolean'   : CmdArgType.DevBoolean,
+        'bytes'     : CmdArgType.DevEncoded,
+        'bytearray' : CmdArgType.DevEncoded,
+        'float'     : CmdArgType.DevDouble,
+        'float32'   : CmdArgType.DevFloat,
+        'float64'   : CmdArgType.DevDouble,
+        'double'    : CmdArgType.DevDouble,
+        'byte'      : CmdArgType.DevUChar,
+        'chr'       : CmdArgType.DevUChar,
+        'char'      : CmdArgType.DevUChar,
+        'None'      : CmdArgType.DevVoid,
+        'state'     : CmdArgType.DevState,
+    }
+
+    for key in dir(CmdArgType):
+        if key.startswith("Dev"):
+            value = getattr(CmdArgType, key)
+            ret[key] = ret[value] = value
+
+    if constants.NUMPY_SUPPORT:
+        import numpy
+        FROM_TANGO_TO_NUMPY_TYPE = { \
+                   CmdArgType.DevBoolean : numpy.bool8,
+                     CmdArgType.DevUChar : numpy.ubyte,
+                     CmdArgType.DevShort : numpy.short,
+                    CmdArgType.DevUShort : numpy.ushort,
+                      CmdArgType.DevLong : numpy.int32,
+                     CmdArgType.DevULong : numpy.uint32,
+                    CmdArgType.DevLong64 : numpy.int64,
+                   CmdArgType.DevULong64 : numpy.uint64,
+                    CmdArgType.DevString : numpy.str,
+                    CmdArgType.DevDouble : numpy.float64,
+                     CmdArgType.DevFloat : numpy.float32,
+        }
+
+        for key, value in FROM_TANGO_TO_NUMPY_TYPE.items():
+            ret[value] = key
+    return ret
+
+TO_TANGO_TYPE = __build_to_tango_type()
+
+
+def get_tango_type_format(dtype=None, dformat=None):
+    if dformat is None:
+        dformat = AttrDataFormat.SCALAR
+        if is_non_str_seq(dtype):
+            dtype = dtype[0]
+            dformat = AttrDataFormat.SPECTRUM
+            if is_non_str_seq(dtype):
+                dtype = dtype[0]
+                dformat = AttrDataFormat.IMAGE
+    return TO_TANGO_TYPE[dtype], dformat
+
+
+def from_typeformat_to_type(dtype, dformat):
+    if dformat == AttrDataFormat.SCALAR:
+        return dtype
+    elif dformat == AttrDataFormat.IMAGE:
+        raise TypeError("Cannot translate IMAGE to tango type")
+    return scalar_to_array_type(dtype)
+
+
+def set_complex_value(attr, value):
+    is_tuple = isinstance(value, tuple)
+    dtype, fmt = attr.get_data_type(), attr.get_data_format()
+    if dtype == CmdArgType.DevEncoded:
+        if is_tuple and len(value) == 4:
+            attr.set_value_date_quality(*value)
+        elif is_tuple and len(value) == 3 and is_non_str_seq(value[0]):
+            attr.set_value_date_quality(value[0][0],
+                                        value[0][1],
+                                        *value[1:])
+        else:
+            attr.set_value(*value)
+    else:
+        if is_tuple:
+            if len(value) == 3:
+                if fmt == AttrDataFormat.SCALAR:
+                    attr.set_value_date_quality(*value)
+                elif fmt == AttrDataFormat.SPECTRUM:
+                    if is_seq(value[0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+                else:
+                    if is_seq(value[0]) and is_seq(value[0][0]):
+                        attr.set_value_date_quality(*value)
+                    else:
+                        attr.set_value(value)
+            else:
+                attr.set_value(value)
+        else:
+            attr.set_value(value)
+
+
+def check_dev_klass_attr_read_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    read_method = getattr(attribute, "fget", None)
+    if read_method:
+        method_name = "__read_{0}__".format(attribute.attr_name)
+        attribute.read_method_name = method_name
+    else:
+        method_name = attribute.read_method_name
+        read_method = getattr(tango_device_klass, method_name)
+
+    read_args = inspect.getargspec(read_method)
+
+    if len(read_args.args) < 2:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, self)
+            else:
+                ret = read_method(self)
+            if not attr.get_value_flag() and ret is not None:
+                set_complex_value(attr, ret)
+            return ret
+    else:
+        @functools.wraps(read_method)
+        def read_attr(self, attr):
+            if _RUNNER:
+                ret = _RUNNER.execute_synch(read_method, self, attr)
+            else:            
+                ret = read_method(self, attr)
+            return ret
+        
+    method_name = "__read_{0}_wrapper__".format(attribute.attr_name)
+    attribute.read_method_name = method_name        
+
+    setattr(tango_device_klass, method_name, read_attr)
+
+
+def check_dev_klass_attr_write_method(tango_device_klass, attribute):
+    """
+    Checks if method given by it's name for the given DeviceImpl
+    class has the correct signature. If a read/write method doesn't
+    have a parameter (the traditional Attribute), then the method is
+    wrapped into another method which has correct parameter definition
+    to make it work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    write_method = getattr(attribute, "fset", None)
+    if write_method:
+        method_name = "__write_{0}__".format(attribute.attr_name)
+        attribute.write_method_name = method_name
+    else:
+        method_name = attribute.write_method_name
+        write_method = getattr(tango_device_klass, method_name)
+
+    @functools.wraps(write_method)
+    def write_attr(self, attr):
+        value = attr.get_write_value()
+        return write_method(self, value)
+    setattr(tango_device_klass, method_name, write_attr)
+
+
+def check_dev_klass_attr_methods(tango_device_klass, attribute):
+    """
+    Checks if the read and write methods have the correct signature.
+    If a read/write method doesn't have a parameter (the traditional
+    Attribute), then the method is wrapped into another method to make
+    this work.
+
+    :param tango_device_klass: a DeviceImpl class
+    :type tango_device_klass: class
+    :param attribute: the attribute data information
+    :type attribute: AttrData
+    """
+    if attribute.attr_write in (AttrWriteType.READ,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_read_method(tango_device_klass,
+                                         attribute)
+    if attribute.attr_write in (AttrWriteType.WRITE,
+                                AttrWriteType.READ_WRITE):
+        check_dev_klass_attr_write_method(tango_device_klass,
+                                          attribute)
+
+
+class _DeviceClass(DeviceClass):
+
+    def __init__(self, name):
+        DeviceClass.__init__(self, name)
+        self.set_type(name)
+
+    def dyn_attr(self, dev_list):
+        """Invoked to create dynamic attributes for the given devices.
+        Default implementation calls
+        :meth:`TT.initialize_dynamic_attributes` for each device
+
+        :param dev_list: list of devices
+        :type dev_list: :class:`PyTango.DeviceImpl`"""
+
+        for dev in dev_list:
+            init_dyn_attrs = getattr(dev,
+                                     "initialize_dynamic_attributes",
+                                     None)
+            if init_dyn_attrs and callable(init_dyn_attrs):
+                try:
+                    init_dyn_attrs()
+                except Exception:
+                    dev.warn_stream("Failed to initialize dynamic " \
+                                    "attributes")
+                    dev.debug_stream("Details: " + \
+                                     traceback.format_exc())
+
+
+def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
+    klass_name = tango_device_klass.__name__
+    if not issubclass(tango_device_klass, (Device)):
+        msg = "{0} device must inherit from " \
+              "PyTango.server.Device".format(klass_name)
+        raise Exception(msg)
+
+    if attrs is None:
+        attrs = tango_device_klass.__dict__
+
+    attr_list = {}
+    class_property_list = {}
+    device_property_list = {}
+    cmd_list = {}
+
+    for attr_name, attr_obj in attrs.items():
+        if isinstance(attr_obj, attribute):
+            if attr_obj.attr_name is None:
+                attr_obj._set_name(attr_name)
+            else:
+                attr_name = attr_obj.attr_name
+            attr_list[attr_name] = attr_obj
+            check_dev_klass_attr_methods(tango_device_klass, attr_obj)
+        elif isinstance(attr_obj, device_property):
+            device_property_list[attr_name] = [attr_obj.dtype,
+                                               attr_obj.doc,
+                                               attr_obj.default_value]
+        elif isinstance(attr_obj, class_property):
+            class_property_list[attr_name] = [attr_obj.dtype,
+                                              attr_obj.doc,
+                                              attr_obj.default_value]
+        elif inspect.isroutine(attr_obj):
+            if hasattr(attr_obj, "__tango_command__"):
+                cmd_name, cmd_info = attr_obj.__tango_command__
+                cmd_list[cmd_name] = cmd_info
+
+    devclass_name = klass_name + "Class"
+
+    devclass_attrs = dict(class_property_list=class_property_list,
+                          device_property_list=device_property_list,
+                          cmd_list=cmd_list, attr_list=attr_list)
+    return type(devclass_name, (_DeviceClass,), devclass_attrs)
+
+
+def init_tango_device_klass(tango_device_klass, attrs=None,
+                            tango_class_name=None):
+    klass_name = tango_device_klass.__name__
+    tango_deviceclass_klass = create_tango_deviceclass_klass(
+        tango_device_klass, attrs=attrs)
+    if tango_class_name is None:
+        if hasattr(tango_device_klass, "TangoClassName"):
+            tango_class_name = tango_device_klass.TangoClassName
+        else:
+            tango_class_name = klass_name
+    tango_device_klass.TangoClassClass = tango_deviceclass_klass
+    tango_device_klass.TangoClassName = tango_class_name
+    tango_device_klass._api = API_VERSION
+    return tango_device_klass
+
+
+def create_tango_device_klass(name, bases, attrs):
+    klass_name = name
+
+    LatestDeviceImplMeta = type(LatestDeviceImpl)
+    klass = LatestDeviceImplMeta(klass_name, bases, attrs)
+    init_tango_device_klass(klass, attrs)
+    return klass
+
+
+def DeviceMeta(name, bases, attrs):
+    """
+    The :py:data:`metaclass` callable for :class:`Device`.Every
+    sub-class of :class:`Device` must have associated this metaclass
+    to itself in order to work properly (boost-python internal
+    limitation).
+
+    Example (python 2.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+    Example (python 3.x)::
+
+        from PyTango.server import Device, DeviceMeta
+
+        class PowerSupply(Device, metaclass=DeviceMeta):
+            pass
+    """
+    return create_tango_device_klass(name, bases, attrs)
+
+
+class Device(LatestDeviceImpl):
+    """
+    High level DeviceImpl API. All Device specific classes should
+    inherit from this class."""
+
+    def __init__(self, cl, name):
+        LatestDeviceImpl.__init__(self, cl, name)
+#        if _RUNNER:
+#            import signal
+#            self.register_signal(signal.SIGINT)
+        self.init_device()
+        
+#    def signal_handler(self, signo):
+#        _RUNNER.stop()
+
+
+    def init_device(self):
+        """
+        Tango init_device method. Default implementation calls
+        :meth:`get_device_properties`"""
+        self.get_device_properties()
+
+    def always_executed_hook(self):
+        """
+        Tango always_executed_hook. Default implementation does
+        nothing
+        """
+        pass
+
+    def initialize_dynamic_attributes(self):
+        """
+        Method executed at initializion phase to create dynamic
+        attributes. Default implementation does nothing. Overwrite
+        when necessary.
+        """
+        pass
+
+
+class attribute(AttrData):
+    '''
+    Declares a new tango attribute in a :class:`Device`. To be used
+    like the python native :obj:`property` function. For example, to
+    declare a scalar, `PyTango.DevDouble`, read-only attribute called
+    *voltage* in a *PowerSupply* :class:`Device` do::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            voltage = attribute()
+
+            def read_voltage(self):
+                return 999.999
+
+    The same can be achieved with::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @attribute
+            def voltage(self):
+                return 999.999
+
+
+    It receives multiple keyword arguments.
+
+    ===================== ================================ ======================================= =======================================================================================
+    parameter              type                                       default value                                 description
+    ===================== ================================ ======================================= =======================================================================================
+    name                   :obj:`str`                       class member name                       alternative attribute name
+    dtype                  :obj:`object`                    :obj:`~PyTango.CmdArgType.DevDouble`    data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
+    dformat                :obj:`~PyTango.AttrDataFormat`   :obj:`~PyTango.AttrDataFormat.SCALAR`   data format
+    max_dim_x              :obj:`int`                       1                                       maximum size for x dimension (ignored for SCALAR format)
+    max_dim_y              :obj:`int`                       0                                       maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
+    display_level          :obj:`~PyTango.DispLevel`        :obj:`~PyTango.DisLevel.OPERATOR`       display level
+    polling_period         :obj:`int`                       -1                                      polling period
+    memorized              :obj:`bool`                      False                                   attribute should or not be memorized
+    hw_memorized           :obj:`bool`                      False                                   write method should be called at startup when restoring memorize value (dangerous!)
+    access                 :obj:`~PyTango.AttrWriteType`    :obj:`~PyTango.AttrWriteType.READ`      read only/ read write / write only access
+    fget (or fread)        :obj:`str` or :obj:`callable`    'read_<attr_name>'                      read method name or method object
+    fset (or fwrite)       :obj:`str` or :obj:`callable`    'write_<attr_name>'                     write method name or method object
+    is_allowed             :obj:`str` or :obj:`callable`    'is_<attr_name>_allowed'                is allowed method name or method object
+    label                  :obj:`str`                       '<attr_name>'                           attribute label
+    doc (or description)   :obj:`str`                       ''                                      attribute description
+    unit                   :obj:`str`                       ''                                      physical units the attribute value is in
+    standard_unit          :obj:`str`                       ''                                      physical standard unit
+    display_unit           :obj:`str`                       ''                                      physical display unit (hint for clients)
+    format                 :obj:`str`                       '6.2f'                                  attribute representation format
+    min_value              :obj:`str`                       None                                    minimum allowed value
+    max_value              :obj:`str`                       None                                    maximum allowed value
+    min_alarm              :obj:`str`                       None                                    minimum value to trigger attribute alarm
+    max_alarm              :obj:`str`                       None                                    maximum value to trigger attribute alarm
+    min_warning            :obj:`str`                       None                                    minimum value to trigger attribute warning
+    max_warning            :obj:`str`                       None                                    maximum value to trigger attribute warning
+    delta_val              :obj:`str`                       None
+    delta_t                :obj:`str`                       None
+    abs_change             :obj:`str`                       None                                    minimum value change between events that causes event filter to send the event
+    rel_change             :obj:`str`                       None                                    minimum relative change between events that causes event filter to send the event (%)
+    period                 :obj:`str`                       None
+    archive_abs_change     :obj:`str`                       None
+    archive_rel_change     :obj:`str`                       None
+    archive_period         :obj:`str`                       None
+    ===================== ================================ ======================================= =======================================================================================
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    Example of a integer writable attribute with a customized label,
+    unit and description::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            current = attribute(label="Current", unit="mA", dtype=int,
+                                access=AttrWriteType.READ_WRITE,
+                                doc="the power supply current")
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            def read_current(self):
+                return self._current
+
+            def write_current(self, current):
+                self._current = current
+
+    The same, but using attribute as a decorator::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            def init_device(self):
+                Device.init_device(self)
+                self._current = -1
+
+            @attribute(label="Current", unit="mA", dtype=int)
+            def current(self):
+                """the power supply current"""
+                return 999.999
+
+            @current.write
+            def current(self, current):
+                self._current = current
+
+    In this second format, defining the `write` implies setting the
+    attribute access to READ_WRITE.
+    '''
+
+    def __init__(self, fget=None, **kwargs):
+        self._kwargs = dict(kwargs)
+        name = kwargs.pop("name", None)
+        class_name = kwargs.pop("class_name", None)
+
+        if fget:
+            if inspect.isroutine(fget):
+                self.fget = fget
+                if 'doc' not in kwargs and 'description' not in kwargs:
+                    kwargs['doc'] = fget.__doc__
+            else:
+                kwargs['fget'] = fget
+
+        super(attribute, self).__init__(name, class_name)
+        if 'dtype' in kwargs:
+            kwargs['dtype'], kwargs['dformat'] = \
+                get_tango_type_format(kwargs['dtype'],
+                                      kwargs.get('dformat'))
+        self.build_from_dict(kwargs)
+
+    def get_attribute(self, obj):
+        return obj.get_device_attr().get_attr_by_name(self.attr_name)
+
+    # --------------------
+    # descriptor interface
+    # --------------------
+
+    def __get__(self, obj, objtype):
+        return self.get_attribute(obj)
+
+    def __set__(self, obj, value):
+        attr = self.get_attribute(obj)
+        set_complex_value(attr, value)
+
+    def __delete__(self, obj):
+        obj.remove_attribute(self.attr_name)
+
+    def setter(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        self.fset = fset
+        if self.attr_write == AttrWriteType.READ:
+            if getattr(self, 'fget', None):
+                self.attr_write = AttrWriteType.READ_WRITE
+            else:
+                self.attr_write = AttrWriteType.WRITE
+        return self
+
+    def write(self, fset):
+        """
+        To be used as a decorator. Will define the decorated method
+        as a write attribute method to be called when client writes
+        the attribute
+        """
+        return self.setter(fset)
+
+    def __call__(self, fget):
+        return type(self)(fget=fget, **self._kwargs)
+
+
+def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
+            dtype_out=None, dformat_out=None, doc_out="",):
+    """
+    Declares a new tango command in a :class:`Device`.
+    To be used like a decorator in the methods you want to declare as
+    tango commands. The following example declares commands:
+
+        * `void TurnOn(void)`
+        * `void Ramp(DevDouble current)`
+        * `DevBool Pressurize(DevDouble pressure)`
+
+    ::
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            @command
+            def TurnOn(self):
+                self.info_stream('Turning on the power supply')
+
+            @command(dtype_in=float)
+            def Ramp(self, current):
+                self.info_stream('Ramping on %f...' % current)
+
+            @command(dtype_in=float, doc_in='the pressure to be set',
+                     dtype_out=bool, doc_out='True if it worked, False otherwise')
+            def Pressurize(self, pressure):
+                self.info_stream('Pressurizing to %f...' % pressure)
+
+    .. note::
+        avoid using *dformat* parameter. If you need a SPECTRUM
+        attribute of say, boolean type, use instead ``dtype=(bool,)``.
+
+    :param dtype_in:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of parameter. Default is None meaning no parameter.
+    :param dformat_in: parameter data format. Default is None.
+    :type dformat_in: AttrDataFormat
+    :param doc_in: parameter documentation
+    :type doc_in: str
+
+    :param dtype_out:
+        a :ref:`data type <pytango-hlapi-datatypes>` describing the
+        type of return value. Default is None meaning no return value.
+    :param dformat_out: return value data format. Default is None.
+    :type dformat_out: AttrDataFormat
+    :param doc_out: return value documentation
+    :type doc_out: str
+    """
+    if f is None:
+        return functools.partial(command,
+            dtype_in=dtype_in, dformat_in=dformat_in, doc_in=doc_in,
+            dtype_out=dtype_out, dformat_out=dformat_out,
+            doc_out=doc_out)
+    name = f.__name__
+
+    dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
+    dtype_out, dformat_out = get_tango_type_format(dtype_out,
+                                                   dformat_out)
+
+    din = [from_typeformat_to_type(dtype_in, dformat_in), doc_in]
+    dout = [from_typeformat_to_type(dtype_out, dformat_out), doc_out]
+    f.__tango_command__ = name, [din, dout]
+    return f
+
+
+class _property(object):
+
+    def __init__(self, dtype, doc='', default_value=None):
+        self.__value = None
+        dtype = from_typeformat_to_type(*get_tango_type_format(dtype))
+        self.dtype = dtype
+        self.doc = doc
+        self.default_value = default_value
+
+    def __get__(self, obj, objtype):
+        return self.__value
+
+    def __set__(self, obj, value):
+        self.__value = value
+
+    def __delete__(self, obj):
+        del self.__value
+
+
+class device_property(_property):
+    """
+    Declares a new tango device property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, device property called
+    *host* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import device_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            host = device_property(dtype=str)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+class class_property(_property):
+    """
+    Declares a new tango class property in a :class:`Device`. To be
+    used like the python native :obj:`property` function. For example,
+    to declare a scalar, `PyTango.DevString`, class property called
+    *port* in a *PowerSupply* :class:`Device` do::
+
+        from PyTango.server import Device, DeviceMeta
+        from PyTango.server import class_property
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+            port = class_property(dtype=int, default_value=9788)
+
+    :param dtype: Data type (see :ref:`pytango-data-types`)
+    :param doc: property documentation (optional)
+    :param default_value: default value for the property (optional)
+    """
+    pass
+
+
+def __to_cb(post_init_callback):
+    if post_init_callback is None:
+        return lambda : None
+
+    err_msg = "post_init_callback must be a callable or " \
+              "sequence <callable [, args, [, kwargs]]>"
+    if operator.isCallable(post_init_callback):
+        f = post_init_callback
+    elif is_non_str_seq(post_init_callback):
+        length = len(post_init_callback)
+        if length < 1 or length > 3:
+            raise TypeError(err_msg)
+        cb = post_init_callback[0]
+        if not operator.isCallable(cb):
+            raise TypeError(err_msg)
+        args, kwargs = [], {}
+        if length > 1:
+            args = post_init_callback[1]
+        if length > 2:
+            kwargs = post_init_callback[2]
+        f = functools.partial(cb, *args, **kwargs)
+    else:
+        raise TypeError(err_msg)
+
+    return f
+
+
+def __register_signals(util):
+    import signal
+    signals = signal.SIGINT, signal.SIGQUIT, signal.SIGTERM
+
+    # first device will be the lucky winner of handling SIGINT
+    d = util.get_class_list()[0]
+#    d = util.get_device_list("*")[0]
+    old_signal_handler = d.signal_handler
+    def signal_handler(signo):
+        if signo in signals:
+            print("signal!!!!")
+            _RUNNER.stop()
+        return old_signal_handler(signo)
+    
+    d.signal_handler = signal_handler
+    for sig in signals:
+        d.register_signal(sig)
+    print ("Registered signals")    
+
+
+def __add_classes(util, classes):
+    if is_seq(classes):
+        for klass_info in classes:
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+    else:
+        for klass_name, klass_info in classes.items():
+            if is_seq(klass_info):
+                if len(klass_info) == 2:
+                    klass_klass, klass = klass_info
+                else:
+                    klass_klass, klass, klass_name = klass_info
+            else:
+                if not hasattr(klass_info, '_api') or klass_info._api < 2:
+                    raise Exception(
+                        "When giving a single class, it must " \
+                        "implement HLAPI (see PyTango.server)")
+                klass_klass = klass_info.TangoClassClass
+                klass_name = klass_info.TangoClassName
+                klass = klass_info
+            util.add_class(klass_klass, klass, klass_name)
+
+
+def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
+                 event_loop=None, post_init_callback=None,
+                 single_threaded=False):
+
+    global _RUNNER
+    if single_threaded:
+        _RUNNER = _Runner()
+        
+    import PyTango
+    if msg_stream is None:
+        write = lambda msg: None
+    else:
+        write = msg_stream.write
+
+    if args is None:
+        args = sys.argv
+
+    post_init_callback = __to_cb(post_init_callback)
+
+    if util is None:
+        util = PyTango.Util(args)
+    u_instance = PyTango.Util.instance()
+
+    __add_classes(util, classes)
+
+    if event_loop is not None:
+        u_instance.server_set_event_loop(event_loop)
+
+    def tango_loop(register_signals=False):
+        u_instance.server_init()
+        if register_signals:
+            __register_signals(u_instance)
+        post_init_callback()
+        write("Ready to accept request\n")
+        u_instance.server_run()
+        write("Tango loop exit")
+        
+    if single_threaded:
+        tango_thread = threading.Thread(target=tango_loop,
+                                        name="PyTangoTh",
+                                        kwargs=dict(register_signals=True))
+        tango_thread.start()
+        _RUNNER.run_loop()
+        tango_thread.join()
+    else:
+        tango_loop()
+        
+    return util
+
+def run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Provides a simple way to run a tango server. It handles exceptions
+    by writting a message to the msg_stream.
+
+    The `classes` parameter can be either a sequence of:
+
+    * :class:`~PyTango.server.Device` or
+    * a sequence of two elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl` or
+    * a sequence of three elements
+      :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+      tango class name (str)
+
+    or a dictionary where:
+
+    * key is the tango class name
+    * value is either:
+        * a :class:`~PyTango.server.Device` class or
+        * a sequence of two elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
+          or
+        * a sequence of three elements
+          :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`,
+          tango class name (str)
+
+    The optional `post_init_callback` can be a callable (without
+    arguments) or a tuple where the first element is the callable,
+    the second is a list of arguments (optional) and the third is a
+    dictionary of keyword arguments (also optional).
+
+    .. note::
+       the order of registration of tango classes defines the order
+       tango uses to initialize the corresponding devices.
+       if using a dictionary as argument for classes be aware that the
+       order of registration becomes arbitrary. If you need a
+       predefined order use a sequence or an OrderedDict.
+
+    Example 1: registering and running a PowerSupply inheriting from
+    :class:`~PyTango.server.Device`::
+
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        run((PowerSupply,))
+
+    Example 2: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import run
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run({'MyServer': (MyServerClass, MyServer)})
+
+    Example 3: registering and running a MyServer defined by tango
+    classes `MyServerClass` and `MyServer`::
+
+        from PyTango import Device_4Impl, DeviceClass
+        from PyTango.server import Device, DeviceMeta, run
+
+        class PowerSupply(Device):
+            __metaclass__ = DeviceMeta
+
+        class MyServer(Device_4Impl):
+            pass
+
+        class MyServerClass(DeviceClass):
+            pass
+
+        run([PowerSupply, [MyServerClass, MyServer]])
+        # or: run({'MyServer': (MyServerClass, MyServer)})
+
+    :param classes:
+        a sequence of :class:`~PyTango.server.Device` classes or
+        a dictionary where keyword is the tango class name and value
+        is a sequence of Tango Device Class python class, and Tango
+        Device python class
+    :type classes: sequence or dict
+
+    :param args:
+        list of command line arguments [default: None, meaning use
+        sys.argv]
+    :type args: list
+
+    :param msg_stream:
+        stream where to put messages [default: sys.stdout]
+
+    :param util:
+        PyTango Util object [default: None meaning create a Util
+        instance]
+    :type util: :class:`~PyTango.Util`
+
+    :param event_loop: event_loop callable
+    :type event_loop: callable
+
+    :param post_init_callback:
+        an optional callback that is executed between the calls
+        Util.server_init and Util.server_run
+    :type post_init_callback:
+        callable or tuple (see description above)
+
+    :return: The Util singleton object
+    :rtype: :class:`~PyTango.Util`
+
+    .. versionadded:: 8.1.2
+
+    .. versionchanged:: 8.1.4
+        when classes argument is a sequence, the items can also be
+        a sequence <TangoClass, TangoClassClass>[, tango class name]
+    """
+    if msg_stream is None:
+        write = lambda msg : None
+    else:
+        write = msg_stream.write
+    try:
+        return __server_run(classes, args=args, msg_stream=msg_stream,
+                            util=util, event_loop=event_loop,
+                            post_init_callback=post_init_callback,
+                            single_threaded=single_threaded)
+    except KeyboardInterrupt:
+        write("Exiting: Keyboard interrupt\n")
+    except DevFailed as df:
+        write("Exiting: Server exited with PyTango.DevFailed:\n" + \
+              str(df) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    except Exception as e:
+        write("Exiting: Server exited with unforseen exception:\n" + \
+              str(e) + "\n")
+        if verbose:
+            write(traceback.format_exc())
+    write("\nExited\n")
+
+def server_run(classes, args=None, msg_stream=sys.stdout,
+        verbose=False, util=None, event_loop=None,
+        post_init_callback=None, single_threaded=False):
+    """
+    Since PyTango 8.1.2 it is just an alias to
+    :func:`~PyTango.server.run`. Use :func:`~PyTango.server.run`
+    instead.
+
+    .. versionadded:: 8.0.0
+
+    .. versionchanged:: 8.0.3
+        Added `util` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.1
+        Changed default msg_stream from *stderr* to *stdout*
+        Added `event_loop` keyword parameter.
+        Returns util object
+
+    .. versionchanged:: 8.1.2
+        Added `post_init_callback` keyword parameter
+
+    .. deprecated:: 8.1.2
+        Use :func:`~PyTango.server.run` instead.
+
+    """
+    return run(classes, args=args, msg_stream=msg_stream,
+               verbose=verbose, util=util, event_loop=event_loop,
+               post_init_callback=post_init_callback,
+               single_threaded=single_threaded)
+
+
+def _run_protect(f, *args, **kwargs):
+    if f is None:
+        return
+    try:
+        return f(*args, **kwargs)
+    except:
+        log = logging.getLogger("PyTango")
+        log.exception("Failed to execute %s", f.__name__)
+
+
+import Queue
+import threading
+
+import gevent
+
+class threadSafeRequest:
+
+    def __init__(self, queue, watcher):
+        self.queue = queue
+        self.watcher = watcher
+        self.done_event = threading.Event()
+        self.result = None
+
+    def __call__(self, *args, **kwargs):
+        self.queue.put((self, args, kwargs))
+        self.watcher.send()
+        self.done_event.wait()
+
+
+class _Runner:
+
+    def __init__(self, max_queue_size=0):
+        self.tasks = Queue.Queue(max_queue_size)
+        self.watcher = gevent.get_hub().loop.async()
+        self.watcher.start(self.run_step)
+        
+    def run_loop(self):
+        gevent.wait()
+            
+    def run_step(self):
+        try:
+            task = self.tasks.get()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        gevent.spawn(self._exec, task)
+        return True
+        
+    def _exec(self, task):
+        tsr, args, kwargs = task
+        f, args = args[0], args[1:]
+        try:
+            tsr.result = f(*args, **kwargs)
+        except:
+            tsr.exc = sys.exc_info()
+        tsr.done_event.set()
+        
+    def step(self):
+        try:
+            task = self.tasks.get_nowait()
+        except Queue.Empty:
+            return True
+        if task is None:
+            return False
+        self._exec(task)
+        return True
+    
+    def execute_synch(self, f, *args, **kwargs):
+        tsr = threadSafeRequest(self.tasks, self.watcher)
+        tsr(f, *args, **kwargs)
+        return tsr.result
+        
+    def stop(self, wait=False):
+        if wait:
+            raise NotImplementedError
+        else:
+            self.tasks.put(None)
diff --git a/src/boost/python/utils.py b/src/boost/python/utils.py
index ca72698..cc71a0a 100644
--- a/src/boost/python/utils.py
+++ b/src/boost/python/utils.py
@@ -16,15 +16,17 @@ This is an internal PyTango module.
 from __future__ import with_statement
 from __future__ import print_function
 
-__all__ = [ "is_pure_str", "is_seq", "is_non_str_seq", "is_integer",
-            "is_number", "is_scalar_type", "is_array_type", "is_numerical_type",
-            "is_int_type", "is_float_type", "is_bool_type", "is_bin_type",
-            "is_str_type", "obj_2_str", "seqStr_2_obj",
-            "scalar_to_array_type",
-            "document_method", "document_static_method", "document_enum",
-            "CaselessList", "CaselessDict", "EventCallBack", "get_home",
-            "from_version_str_to_hex_str", "from_version_str_to_int",
-            "seq_2_StdStringVector", "StdStringVector_2_seq" ]
+__all__ = [
+    "requires_pytango", "requires_tango",
+    "is_pure_str", "is_seq", "is_non_str_seq", "is_integer",
+    "is_number", "is_scalar_type", "is_array_type", "is_numerical_type",
+    "is_int_type", "is_float_type", "is_bool_type", "is_bin_type",
+    "is_str_type", "obj_2_str", "seqStr_2_obj",
+    "scalar_to_array_type",
+    "document_method", "document_static_method", "document_enum",
+    "CaselessList", "CaselessDict", "EventCallBack", "get_home",
+    "from_version_str_to_hex_str", "from_version_str_to_int",
+    "seq_2_StdStringVector", "StdStringVector_2_seq" ]
 
 __docformat__ = "restructuredtext"
 
@@ -37,7 +39,8 @@ from ._PyTango import StdStringVector, StdDoubleVector, \
     DbData, DbDevInfos, DbDevExportInfos, CmdArgType, AttrDataFormat, \
     EventData, AttrConfEventData, DataReadyEventData, DevFailed, constants, \
     GreenMode
-
+from .constants import AlrmValueNotSpec, StatusNotSet, TgLibVers
+from .release import Release
 
 _scalar_int_types = (CmdArgType.DevShort, CmdArgType.DevUShort,
     CmdArgType.DevInt, CmdArgType.DevLong, CmdArgType.DevULong,
@@ -88,8 +91,121 @@ _scalar_to_array_type = {
     CmdArgType.ConstDevString : CmdArgType.DevVarStringArray,
 }
 
+__NO_STR_VALUE = AlrmValueNotSpec, StatusNotSet
+
 __device_classes = None
 
+bool_ = lambda value_str : value_str.lower() == "true"
+
+
+def __import(name):
+    __import__(name)
+    return sys.modules[name]
+    
+def __requires(package_name, min_version=None, conflicts=(),
+               software_name="Software"):
+    from distutils.version import LooseVersion
+    package_name_l = package_name.lower()
+    if package_name_l == 'pytango':
+        curr_version = LooseVersion(Release.version)
+    elif package_name_l == 'tango':
+        curr_version = LooseVersion(TgLibVers)
+    else:
+        try:
+            package = __import(package_name)
+            curr_version = LooseVersion(package.__version__)
+        except ImportError:
+            msg = "Could not find package {0} required by {1}".format(
+                package_name, software_name)
+            raise Exception(msg)
+        except:
+            msg = "Error importing package {0} required by {1}".format(
+                package_name, software_name)
+            raise Exception(msg)
+        
+    if min_version is not None:
+        min_version = LooseVersion(min_version)
+        if min_version > curr_version:        
+            msg = "{0} requires {1} {2} but {3} installed".format(
+                software_name, package_name, min_version, curr_version)
+            raise Exception(msg)
+
+    conflicts = map(LooseVersion, conflicts)
+    if curr_version in conflicts:
+        msg = "{0} cannot run with {1} {2}".format(
+            software_name, package_name, curr_version)
+        raise Exception(msg)        
+    return True
+
+def requires_pytango(min_version=None, conflicts=(),
+                     software_name="Software"):
+    """
+    Determines if the required PyTango version for the running
+    software is present. If not an exception is thrown.
+    Example usage::
+
+        from PyTango import requires_pytango
+
+        requires_pytango('7.1', conflicts=['8.1.1'], software='MyDS')
+
+    :param min_version:
+        minimum PyTango version [default: None, meaning no minimum
+        required]. If a string is given, it must be in the valid
+        version number format
+        (see: :class:`~distutils.version.LooseVersion`)
+    :type min_version:
+        None, str, :class:`~distutils.version.LooseVersion`
+    :param conflics:
+        a sequence of PyTango versions which conflict with the
+        software using it
+    :type conflics:
+        seq<str|LooseVersion>
+    :param software_name:
+        software name using PyTango. Used in the exception message
+    :type software_name: str
+
+    :raises Exception: if the required PyTango version is not met
+
+    New in PyTango 8.1.4
+    """
+    return __requires("PyTango", min_version=min_version,
+                      conflicts=conflicts, software_name=software_name)
+
+
+def requires_tango(min_version=None, conflicts=(),
+                     software_name="Software"):
+    """
+    Determines if the required Tango version for the running
+    software is present. If not an exception is thrown.
+    Example usage::
+
+        from Tango import requires_tango
+
+        requires_tango('7.1', conflicts=['8.1.1'], software='MyDS')
+
+    :param min_version:
+        minimum Tango version [default: None, meaning no minimum
+        required]. If a string is given, it must be in the valid
+        version number format
+        (see: :class:`~distutils.version.LooseVersion`)
+    :type min_version:
+        None, str, :class:`~distutils.version.LooseVersion`
+    :param conflics:
+        a sequence of Tango versions which conflict with the
+        software using it
+    :type conflics:
+        seq<str|LooseVersion>
+    :param software_name:
+        software name using Tango. Used in the exception message
+    :type software_name: str
+
+    :raises Exception: if the required Tango version is not met
+
+    New in PyTango 8.1.4
+    """
+    return __requires("Tango", min_version=min_version,
+                      conflicts=conflicts, software_name=software_name)    
+
 
 def get_tango_device_classes():
     global __device_classes
@@ -106,7 +222,12 @@ def get_tango_device_classes():
                 break
     return __device_classes
 
-__str_klasses = str,
+try:
+    __str_klasses = basestring,
+except NameError:
+    __str_klasses = str,
+
+
 __int_klasses = int,
 __number_klasses = numbers.Number,
 __seq_klasses = collections.Sequence, bytearray
@@ -116,7 +237,7 @@ try:
     unicode
     __use_unicode = True
     __str_klasses = tuple(list(__str_klasses) + [unicode])
-except:
+except NameError:
     pass
 
 __use_long = False
@@ -124,7 +245,7 @@ try:
     long
     __use_long = True
     __int_klasses = tuple(list(__int_klasses) + [long])
-except:
+except NameError:
     pass
 
 if constants.NUMPY_SUPPORT:
@@ -140,22 +261,87 @@ __seq_klasses = tuple(__seq_klasses)
 
 
 def is_pure_str(obj):
+    """
+    Tells if the given object is a python string.
+
+    In python 2.x this means any subclass of basestring.
+    In python 3.x this means any subclass of str.
+
+    :param obj: the object to be inspected
+    :type obj: :py:obj:`object`
+
+    :return: True is the given obj is a string or False otherwise
+    :rtype: :py:obj:`bool`
+    """
     return isinstance(obj , __str_klasses)
 
 
 def is_seq(obj):
+    """
+    Tells if the given object is a python sequence.
+
+    It will return True for any collections.Sequence (list, tuple,
+    str, bytes, unicode), bytearray and (if numpy is enabled)
+    numpy.ndarray
+
+    :param obj: the object to be inspected
+    :type obj: :py:obj:`object`
+
+    :return: True is the given obj is a sequence or False otherwise
+    :rtype: :py:obj:`bool`
+    """
     return isinstance(obj, __seq_klasses)
 
 
 def is_non_str_seq(obj):
+    """
+    Tells if the given object is a python sequence (excluding string
+    sequences).
+
+    It will return True for any collections.Sequence (list, tuple (and
+    bytes in python3)), bytearray and (if numpy is enabled)
+    numpy.ndarray
+
+    :param obj: the object to be inspected
+    :type obj: :py:obj:`object`
+
+    :return: True is the given obj is a sequence or False otherwise
+    :rtype: :py:obj:`bool`
+    """    
     return is_seq(obj) and not is_pure_str(obj)
 
 
 def is_integer(obj):
+    """
+    Tells if the given object is a python integer.
+
+    It will return True for any int, long (in python 2) and
+    (if numpy is enabled) numpy.integer
+
+    :param obj: the object to be inspected
+    :type obj: :py:obj:`object`
+
+    :return:
+        True is the given obj is a python integer or False otherwise
+    :rtype: :py:obj:`bool`
+    """
     return isinstance(obj, __int_klasses)
 
 
 def is_number(obj):
+    """
+    Tells if the given object is a python number.
+
+    It will return True for any numbers.Number and (if numpy is
+    enabled) numpy.number
+
+    :param obj: the object to be inspected
+    :type obj: :py:obj:`object`
+
+    :return:
+        True is the given obj is a python number or False otherwise
+    :rtype: :py:obj:`bool`
+    """
     return isinstance(obj, __number_klasses)
 
 
@@ -560,11 +746,52 @@ def _seqStr_2_obj_from_type_format(seq, tg_type, tg_format):
     return _seqStr_2_obj_from_type(tg_type, seq)
 
 
-def scalar_to_array_type(dtype):
-    return _scalar_to_array_type[dtype]
+def scalar_to_array_type(tg_type):
+    """
+    Gives the array tango type corresponding to the given tango
+    scalar type. Example: giving DevLong will return DevVarLongArray.
+    
+    :param tg_type: tango type
+    :type tg_type: :class:`PyTango.CmdArgType`
+
+    :return: the array tango type for the given scalar tango type
+    :rtype: :class:`PyTango.CmdArgType`
+    
+    :raises ValueError: in case the given dtype is not a tango scalar type
+    """
+    try:
+        return _scalar_to_array_type[tg_type]
+    except KeyError:
+        raise ValueError("Invalid tango scalar type: {0}".format(tg_type))
 
 
-def obj_2_str(obj, tg_type):
+def str_2_obj(obj_str, tg_type=None):
+    """Converts a string into an object according to the given tango type
+    
+           :param obj_str: the string to be converted
+           :type obj_str: :py:obj:`str`
+           :param tg_type: tango type
+           :type tg_type: :class:`PyTango.CmdArgType`
+           :return: an object calculated from the given string
+           :rtype: :py:obj:`object`
+    """
+    if tg_type is None:
+        return obj_str
+    f = str
+    if is_scalar_type(tg_type):
+        if is_numerical_type(tg_type):
+            if obj_str in __NO_STR_VALUE:
+                return None
+        if is_int_type(tg_type):
+            f = int
+        elif is_float_type(tg_type):
+            f = float
+        elif is_bool_type(tg_type):
+            f = bool_
+    return f(obj_str)
+
+
+def obj_2_str(obj, tg_type=None):
     """Converts a python object into a string according to the given tango type
     
            :param obj: the object to be converted
@@ -574,6 +801,8 @@ def obj_2_str(obj, tg_type):
            :return: a string representation of the given object
            :rtype: :py:obj:`str`
     """
+    if tg_type is None:
+        return obj
     if tg_type in _scalar_types:
         # scalar cases
         if is_pure_str(obj):
@@ -586,6 +815,7 @@ def obj_2_str(obj, tg_type):
     # sequence cases
     return '\n'.join([str(i) for i in obj])
 
+
 def __get_meth_func(klass, method_name):
     meth = getattr(klass, method_name)
     func = meth

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pytango.git



More information about the debian-science-commits mailing list